From a354a80de41bb588754514eac83d965f8a387a57 Mon Sep 17 00:00:00 2001
From: Jonathan Kang <jonathan121537@gmail.com>
Date: Mon, 8 Aug 2016 14:34:05 +0800
Subject: [PATCH] revert upstream commits to bring back the updates plugin

---
 configure.ac                                       |   30 +
 data/Makefile.am                                   |    1 +
 ...gnome.settings-daemon.plugins.gschema.xml.in.in |    1 +
 ...ttings-daemon.plugins.updates.gschema.xml.in.in |   79 +
 plugins/Makefile.am                                |    6 +
 plugins/updates/Makefile.am                        |   93 ++
 plugins/updates/gsd-updates-common.h               |   44 +
 plugins/updates/gsd-updates-firmware.c             | 1040 ++++++++++++++
 plugins/updates/gsd-updates-firmware.h             |   52 +
 plugins/updates/gsd-updates-manager.c              | 1519 ++++++++++++++++++++
 plugins/updates/gsd-updates-manager.h              |   56 +
 plugins/updates/gsd-updates-plugin.c               |   28 +
 plugins/updates/gsd-updates-refresh.c              |  573 ++++++++
 plugins/updates/gsd-updates-refresh.h              |   52 +
 plugins/updates/test-updates.c                     |    7 +
 plugins/updates/updates-design.svg                 |  765 ++++++++++
 plugins/updates/updates.gnome-settings-plugin.in   |    9 +
 po/POTFILES.in                                     |    4 +
 po/POTFILES.skip                                   |    1 +
 19 files changed, 4360 insertions(+)
 create mode 100644 data/org.gnome.settings-daemon.plugins.updates.gschema.xml.in.in
 create mode 100644 plugins/updates/Makefile.am
 create mode 100644 plugins/updates/gsd-updates-common.h
 create mode 100644 plugins/updates/gsd-updates-firmware.c
 create mode 100644 plugins/updates/gsd-updates-firmware.h
 create mode 100644 plugins/updates/gsd-updates-manager.c
 create mode 100644 plugins/updates/gsd-updates-manager.h
 create mode 100644 plugins/updates/gsd-updates-plugin.c
 create mode 100644 plugins/updates/gsd-updates-refresh.c
 create mode 100644 plugins/updates/gsd-updates-refresh.h
 create mode 100644 plugins/updates/test-updates.c
 create mode 100644 plugins/updates/updates-design.svg
 create mode 100644 plugins/updates/updates.gnome-settings-plugin.in

Index: gnome-settings-daemon-3.20.1/configure.ac
===================================================================
--- gnome-settings-daemon-3.20.1.orig/configure.ac
+++ gnome-settings-daemon-3.20.1/configure.ac
@@ -297,6 +297,33 @@ esac
 AM_CONDITIONAL(HAVE_WACOM, test x$have_wacom = xyes)
 
 dnl ==============================================
+dnl PackageKit section
+dnl ==============================================
+
+have_packagekit=false
+AC_ARG_ENABLE(packagekit,
+  AC_HELP_STRING([--disable-packagekit],
+                 [turn off PackageKit support]),
+       [case "${enableval}" in
+               yes) WANT_PACKAGEKIT=yes ;;
+               no)  WANT_PACKAGEKIT=no ;;
+               *) AC_MSG_ERROR(bad value ${enableval} for --disable-packagekit) ;;
+       esac],
+       [WANT_PACKAGEKIT=yes]) dnl Default value
+
+if test x$WANT_PACKAGEKIT = xyes ; then
+       PK_REQUIRED_VERSION=0.8.1
+       PKG_CHECK_MODULES(PACKAGEKIT, glib-2.0 packagekit-glib2 >= $PK_REQUIRED_VERSION upower-glib >= $UPOWER_REQUIRED_VERSION gudev-1.0 libnotify >= $LIBNOTIFY_REQUIRED_VERSION,
+             [have_packagekit=true
+              AC_DEFINE(HAVE_PACKAGEKIT, 1, [Define if PackageKit should be used])],
+             [have_packagekit=false])
+fi
+AM_CONDITIONAL(HAVE_PACKAGEKIT, test "x$have_packagekit" = "xtrue")
+
+AC_SUBST(PACKAGEKIT_CFLAGS)
+AC_SUBST(PACKAGEKIT_LIBS)
+
+dnl ==============================================
 dnl smartcard section
 dnl ==============================================
 have_smartcard_support=false
@@ -542,6 +569,7 @@ plugins/screensaver-proxy/Makefile
 plugins/sharing/Makefile
 plugins/smartcard/Makefile
 plugins/sound/Makefile
+plugins/updates/Makefile
 plugins/wacom/Makefile
 plugins/xrandr/Makefile
 plugins/xsettings/Makefile
@@ -558,6 +586,7 @@ data/org.gnome.settings-daemon.plugins.m
 data/org.gnome.settings-daemon.peripherals.gschema.xml.in
 data/org.gnome.settings-daemon.plugins.housekeeping.gschema.xml.in
 data/org.gnome.settings-daemon.plugins.orientation.gschema.xml.in
+data/org.gnome.settings-daemon.plugins.updates.gschema.xml.in
 data/org.gnome.settings-daemon.plugins.sharing.gschema.xml.in
 data/org.gnome.settings-daemon.plugins.xrandr.gschema.xml.in
 data/org.gnome.settings-daemon.peripherals.wacom.gschema.xml.in
@@ -593,6 +622,7 @@ echo "
         LCMS DICT support:        ${have_new_lcms}
         NetworkManager support:   ${enable_network_manager}
         Libnotify support:        ${have_libnotify}
+        PackageKit support:       ${have_packagekit}
         Smartcard support:        ${have_smartcard_support}
         Cups support:             ${enable_cups}
         Wacom support:            ${have_wacom}
Index: gnome-settings-daemon-3.20.1/data/Makefile.am
===================================================================
--- gnome-settings-daemon-3.20.1.orig/data/Makefile.am
+++ gnome-settings-daemon-3.20.1/data/Makefile.am
@@ -19,6 +19,7 @@ gsettings_SCHEMAS =							\
 	org.gnome.settings-daemon.plugins.housekeeping.gschema.xml	\
 	org.gnome.settings-daemon.plugins.print-notifications.gschema.xml	\
 	org.gnome.settings-daemon.plugins.xrandr.gschema.xml		\
+	org.gnome.settings-daemon.plugins.updates.gschema.xml		\
 	org.gnome.settings-daemon.plugins.orientation.gschema.xml	\
 	org.gnome.settings-daemon.peripherals.wacom.gschema.xml
 
Index: gnome-settings-daemon-3.20.1/data/org.gnome.settings-daemon.plugins.updates.gschema.xml.in.in
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/data/org.gnome.settings-daemon.plugins.updates.gschema.xml.in.in
@@ -0,0 +1,79 @@
+<schemalist>
+  <schema gettext-domain="@GETTEXT_PACKAGE@" id="org.gnome.settings-daemon.plugins.updates" path="/org/gnome/settings-daemon/plugins/updates/">
+    <key name="active" type="b">
+      <default>true</default>
+      <_summary>Activation of this plugin</_summary>
+      <_description>Whether this plugin would be activated by gnome-settings-daemon or not</_description>
+    </key>
+    <key name="priority" type="i">
+      <default>0</default>
+      <_summary>Priority to use for this plugin</_summary>
+      <_description>Priority to use for this plugin in gnome-settings-daemon startup queue</_description>
+    </key>
+    <key name="connection-use-mobile" type="b">
+      <default>false</default>
+      <_summary>Use mobile broadband connections</_summary>
+      <_description>Use mobile broadband connections such as GSM and CDMA to check for updates.</_description>
+    </key>
+    <key name="auto-download-updates" type="b">
+      <default>true</default>
+      <_summary>Automatically download updates in the background without confirmation</_summary>
+      <_description>Automatically download updates in the background without confirmation. Updates will be auto-downloaded when using wired network connnections, and mobile broadband if 'connection-use-mobile' is enabled.</_description>
+    </key>
+    <key name="frequency-get-updates" type="i">
+      <default>86400</default>
+      <_summary>How often to check for updates</_summary>
+      <_description>How often to check for updates. Value is in seconds. This is a maximum amount of time that can pass between a security update being published, and the update being automatically installed or the user notified.</_description>
+    </key>
+    <key name="frequency-updates-notification" type="i">
+      <default>604800</default>
+      <_summary>How often to notify the user that non-critical updates are available</_summary>
+      <_description>How often to tell the user there are non-critical updates. Value is in seconds. Security update notifications are always shown after the check for updates, but non-critical notifications should be shown a lot less frequently.</_description>
+    </key>
+    <key name="last-updates-notification" type="t">
+      <default>0</default>
+      <_summary>The last time we told the user about non-critical notifications</_summary>
+      <_description>The last time we notified the user about non-critical updates. Value is in seconds since the epoch, or zero for never.</_description>
+    </key>
+    <key name="frequency-get-upgrades" type="i">
+      <default>604800</default>
+      <_summary>How often to check for distribution upgrades</_summary>
+      <_description>How often to check for distribution upgrades. Value is in seconds.</_description>
+    </key>
+    <key name="frequency-refresh-cache" type="i">
+      <default>86400</default>
+      <_summary>How often to refresh the package cache</_summary>
+      <_description>How often to refresh the package cache. Value is in seconds.</_description>
+    </key>
+    <key name="update-battery" type="b">
+      <default>false</default>
+      <_summary>Check for updates when running on battery power</_summary>
+      <_description>Check for updates when running on battery power.</_description>
+    </key>
+    <key name="notify-distro-upgrades" type="b">
+      <default>false</default>
+      <_summary>Notify the user when distribution upgrades are available</_summary>
+      <_description>Notify the user when distribution upgrades are available.</_description>
+    </key>
+    <key name="enable-check-firmware" type="b">
+      <default>true</default>
+      <_summary>Ask the user if additional firmware should be installed</_summary>
+      <_description>Ask the user if additional firmware should be installed if it is available.</_description>
+    </key>
+    <key name="banned-firmware" type="s">
+      <default>'*/intel-ucode/*'</default>
+      <_summary>Firmware files that should not be searched for</_summary>
+      <_description>Firmware files that should not be searched for, separated by commas. These can include '*' and '?' characters.</_description>
+    </key>
+    <key name="ignored-devices" type="s">
+      <default>''</default>
+      <_summary>Devices that should be ignored</_summary>
+      <_description>Devices that should be ignored, separated by commas. These can include '*' and '?' characters.</_description>
+    </key>
+    <key name="media-repo-filenames" type="s">
+      <default>'media.repo,.discinfo'</default>
+      <_summary>The filenames on removable media that designate it a software source.</_summary>
+      <_description>When removable media is inserted, it is checked to see if it contains any important filenames in the root directory. If the filename matches, then an updates check is performed. This allows post-install disks to be used to update running systems.</_description>
+    </key>
+  </schema>
+</schemalist>
Index: gnome-settings-daemon-3.20.1/plugins/Makefile.am
===================================================================
--- gnome-settings-daemon-3.20.1.orig/plugins/Makefile.am
+++ gnome-settings-daemon-3.20.1/plugins/Makefile.am
@@ -22,6 +22,12 @@ enabled_plugins =	\
 
 disabled_plugins = $(NULL)
 
+if HAVE_PACKAGEKIT
+enabled_plugins += updates
+else
+disabled_plugins += updates
+endif
+
 if SMARTCARD_SUPPORT
 enabled_plugins += smartcard
 else
Index: gnome-settings-daemon-3.20.1/plugins/updates/Makefile.am
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/Makefile.am
@@ -0,0 +1,102 @@
+plugin_name = updates
+
+plugin_LTLIBRARIES = \
+	libupdates.la
+
+libupdates_la_SOURCES = \
+	gsd-updates-common.h \
+	gsd-updates-plugin.c \
+	gsd-updates-refresh.h \
+	gsd-updates-refresh.c \
+	gsd-updates-firmware.h \
+	gsd-updates-firmware.c \
+	gsd-updates-manager.h \
+	gsd-updates-manager.c
+
+libupdates_la_CPPFLAGS = \
+	-I$(top_srcdir)/gnome-settings-daemon \
+	-I$(top_builddir)/gnome-settings-daemon		\
+	-DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \
+	$(AM_CPPFLAGS)
+
+libupdates_la_CFLAGS = \
+	$(PLUGIN_CFLAGS) \
+	$(SETTINGS_PLUGIN_CFLAGS) \
+	$(GUDEV_CFLAGS)	\
+	-DG_UDEV_API_IS_SUBJECT_TO_CHANGE \
+	$(PACKAGEKIT_CFLAGS) \
+	-DI_KNOW_THE_PACKAGEKIT_GLIB2_API_IS_SUBJECT_TO_CHANGE \
+	-DDATADIR=\"$(datadir)\" \
+	-DBINDIR=\"$(bindir)\" \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-I$(top_srcdir)/data \
+	$(AM_CFLAGS)
+
+libupdates_la_LDFLAGS = \
+	$(GSD_PLUGIN_LDFLAGS)
+
+libupdates_la_LIBADD = \
+	$(SETTINGS_PLUGIN_LIBS)	\
+	$(PACKAGEKIT_LIBS)
+
+plugin_in_files = \
+	updates.gnome-settings-plugin.in
+
+plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin)
+
+libexec_PROGRAMS = gsd-test-updates pk-clear-offline-update
+
+pk_clear_offline_update_SOURCES = pk-offline-clear-update.c \
+	gsd-updates-common.h
+
+pk_clear_offline_update_CFLAGS = $(SETTINGS_PLUGIN_CFLAGS) $(PLUGIN_CFLAGS)
+
+
+polkit_policydir = $(datadir)/polkit-1/actions
+polkit_policy_DATA = org.gnome.settings-daemon.plugins.update.policy
+
+gsd_test_updates_SOURCES =		\
+	test-updates.c			\
+	gsd-updates-common.h		\
+	gsd-updates-refresh.h		\
+	gsd-updates-refresh.c		\
+	gsd-updates-firmware.h		\
+	gsd-updates-firmware.c		\
+	gsd-updates-manager.h		\
+	gsd-updates-manager.c
+
+gsd_test_updates_CPPFLAGS =					\
+	-I$(top_srcdir)/data/					\
+	-I$(top_srcdir)/gnome-settings-daemon			\
+	-I$(top_builddir)/gnome-settings-daemon			\
+	-I$(top_srcdir)/plugins/common				\
+	-DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\"	\
+	-DLIBEXECDIR=\""$(libexecdir)"\"			\
+	$(AM_CPPFLAGS)
+
+gsd_test_updates_CFLAGS =			\
+	-DI_KNOW_THE_PACKAGEKIT_GLIB2_API_IS_SUBJECT_TO_CHANGE \
+	-DDATADIR=\"$(datadir)\"	\
+	-DBINDIR=\"$(bindir)\"		\
+	$(PLUGIN_CFLAGS)		\
+	$(SETTINGS_PLUGIN_CFLAGS)	\
+	$(PACKAGEKIT_CFLAGS)		\
+	$(AM_CFLAGS)
+
+gsd_test_updates_LDADD =						\
+	$(top_builddir)/gnome-settings-daemon/libgsd.la		\
+	$(SETTINGS_DAEMON_LIBS)			\
+	$(SETTINGS_PLUGIN_LIBS)			\
+	$(PACKAGEKIT_LIBS)			\
+	-lm
+
+EXTRA_DIST = \
+	$(plugin_in_files)
+
+CLEANFILES = \
+	$(plugin_DATA)
+
+DISTCLEANFILES = \
+	$(plugin_DATA)
+
+@GSD_INTLTOOL_PLUGIN_RULE@
Index: gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-common.h
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-common.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GSD_UPDATES_COMMON_H
+#define __GSD_UPDATES_COMMON_H
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define GSD_SETTINGS_BANNED_FIRMWARE                    "banned-firmware"
+#define GSD_SETTINGS_CONNECTION_USE_MOBILE              "connection-use-mobile"
+#define GSD_SETTINGS_ENABLE_CHECK_FIRMWARE              "enable-check-firmware"
+#define GSD_SETTINGS_FREQUENCY_GET_UPDATES              "frequency-get-updates"
+#define GSD_SETTINGS_FREQUENCY_GET_UPGRADES             "frequency-get-upgrades"
+#define GSD_SETTINGS_FREQUENCY_REFRESH_CACHE            "frequency-refresh-cache"
+#define GSD_SETTINGS_FREQUENCY_UPDATES_NOTIFICATION     "frequency-updates-notification"
+#define GSD_SETTINGS_IGNORED_DEVICES                    "ignored-devices"
+#define GSD_SETTINGS_LAST_UPDATES_NOTIFICATION          "last-updates-notification"
+#define GSD_SETTINGS_MEDIA_REPO_FILENAMES               "media-repo-filenames"
+#define GSD_SETTINGS_NOTIFY_DISTRO_UPGRADES             "notify-distro-upgrades"
+#define GSD_SETTINGS_SCHEMA                             "org.gnome.settings-daemon.plugins.updates"
+#define GSD_SETTINGS_UPDATE_BATTERY                     "update-battery"
+#define GSD_SETTINGS_AUTO_DOWNLOAD_UPDATES              "auto-download-updates"
+
+#define PK_OFFLINE_UPDATE_RESULTS_FILENAME              "/var/lib/PackageKit/offline-update-competed"
+G_END_DECLS
+
+#endif /* __GSD_UPDATES_COMMON_H */
Index: gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-firmware.c
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-firmware.c
@@ -0,0 +1,1040 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007-2012 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <libnotify/notify.h>
+#include <packagekit-glib2/packagekit.h>
+#ifdef HAVE_GUDEV
+#include <gudev/gudev.h>
+#endif
+
+#include "gsd-updates-common.h"
+#include "gsd-updates-firmware.h"
+
+static void     gsd_updates_firmware_finalize   (GObject          *object);
+
+#define GSD_UPDATES_FIRMWARE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_UPDATES_TYPE_FIRMWARE, GsdUpdatesFirmwarePrivate))
+#define GSD_UPDATES_FIRMWARE_MISSING_DIR                "/run/udev/firmware-missing"
+#define GSD_UPDATES_FIRMWARE_LOADING_DIR                "/lib/firmware"
+#define GSD_UPDATES_FIRMWARE_LOGIN_DELAY                10 /* seconds */
+#define GSD_UPDATES_FIRMWARE_PROCESS_DELAY              2 /* seconds */
+#define GSD_UPDATES_FIRMWARE_INSERT_DELAY               2 /* seconds */
+#define GSD_UPDATES_FIRMWARE_DEVICE_REBIND_PROGRAM      "/usr/sbin/pk-device-rebind"
+
+struct GsdUpdatesFirmwarePrivate
+{
+        GSettings               *settings;
+        GFileMonitor            *monitor;
+        GPtrArray               *array_requested;
+        PkTask                  *task;
+        GPtrArray               *packages_found;
+        guint                    timeout_id;
+};
+
+typedef enum {
+        FIRMWARE_SUBSYSTEM_USB,
+        FIRMWARE_SUBSYSTEM_PCI,
+        FIRMWARE_SUBSYSTEM_UNKNOWN
+} FirmwareSubsystem;
+
+typedef struct {
+        gchar                   *filename;
+        gchar                   *sysfs_path;
+        gchar                   *model;
+        gchar                   *id;
+        FirmwareSubsystem        subsystem;
+} GsdUpdatesFirmwareRequest;
+
+G_DEFINE_TYPE (GsdUpdatesFirmware, gsd_updates_firmware, G_TYPE_OBJECT)
+
+static void install_package_ids (GsdUpdatesFirmware *firmware);
+static void ignore_devices (GsdUpdatesFirmware *firmware);
+
+static gboolean
+subsystem_can_replug (FirmwareSubsystem subsystem)
+{
+        if (subsystem == FIRMWARE_SUBSYSTEM_USB)
+                return TRUE;
+        return FALSE;
+}
+
+static GsdUpdatesFirmwareRequest *
+request_new (const gchar *filename, const gchar *sysfs_path)
+{
+        GsdUpdatesFirmwareRequest *req;
+#ifdef HAVE_GUDEV
+        GUdevDevice *device;
+        GUdevClient *client;
+        const gchar *subsystem;
+        const gchar *model;
+        const gchar *id_vendor;
+        const gchar *id_product;
+#endif
+
+        req = g_new0 (GsdUpdatesFirmwareRequest, 1);
+        req->filename = g_strdup (filename);
+        req->sysfs_path = g_strdup (sysfs_path);
+        req->subsystem = FIRMWARE_SUBSYSTEM_UNKNOWN;
+#ifdef HAVE_GUDEV
+
+        /* get all subsystems */
+        client = g_udev_client_new (NULL);
+        device = g_udev_client_query_by_sysfs_path (client, sysfs_path);
+        if (device == NULL)
+                goto out;
+
+        /* find subsystem, which will affect if we have to replug, or reboot */
+        subsystem = g_udev_device_get_subsystem (device);
+        if (g_strcmp0 (subsystem, "usb") == 0) {
+                req->subsystem = FIRMWARE_SUBSYSTEM_USB;
+        } else if (g_strcmp0 (subsystem, "pci") == 0) {
+                req->subsystem = FIRMWARE_SUBSYSTEM_PCI;
+        } else {
+                g_warning ("subsystem unrecognised: %s", subsystem);
+        }
+
+        /* get model, so we can show something sensible */
+        model = g_udev_device_get_property (device, "ID_MODEL");
+        if (model != NULL && model[0] != '\0') {
+                req->model = g_strdup (model);
+                /* replace invalid chars */
+                g_strdelimit (req->model, "_", ' ');
+        }
+
+        /* create ID so we can ignore the specific device */
+        id_vendor = g_udev_device_get_property (device, "ID_VENDOR");
+        id_product = g_udev_device_get_property (device, "ID_MODEL_ID");
+        req->id = g_strdup_printf ("%s_%s", id_vendor, id_product);
+out:
+        if (device != NULL)
+                g_object_unref (device);
+        g_object_unref (client);
+#endif
+        return req;
+}
+
+static void
+request_free (GsdUpdatesFirmwareRequest *req)
+{
+        g_free (req->filename);
+        g_free (req->model);
+        g_free (req->sysfs_path);
+        g_free (req->id);
+        g_free (req);
+}
+
+static gboolean
+device_rebind (GsdUpdatesFirmware *firmware)
+{
+        gboolean ret;
+        gchar *argv[4];
+        gchar *rebind_stderr = NULL;
+        gchar *rebind_stdout = NULL;
+        GError *error = NULL;
+        gint exit_status = 0;
+        guint i;
+        GPtrArray *array;
+        const GsdUpdatesFirmwareRequest *req;
+        GString *string;
+
+        string = g_string_new ("");
+
+        /* make a string array of all the devices to replug */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                g_string_append_printf (string, "%s ", req->sysfs_path);
+        }
+
+        /* remove trailing space */
+        if (string->len > 0)
+                g_string_set_size (string, string->len-1);
+
+        /* use PolicyKit to do this as root */
+        argv[0] = "pkexec";
+        argv[1] = GSD_UPDATES_FIRMWARE_DEVICE_REBIND_PROGRAM;
+        argv[2] = string->str;
+        argv[3] = NULL;
+        ret = g_spawn_sync (NULL,
+                            argv,
+                            NULL,
+                            G_SPAWN_SEARCH_PATH,
+                            NULL, NULL,
+                            &rebind_stdout,
+                            &rebind_stderr,
+                            &exit_status,
+                            &error);
+        if (!ret) {
+                g_warning ("failed to spawn '%s': %s",
+                           argv[1], error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* if we failed to rebind the device */
+        if (exit_status != 0) {
+                g_warning ("failed to rebind: %s, %s",
+                           rebind_stdout, rebind_stderr);
+                ret = FALSE;
+                goto out;
+        }
+out:
+        g_free (rebind_stdout);
+        g_free (rebind_stderr);
+        g_string_free (string, TRUE);
+        return ret;
+}
+static void
+libnotify_cb (NotifyNotification *notification, gchar *action, gpointer data)
+{
+        GsdUpdatesFirmware *firmware = GSD_UPDATES_FIRMWARE (data);
+
+        if (g_strcmp0 (action, "install-firmware") == 0) {
+                install_package_ids (firmware);
+        } else if (g_strcmp0 (action, "ignore-devices") == 0) {
+                ignore_devices (firmware);
+        } else {
+                g_warning ("unknown action id: %s", action);
+        }
+        notify_notification_close (notification, NULL);
+}
+
+static void
+on_notification_closed (NotifyNotification *notification, gpointer data)
+{
+        g_object_unref (notification);
+}
+
+static void
+require_restart (GsdUpdatesFirmware *firmware)
+{
+        const gchar *message;
+        gboolean ret;
+        GError *error = NULL;
+        NotifyNotification *notification;
+
+        /* TRANSLATORS: we need to restart so the new hardware can re-request the firmware */
+        message = _("You will need to restart this computer before the hardware will work correctly.");
+
+        /* TRANSLATORS: title of libnotify bubble */
+        notification = notify_notification_new (_("Additional software was installed"), message, NULL);
+        notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer");
+        notify_notification_set_app_name (notification, _("Software Updates"));
+        notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW);
+        g_signal_connect (notification, "closed",
+                          G_CALLBACK (on_notification_closed), NULL);
+
+        /* show the bubble */
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+}
+
+static void
+require_replug (GsdUpdatesFirmware *firmware)
+{
+        const gchar *message;
+        gboolean ret;
+        GError *error = NULL;
+        NotifyNotification *notification;
+
+        /* TRANSLATORS: we need to remove an replug so the new hardware can re-request the firmware */
+        message = _("You will need to remove and then reinsert the hardware before it will work correctly.");
+
+        /* TRANSLATORS: title of libnotify bubble */
+        notification = notify_notification_new (_("Additional software was installed"), message, NULL);
+        notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer");
+        notify_notification_set_app_name (notification, _("Software Updates"));
+        notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW);
+        g_signal_connect (notification, "closed",
+                          G_CALLBACK (on_notification_closed), NULL);
+
+        /* show the bubble */
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+}
+
+static void
+require_nothing (GsdUpdatesFirmware *firmware)
+{
+        const gchar *message;
+        gboolean ret;
+        GError *error = NULL;
+        NotifyNotification *notification;
+
+        /* TRANSLATORS: we need to remove an replug so the new hardware can re-request the firmware */
+        message = _("Your hardware has been set up and is now ready to use.");
+
+        /* TRANSLATORS: title of libnotify bubble */
+        notification = notify_notification_new (_("Additional software was installed"), message, NULL);
+        notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer");
+        notify_notification_set_app_name (notification, _("Software Updates"));
+        notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW);
+        g_signal_connect (notification, "closed",
+                          G_CALLBACK (on_notification_closed), NULL);
+
+        /* show the bubble */
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+}
+
+static void
+install_packages_cb (GObject *object,
+                     GAsyncResult *res,
+                     GsdUpdatesFirmware *firmware)
+{
+        PkClient *client = PK_CLIENT (object);
+        GError *error = NULL;
+        PkResults *results = NULL;
+        GPtrArray *array = NULL;
+        gboolean restart = FALSE;
+        const GsdUpdatesFirmwareRequest *req;
+        gboolean ret;
+        guint i;
+        PkError *error_code = NULL;
+
+        /* get the results */
+        results = pk_client_generic_finish (client, res, &error);
+        if (results == NULL) {
+                g_warning ("failed to install file: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* check error code */
+        error_code = pk_results_get_error_code (results);
+        if (error_code != NULL) {
+                g_warning ("failed to install file: %s, %s",
+                           pk_error_enum_to_string (pk_error_get_code (error_code)),
+                           pk_error_get_details (error_code));
+                goto out;
+        }
+
+        /* go through all the requests, and find the worst type */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                ret = subsystem_can_replug (req->subsystem);
+                if (!ret) {
+                        restart = TRUE;
+                        break;
+                }
+        }
+
+        /* can we just rebind the device */
+        ret = g_file_test (GSD_UPDATES_FIRMWARE_DEVICE_REBIND_PROGRAM, G_FILE_TEST_EXISTS);
+        if (ret) {
+                ret = device_rebind (firmware);
+                if (ret) {
+                        require_nothing (firmware);
+                        goto out;
+                }
+        } else {
+                /* give the user the correct message */
+                if (restart)
+                        require_restart (firmware);
+                else
+                        require_replug (firmware);
+        }
+
+        /* clear array */
+        g_ptr_array_set_size (firmware->priv->array_requested, 0);
+out:
+        if (error_code != NULL)
+                g_object_unref (error_code);
+        if (array != NULL)
+                g_ptr_array_unref (array);
+        if (results != NULL)
+                g_object_unref (results);
+}
+
+static gchar **
+package_array_to_strv (GPtrArray *array)
+{
+	PkPackage *item;
+	gchar **results;
+	guint i;
+
+	results = g_new0 (gchar *, array->len+1);
+	for (i=0; i<array->len; i++) {
+		item = g_ptr_array_index (array, i);
+		results[i] = g_strdup (pk_package_get_id (item));
+	}
+	return results;
+}
+
+static void
+install_package_ids (GsdUpdatesFirmware *firmware)
+{
+        gchar **package_ids;
+
+        /* install all of the firmware files */
+        package_ids = package_array_to_strv (firmware->priv->packages_found);
+        pk_client_install_packages_async (PK_CLIENT(firmware->priv->task),
+                                          TRUE, package_ids,
+                                          NULL,
+                                          NULL, NULL,
+                                          (GAsyncReadyCallback) install_packages_cb,
+                                          firmware);
+        g_strfreev (package_ids);
+}
+
+static void
+ignore_devices (GsdUpdatesFirmware *firmware)
+{
+        gchar *existing = NULL;
+        GsdUpdatesFirmwareRequest *req;
+        GPtrArray *array;
+        GString *string;
+        guint i;
+
+        /* get from settings */
+        existing = g_settings_get_string (firmware->priv->settings,
+                                          GSD_SETTINGS_IGNORED_DEVICES);
+
+        /* get existing string */
+        string = g_string_new (existing);
+        if (string->len > 0)
+                g_string_append (string, ",");
+
+        /* add all listed devices */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                g_string_append_printf (string, "%s,", req->id);
+        }
+
+        /* remove final ',' */
+        if (string->len > 2)
+                g_string_set_size (string, string->len - 1);
+
+        /* set new string */
+        g_settings_set_string (firmware->priv->settings,
+                               GSD_SETTINGS_IGNORED_DEVICES,
+                               string->str);
+
+        g_free (existing);
+        g_string_free (string, TRUE);
+}
+
+static PkPackage *
+check_available (GsdUpdatesFirmware *firmware, const gchar *filename)
+{
+        guint length = 0;
+        GPtrArray *array = NULL;
+        GError *error = NULL;
+        PkPackage *item = NULL;
+        PkBitfield filter;
+        PkResults *results;
+        gchar **values = NULL;
+        PkError *error_code = NULL;
+
+        /* search for newest not installed package */
+        filter = pk_bitfield_from_enums (PK_FILTER_ENUM_NOT_INSTALLED,
+                                         PK_FILTER_ENUM_NEWEST, -1);
+        values = g_strsplit (filename, "&", -1);
+        results = pk_client_search_files (PK_CLIENT(firmware->priv->task),
+                                          filter,
+                                          values,
+                                          NULL,
+                                          NULL, NULL,
+                                          &error);
+        if (results == NULL) {
+                g_warning ("failed to search file %s: %s",
+                           filename, error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* check error code */
+        error_code = pk_results_get_error_code (results);
+        if (error_code != NULL) {
+                g_warning ("failed to search file: %s, %s",
+                           pk_error_enum_to_string (pk_error_get_code (error_code)),
+                           pk_error_get_details (error_code));
+                goto out;
+        }
+
+        /* make sure we have one package */
+        array = pk_results_get_package_array (results);
+        if (array->len == 0)
+                g_debug ("no package providing %s found", filename);
+        else if (array->len != 1)
+                g_warning ("not one package providing %s found (%i)", filename, length);
+        else
+                item = g_object_ref (g_ptr_array_index (array, 0));
+out:
+        g_strfreev (values);
+        if (error_code != NULL)
+                g_object_unref (error_code);
+        if (array != NULL)
+                g_ptr_array_unref (array);
+        if (results != NULL)
+                g_object_unref (results);
+        return item;
+}
+
+static void
+remove_duplicate (GPtrArray *array)
+{
+        guint i, j;
+        const gchar *val;
+        const gchar *val_tmp;
+
+        /* remove each duplicate entry */
+        for (i=0; i<array->len; i++) {
+                val = g_ptr_array_index (array, i);
+                for (j=i+1; j<array->len; j++) {
+                        val_tmp = g_ptr_array_index (array, j);
+                        if (g_strcmp0 (val_tmp, val) == 0)
+                                g_ptr_array_remove_index_fast (array, j);
+                }
+        }
+}
+
+static gboolean
+delay_timeout_cb (gpointer data)
+{
+        guint i;
+        gboolean ret;
+        GString *string;
+        GsdUpdatesFirmware *firmware = GSD_UPDATES_FIRMWARE (data);
+        NotifyNotification *notification;
+        GPtrArray *array;
+        GError *error = NULL;
+        PkPackage *item = NULL;
+        const GsdUpdatesFirmwareRequest *req;
+        gboolean has_data = FALSE;
+
+        /* message string */
+        string = g_string_new ("");
+
+        /* try to find each firmware file in an available package */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                /* save to new array if we found one package for this file */
+                item = check_available (firmware, req->filename);
+                if (item != NULL) {
+                        g_ptr_array_add (firmware->priv->packages_found, item);
+                        g_object_unref (item);
+                }
+        }
+
+        /* nothing to do */
+        if (firmware->priv->packages_found->len == 0) {
+                g_debug ("no packages providing any of the missing firmware");
+                goto out;
+        }
+
+        /* check we don't want the same package more than once */
+        remove_duplicate (firmware->priv->packages_found);
+
+        /* have we got any models to array */
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                if (req->model != NULL) {
+                        has_data = TRUE;
+                        break;
+                }
+        }
+
+        /* TRANSLATORS: we need another package to keep udev quiet */
+        g_string_append (string, _("Additional firmware is required to make hardware in this computer function correctly."));
+
+        /* sdd what information we have */
+        if (has_data) {
+                g_string_append (string, "\n");
+                for (i=0; i<array->len; i++) {
+                        req = g_ptr_array_index (array, i);
+                        if (req->model != NULL)
+                                g_string_append_printf (string, "\n• %s", req->model);
+                }
+                g_string_append (string, "\n");
+        }
+
+        /* TRANSLATORS: title of libnotify bubble */
+        notification = notify_notification_new (_("Additional firmware required"), string->str, NULL);
+        notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer");
+        notify_notification_set_app_name (notification, _("Software Updates"));
+        notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW);
+        notify_notification_add_action (notification, "install-firmware",
+                                        /* TRANSLATORS: button label */
+                                        _("Install firmware"), libnotify_cb, firmware, NULL);
+        notify_notification_add_action (notification, "ignore-devices",
+                                        /* TRANSLATORS: we should ignore this device and not ask anymore */
+                                        _("Ignore devices"), libnotify_cb, firmware, NULL);
+        g_signal_connect (notification, "closed",
+                          G_CALLBACK (on_notification_closed), NULL);
+
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+
+out:
+        g_string_free (string, TRUE);
+        /* never repeat */
+        return FALSE;
+}
+
+static void
+remove_banned (GsdUpdatesFirmware *firmware, GPtrArray *array)
+{
+        gboolean ret;
+        gchar **banned = NULL;
+        gchar *banned_str;
+        GsdUpdatesFirmwareRequest *req;
+        guint i, j;
+
+        /* get from settings */
+        banned_str = g_settings_get_string (firmware->priv->settings,
+                                            GSD_SETTINGS_BANNED_FIRMWARE);
+        if (banned_str == NULL) {
+                g_warning ("could not read banned list");
+                goto out;
+        }
+
+        /* nothing in list, common case */
+        if (banned_str[0] == '\0') {
+                g_debug ("nothing in banned list");
+                goto out;
+        }
+
+        /* split using "," */
+        banned = g_strsplit (banned_str, ",", 0);
+
+        /* remove any banned pattern matches */
+        i = 0;
+        while (i < array->len) {
+                ret = FALSE;
+                req = g_ptr_array_index (array, i);
+                for (j=0; banned[j] != NULL; j++) {
+                        ret = g_pattern_match_simple (banned[j], req->filename);
+                        if (ret) {
+                                g_debug ("match %s for %s, removing",
+                                         banned[j], req->filename);
+                                g_ptr_array_remove_index_fast (array, i);
+                                break;
+                        }
+                }
+                if (!ret)
+                        i++;
+        }
+out:
+        g_free (banned_str);
+        g_strfreev (banned);
+}
+
+static void
+remove_ignored (GsdUpdatesFirmware *firmware, GPtrArray *array)
+{
+        gboolean ret;
+        gchar **ignored = NULL;
+        gchar *ignored_str;
+        GsdUpdatesFirmwareRequest *req;
+        guint i, j;
+
+        /* get from settings */
+        ignored_str = g_settings_get_string (firmware->priv->settings,
+                                             GSD_SETTINGS_IGNORED_DEVICES);
+        if (ignored_str == NULL) {
+                g_warning ("could not read ignored list");
+                goto out;
+        }
+
+        /* nothing in list, common case */
+        if (ignored_str[0] == '\0') {
+                g_debug ("nothing in ignored list");
+                goto out;
+        }
+
+        /* split using "," */
+        ignored = g_strsplit (ignored_str, ",", 0);
+
+        /* remove any ignored pattern matches */
+        i = 0;
+        while (i < array->len) {
+                ret = FALSE;
+                req = g_ptr_array_index (array, i);
+                if (req->id == NULL)
+                        continue;
+                for (j=0; ignored[j] != NULL; j++) {
+                        ret = g_pattern_match_simple (ignored[j], req->id);
+                        if (ret) {
+                                g_debug ("match %s for %s, removing", ignored[j], req->id);
+                                g_ptr_array_remove_index_fast (array, i);
+                                break;
+                        }
+                }
+                if (!ret)
+                        i++;
+        }
+out:
+        g_free (ignored_str);
+        g_strfreev (ignored);
+}
+
+static gchar *
+udev_text_decode (const gchar *data)
+{
+        guint i;
+        guint j;
+        gchar *decode;
+
+        decode = g_strdup (data);
+        for (i = 0, j = 0; data[i] != '\0'; j++) {
+                if (memcmp (&data[i], "\\x2f", 4) == 0) {
+                        decode[j] = '/';
+                        i += 4;
+                } else if (memcmp (&data[i], "\\x5c", 4) == 0) {
+                        decode[j] = '\\';
+                        i += 4;
+                } else {
+                        decode[j] = data[i];
+                        i++;
+                }
+        }
+        decode[j] = '\0';
+        return decode;
+}
+
+static gchar *
+get_device (GsdUpdatesFirmware *firmware, const gchar *filename)
+{
+        GFile *file;
+        GFileInfo *info;
+        const gchar *symlink_path;
+        gchar *syspath = NULL;
+        GError *error = NULL;
+        gchar *target = NULL;
+        gchar *tmp;
+
+        /* get the file data */
+        file = g_file_new_for_path (filename);
+        info = g_file_query_info (file,
+                                  G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
+                                  G_FILE_QUERY_INFO_NONE,
+                                  NULL,
+                                  &error);
+        if (info == NULL) {
+                g_warning ("Failed to get symlink: %s",
+                           error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* /devices/pci0000:00/0000:00:1d.0/usb5/5-2/firmware/5-2 */
+        symlink_path = g_file_info_get_symlink_target (info);
+        if (symlink_path == NULL) {
+                g_warning ("failed to get symlink target");
+                goto out;
+        }
+
+        /* prepend sys to make '/sys/devices/pci0000:00/0000:00:1d.0/usb5/5-2/firmware/5-2' */
+        syspath = g_strconcat ("/sys", symlink_path, NULL);
+
+        /* start with the longest, and try to find a sub-path that exists */
+        tmp = &syspath[strlen (syspath)];
+        while (tmp != NULL) {
+                *tmp = '\0';
+                g_debug ("testing %s", target);
+                if (g_file_test (syspath, G_FILE_TEST_EXISTS)) {
+                        target = g_strdup (syspath);
+                        goto out;
+                }
+                tmp = g_strrstr (syspath, "/");
+        }
+out:
+        if (info != NULL)
+                g_object_unref (info);
+        g_object_unref (file);
+        g_free (syspath);
+        return target;
+}
+
+static void
+add_filename (GsdUpdatesFirmware *firmware, const gchar *filename_no_path)
+{
+        gboolean ret;
+        gchar *filename_path = NULL;
+        gchar *missing_path = NULL;
+        gchar *sysfs_path = NULL;
+        GsdUpdatesFirmwareRequest *req;
+        GPtrArray *array;
+        guint i;
+
+        /* this is the file we want to load */
+        filename_path = g_build_filename (GSD_UPDATES_FIRMWARE_LOADING_DIR,
+                                          filename_no_path, NULL);
+
+        /* file already exists */
+        ret = g_file_test (filename_path, G_FILE_TEST_EXISTS);
+        if (ret)
+                goto out;
+
+        /* this is the file that udev created for us */
+        missing_path = g_build_filename (GSD_UPDATES_FIRMWARE_MISSING_DIR,
+                                         filename_no_path, NULL);
+        g_debug ("filename=%s -> %s", missing_path, filename_path);
+
+        /* get symlink target */
+        sysfs_path = get_device (firmware, missing_path);
+        if (sysfs_path == NULL)
+                goto out;
+
+        /* find any previous requests with this path or firmware */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                if (g_strcmp0 (sysfs_path, req->sysfs_path) == 0) {
+                        g_debug ("ignoring previous sysfs request for %s",
+                                 sysfs_path);
+                        goto out;
+                }
+                if (g_strcmp0 (filename_path, req->filename) == 0) {
+                        g_debug ("ignoring previous filename request for %s",
+                                 filename_path);
+                        goto out;
+                }
+        }
+
+        /* create new request object */
+        req = request_new (filename_path, sysfs_path);
+        g_ptr_array_add (firmware->priv->array_requested, req);
+out:
+        g_free (missing_path);
+        g_free (filename_path);
+        g_free (sysfs_path);
+}
+
+static void
+scan_directory (GsdUpdatesFirmware *firmware)
+{
+        gboolean ret;
+        GError *error = NULL;
+        GDir *dir;
+        const gchar *filename;
+        gchar *filename_decoded;
+        guint i;
+        GPtrArray *array;
+        const GsdUpdatesFirmwareRequest *req;
+        guint scan_id = 0;
+
+        /* should we check and show the user */
+        ret = g_settings_get_boolean (firmware->priv->settings,
+                                      GSD_SETTINGS_ENABLE_CHECK_FIRMWARE);
+        if (!ret) {
+                g_debug ("not showing thanks to GSettings");
+                return;
+        }
+
+        /* open the directory of requests */
+        dir = g_dir_open (GSD_UPDATES_FIRMWARE_MISSING_DIR, 0, &error);
+        if (dir == NULL) {
+                if (error->code != G_FILE_ERROR_NOENT) {
+                        g_warning ("failed to open directory: %s",
+                                   error->message);
+                }
+                g_error_free (error);
+                return;
+        }
+
+        /* find all the firmware requests */
+        filename = g_dir_read_name (dir);
+        while (filename != NULL) {
+
+                filename_decoded = udev_text_decode (filename);
+                add_filename (firmware, filename_decoded);
+                g_free (filename_decoded);
+
+                /* next file */
+                filename = g_dir_read_name (dir);
+        }
+        g_dir_close (dir);
+
+        /* debugging */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                g_debug ("requested: %s", req->filename);
+        }
+
+        /* remove banned files */
+        remove_banned (firmware, array);
+
+        /* remove ignored devices */
+        remove_ignored (firmware, array);
+
+        /* debugging */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                g_debug ("searching for: %s", req->filename);
+        }
+
+        /* don't spam the user at startup, so wait a little delay */
+        if (array->len > 0) {
+                scan_id = g_timeout_add_seconds (GSD_UPDATES_FIRMWARE_PROCESS_DELAY,
+                                                 delay_timeout_cb,
+                                                 firmware);
+                g_source_set_name_by_id (scan_id, "[GsdUpdatesFirmware] process");
+        }
+}
+
+static gboolean
+scan_directory_cb (GsdUpdatesFirmware *firmware)
+{
+        scan_directory (firmware);
+        firmware->priv->timeout_id = 0;
+        return FALSE;
+}
+
+static void
+monitor_changed_cb (GFileMonitor *monitor,
+                    GFile *file,
+                    GFile *other_file,
+                    GFileMonitorEvent event_type,
+                    GsdUpdatesFirmware *firmware)
+{
+        if (firmware->priv->timeout_id > 0) {
+                g_debug ("clearing timeout as device changed");
+                g_source_remove (firmware->priv->timeout_id);
+        }
+
+        /* wait for the device to settle */
+        firmware->priv->timeout_id =
+                g_timeout_add_seconds (GSD_UPDATES_FIRMWARE_INSERT_DELAY,
+                                       (GSourceFunc) scan_directory_cb,
+                                       firmware);
+        g_source_set_name_by_id (firmware->priv->timeout_id,
+                                 "[GsdUpdatesFirmware] changed");
+}
+
+static void
+gsd_updates_firmware_class_init (GsdUpdatesFirmwareClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+        object_class->finalize = gsd_updates_firmware_finalize;
+        g_type_class_add_private (klass, sizeof (GsdUpdatesFirmwarePrivate));
+}
+
+static void
+gsd_updates_firmware_init (GsdUpdatesFirmware *firmware)
+{
+        GFile *file;
+        GError *error = NULL;
+
+        firmware->priv = GSD_UPDATES_FIRMWARE_GET_PRIVATE (firmware);
+        firmware->priv->timeout_id = 0;
+        firmware->priv->packages_found = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+        firmware->priv->array_requested = g_ptr_array_new_with_free_func ((GDestroyNotify) request_free);
+        firmware->priv->settings = g_settings_new (GSD_SETTINGS_SCHEMA);
+        firmware->priv->task = pk_task_new ();
+        g_object_set (firmware->priv->task,
+                      "background", TRUE,
+                      NULL);
+
+        /* setup watch for new hardware */
+        file = g_file_new_for_path (GSD_UPDATES_FIRMWARE_MISSING_DIR);
+        firmware->priv->monitor = g_file_monitor (file,
+                                                  G_FILE_MONITOR_NONE,
+                                                  NULL,
+                                                  &error);
+        if (firmware->priv->monitor == NULL) {
+                g_warning ("failed to setup monitor: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* limit to one per second */
+        g_file_monitor_set_rate_limit (firmware->priv->monitor, 1000);
+
+        /* get notified of changes */
+        g_signal_connect (firmware->priv->monitor, "changed",
+                          G_CALLBACK (monitor_changed_cb), firmware);
+out:
+        g_object_unref (file);
+        firmware->priv->timeout_id =
+                g_timeout_add_seconds (GSD_UPDATES_FIRMWARE_LOGIN_DELAY,
+                                       (GSourceFunc) scan_directory_cb,
+                                       firmware);
+        g_source_set_name_by_id (firmware->priv->timeout_id,
+                                 "[GsdUpdatesFirmware] login coldplug");
+}
+
+static void
+gsd_updates_firmware_finalize (GObject *object)
+{
+        GsdUpdatesFirmware *firmware;
+
+        g_return_if_fail (GSD_UPDATES_IS_FIRMWARE (object));
+
+        firmware = GSD_UPDATES_FIRMWARE (object);
+
+        g_return_if_fail (firmware->priv != NULL);
+        g_ptr_array_unref (firmware->priv->array_requested);
+        g_ptr_array_unref (firmware->priv->packages_found);
+        g_object_unref (PK_CLIENT(firmware->priv->task));
+        g_object_unref (firmware->priv->settings);
+        if (firmware->priv->monitor != NULL)
+                g_object_unref (firmware->priv->monitor);
+        if (firmware->priv->timeout_id > 0)
+                g_source_remove (firmware->priv->timeout_id);
+
+        G_OBJECT_CLASS (gsd_updates_firmware_parent_class)->finalize (object);
+}
+
+GsdUpdatesFirmware *
+gsd_updates_firmware_new (void)
+{
+        GsdUpdatesFirmware *firmware;
+        firmware = g_object_new (GSD_UPDATES_TYPE_FIRMWARE, NULL);
+        return GSD_UPDATES_FIRMWARE (firmware);
+}
Index: gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-firmware.h
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-firmware.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007-2011 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GSD_UPDATES_FIRMWARE_H
+#define __GSD_UPDATES_FIRMWARE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GSD_UPDATES_TYPE_FIRMWARE               (gsd_updates_firmware_get_type ())
+#define GSD_UPDATES_FIRMWARE(o)                 (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_UPDATES_TYPE_FIRMWARE, GsdUpdatesFirmware))
+#define GSD_UPDATES_FIRMWARE_CLASS(k)           (G_TYPE_CHECK_CLASS_CAST((k), GSD_UPDATES_TYPE_FIRMWARE, GsdUpdatesFirmwareClass))
+#define GSD_UPDATES_IS_FIRMWARE(o)              (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_UPDATES_TYPE_FIRMWARE))
+
+typedef struct GsdUpdatesFirmwarePrivate GsdUpdatesFirmwarePrivate;
+
+typedef struct
+{
+         GObject                         parent;
+         GsdUpdatesFirmwarePrivate      *priv;
+} GsdUpdatesFirmware;
+
+typedef struct
+{
+        GObjectClass    parent_class;
+} GsdUpdatesFirmwareClass;
+
+GType                    gsd_updates_firmware_get_type          (void);
+GsdUpdatesFirmware      *gsd_updates_firmware_new               (void);
+
+G_END_DECLS
+
+#endif /* __GSD_UPDATES_FIRMWARE_H */
Index: gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-manager.c
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-manager.c
@@ -0,0 +1,1509 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Richard Hughes <richard@hughsie.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <packagekit-glib2/packagekit.h>
+#include <libnotify/notify.h>
+#include <gdesktop-enums.h>
+
+#include "gsd-enums.h"
+#include "gsd-updates-manager.h"
+#include "gsd-updates-firmware.h"
+#include "gsd-updates-refresh.h"
+#include "gsd-updates-common.h"
+#include "gnome-settings-profile.h"
+#include "gnome-settings-bus.h"
+
+#define GSD_UPDATES_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_UPDATES_MANAGER, GsdUpdatesManagerPrivate))
+
+#define MAX_FAILED_GET_UPDATES              10 /* the maximum number of tries */
+#define GSD_UPDATES_ICON_NORMAL             "software-update-available-symbolic"
+#define GSD_UPDATES_ICON_URGENT             "software-update-urgent-symbolic"
+#define GSD_UPDATES_CHECK_OFFLINE_TIMEOUT   30 /* time in seconds */
+
+struct GsdUpdatesManagerPrivate
+{
+        GCancellable            *cancellable;
+        GsdUpdatesRefresh       *refresh;
+        GsdUpdatesFirmware      *firmware;
+        GSettings               *settings_proxy;
+        GSettings               *settings_ftp;
+        GSettings               *settings_gsd;
+        GSettings               *settings_http;
+        guint                    number_updates_critical_last_shown;
+        guint                    offline_update_id;
+        PkError                 *offline_update_error;
+        NotifyNotification      *notification_updates;
+        NotifyNotification      *notification_offline_success;
+        PkControl               *control;
+        PkTask                  *task;
+        guint                    inhibit_cookie;
+        GsdSessionManager       *proxy_session;
+        guint                    update_viewer_watcher_id;
+        GVolumeMonitor          *volume_monitor;
+        guint                    failed_get_updates_count;
+        GPtrArray               *update_packages;
+};
+
+static void gsd_updates_manager_class_init (GsdUpdatesManagerClass *klass);
+static void gsd_updates_manager_init (GsdUpdatesManager *updates_manager);
+static void restart_and_install_updates (void);
+
+G_DEFINE_TYPE (GsdUpdatesManager, gsd_updates_manager, G_TYPE_OBJECT)
+
+static gpointer manager_object = NULL;
+
+static void
+child_exit_cb (GPid pid, gint status, gpointer user_data)
+{
+        g_spawn_close_pid (pid);
+}
+
+static void
+clear_offline_updates_message (void)
+{
+        gboolean ret;
+        GError *error = NULL;
+        gchar *argv[3];
+        GPid pid;
+
+        argv[0] = "pkexec";
+        argv[1] = LIBEXECDIR "/pk-clear-offline-update";
+        argv[2] = NULL;
+        ret = g_spawn_async (NULL,
+                             argv,
+                             NULL,
+                             G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
+                             NULL,
+                             NULL,
+                             &pid,
+                             &error);
+        if (!ret) {
+                g_warning ("Failure clearing offline update message: %s",
+                           error->message);
+                g_error_free (error);
+                return;
+        }
+        g_child_watch_add (pid, child_exit_cb, NULL);
+}
+
+static void
+show_offline_updates_error (GsdUpdatesManager *manager)
+{
+        const gchar *title;
+        gboolean show_geeky = FALSE;
+        GString *msg;
+        GtkWidget *dialog;
+
+        /* TRANSLATORS: this is when the offline update failed */
+        title = _("Failed To Update");
+        msg = g_string_new ("");
+        switch (pk_error_get_code (manager->priv->offline_update_error)) {
+        case PK_ERROR_ENUM_UNFINISHED_TRANSACTION:
+                /* TRANSLATORS: the transaction could not be completed
+                 * as a previous transaction was unfinished */
+                g_string_append (msg, _("A previous update was unfinished."));
+                show_geeky = TRUE;
+                break;
+        case PK_ERROR_ENUM_PACKAGE_DOWNLOAD_FAILED:
+        case PK_ERROR_ENUM_NO_CACHE:
+        case PK_ERROR_ENUM_NO_NETWORK:
+        case PK_ERROR_ENUM_NO_MORE_MIRRORS_TO_TRY:
+        case PK_ERROR_ENUM_CANNOT_FETCH_SOURCES:
+                /* TRANSLATORS: the package manager needed to download
+                 * something with no network available */
+                g_string_append (msg, _("Network access was required but not available."));
+                break;
+        case PK_ERROR_ENUM_BAD_GPG_SIGNATURE:
+        case PK_ERROR_ENUM_CANNOT_UPDATE_REPO_UNSIGNED:
+        case PK_ERROR_ENUM_GPG_FAILURE:
+        case PK_ERROR_ENUM_MISSING_GPG_SIGNATURE:
+        case PK_ERROR_ENUM_PACKAGE_CORRUPT:
+                /* TRANSLATORS: if the package is not signed correctly
+                 *  */
+                g_string_append (msg, _("An update was not signed in the correct way."));
+                show_geeky = TRUE;
+                break;
+        case PK_ERROR_ENUM_DEP_RESOLUTION_FAILED:
+        case PK_ERROR_ENUM_FILE_CONFLICTS:
+        case PK_ERROR_ENUM_INCOMPATIBLE_ARCHITECTURE:
+        case PK_ERROR_ENUM_PACKAGE_CONFLICTS:
+                /* TRANSLATORS: the transaction failed in a way the user
+                 * probably cannot comprehend. Package management systems
+                 * really are teh suck.*/
+                g_string_append (msg, _("The update could not be completed."));
+                show_geeky = TRUE;
+                break;
+        case PK_ERROR_ENUM_TRANSACTION_CANCELLED:
+                /* TRANSLATORS: the user aborted the update manually */
+                g_string_append (msg, _("The update was cancelled."));
+                break;
+        case PK_ERROR_ENUM_NO_PACKAGES_TO_UPDATE:
+        case PK_ERROR_ENUM_UPDATE_NOT_FOUND:
+                /* TRANSLATORS: the user must have updated manually after
+                 * the updates were prepared */
+                g_string_append (msg, _("An offline update was requested but no packages required updating."));
+                break;
+        case PK_ERROR_ENUM_NO_SPACE_ON_DEVICE:
+                /* TRANSLATORS: we ran out of disk space */
+                g_string_append (msg, _("No space was left on the drive."));
+                break;
+        case PK_ERROR_ENUM_PACKAGE_FAILED_TO_BUILD:
+        case PK_ERROR_ENUM_PACKAGE_FAILED_TO_INSTALL:
+        case PK_ERROR_ENUM_PACKAGE_FAILED_TO_REMOVE:
+                /* TRANSLATORS: the update process failed in a general
+                 * way, usually this message will come from source distros
+                 * like gentoo */
+                g_string_append (msg, _("An update failed to install correctly."));
+                show_geeky = TRUE;
+                break;
+        default:
+                /* TRANSLATORS: We didn't handle the error type */
+                g_string_append (msg, _("The offline update failed in an unexpected way."));
+                show_geeky = TRUE;
+                break;
+        }
+        if (show_geeky) {
+                g_string_append_printf (msg, "\n%s\n\n%s",
+                                        /* TRANSLATORS: these are geeky messages from the
+                                         * package manager no mortal is supposed to understand,
+                                         * but google might know what they mean */
+                                        _("Detailed errors from the package manager follow:"),
+                                        pk_error_get_details (manager->priv->offline_update_error));
+        }
+        dialog = gtk_message_dialog_new (NULL,
+                                         0,
+                                         GTK_MESSAGE_INFO,
+                                         GTK_BUTTONS_CLOSE,
+                                         "%s", title);
+        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                                  "%s", msg->str);
+        g_signal_connect_swapped (dialog, "response",
+                                  G_CALLBACK (gtk_widget_destroy),
+                                  dialog);
+        gtk_widget_show (dialog);
+        clear_offline_updates_message ();
+        g_string_free (msg, TRUE);
+}
+
+static void
+libnotify_action_cb (NotifyNotification *notification,
+                     gchar *action,
+                     gpointer user_data)
+{
+        gboolean ret;
+        GError *error = NULL;
+        GsdUpdatesManager *manager = GSD_UPDATES_MANAGER (user_data);
+
+        notify_notification_close (notification, NULL);
+        if (g_strcmp0 (action, "ignore") == 0)
+                goto out;
+        if (g_strcmp0 (action, "distro-upgrade-info") == 0) {
+                ret = g_spawn_command_line_async (DATADIR "/PackageKit/pk-upgrade-distro.sh",
+                                                  &error);
+                if (!ret) {
+                        g_warning ("Failure launching pk-upgrade-distro.sh: %s",
+                                   error->message);
+                        g_error_free (error);
+                }
+                goto out;
+        }
+        if (g_strcmp0 (action, "show-update-viewer") == 0) {
+                ret = g_spawn_command_line_async (BINDIR "/gpk-update-viewer",
+                                                  &error);
+                if (!ret) {
+                        g_warning ("Failure launching update viewer: %s",
+                                   error->message);
+                        g_error_free (error);
+                }
+                goto out;
+        }
+        if (g_strcmp0 (action, "clear-offline-updates") == 0) {
+                clear_offline_updates_message ();
+                goto out;
+        }
+        if (g_strcmp0 (action, "error-offline-updates") == 0) {
+                show_offline_updates_error (manager);
+                goto out;
+        }
+        if (g_strcmp0 (action, "install") == 0) {
+                restart_and_install_updates ();
+                goto out;
+        }
+        if (g_strcmp0 (action, "cancel") == 0) {
+                /* try to cancel */
+                g_cancellable_cancel (manager->priv->cancellable);
+                goto out;
+        }
+        g_warning ("unknown action id: %s", action);
+out:
+        return;
+}
+
+static void
+on_notification_closed (NotifyNotification *notification, gpointer data)
+{
+        g_object_unref (notification);
+}
+
+static void
+get_distro_upgrades_finished_cb (GObject *object,
+                                 GAsyncResult *res,
+                                 GsdUpdatesManager *manager)
+{
+        const gchar *title;
+        gboolean ret;
+        gchar *name = NULL;
+        GError *error = NULL;
+        GPtrArray *array = NULL;
+        GString *string = NULL;
+        guint i;
+        NotifyNotification *notification;
+        PkClient *client = PK_CLIENT(object);
+        PkDistroUpgrade *item;
+        PkError *error_code = NULL;
+        PkResults *results;
+        PkUpdateStateEnum state;
+
+        /* get the results */
+        results = pk_client_generic_finish (PK_CLIENT(client), res, &error);
+        if (results == NULL) {
+                if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+                        g_error_free (error);
+                        return;
+                }
+                if (error->domain != PK_CLIENT_ERROR ||
+                    error->code != PK_CLIENT_ERROR_NOT_SUPPORTED) {
+                        g_warning ("failed to get upgrades: %s",
+                                   error->message);
+                }
+                g_error_free (error);
+                goto out;
+        }
+
+        /* check error code */
+        error_code = pk_results_get_error_code (results);
+        if (error_code != NULL) {
+                g_warning ("failed to get upgrades: %s, %s",
+                           pk_error_enum_to_string (pk_error_get_code (error_code)),
+                           pk_error_get_details (error_code));
+                goto out;
+        }
+
+        /* process results */
+        array = pk_results_get_distro_upgrade_array (results);
+
+        /* any updates? */
+        if (array->len == 0) {
+                g_debug ("no upgrades");
+                goto out;
+        }
+
+        /* do we do the notification? */
+        ret = g_settings_get_boolean (manager->priv->settings_gsd,
+                                      GSD_SETTINGS_NOTIFY_DISTRO_UPGRADES);
+        if (!ret) {
+                g_debug ("ignoring due to GSettings");
+                goto out;
+        }
+
+        /* find the upgrade string */
+        string = g_string_new ("");
+        for (i=0; i < array->len; i++) {
+                item = (PkDistroUpgrade *) g_ptr_array_index (array, i);
+                g_object_get (item,
+                              "name", &name,
+                              "state", &state,
+                              NULL);
+                g_string_append_printf (string, "%s (%s)\n",
+                                        name,
+                                        pk_distro_upgrade_enum_to_string (state));
+                g_free (name);
+        }
+        if (string->len != 0)
+                g_string_set_size (string, string->len-1);
+
+        /* TRANSLATORS: a distro update is available, e.g. Fedora 8 to Fedora 9 */
+        title = _("Distribution upgrades available");
+        notification = notify_notification_new (title,
+                                                string->str,
+                                                GSD_UPDATES_ICON_NORMAL);
+        notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer");
+        notify_notification_set_app_name (notification, _("Software Updates"));
+        notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL);
+        notify_notification_add_action (notification, "ignore",
+                                        /* TRANSLATORS: don't install updates now */
+                                        _("Not Now"),
+                                        libnotify_action_cb,
+                                        manager, NULL);
+        notify_notification_add_action (notification, "distro-upgrade-info",
+                                        /* TRANSLATORS: provides more information about the upgrade */
+                                        _("More information"),
+                                        libnotify_action_cb,
+                                        manager, NULL);
+        g_signal_connect (notification, "closed",
+                          G_CALLBACK (on_notification_closed), NULL);
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+out:
+        if (error_code != NULL)
+                g_object_unref (error_code);
+        if (array != NULL)
+                g_ptr_array_unref (array);
+        if (string != NULL)
+                g_string_free (string, TRUE);
+        if (results != NULL)
+                g_object_unref (results);
+}
+
+static void
+due_get_upgrades_cb (GsdUpdatesRefresh *refresh, GsdUpdatesManager *manager)
+{
+        /* optimize the amount of downloaded data by setting the cache age */
+        pk_client_set_cache_age (PK_CLIENT(manager->priv->task),
+                                 g_settings_get_int (manager->priv->settings_gsd,
+                                                     GSD_SETTINGS_FREQUENCY_GET_UPGRADES));
+
+        /* get new distro upgrades list */
+        pk_client_get_distro_upgrades_async (PK_CLIENT(manager->priv->task),
+                                             NULL,
+                                             NULL, NULL,
+                                             (GAsyncReadyCallback) get_distro_upgrades_finished_cb,
+                                             manager);
+}
+
+static void
+refresh_cache_finished_cb (GObject *object, GAsyncResult *res, GsdUpdatesManager *manager)
+{
+        PkClient *client = PK_CLIENT(object);
+        PkResults *results;
+        GError *error = NULL;
+        PkError *error_code = NULL;
+
+        /* get the results */
+        results = pk_client_generic_finish (PK_CLIENT(client), res, &error);
+        if (results == NULL) {
+                if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+                        g_error_free (error);
+                        return;
+                }
+                g_warning ("failed to refresh the cache: %s",
+                           error->message);
+                g_error_free (error);
+                return;
+        }
+
+        /* check error code */
+        error_code = pk_results_get_error_code (results);
+        if (error_code != NULL) {
+                g_warning ("failed to refresh the cache: %s, %s",
+                           pk_error_enum_to_string (pk_error_get_code (error_code)),
+                           pk_error_get_details (error_code));
+        }
+
+        if (error_code != NULL)
+                g_object_unref (error_code);
+        if (results != NULL)
+                g_object_unref (results);
+}
+
+static void
+due_refresh_cache_cb (GsdUpdatesRefresh *refresh, GsdUpdatesManager *manager)
+{
+        /* optimize the amount of downloaded data by setting the cache age */
+        pk_client_set_cache_age (PK_CLIENT(manager->priv->task),
+                                 g_settings_get_int (manager->priv->settings_gsd,
+                                                     GSD_SETTINGS_FREQUENCY_REFRESH_CACHE));
+
+        pk_client_refresh_cache_async (PK_CLIENT(manager->priv->task),
+                                       TRUE,
+                                       NULL,
+                                       NULL, NULL,
+                                       (GAsyncReadyCallback) refresh_cache_finished_cb,
+                                       manager);
+}
+
+static void
+notify_critical_updates (GsdUpdatesManager *manager, GPtrArray *array)
+{
+        const gchar *message;
+        const gchar *title;
+        gboolean ret;
+        GError *error = NULL;
+        NotifyNotification *notification;
+
+        /* if the number of critical updates is the same as the last notification,
+         * then skip the notifcation as we don't want to bombard the user every hour */
+        if (array->len == manager->priv->number_updates_critical_last_shown) {
+                g_debug ("ignoring as user ignored last warning");
+                return;
+        }
+
+        /* save for comparison later */
+        manager->priv->number_updates_critical_last_shown = array->len;
+
+        /* TRANSLATORS: title in the libnotify popup */
+        title = ngettext ("Update", "Updates", array->len);
+
+        /* TRANSLATORS: message when there are security updates */
+        message = ngettext ("An important software update is available",
+                            "Important software updates are available", array->len);
+
+        /* close any existing notification */
+        if (manager->priv->notification_updates != NULL) {
+                notify_notification_close (manager->priv->notification_updates, NULL);
+                manager->priv->notification_updates = NULL;
+        }
+
+        /* do the bubble */
+        g_debug ("title=%s, message=%s", title, message);
+        notification = notify_notification_new (title,
+                                                message,
+                                                GSD_UPDATES_ICON_URGENT);
+        notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer");
+        notify_notification_set_app_name (notification, _("Software Updates"));
+        notify_notification_set_timeout (notification, 15000);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_CRITICAL);
+        notify_notification_add_action (notification, "ignore",
+                                        /* TRANSLATORS: don't install updates now */
+                                        _("Not Now"),
+                                        libnotify_action_cb,
+                                        manager, NULL);
+        notify_notification_add_action (notification, "show-update-viewer",
+                                        /* TRANSLATORS: button: open the update viewer to install updates */
+                                        _("Install updates"), libnotify_action_cb, manager, NULL);
+        g_signal_connect (notification, "closed",
+                          G_CALLBACK (on_notification_closed), NULL);
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+        /* track so we can prevent doubled notifications */
+        manager->priv->notification_updates = notification;
+        g_object_add_weak_pointer (G_OBJECT (manager->priv->notification_updates),
+                                   (void **) &manager->priv->notification_updates);
+}
+
+static void
+notify_normal_updates_maybe (GsdUpdatesManager *manager, GPtrArray *array)
+{
+        const gchar *message;
+        const gchar *title;
+        gboolean ret;
+        GError *error = NULL;
+        guint64 time_last_notify;
+        guint64 time_now;
+        guint freq_updates_notify;
+        NotifyNotification *notification;
+
+        /* find out if enough time has passed since the last notification */
+        time_now = g_get_real_time () / 1000000;
+        freq_updates_notify = g_settings_get_int (manager->priv->settings_gsd,
+                                                  GSD_SETTINGS_FREQUENCY_UPDATES_NOTIFICATION);
+        g_settings_get (manager->priv->settings_gsd,
+                        GSD_SETTINGS_LAST_UPDATES_NOTIFICATION,
+                        "t", &time_last_notify);
+        if (time_last_notify > 0 &&
+            (guint64) freq_updates_notify > time_now - time_last_notify) {
+                g_debug ("not showing non-critical notification as already shown %i hours ago",
+                        (guint) (time_now - time_last_notify) / (60 * 60));
+                return;
+        }
+
+        /* TRANSLATORS: title in the libnotify popup */
+        title = ngettext ("Update", "Updates", array->len);
+
+        /* TRANSLATORS: message when there are non-security updates */
+        message = ngettext ("A software update is available.",
+                            "Software updates are available.", array->len);
+
+        /* close any existing notification */
+        if (manager->priv->notification_updates != NULL) {
+                notify_notification_close (manager->priv->notification_updates, NULL);
+                manager->priv->notification_updates = NULL;
+        }
+
+        /* do the bubble */
+        g_debug ("title=%s, message=%s", title, message);
+        notification = notify_notification_new (title,
+                                                message,
+                                                GSD_UPDATES_ICON_NORMAL);
+        notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer");
+        notify_notification_set_app_name (notification, _("Software Updates"));
+        notify_notification_set_timeout (notification, 15000);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL);
+        notify_notification_add_action (notification, "ignore",
+                                        /* TRANSLATORS: don't install updates now */
+                                        _("Not Now"),
+                                        libnotify_action_cb,
+                                        manager, NULL);
+        notify_notification_add_action (notification, "show-update-viewer",
+                                        /* TRANSLATORS: button: open the update viewer to install updates */
+                                        _("Install updates"), libnotify_action_cb, manager, NULL);
+        g_signal_connect (notification, "closed",
+                          G_CALLBACK (on_notification_closed), NULL);
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+
+        /* reset notification time */
+        g_settings_set (manager->priv->settings_gsd,
+                        GSD_SETTINGS_LAST_UPDATES_NOTIFICATION,
+                        "t", time_now);
+
+        /* track so we can prevent doubled notifications */
+        manager->priv->notification_updates = notification;
+        g_object_add_weak_pointer (G_OBJECT (manager->priv->notification_updates),
+                                   (void **) &manager->priv->notification_updates);
+}
+
+static void
+notify_failed_get_updates_maybe (GsdUpdatesManager *manager)
+{
+        const gchar *button;
+        const gchar *message;
+        const gchar *title;
+        gboolean ret;
+        GError *error = NULL;
+        NotifyNotification *notification;
+
+        /* give the user a break */
+        if (manager->priv->failed_get_updates_count++ < MAX_FAILED_GET_UPDATES) {
+                g_debug ("failed GetUpdates, but will retry %i more times before notification",
+                         MAX_FAILED_GET_UPDATES - manager->priv->failed_get_updates_count);
+                goto out;
+        }
+
+        /* TRANSLATORS: the updates mechanism */
+        title = _("Updates");
+
+        /* TRANSLATORS: we failed to get the updates multiple times,
+         * and now we need to inform the user that something might be wrong */
+        message = _("Unable to access software updates");
+
+        /* TRANSLATORS: try again, this time launching the update viewer */
+        button = _("Try again");
+
+        notification = notify_notification_new (title,
+                                                message,
+                                                GSD_UPDATES_ICON_NORMAL);
+        notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer");
+        notify_notification_set_app_name (notification, _("Software Updates"));
+        notify_notification_set_timeout (notification, 120*1000);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL);
+        notify_notification_add_action (notification, "show-update-viewer",
+                                        button,
+                                        libnotify_action_cb,
+                                        manager, NULL);
+        g_signal_connect (notification, "closed",
+                          G_CALLBACK (on_notification_closed), NULL);
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("failed to show notification: %s",
+                           error->message);
+                g_error_free (error);
+        }
+out:
+        /* reset, even if the message failed */
+        manager->priv->failed_get_updates_count = 0;
+}
+
+static void
+check_updates_for_importance (GsdUpdatesManager *manager)
+{
+        guint i;
+        PkPackage *pkg;
+        GPtrArray *important_array;
+
+        /* check each package */
+        important_array = g_ptr_array_new ();
+        for (i = 0; i < manager->priv->update_packages->len; i++) {
+                pkg = g_ptr_array_index (manager->priv->update_packages, i);
+                if (pk_package_get_info (pkg) == PK_INFO_ENUM_SECURITY ||
+                    pk_package_get_info (pkg) == PK_INFO_ENUM_IMPORTANT)
+                        g_ptr_array_add (important_array, pkg);
+        }
+        if (important_array->len > 0) {
+                notify_critical_updates (manager,
+                                         important_array);
+        } else {
+                notify_normal_updates_maybe (manager,
+                                             manager->priv->update_packages);
+        }
+        g_ptr_array_unref (important_array);
+}
+
+static void
+package_download_finished_cb (GObject *object,
+                              GAsyncResult *res,
+                              GsdUpdatesManager *manager)
+{
+        PkClient *client = PK_CLIENT(object);
+        PkResults *results;
+        GError *error = NULL;
+        PkError *error_code = NULL;
+
+        /* get the results */
+        results = pk_client_generic_finish (PK_CLIENT(client), res, &error);
+        if (results == NULL) {
+                if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+                        g_error_free (error);
+                        return;
+                }
+                g_warning ("failed to download: %s",
+                           error->message);
+                g_error_free (error);
+                notify_failed_get_updates_maybe (manager);
+                return;
+        }
+
+        /* check error code */
+        error_code = pk_results_get_error_code (results);
+        if (error_code != NULL) {
+                g_warning ("failed to download: %s, %s",
+                           pk_error_enum_to_string (pk_error_get_code (error_code)),
+                           pk_error_get_details (error_code));
+                switch (pk_error_get_code (error_code)) {
+                case PK_ERROR_ENUM_CANCELLED_PRIORITY:
+                case PK_ERROR_ENUM_TRANSACTION_CANCELLED:
+                        g_debug ("ignoring error");
+                        break;
+                default:
+                        notify_failed_get_updates_maybe (manager);
+                        break;
+                }
+                goto out;
+        }
+
+        /* check to see if should notify */
+        check_updates_for_importance (manager);
+
+out:
+        if (error_code != NULL)
+                g_object_unref (error_code);
+        if (results != NULL)
+                g_object_unref (results);
+}
+
+static void
+auto_download_updates (GsdUpdatesManager *manager)
+{
+        gchar **package_ids;
+        guint i;
+        PkPackage *pkg;
+
+        /* download each package */
+        package_ids = g_new0 (gchar *, manager->priv->update_packages->len + 1);
+        for (i = 0; i < manager->priv->update_packages->len; i++) {
+                pkg = g_ptr_array_index (manager->priv->update_packages, i);
+                package_ids[i] = g_strdup (pk_package_get_id (pkg));
+        }
+
+        /* we've set only-download in PkTask */
+        pk_task_update_packages_async (manager->priv->task,
+                                       package_ids,
+                                       manager->priv->cancellable,
+                                       NULL, NULL,
+                                       (GAsyncReadyCallback) package_download_finished_cb,
+                                       manager);
+        g_strfreev (package_ids);
+}
+
+static void
+get_updates_finished_cb (GObject *object,
+                         GAsyncResult *res,
+                         GsdUpdatesManager *manager)
+{
+        PkClient *client = PK_CLIENT(object);
+        PkResults *results;
+        GError *error = NULL;
+        gboolean ret;
+        PkError *error_code = NULL;
+
+        /* get the results */
+        results = pk_client_generic_finish (PK_CLIENT(client), res, &error);
+        if (results == NULL) {
+                if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+                        g_error_free (error);
+                        return;
+                }
+                g_warning ("failed to get updates: %s",
+                           error->message);
+                g_error_free (error);
+                notify_failed_get_updates_maybe (manager);
+                goto out;
+        }
+
+        /* check error code */
+        error_code = pk_results_get_error_code (results);
+        if (error_code != NULL) {
+                g_warning ("failed to get updates: %s, %s",
+                           pk_error_enum_to_string (pk_error_get_code (error_code)),
+                           pk_error_get_details (error_code));
+                switch (pk_error_get_code (error_code)) {
+                case PK_ERROR_ENUM_CANCELLED_PRIORITY:
+                case PK_ERROR_ENUM_TRANSACTION_CANCELLED:
+                        g_debug ("ignoring error");
+                        break;
+                default:
+                        notify_failed_get_updates_maybe (manager);
+                        break;
+                }
+                goto out;
+        }
+
+        /* we succeeded, so clear the count */
+        manager->priv->failed_get_updates_count = 0;
+
+        /* so we can download or check for important & security updates */
+        if (manager->priv->update_packages != NULL)
+                g_ptr_array_unref (manager->priv->update_packages);
+        manager->priv->update_packages = pk_results_get_package_array (results);
+
+        /* we have no updates */
+        if (manager->priv->update_packages->len == 0) {
+                g_debug ("no updates");
+                goto out;
+        }
+
+        /* should we auto-download the updates? */
+        ret = g_settings_get_boolean (manager->priv->settings_gsd,
+                                      GSD_SETTINGS_AUTO_DOWNLOAD_UPDATES);
+        if (ret) {
+                auto_download_updates (manager);
+                goto out;
+        }
+
+        /* just check to see if should notify */
+        check_updates_for_importance (manager);
+out:
+        if (error_code != NULL)
+                g_object_unref (error_code);
+        if (results != NULL)
+                g_object_unref (results);
+}
+
+static void
+query_updates (GsdUpdatesManager *manager)
+{
+        /* optimize the amount of downloaded data by setting the cache age */
+        pk_client_set_cache_age (PK_CLIENT(manager->priv->task),
+                                 g_settings_get_int (manager->priv->settings_gsd,
+                                                     GSD_SETTINGS_FREQUENCY_GET_UPDATES));
+
+        /* get new update list */
+        pk_client_get_updates_async (PK_CLIENT(manager->priv->task),
+                                     pk_bitfield_value (PK_FILTER_ENUM_NONE),
+                                     manager->priv->cancellable,
+                                     NULL, NULL,
+                                     (GAsyncReadyCallback) get_updates_finished_cb,
+                                     manager);
+}
+
+static void
+due_get_updates_cb (GsdUpdatesRefresh *refresh, GsdUpdatesManager *manager)
+{
+        query_updates (manager);
+}
+
+static gchar *
+get_proxy_http (GsdUpdatesManager *manager)
+{
+        gboolean ret;
+        gchar *host = NULL;
+        gchar *password = NULL;
+        gchar *proxy = NULL;
+        gchar *username = NULL;
+        GString *string = NULL;
+        guint port;
+        GDesktopProxyMode proxy_mode;
+
+        proxy_mode = g_settings_get_enum (manager->priv->settings_proxy, "mode");
+        if (proxy_mode != G_DESKTOP_PROXY_MODE_MANUAL)
+                goto out;
+
+        host = g_settings_get_string (manager->priv->settings_http,
+                                      "host");
+        if (host == NULL)
+                goto out;
+        port = g_settings_get_int (manager->priv->settings_http,
+                                   "port");
+
+        /* use an HTTP auth string? */
+        ret = g_settings_get_boolean (manager->priv->settings_http,
+                                      "use-authentication");
+        if (ret) {
+                username = g_settings_get_string (manager->priv->settings_http,
+                                                  "authentication-user");
+                password = g_settings_get_string (manager->priv->settings_http,
+                                                  "authentication-password");
+        }
+
+        /* make PackageKit proxy string */
+        string = g_string_new (host);
+        if (port > 0)
+                g_string_append_printf (string, ":%i", port);
+        if (username != NULL && password != NULL)
+                g_string_append_printf (string, "@%s:%s", username, password);
+        else if (username != NULL)
+                g_string_append_printf (string, "@%s", username);
+        else if (password != NULL)
+                g_string_append_printf (string, "@:%s", password);
+        proxy = g_string_free (string, FALSE);
+out:
+        g_free (host);
+        g_free (username);
+        g_free (password);
+        return proxy;
+}
+
+static gchar *
+get_proxy_ftp (GsdUpdatesManager *manager)
+{
+        gchar *host = NULL;
+        gchar *proxy = NULL;
+        GString *string = NULL;
+        guint port;
+        GDesktopProxyMode proxy_mode;
+
+        proxy_mode = g_settings_get_enum (manager->priv->settings_proxy, "mode");
+        if (proxy_mode != G_DESKTOP_PROXY_MODE_MANUAL)
+                goto out;
+
+        host = g_settings_get_string (manager->priv->settings_ftp,
+                                      "host");
+        if (host == NULL)
+                goto out;
+        port = g_settings_get_int (manager->priv->settings_ftp,
+                                   "port");
+        if (port == 0)
+                goto out;
+
+        /* make PackageKit proxy string */
+        string = g_string_new (host);
+        if (port > 0)
+                g_string_append_printf (string, ":%i", port);
+        proxy = g_string_free (string, FALSE);
+out:
+        g_free (host);
+        return proxy;
+}
+
+
+static void
+set_proxy_cb (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+        gboolean ret;
+        GError *error = NULL;
+        PkControl *control = PK_CONTROL (object);
+
+        /* get the result */
+        ret = pk_control_set_proxy_finish (control, res, &error);
+        if (!ret) {
+                if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+                        g_warning ("failed to set proxies: %s", error->message);
+                g_error_free (error);
+        }
+}
+
+static void
+reload_proxy_settings (GsdUpdatesManager *manager)
+{
+#if 0
+        gchar *proxy_http;
+        gchar *proxy_ftp;
+
+        proxy_http = get_proxy_http (manager);
+        proxy_ftp = get_proxy_ftp (manager);
+
+        /* send to daemon */
+        pk_control_set_proxy_async (manager->priv->control,
+                                    proxy_http,
+                                    proxy_ftp,
+                                    NULL,
+                                    set_proxy_cb,
+                                    manager);
+
+        g_free (proxy_http);
+        g_free (proxy_ftp);
+#endif
+}
+
+static void
+settings_changed_cb (GSettings         *settings,
+                     const char        *key,
+                     GsdUpdatesManager *manager)
+{
+        reload_proxy_settings (manager);
+}
+
+static void
+settings_gsd_changed_cb (GSettings         *settings,
+                         const char        *key,
+                         GsdUpdatesManager *manager)
+{
+}
+
+static void
+session_inhibit (GsdUpdatesManager *manager)
+{
+        const gchar *reason;
+        GError *error = NULL;
+        GVariant *retval = NULL;
+
+        /* state invalid somehow */
+        if (manager->priv->inhibit_cookie != 0) {
+                g_warning ("already locked");
+                goto out;
+        }
+
+        /* TRANSLATORS: the reason why we've inhibited it */
+        reason = _("A transaction that cannot be interrupted is running");
+        retval = g_dbus_proxy_call_sync (G_DBUS_PROXY (manager->priv->proxy_session),
+                                         "Inhibit",
+                                         g_variant_new ("(susu)",
+                                                        "gnome-settings-daemon", /* app-id */
+                                                        0, /* xid */
+                                                        reason, /* reason */
+                                                        4 /* flags */),
+                                         G_DBUS_CALL_FLAGS_NONE,
+                                         -1,
+                                         manager->priv->cancellable,
+                                         &error);
+        if (retval == NULL) {
+                g_warning ("failed to inhibit gnome-session: %s",
+                           error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* get cookie */
+        g_variant_get (retval, "(u)",
+                       &manager->priv->inhibit_cookie);
+out:
+        if (retval != NULL)
+                g_variant_unref (retval);
+}
+
+static void
+session_uninhibit (GsdUpdatesManager *manager)
+{
+        GError *error = NULL;
+        GVariant *retval = NULL;
+
+        /* state invalid somehow */
+        if (manager->priv->inhibit_cookie == 0) {
+                g_warning ("not locked");
+                goto out;
+        }
+        retval = g_dbus_proxy_call_sync (G_DBUS_PROXY (manager->priv->proxy_session),
+                                         "Uninhibit",
+                                         g_variant_new ("(u)",
+                                                        manager->priv->inhibit_cookie),
+                                         G_DBUS_CALL_FLAGS_NONE,
+                                         -1,
+                                         manager->priv->cancellable,
+                                         &error);
+        if (retval == NULL) {
+                g_warning ("failed to uninhibit gnome-session: %s",
+                           error->message);
+                g_error_free (error);
+                goto out;
+        }
+out:
+        manager->priv->inhibit_cookie = 0;
+        if (retval != NULL)
+                g_variant_unref (retval);
+}
+
+static void
+notify_locked_cb (PkControl *control,
+                  GParamSpec *pspec,
+                  GsdUpdatesManager *manager)
+{
+        gboolean locked;
+
+        g_object_get (control, "locked", &locked, NULL);
+
+        /* TODO: locked is a bit harsh, we can probably still allow
+         * reboot when packages are downloading or the transaction is
+         * depsolving */
+        if (locked) {
+                session_inhibit (manager);
+        } else {
+                session_uninhibit (manager);
+        }
+}
+
+static void
+update_viewer_appeared_cb (GDBusConnection *connection,
+                           const gchar *name,
+                           const gchar *name_owner,
+                           gpointer user_data)
+{
+        GsdUpdatesManager *manager = GSD_UPDATES_MANAGER (user_data);
+
+        /* close any existing notification */
+        if (manager->priv->notification_updates != NULL) {
+                g_debug ("update viewer on the bus, clearing bubble");
+                notify_notification_close (manager->priv->notification_updates, NULL);
+                manager->priv->notification_updates = NULL;
+        }
+}
+
+static gboolean
+file_exists_in_root (const gchar *root, const gchar *filename)
+{
+        gboolean ret = FALSE;
+        GFile *source;
+        gchar *source_path;
+
+        source_path = g_build_filename (root, filename, NULL);
+        source = g_file_new_for_path (source_path);
+
+        /* ignore virtual mountpoints */
+        if (!g_file_is_native (source))
+                goto out;
+
+        /* an interesting file exists */
+        ret = g_file_query_exists (source, NULL);
+        g_debug ("checking for %s: %s", source_path, ret ? "yes" : "no");
+        if (!ret)
+                goto out;
+out:
+        g_free (source_path);
+        g_object_unref (source);
+        return ret;
+}
+
+static void
+mount_added_cb (GVolumeMonitor *volume_monitor,
+                GMount *mount,
+                GsdUpdatesManager *manager)
+{
+        gboolean ret = FALSE;
+        gchar **filenames = NULL;
+        gchar *media_repo_filenames;
+        gchar *root_path;
+        GFile *root;
+        guint i;
+
+        /* check if any installed media is an install disk */
+        root = g_mount_get_root (mount);
+        root_path = g_file_get_path (root);
+
+        /* use settings */
+        media_repo_filenames = g_settings_get_string (manager->priv->settings_gsd,
+                                                      GSD_SETTINGS_MEDIA_REPO_FILENAMES);
+        if (media_repo_filenames == NULL) {
+                g_warning ("failed to get media repo filenames");
+                goto out;
+        }
+
+        /* search each possible filename */
+        filenames = g_strsplit (media_repo_filenames, ",", -1);
+        for (i=0; filenames[i] != NULL; i++) {
+                ret = file_exists_in_root (root_path, filenames[i]);
+                if (ret)
+                        break;
+        }
+
+        /* do an updates check with the new media */
+        if (ret)
+                query_updates (manager);
+out:
+        g_strfreev (filenames);
+        g_free (media_repo_filenames);
+        g_free (root_path);
+        g_object_unref (root);
+}
+
+#define PK_OFFLINE_UPDATE_RESULTS_GROUP		"PackageKit Offline Update Results"
+
+static gboolean
+check_offline_update_cb (gpointer user_data)
+{
+        const gchar *message;
+        const gchar *title;
+        gboolean ret;
+        gboolean success;
+        gchar *error_code = NULL;
+        gchar *error_details = NULL;
+        gchar *packages = NULL;
+        GError *error = NULL;
+        GKeyFile *key_file = NULL;
+        GsdUpdatesManager *manager = (GsdUpdatesManager *) user_data;
+        guint i;
+        guint num_packages = 1;
+        NotifyNotification *notification;
+        PkErrorEnum error_enum = PK_ERROR_ENUM_UNKNOWN;
+
+        /* was any offline update attempted */
+        if (!g_file_test (PK_OFFLINE_UPDATE_RESULTS_FILENAME, G_FILE_TEST_EXISTS))
+                goto out;
+
+        /* open the file and see what happened */
+        key_file = g_key_file_new ();
+        ret = g_key_file_load_from_file (key_file,
+                                         PK_OFFLINE_UPDATE_RESULTS_FILENAME,
+                                         G_KEY_FILE_NONE,
+                                         &error);
+        if (!ret) {
+                g_warning ("failed to open %s: %s",
+                           PK_OFFLINE_UPDATE_RESULTS_FILENAME,
+                           error->message);
+                g_error_free (error);
+                goto out;
+        }
+        success = g_key_file_get_boolean (key_file,
+                                          PK_OFFLINE_UPDATE_RESULTS_GROUP,
+                                          "Success",
+                                          NULL);
+        if (success) {
+                packages = g_key_file_get_string (key_file,
+                                                  PK_OFFLINE_UPDATE_RESULTS_GROUP,
+                                                  "Packages",
+                                                  NULL);
+                if (packages == NULL) {
+                        g_warning ("No 'Packages' in %s",
+                                   PK_OFFLINE_UPDATE_RESULTS_FILENAME);
+                        goto out;
+                }
+
+                /* count the packages for translators */
+                for (i = 0; packages[i] != '\0'; i++) {
+                        if (packages[i] == ',')
+                                num_packages++;
+                }
+
+                /* TRANSLATORS: title in the libnotify popup */
+                title = ngettext ("Software Update Installed",
+                                  "Software Updates Installed",
+                                  num_packages);
+
+                /* TRANSLATORS: message when we've done offline updates */
+                message = ngettext ("An important OS update has been installed.",
+                                    "Important OS updates have been installed.",
+                                    num_packages);
+        } else {
+                /* get error details */
+                manager->priv->offline_update_error = pk_error_new ();
+
+                error_code = g_key_file_get_string (key_file,
+                                                    PK_OFFLINE_UPDATE_RESULTS_GROUP,
+                                                    "ErrorCode",
+                                                    NULL);
+                if (error_code != NULL)
+                        error_enum = pk_error_enum_from_string (error_code);
+                error_details = g_key_file_get_string (key_file,
+                                                       PK_OFFLINE_UPDATE_RESULTS_GROUP,
+                                                       "ErrorDetails",
+                                                       NULL);
+                g_object_set (manager->priv->offline_update_error,
+                              "code", error_enum,
+                              "details", error_details,
+                              NULL);
+
+                /* TRANSLATORS: title in the libnotify popup */
+                title = _("Software Updates Failed");
+
+                /* TRANSLATORS: message when we've not done offline updates */
+                message = _("An important OS update failed to be installed.");
+        }
+
+        /* do the bubble */
+        g_debug ("title=%s, message=%s", title, message);
+
+        /* close any existing notification */
+        if (manager->priv->notification_offline_success != NULL) {
+                notify_notification_close (manager->priv->notification_offline_success, NULL);
+                manager->priv->notification_offline_success = NULL;
+        }
+
+        notification = notify_notification_new (title,
+                                                message,
+                                                GSD_UPDATES_ICON_URGENT);
+        notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer");
+        notify_notification_set_app_name (notification, _("Software Updates"));
+        notify_notification_set_timeout (notification, -1);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL);
+        if (!success) {
+                notify_notification_add_action (notification, "error-offline-updates",
+                                                /* TRANSLATORS: button: review the offline update changes */
+                                                _("Show details"), libnotify_action_cb, manager, NULL);
+        }
+        notify_notification_add_action (notification, "clear-offline-updates",
+                                        /* TRANSLATORS: button: clear notification */
+                                        _("OK"), libnotify_action_cb, manager, NULL);
+        g_signal_connect (notification, "closed",
+                          G_CALLBACK (on_notification_closed), NULL);
+        manager->priv->notification_offline_success = notification;
+        g_object_add_weak_pointer (G_OBJECT (manager->priv->notification_offline_success),
+                                   (void **) &manager->priv->notification_offline_success);
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+out:
+        g_free (packages);
+        g_free (error_code);
+        g_free (error_details);
+        if (key_file != NULL)
+                g_key_file_free (key_file);
+        manager->priv->offline_update_id = 0;
+        return FALSE;
+}
+
+static void
+reboot_failed (GObject *source, GAsyncResult *res, gpointer data)
+{
+        GVariant *ret;
+        const gchar *command;
+        GError *error = NULL;
+
+        ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
+        if (ret) {
+                g_variant_unref (ret);
+                return;
+        }
+
+        if (error) {
+                g_warning ("Calling org.gnome.SessionManager.Reboot failed: %s\n", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        command = "pkexec /usr/libexec/pk-trigger-offline-update --cancel";
+        g_debug ("calling '%s'", command);
+        if (!g_spawn_command_line_sync (command, NULL, NULL, NULL, &error)) {
+                g_warning ("Failed to call '%s': %s\n", command, error->message);
+                g_error_free (error);
+        }
+}
+
+static void
+restart_and_install_updates (void)
+{
+        GDBusConnection *bus;
+        const gchar *command;
+        GError *error = NULL;
+
+        command = "pkexec /usr/libexec/pk-trigger-offline-update";
+        g_debug ("calling '%s'", command);
+        if (!g_spawn_command_line_sync (command, NULL, NULL, NULL, &error)) {
+                g_warning ("Failed to call '%s': %s\n", command, error->message);
+                g_error_free (error);
+                return;
+        }
+
+        g_debug ("calling org.gnome.SessionManager.Reboot");
+        bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+        g_dbus_connection_call (bus,
+                                "org.gnome.SessionManager",
+                                "/org/gnome/SessionManager",
+                                "org.gnome.SessionManager",
+                                "Reboot",
+                                NULL,
+                                NULL,
+                                G_DBUS_CALL_FLAGS_NONE,
+                                G_MAXINT,
+                                NULL,
+                                reboot_failed,
+                                NULL);
+        g_object_unref (bus);
+}
+
+gboolean
+gsd_updates_manager_start (GsdUpdatesManager *manager,
+                           GError **error)
+{
+        gboolean ret = FALSE;
+
+        g_debug ("Starting updates manager");
+
+        /* use PackageKit */
+        manager->priv->cancellable = g_cancellable_new ();
+        manager->priv->control = pk_control_new ();
+        g_signal_connect (manager->priv->control, "notify::locked",
+                          G_CALLBACK (notify_locked_cb), manager);
+        manager->priv->task = pk_task_new ();
+        g_object_set (manager->priv->task,
+                      "background", TRUE,
+                      "interactive", FALSE,
+                      "only-download", TRUE,
+                      NULL);
+
+        /* watch UDev for missing firmware */
+        manager->priv->firmware = gsd_updates_firmware_new ();
+
+        /* get automatic callbacks about when we should check for
+         * updates, refresh-caches and upgrades */
+        manager->priv->refresh = gsd_updates_refresh_new ();
+        g_signal_connect (manager->priv->refresh, "get-upgrades",
+                          G_CALLBACK (due_get_upgrades_cb), manager);
+        g_signal_connect (manager->priv->refresh, "refresh-cache",
+                          G_CALLBACK (due_refresh_cache_cb), manager);
+        g_signal_connect (manager->priv->refresh, "get-updates",
+                          G_CALLBACK (due_get_updates_cb), manager);
+
+        /* get proxy settings */
+        manager->priv->settings_proxy = g_settings_new ("org.gnome.system.proxy");
+        g_signal_connect (manager->priv->settings_proxy, "changed",
+                          G_CALLBACK (settings_changed_cb), manager);
+
+        /* get http settings */
+        manager->priv->settings_http = g_settings_new ("org.gnome.system.proxy.http");
+        g_signal_connect (manager->priv->settings_http, "changed",
+                          G_CALLBACK (settings_changed_cb), manager);
+
+        /* get ftp settings */
+        manager->priv->settings_ftp = g_settings_new ("org.gnome.system.proxy.ftp");
+        g_signal_connect (manager->priv->settings_ftp, "changed",
+                          G_CALLBACK (settings_changed_cb), manager);
+
+        /* get ftp settings */
+        manager->priv->settings_gsd = g_settings_new ("org.gnome.settings-daemon.plugins.updates");
+        g_signal_connect (manager->priv->settings_gsd, "changed",
+                          G_CALLBACK (settings_gsd_changed_cb), manager);
+
+        /* use gnome-session for the idle detection */
+        manager->priv->proxy_session =
+                gnome_settings_bus_get_session_proxy ();
+        if (manager->priv->proxy_session == NULL)
+                goto out;
+
+        /* if the update viewer is started, then hide the notification */
+        manager->priv->update_viewer_watcher_id =
+                g_bus_watch_name (G_BUS_TYPE_SESSION,
+                                  "org.freedesktop.PackageKit.UpdateViewer",
+                                  G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                  update_viewer_appeared_cb,
+                                  NULL,
+                                  manager,
+                                  NULL);
+
+        /* get a volume monitor so we can watch media */
+        manager->priv->volume_monitor = g_volume_monitor_get ();
+        g_signal_connect (manager->priv->volume_monitor, "mount-added",
+                          G_CALLBACK (mount_added_cb), manager);
+
+        /* coldplug */
+        reload_proxy_settings (manager);
+
+        /* check for offline update */
+        manager->priv->offline_update_id =
+                g_timeout_add_seconds (GSD_UPDATES_CHECK_OFFLINE_TIMEOUT,
+                                       check_offline_update_cb,
+                                       manager);
+
+        /* success */
+        ret = TRUE;
+        g_debug ("Started updates manager");
+out:
+        return ret;
+}
+
+void
+gsd_updates_manager_stop (GsdUpdatesManager *manager)
+{
+        g_debug ("Stopping updates manager");
+
+        g_clear_object (&manager->priv->settings_proxy);
+        g_clear_object (&manager->priv->settings_http);
+        g_clear_object (&manager->priv->settings_ftp);
+        g_clear_object (&manager->priv->settings_gsd);
+        g_clear_object (&manager->priv->control);
+        g_clear_object (&manager->priv->task);
+        g_clear_object (&manager->priv->refresh);
+        g_clear_object (&manager->priv->firmware);
+        g_clear_object (&manager->priv->proxy_session);
+        g_clear_object (&manager->priv->volume_monitor);
+        if (manager->priv->cancellable) {
+                g_cancellable_cancel (manager->priv->cancellable);
+                g_clear_object (&manager->priv->cancellable);
+        }
+        if (manager->priv->update_viewer_watcher_id != 0) {
+                g_bus_unwatch_name (manager->priv->update_viewer_watcher_id);
+                manager->priv->update_viewer_watcher_id = 0;
+        }
+        if (manager->priv->offline_update_id) {
+                g_source_remove (manager->priv->offline_update_id);
+                manager->priv->offline_update_id = 0;
+        }
+        if (manager->priv->update_packages != NULL) {
+                g_ptr_array_unref (manager->priv->update_packages);
+                manager->priv->update_packages = NULL;
+        }
+        g_clear_object (&manager->priv->offline_update_error);
+}
+
+static void
+gsd_updates_manager_dispose (GObject *object)
+{
+        GsdUpdatesManager *manager;
+
+        manager = GSD_UPDATES_MANAGER (object);
+
+        gsd_updates_manager_stop (manager);
+
+        G_OBJECT_CLASS (gsd_updates_manager_parent_class)->dispose (object);
+}
+
+static void
+gsd_updates_manager_class_init (GsdUpdatesManagerClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->dispose = gsd_updates_manager_dispose;
+
+        g_type_class_add_private (klass, sizeof (GsdUpdatesManagerPrivate));
+}
+
+static void
+gsd_updates_manager_init (GsdUpdatesManager *manager)
+{
+        manager->priv = GSD_UPDATES_MANAGER_GET_PRIVATE (manager);
+}
+
+GsdUpdatesManager *
+gsd_updates_manager_new (void)
+{
+        if (manager_object) {
+                g_object_ref (manager_object);
+        } else {
+                manager_object = g_object_new (GSD_TYPE_UPDATES_MANAGER, NULL);
+                g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object);
+        }
+
+        return GSD_UPDATES_MANAGER (manager_object);
+}
Index: gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-manager.h
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-manager.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Richard Hughes <richard@hughsie.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GSD_UPDATES_MANAGER_H
+#define __GSD_UPDATES_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GSD_TYPE_UPDATES_MANAGER         (gsd_updates_manager_get_type ())
+#define GSD_UPDATES_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_UPDATES_MANAGER, GsdUpdatesManager))
+#define GSD_UPDATES_MANAGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GSD_TYPE_UPDATES_MANAGER, GsdUpdatesManagerClass))
+#define GSD_IS_UPDATES_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_UPDATES_MANAGER))
+#define GSD_IS_UPDATES_MANAGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_UPDATES_MANAGER))
+#define GSD_UPDATES_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_UPDATES_MANAGER, GsdUpdatesManagerClass))
+
+typedef struct GsdUpdatesManagerPrivate GsdUpdatesManagerPrivate;
+
+typedef struct
+{
+        GObject parent;
+        GsdUpdatesManagerPrivate *priv;
+} GsdUpdatesManager;
+
+typedef struct
+{
+        GObjectClass parent_class;
+} GsdUpdatesManagerClass;
+
+GType gsd_updates_manager_get_type (void) G_GNUC_CONST;
+
+GsdUpdatesManager *gsd_updates_manager_new (void);
+gboolean gsd_updates_manager_start (GsdUpdatesManager *manager, GError **error);
+void gsd_updates_manager_stop (GsdUpdatesManager *manager);
+
+G_END_DECLS
+
+#endif /* __GSD_UPDATES_MANAGER_H */
Index: gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-plugin.c
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-plugin.c
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Richard Hughes <richard@hughsie.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+
+#include "gnome-settings-plugin.h"
+#include "gsd-updates-manager.h"
+
+GNOME_SETTINGS_PLUGIN_REGISTER (GsdUpdates, gsd_updates)
Index: gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-refresh.c
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-refresh.c
@@ -0,0 +1,573 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007-2011 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <packagekit-glib2/packagekit.h>
+#include <libupower-glib/upower.h>
+
+#include "gnome-settings-bus.h"
+
+#include "gsd-updates-common.h"
+#include "gsd-updates-refresh.h"
+
+static void     gsd_updates_refresh_finalize    (GObject            *object);
+
+#define GSD_UPDATES_REFRESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_UPDATES_REFRESH, GsdUpdatesRefreshPrivate))
+
+#define PERIODIC_CHECK_TIME     60*60   /* poke PackageKit every hour */
+#define LOGIN_TIMEOUT           3       /* seconds */
+#define SESSION_STARTUP_TIMEOUT 10      /* seconds */
+
+enum {
+        PRESENCE_STATUS_AVAILABLE = 0,
+        PRESENCE_STATUS_INVISIBLE,
+        PRESENCE_STATUS_BUSY,
+        PRESENCE_STATUS_IDLE,
+        PRESENCE_STATUS_UNKNOWN
+};
+
+/*
+ * at startup, after a small delay, force a GetUpdates call
+ * every hour (or any event) check:
+   - if we are online, idle and on AC power, it's been more than a day
+     since we refreshed then RefreshCache
+   - if we are online and it's been longer than the timeout since
+     getting the updates period then GetUpdates
+*/
+
+struct GsdUpdatesRefreshPrivate
+{
+        gboolean                 session_idle;
+        gboolean                 on_battery;
+        gboolean                 network_active;
+        guint                    timeout_id;
+        guint                    periodic_id;
+        UpClient                *client;
+        GSettings               *settings;
+        GsdSessionManager       *proxy_session;
+        PkControl               *control;
+};
+
+enum {
+        REFRESH_CACHE,
+        GET_UPDATES,
+        GET_UPGRADES,
+        LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GsdUpdatesRefresh, gsd_updates_refresh, G_TYPE_OBJECT)
+
+static void
+gsd_updates_refresh_class_init (GsdUpdatesRefreshClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+        object_class->finalize = gsd_updates_refresh_finalize;
+        g_type_class_add_private (klass, sizeof (GsdUpdatesRefreshPrivate));
+        signals [REFRESH_CACHE] =
+                g_signal_new ("refresh-cache",
+                              G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+                              0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [GET_UPDATES] =
+                g_signal_new ("get-updates",
+                              G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+                              0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [GET_UPGRADES] =
+                g_signal_new ("get-upgrades",
+                              G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+                              0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+}
+
+static void
+get_time_refresh_cache_cb (GObject *object,
+                           GAsyncResult *res,
+                           GsdUpdatesRefresh *refresh)
+{
+        PkControl *control = PK_CONTROL (object);
+        GError *error = NULL;
+        guint seconds;
+        guint thresh;
+
+        /* get the result */
+        seconds = pk_control_get_time_since_action_finish (control, res, &error);
+        if (seconds == 0) {
+                g_warning ("failed to get time: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        /* have we passed the timout? */
+        thresh = g_settings_get_int (refresh->priv->settings,
+                                     GSD_SETTINGS_FREQUENCY_GET_UPDATES);
+        if (seconds < thresh) {
+                g_debug ("not before timeout, thresh=%u, now=%u", thresh, seconds);
+                return;
+        }
+
+        /* send signal */
+        g_debug ("emitting refresh-cache");
+        g_signal_emit (refresh, signals [REFRESH_CACHE], 0);
+}
+
+static void
+maybe_refresh_cache (GsdUpdatesRefresh *refresh)
+{
+        guint thresh;
+
+        g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh));
+
+        /* if we don't want to auto check for updates, don't do this either */
+        thresh = g_settings_get_int (refresh->priv->settings,
+                                     GSD_SETTINGS_FREQUENCY_GET_UPDATES);
+        if (thresh == 0) {
+                g_debug ("not when policy is set to never");
+                return;
+        }
+
+        /* only do the refresh cache when the user is idle */
+        if (!refresh->priv->session_idle) {
+                g_debug ("not when session active");
+                return;
+        }
+
+        /* get this each time, as it may have changed behind out back */
+        thresh = g_settings_get_int (refresh->priv->settings,
+                                     GSD_SETTINGS_FREQUENCY_REFRESH_CACHE);
+        if (thresh == 0) {
+                g_debug ("not when policy is set to never");
+                return;
+        }
+
+        /* get the time since the last refresh */
+        pk_control_get_time_since_action_async (refresh->priv->control,
+                                                PK_ROLE_ENUM_REFRESH_CACHE,
+                                                NULL,
+                                                (GAsyncReadyCallback) get_time_refresh_cache_cb,
+                                                refresh);
+}
+
+static void
+get_time_get_updates_cb (GObject *object, GAsyncResult *res, GsdUpdatesRefresh *refresh)
+{
+        PkControl *control = PK_CONTROL (object);
+        GError *error = NULL;
+        guint seconds;
+        guint thresh;
+
+        /* get the result */
+        seconds = pk_control_get_time_since_action_finish (control, res, &error);
+        if (seconds == 0) {
+                g_warning ("failed to get time: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        /* have we passed the timout? */
+        thresh = g_settings_get_int (refresh->priv->settings,
+                                     GSD_SETTINGS_FREQUENCY_GET_UPDATES);
+        if (seconds < thresh) {
+                g_debug ("not before timeout, thresh=%u, now=%u", thresh, seconds);
+                return;
+        }
+
+        /* send signal */
+        g_debug ("emitting get-updates");
+        g_signal_emit (refresh, signals [GET_UPDATES], 0);
+}
+
+static void
+maybe_get_updates (GsdUpdatesRefresh *refresh)
+{
+        guint thresh;
+
+        g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh));
+
+        /* if we don't want to auto check for updates, don't do this either */
+        thresh = g_settings_get_int (refresh->priv->settings,
+                                     GSD_SETTINGS_FREQUENCY_GET_UPDATES);
+        if (thresh == 0) {
+                g_debug ("not when policy is set to never");
+                return;
+        }
+
+        /* get the time since the last refresh */
+        pk_control_get_time_since_action_async (refresh->priv->control,
+                                                PK_ROLE_ENUM_GET_UPDATES,
+                                                NULL,
+                                                (GAsyncReadyCallback) get_time_get_updates_cb,
+                                                refresh);
+}
+
+static void
+get_time_get_upgrades_cb (GObject *object,
+                          GAsyncResult *res,
+                          GsdUpdatesRefresh *refresh)
+{
+        PkControl *control = PK_CONTROL (object);
+        GError *error = NULL;
+        guint seconds;
+        guint thresh;
+
+        /* get the result */
+        seconds = pk_control_get_time_since_action_finish (control, res, &error);
+        if (seconds == 0) {
+                g_warning ("failed to get time: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        /* have we passed the timout? */
+        thresh = g_settings_get_int (refresh->priv->settings,
+                                     GSD_SETTINGS_FREQUENCY_GET_UPDATES);
+        if (seconds < thresh) {
+                g_debug ("not before timeout, thresh=%u, now=%u",
+                         thresh, seconds);
+                return;
+        }
+
+        /* send signal */
+        g_debug ("emitting get-upgrades");
+        g_signal_emit (refresh, signals [GET_UPGRADES], 0);
+}
+
+static void
+maybe_get_upgrades (GsdUpdatesRefresh *refresh)
+{
+        guint thresh;
+
+        g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh));
+
+        /* get this each time, as it may have changed behind out back */
+        thresh = g_settings_get_int (refresh->priv->settings,
+                                     GSD_SETTINGS_FREQUENCY_GET_UPGRADES);
+        if (thresh == 0) {
+                g_debug ("not when policy is set to never");
+                return;
+        }
+
+        /* get the time since the last refresh */
+        pk_control_get_time_since_action_async (refresh->priv->control,
+                                                PK_ROLE_ENUM_GET_DISTRO_UPGRADES,
+                                                NULL,
+                                                (GAsyncReadyCallback) get_time_get_upgrades_cb,
+                                                refresh);
+}
+
+static gboolean
+change_state_cb (GsdUpdatesRefresh *refresh)
+{
+        /* check all actions */
+        maybe_refresh_cache (refresh);
+        maybe_get_updates (refresh);
+        maybe_get_upgrades (refresh);
+        return FALSE;
+}
+
+static gboolean
+change_state (GsdUpdatesRefresh *refresh)
+{
+        gboolean ret;
+
+        g_return_val_if_fail (GSD_IS_UPDATES_REFRESH (refresh), FALSE);
+
+        /* no point continuing if we have no network */
+        if (!refresh->priv->network_active) {
+                g_debug ("not when no network");
+                return FALSE;
+        }
+
+        /* not on battery unless overridden */
+        ret = g_settings_get_boolean (refresh->priv->settings,
+                                      GSD_SETTINGS_UPDATE_BATTERY);
+        if (!ret && refresh->priv->on_battery) {
+                g_debug ("not when on battery");
+                return FALSE;
+        }
+
+        /* wait a little time for things to settle down */
+        if (refresh->priv->timeout_id != 0)
+                g_source_remove (refresh->priv->timeout_id);
+        g_debug ("defering action for %i seconds",
+                 SESSION_STARTUP_TIMEOUT);
+        refresh->priv->timeout_id =
+                g_timeout_add_seconds (SESSION_STARTUP_TIMEOUT,
+                                       (GSourceFunc) change_state_cb,
+                                       refresh);
+        g_source_set_name_by_id (refresh->priv->timeout_id,
+                                 "[GsdUpdatesRefresh] change-state");
+
+        return TRUE;
+}
+
+static void
+settings_key_changed_cb (GSettings *client,
+                         const gchar *key,
+                         GsdUpdatesRefresh *refresh)
+{
+        g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh));
+        if (g_strcmp0 (key, GSD_SETTINGS_FREQUENCY_GET_UPDATES) == 0 ||
+            g_strcmp0 (key, GSD_SETTINGS_FREQUENCY_GET_UPGRADES) == 0 ||
+            g_strcmp0 (key, GSD_SETTINGS_FREQUENCY_REFRESH_CACHE) == 0 ||
+            g_strcmp0 (key, GSD_SETTINGS_UPDATE_BATTERY) == 0)
+                change_state (refresh);
+}
+
+static gboolean
+convert_network_state (GsdUpdatesRefresh *refresh, PkNetworkEnum state)
+{
+        /* offline */
+        if (state == PK_NETWORK_ENUM_OFFLINE)
+                return FALSE;
+
+        /* online */
+        if (state == PK_NETWORK_ENUM_ONLINE ||
+            state == PK_NETWORK_ENUM_WIFI ||
+            state == PK_NETWORK_ENUM_WIRED)
+                return TRUE;
+
+        /* check policy */
+        if (state == PK_NETWORK_ENUM_MOBILE)
+                return g_settings_get_boolean (refresh->priv->settings,
+                                               GSD_SETTINGS_CONNECTION_USE_MOBILE);
+
+        /* not recognised */
+        g_warning ("state unknown: %i", state);
+        return TRUE;
+}
+
+static void
+notify_network_state_cb (PkControl *control,
+                         GParamSpec *pspec,
+                         GsdUpdatesRefresh *refresh)
+{
+        PkNetworkEnum state;
+
+        g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh));
+
+        g_object_get (control, "network-state", &state, NULL);
+        refresh->priv->network_active = convert_network_state (refresh, state);
+        g_debug ("setting online %i", refresh->priv->network_active);
+        if (refresh->priv->network_active)
+                change_state (refresh);
+}
+
+static gboolean
+periodic_timeout_cb (gpointer user_data)
+{
+        GsdUpdatesRefresh *refresh = GSD_UPDATES_REFRESH (user_data);
+
+        g_return_val_if_fail (GSD_IS_UPDATES_REFRESH (refresh), FALSE);
+
+        /* debug so we can catch polling */
+        g_debug ("polling check");
+
+        /* triggered once an hour */
+        change_state (refresh);
+
+        /* always return */
+        return TRUE;
+}
+
+static void
+gsd_updates_refresh_client_on_battery_cb (UpClient *client,
+					  GParamSpec *pspec,
+					  GsdUpdatesRefresh *refresh)
+{
+        gboolean on_battery;
+
+        g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh));
+
+        /* get the on-battery state */
+        on_battery = up_client_get_on_battery (refresh->priv->client);
+        if (on_battery == refresh->priv->on_battery) {
+                g_debug ("same state as before, ignoring");
+                return;
+        }
+
+        /* save in local cache */
+        g_debug ("setting on_battery %i", on_battery);
+        refresh->priv->on_battery = on_battery;
+        if (!on_battery)
+                change_state (refresh);
+}
+
+static void
+get_properties_cb (GObject *object,
+                   GAsyncResult *res,
+                   GsdUpdatesRefresh *refresh)
+{
+        PkNetworkEnum state;
+        GError *error = NULL;
+        PkControl *control = PK_CONTROL(object);
+        gboolean ret;
+
+        /* get the result */
+        ret = pk_control_get_properties_finish (control, res, &error);
+        if (!ret) {
+                /* TRANSLATORS: backend is broken, and won't tell us what it supports */
+                g_warning ("could not get properties");
+                g_error_free (error);
+                goto out;
+        }
+
+        /* get values */
+        g_object_get (control,
+                      "network-state", &state,
+                      NULL);
+        refresh->priv->network_active = convert_network_state (refresh, state);
+out:
+        return;
+}
+
+static void
+session_presence_signal_cb (GDBusProxy *proxy,
+                            gchar *sender_name,
+                            gchar *signal_name,
+                            GVariant *parameters,
+                            GsdUpdatesRefresh *refresh)
+{
+        guint status;
+
+        g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh));
+
+        if (g_strcmp0 (signal_name, "StatusChanged") != 0)
+                return;
+
+        /* map status code into boolean */
+        g_variant_get (parameters, "(u)", &status);
+        refresh->priv->session_idle = (status == PRESENCE_STATUS_IDLE);
+        g_debug ("setting is_idle %i",
+                 refresh->priv->session_idle);
+        if (refresh->priv->session_idle)
+                change_state (refresh);
+
+}
+
+static void
+gsd_updates_refresh_init (GsdUpdatesRefresh *refresh)
+{
+        GVariant *status;
+        guint status_code;
+
+        refresh->priv = GSD_UPDATES_REFRESH_GET_PRIVATE (refresh);
+        refresh->priv->on_battery = FALSE;
+        refresh->priv->network_active = FALSE;
+        refresh->priv->timeout_id = 0;
+        refresh->priv->periodic_id = 0;
+
+        /* we need to know the updates frequency */
+        refresh->priv->settings = g_settings_new (GSD_SETTINGS_SCHEMA);
+        g_signal_connect (refresh->priv->settings, "changed",
+                          G_CALLBACK (settings_key_changed_cb), refresh);
+
+        /* we need to query the last cache refresh time */
+        refresh->priv->control = pk_control_new ();
+        g_signal_connect (refresh->priv->control, "notify::network-state",
+                          G_CALLBACK (notify_network_state_cb),
+                          refresh);
+
+        /* get network state */
+        pk_control_get_properties_async (refresh->priv->control,
+                                         NULL,
+                                         (GAsyncReadyCallback) get_properties_cb,
+                                         refresh);
+
+        /* use a UpClient */
+        refresh->priv->client = up_client_new ();
+        g_signal_connect (refresh->priv->client, "notify::on-battery",
+                          G_CALLBACK (gsd_updates_refresh_client_on_battery_cb), refresh);
+
+        /* get the battery state */
+        refresh->priv->on_battery = up_client_get_on_battery (refresh->priv->client);
+        g_debug ("setting on battery %i", refresh->priv->on_battery);
+
+        /* use gnome-session for the idle detection */
+        refresh->priv->proxy_session =
+                gnome_settings_bus_get_session_proxy ();
+        if (refresh->priv->proxy_session != NULL) {
+                g_signal_connect (G_DBUS_PROXY (refresh->priv->proxy_session),
+                                  "g-signal",
+                                  G_CALLBACK (session_presence_signal_cb),
+                                  refresh);
+                status = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (refresh->priv->proxy_session),
+                                                           "status");
+                if (status) {
+                        g_variant_get (status, "u", &status_code);
+                        refresh->priv->session_idle = (status_code == PRESENCE_STATUS_IDLE);
+                        g_variant_unref (status);
+                }
+                else {
+                        refresh->priv->session_idle = FALSE;
+                }
+        }
+
+        /* we check this in case we miss one of the async signals */
+        refresh->priv->periodic_id =
+                g_timeout_add_seconds (PERIODIC_CHECK_TIME,
+                                       periodic_timeout_cb, refresh);
+        g_source_set_name_by_id (refresh->priv->periodic_id,
+                                 "[GsdUpdatesRefresh] periodic check");
+
+        /* check system state */
+        change_state (refresh);
+}
+
+static void
+gsd_updates_refresh_finalize (GObject *object)
+{
+        GsdUpdatesRefresh *refresh;
+
+        g_return_if_fail (GSD_IS_UPDATES_REFRESH (object));
+
+        refresh = GSD_UPDATES_REFRESH (object);
+        g_return_if_fail (refresh->priv != NULL);
+
+        if (refresh->priv->timeout_id != 0)
+                g_source_remove (refresh->priv->timeout_id);
+        if (refresh->priv->periodic_id != 0)
+                g_source_remove (refresh->priv->periodic_id);
+
+        g_signal_handlers_disconnect_by_data (refresh->priv->client, refresh);
+        g_signal_handlers_disconnect_by_data (refresh->priv->proxy_session, refresh);
+
+        g_object_unref (refresh->priv->control);
+        g_object_unref (refresh->priv->settings);
+        g_object_unref (refresh->priv->client);
+        if (refresh->priv->proxy_session != NULL)
+                g_object_unref (refresh->priv->proxy_session);
+
+        G_OBJECT_CLASS (gsd_updates_refresh_parent_class)->finalize (object);
+}
+
+GsdUpdatesRefresh *
+gsd_updates_refresh_new (void)
+{
+        GsdUpdatesRefresh *refresh;
+        refresh = g_object_new (GSD_TYPE_UPDATES_REFRESH, NULL);
+        return GSD_UPDATES_REFRESH (refresh);
+}
+
Index: gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-refresh.h
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/gsd-updates-refresh.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007-2011 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GSD_UPDATES_REFRESH_H
+#define __GSD_UPDATES_REFRESH_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GSD_TYPE_UPDATES_REFRESH        (gsd_updates_refresh_get_type ())
+#define GSD_UPDATES_REFRESH(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_UPDATES_REFRESH, GsdUpdatesRefresh))
+#define GSD_UPDATES_REFRESH_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_UPDATES_REFRESH, GsdUpdatesRefreshClass))
+#define GSD_IS_UPDATES_REFRESH(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_UPDATES_REFRESH))
+
+typedef struct GsdUpdatesRefreshPrivate GsdUpdatesRefreshPrivate;
+
+typedef struct
+{
+         GObject                         parent;
+         GsdUpdatesRefreshPrivate       *priv;
+} GsdUpdatesRefresh;
+
+typedef struct
+{
+        GObjectClass    parent_class;
+} GsdUpdatesRefreshClass;
+
+GType                    gsd_updates_refresh_get_type           (void);
+GsdUpdatesRefresh       *gsd_updates_refresh_new                (void);
+
+G_END_DECLS
+
+#endif /* __GSD_UPDATES_REFRESH_H */
Index: gnome-settings-daemon-3.20.1/plugins/updates/test-updates.c
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/test-updates.c
@@ -0,0 +1,7 @@
+#define NEW gsd_updates_manager_new
+#define START gsd_updates_manager_start
+#define STOP gsd_updates_manager_stop
+#define MANAGER GsdUpdatesManager
+#include "gsd-updates-manager.h"
+
+#include "test-plugin.h"
Index: gnome-settings-daemon-3.20.1/plugins/updates/updates-design.svg
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/updates-design.svg
@@ -0,0 +1,765 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="plan.svg"
+   inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-settings-daemon/plugins/updates/docs/plan.png"
+   inkscape:export-xdpi="90.094635"
+   inkscape:export-ydpi="90.094635">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow2Lend"
+       style="overflow:visible;">
+      <path
+         id="path3789"
+         style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+         d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+         transform="scale(1.1) rotate(180) translate(1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Mend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Mend"
+       style="overflow:visible;">
+      <path
+         id="path3777"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+         transform="scale(0.4) rotate(180) translate(10,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-7"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-0"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-0"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-4"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-2"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-02"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-4"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-03"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-6"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-7"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-48"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-3"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-8"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-9"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-5"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-32"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-54"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-47"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-60"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-00"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-9"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-72"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-09"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-8"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-45"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-1"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-98"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-45"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-50"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-457"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Lend-63"
+       style="overflow:visible">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3789-06"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4"
+     inkscape:cx="370.94472"
+     inkscape:cy="350.46964"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1600"
+     inkscape:window-height="845"
+     inkscape:window-x="1920"
+     inkscape:window-y="542"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2985"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <rect
+       style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09627009;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect5533"
+       width="550.0965"
+       height="930"
+       x="39.950954"
+       y="42.362183" />
+    <rect
+       style="color:#000000;fill:#e9e9f1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.91287094;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2987"
+       width="180"
+       height="50"
+       x="110"
+       y="102.36217" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="122.36218"
+       id="text3757"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3759"
+         x="200"
+         y="122.36218"
+         style="font-size:14px;text-align:center;text-anchor:middle">Wait for refresh due</tspan><tspan
+         sodipodi:role="line"
+         x="200"
+         y="139.86218"
+         id="tspan3761"
+         style="font-size:14px;text-align:center;text-anchor:middle">GetUpdates (typ. 1 day)</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 200,152.36217 0,30"
+       id="path3763"
+       inkscape:connector-curvature="0" />
+    <rect
+       style="color:#000000;fill:#e9e9f1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.91287094;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2987-1"
+       width="180"
+       height="50"
+       x="110"
+       y="442.36218" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="470.36218"
+       id="text3757-1"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="200"
+         y="470.36218"
+         id="tspan3761-5"
+         style="font-size:14px;text-align:center;text-anchor:middle">GetUpdates()</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 200,492.36217 0,30"
+       id="path3763-7"
+       inkscape:connector-curvature="0" />
+    <rect
+       style="color:#000000;fill:#e9e9f1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11863005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2987-4"
+       width="111.96233"
+       height="111.96233"
+       x="696.61267"
+       y="473.08511"
+       transform="matrix(0.89474433,0.44657876,-0.89474433,0.44657876,0,0)" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="568.36218"
+       id="text3757-12-7"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="200"
+         y="568.36218"
+         id="tspan3761-8-2"
+         style="font-size:14px;text-align:center;text-anchor:middle">'Auto download'</tspan><tspan
+         sodipodi:role="line"
+         x="200"
+         y="585.86218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan4724">checkbox set?</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="612.36218"
+       id="text3757-12-0-5"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="200"
+         y="612.36218"
+         id="tspan3761-8-25-7"
+         style="font-size:14px;text-align:center;text-anchor:middle">Y</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="274"
+       y="576.36218"
+       id="text3757-12-0-8-7"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="274"
+         y="576.36218"
+         id="tspan3761-8-25-8-3"
+         style="font-size:14px;text-align:center;text-anchor:middle">N</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 200,622.36217 0,30"
+       id="path3763-2-7"
+       inkscape:connector-curvature="0" />
+    <rect
+       style="color:#000000;fill:#e9e9f1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.91287094;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2987-3"
+       width="180"
+       height="50"
+       x="110"
+       y="652.36218" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="672.36218"
+       id="text3757-2"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="200"
+         y="672.36218"
+         id="tspan3761-7"
+         style="font-size:14px;text-align:center;text-anchor:middle">UpdatePackages</tspan><tspan
+         sodipodi:role="line"
+         x="200"
+         y="689.86218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan4862">(only-download)</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 200,702.36217 0,30"
+       id="path3763-1"
+       inkscape:connector-curvature="0" />
+    <rect
+       style="color:#000000;fill:#e9e9f1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11863005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2987-4-8"
+       width="111.96233"
+       height="111.96233"
+       x="931.73358"
+       y="708.20605"
+       transform="matrix(0.89474433,0.44657876,-0.89474433,0.44657876,0,0)" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="778.36218"
+       id="text3757-12-7-0"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="200"
+         y="778.36218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan4724-3">Any security or</tspan><tspan
+         sodipodi:role="line"
+         x="200"
+         y="795.86218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan5002">critical updates?</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="822.36218"
+       id="text3757-12-0-5-9"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="200"
+         y="822.36218"
+         id="tspan3761-8-25-7-2"
+         style="font-size:14px;text-align:center;text-anchor:middle">Y</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="274"
+       y="786.36218"
+       id="text3757-12-0-8-7-2"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="274"
+         y="786.36218"
+         id="tspan3761-8-25-8-3-2"
+         style="font-size:14px;text-align:center;text-anchor:middle">N</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 200,832.36216 0,30"
+       id="path3763-2-7-9"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 300,572.36217 20,0 0,140 -120,0"
+       id="path3763-2-4"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 430,832.36217 0,30"
+       id="path3763-2-5"
+       inkscape:connector-curvature="0" />
+    <rect
+       style="color:#000000;fill:#e9e9f1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11863005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2987-4-9-5"
+       width="111.96233"
+       height="111.96233"
+       x="1060.2618"
+       y="579.67767"
+       transform="matrix(0.89474433,0.44657876,-0.89474433,0.44657876,0,0)" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="430"
+       y="822.36218"
+       id="text3757-12-0-2"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="430"
+         y="822.36218"
+         id="tspan3761-8-25-4"
+         style="font-size:14px;text-align:center;text-anchor:middle">Y</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="504"
+       y="786.36218"
+       id="text3757-12-0-8-8"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="504"
+         y="786.36218"
+         id="tspan3761-8-25-8-34"
+         style="font-size:14px;text-align:center;text-anchor:middle">N</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 300,782.36217 30,0"
+       id="path3763-2-5-8"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="430"
+       y="778.36218"
+       id="text3757-12-7-0-7"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="430"
+         y="778.36218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan5117">&gt; notify threshold?</tspan><tspan
+         sodipodi:role="line"
+         x="430"
+         y="795.86218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan5512">typ. 1 week</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 200,412.36218 0,30"
+       id="path3763-2-3"
+       inkscape:connector-curvature="0" />
+    <rect
+       style="color:#000000;fill:#e9e9f1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11863005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2987-4-9-50"
+       width="111.96233"
+       height="111.96233"
+       x="461.49176"
+       y="237.9642"
+       transform="matrix(0.89474433,0.44657876,-0.89474433,0.44657876,0,0)" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="402.36218"
+       id="text3757-12-0-4"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="200"
+         y="402.36218"
+         id="tspan3761-8-25-6"
+         style="font-size:14px;text-align:center;text-anchor:middle">Y</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="274"
+       y="366.36218"
+       id="text3757-12-0-8-1"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="274"
+         y="366.36218"
+         id="tspan3761-8-25-8-8"
+         style="font-size:14px;text-align:center;text-anchor:middle">N</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="358.36218"
+       id="text3757-12-7-9-0"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="200"
+         y="358.36218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan5187-6">'Use Mobile'</tspan><tspan
+         sodipodi:role="line"
+         x="200"
+         y="375.86218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan5360">checkbox set?</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 200,282.36217 0,29.99999"
+       id="path3763-2-70"
+       inkscape:connector-curvature="0" />
+    <rect
+       style="color:#000000;fill:#e9e9f1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11863005;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2987-4-9-2"
+       width="111.96233"
+       height="111.96233"
+       x="315.9407"
+       y="92.413185"
+       transform="matrix(0.89474433,0.44657876,-0.89474433,0.44657876,0,0)" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="272.36218"
+       id="text3757-12-0-7"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="200"
+         y="272.36218"
+         id="tspan3761-8-25-3"
+         style="font-size:14px;text-align:center;text-anchor:middle">Y</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="126"
+       y="236.36218"
+       id="text3757-12-0-8-0"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="126"
+         y="236.36218"
+         id="tspan3761-8-25-8-7"
+         style="font-size:14px;text-align:center;text-anchor:middle">N</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="228.36217"
+       id="text3757-12-7-9-9"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="200"
+         y="228.36217"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan5187-0">On GPRS or</tspan><tspan
+         sodipodi:role="line"
+         x="200"
+         y="245.86217"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan5356">CDMA?</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 100,232.36218 -30,0 0,189.99999 130,0"
+       id="path3763-2-8"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 200,912.36217 0,30 360,0 0,-869.999998 -360,0 0,29.999998"
+       id="path3763-2-54"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccc" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 300,362.36218 260,0"
+       id="path3763-2-70-1"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <rect
+       style="color:#000000;fill:#e9e9f1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.91287094;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2987-3-6"
+       width="180"
+       height="50"
+       x="110"
+       y="862.36218" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="200"
+       y="882.36218"
+       id="text3757-2-6"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="200"
+         y="882.36218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan4862-3">Notify the user about</tspan><tspan
+         sodipodi:role="line"
+         x="200"
+         y="899.86218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan5472">important updates</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 530,782.36217 30,0"
+       id="path3763-1-6"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <rect
+       style="color:#000000;fill:#e9e9f1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.91287094;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2987-3-6-0"
+       width="180"
+       height="50"
+       x="340"
+       y="862.36218" />
+    <text
+       xml:space="preserve"
+       style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="430"
+       y="882.36218"
+       id="text3757-2-6-5"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="430"
+         y="882.36218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan4862-3-5">Notify the user about</tspan><tspan
+         sodipodi:role="line"
+         x="430"
+         y="899.86218"
+         style="font-size:14px;text-align:center;text-anchor:middle"
+         id="tspan5472-3">regular updates</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow2Lend)"
+       d="m 430,912.36217 0,30"
+       id="path3763-1-6-0"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
Index: gnome-settings-daemon-3.20.1/plugins/updates/updates.gnome-settings-plugin.in
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/updates.gnome-settings-plugin.in
@@ -0,0 +1,9 @@
+[GNOME Settings Plugin]
+Module=updates
+IAge=0
+Priority=300
+_Name=Updates
+_Description=Updates plugin
+Authors=Richard Hughes
+Copyright=Copyright © 2011
+Website=
Index: gnome-settings-daemon-3.20.1/po/POTFILES.in
===================================================================
--- gnome-settings-daemon-3.20.1.orig/po/POTFILES.in
+++ gnome-settings-daemon-3.20.1/po/POTFILES.in
@@ -13,6 +13,7 @@ data/org.gnome.settings-daemon.plugins.o
 data/org.gnome.settings-daemon.plugins.power.gschema.xml.in.in
 data/org.gnome.settings-daemon.plugins.print-notifications.gschema.xml.in.in
 data/org.gnome.settings-daemon.plugins.sharing.gschema.xml.in.in
+data/org.gnome.settings-daemon.plugins.updates.gschema.xml.in.in
 data/org.gnome.settings-daemon.plugins.xrandr.gschema.xml.in.in
 data/org.gnome.settings-daemon.plugins.xsettings.gschema.xml.in.in
 gnome-settings-daemon/main.c
@@ -52,6 +53,9 @@ plugins/smartcard/gsd-smartcard-manager.
 plugins/smartcard/gsd-smartcard-service.c
 [type: gettext/ini]plugins/smartcard/smartcard.gnome-settings-plugin.in
 [type: gettext/ini]plugins/sound/sound.gnome-settings-plugin.in
+plugins/updates/gsd-updates-firmware.c
+plugins/updates/gsd-updates-manager.c
+[type: gettext/ini]plugins/updates/updates.gnome-settings-plugin.in
 plugins/wacom/gsd-wacom-button-editor.c
 plugins/wacom/gsd-wacom-device.c
 plugins/wacom/gsd-wacom-key-shortcut-button.c
Index: gnome-settings-daemon-3.20.1/po/POTFILES.skip
===================================================================
--- gnome-settings-daemon-3.20.1.orig/po/POTFILES.skip
+++ gnome-settings-daemon-3.20.1/po/POTFILES.skip
@@ -19,6 +19,7 @@ data/org.gnome.settings-daemon.plugins.o
 data/org.gnome.settings-daemon.plugins.power.gschema.xml.in
 data/org.gnome.settings-daemon.plugins.print-notifications.gschema.xml.in
 data/org.gnome.settings-daemon.plugins.sharing.gschema.xml.in
+data/org.gnome.settings-daemon.plugins.updates.gschema.xml.in
 data/org.gnome.settings-daemon.plugins.xrandr.gschema.xml.in
 data/org.gnome.settings-daemon.plugins.xsettings.gschema.xml.in
 plugins/automount/gnome-fallback-mount-helper.desktop.in
Index: gnome-settings-daemon-3.20.1/data/org.gnome.settings-daemon.plugins.gschema.xml.in.in
===================================================================
--- gnome-settings-daemon-3.20.1.orig/data/org.gnome.settings-daemon.plugins.gschema.xml.in.in
+++ gnome-settings-daemon-3.20.1/data/org.gnome.settings-daemon.plugins.gschema.xml.in.in
@@ -26,6 +26,7 @@
     <child name="sharing" schema="org.gnome.settings-daemon.plugins.sharing"/>
     <child name="smartcard" schema="org.gnome.settings-daemon.plugins.smartcard"/>
     <child name="sound" schema="org.gnome.settings-daemon.plugins.sound"/>
+    <child name="updates" schema="org.gnome.settings-daemon.plugins.updates"/>
     <child name="xrandr" schema="org.gnome.settings-daemon.plugins.xrandr"/>
     <child name="xsettings" schema="org.gnome.settings-daemon.plugins.xsettings"/>
   </schema>
Index: gnome-settings-daemon-3.20.1/plugins/updates/pk-offline-clear-update.c
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/pk-offline-clear-update.c
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2012 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Header needed for the location of PK_OFFLINE_UPDATE_RESULTS_FILENAME */
+#include "gsd-updates-common.h"
+
+int
+main (int argc, char *argv[])
+{
+	int rc;
+
+	/* ensure root user */
+	if (getuid () != 0 || geteuid () != 0) {
+		fprintf (stderr, "This program can only be used using pkexec\n");
+		return EXIT_FAILURE;
+	}
+
+	/* Just delete the file, no questions asked :) */
+	rc = unlink (PK_OFFLINE_UPDATE_RESULTS_FILENAME);
+	if (rc < 0) {
+		fprintf (stderr, "Failed to remove file: %s\n", strerror (errno));
+		return EXIT_FAILURE;
+	}
+	return EXIT_SUCCESS;
+}
+
Index: gnome-settings-daemon-3.20.1/plugins/updates/org.gnome.settings-daemon.plugins.update.policy
===================================================================
--- /dev/null
+++ gnome-settings-daemon-3.20.1/plugins/updates/org.gnome.settings-daemon.plugins.update.policy
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+<policyconfig>
+  <vendor>The PackageKit Project</vendor>
+  <vendor_url>http://www.packagekit.org/</vendor_url>
+  <icon_name>package-x-generic</icon_name>
+  <action id="org.freedesktop.packagekit.clear-offline-update">
+    <description>Clear offline update message</description>
+    <annotate key="org.freedesktop.policykit.exec.path">/usr/lib/gnome-settings-daemon-3.0/pk-clear-offline-update</annotate>
+  </action>
+</policyconfig>
