AU: DBus support.

A few changes to support dbus in the Update Engine daemon:

- SConstruct: build marshaller for the dbus signal.

- Update Attempter: respond to dbus calls and broadcast status on dbus
  signal.

- Update Engine Client: flag to listen for status updates.

- Also, cleanup outdated code in Omaha Response Handler.

BUG=None
TEST=attached unittests/on device tests

Review URL: http://codereview.chromium.org/2037002
diff --git a/SConstruct b/SConstruct
index 5adf16b..1cf837a 100644
--- a/SConstruct
+++ b/SConstruct
@@ -5,14 +5,14 @@
 import os
 
 # Protobuffer compilation
-""" Inputs:
-        target: list of targets to compile to
-        source: list of sources to compile
-        env: the scons environment in which we are compiling
-    Outputs:
-        target: the list of targets we'll emit
-        source: the list of sources we'll compile"""
 def ProtocolBufferEmitter(target, source, env):
+  """ Inputs:
+          target: list of targets to compile to
+          source: list of sources to compile
+          env: the scons environment in which we are compiling
+      Outputs:
+          target: the list of targets we'll emit
+          source: the list of sources we'll compile"""
   output = str(source[0])
   output = output[0:output.rfind('.proto')]
   target = [
@@ -21,14 +21,14 @@
   ]
   return target, source
 
-""" Inputs:
-        source: list of sources to process
-        target: list of targets to generate
-        env: scons environment in which we are working
-        for_signature: unused
-    Outputs: a list of commands to execute to generate the targets from
-             the sources."""
 def ProtocolBufferGenerator(source, target, env, for_signature):
+  """ Inputs:
+          source: list of sources to process
+          target: list of targets to generate
+          env: scons environment in which we are working
+          for_signature: unused
+      Outputs: a list of commands to execute to generate the targets from
+               the sources."""
   commands = [
     '/usr/bin/protoc '
     ' --proto_path . ${SOURCES} --cpp_out .']
@@ -39,14 +39,14 @@
                         single_source = 1,
                         suffix = '.pb.cc')
 
-""" Inputs:
-        target: unused
-        source: list containing the source .xml file
-        env: the scons environment in which we are compiling
-    Outputs:
-        target: the list of targets we'll emit
-        source: the list of sources we'll process"""
 def DbusBindingsEmitter(target, source, env):
+  """ Inputs:
+          target: unused
+          source: list containing the source .xml file
+          env: the scons environment in which we are compiling
+      Outputs:
+          target: the list of targets we'll emit
+          source: the list of sources we'll process"""
   output = str(source[0])
   output = output[0:output.rfind('.xml')]
   target = [
@@ -55,14 +55,14 @@
   ]
   return target, source
 
-""" Inputs:
-        source: list of sources to process
-        target: list of targets to generate
-        env: scons environment in which we are working
-        for_signature: unused
-    Outputs: a list of commands to execute to generate the targets from
-             the sources."""
 def DbusBindingsGenerator(source, target, env, for_signature):
+  """ Inputs:
+          source: list of sources to process
+          target: list of targets to generate
+          env: scons environment in which we are working
+          for_signature: unused
+      Outputs: a list of commands to execute to generate the targets from
+               the sources."""
   commands = []
   for target_file in target:
     if str(target_file).endswith('.dbusserver.h'):
@@ -79,6 +79,46 @@
                                 single_source = 1,
                                 suffix = 'dbusclient.h')
 
+def GlibMarshalEmitter(target, source, env):
+  """ Inputs:
+          target: unused
+          source: list containing the source .list file
+          env: the scons environment in which we are compiling
+      Outputs:
+          target: the list of targets we'll emit
+          source: the list of sources we'll process"""
+  output = str(source[0])
+  output = output[0:output.rfind('.list')]
+  target = [
+    output + '.glibmarshal.c',
+    output + '.glibmarshal.h'
+  ]
+  return target, source
+
+def GlibMarshalGenerator(source, target, env, for_signature):
+  """ Inputs:
+          source: list of sources to process
+          target: list of targets to generate
+          env: scons environment in which we are working
+          for_signature: unused
+      Outputs: a list of commands to execute to generate the targets from
+               the sources."""
+  commands = []
+  for target_file in target:
+    if str(target_file).endswith('.glibmarshal.h'):
+      mode_flag = '--header '
+    else:
+      mode_flag = '--body '
+    cmd = '/usr/bin/glib-genmarshal %s --prefix=update_engine ' \
+          '%s > %s' % (mode_flag, str(source[0]), str(target_file))
+    commands.append(cmd)
+  return commands
+
+glib_marshal_builder = Builder(generator = GlibMarshalGenerator,
+                               emitter = GlibMarshalEmitter,
+                               single_source = 1,
+                               suffix = 'glibmarshal.c')
+
 env = Environment()
 for key in Split('CC CXX AR RANLIB LD NM'):
   value = os.environ.get(key)
@@ -131,6 +171,7 @@
 env['LIBPATH'] = ['../../third_party/chrome']
 env['BUILDERS']['ProtocolBuffer'] = proto_builder
 env['BUILDERS']['DbusBindings'] = dbus_bindings_builder
+env['BUILDERS']['GlibMarshal'] = glib_marshal_builder
 
 # Fix issue with scons not passing pkg-config vars through the environment.
 for key in Split('PKG_CONFIG_LIBDIR PKG_CONFIG_PATH'):
@@ -143,6 +184,8 @@
 
 env.DbusBindings('update_engine.dbusclient.h', 'update_engine.xml')
 
+env.GlibMarshal('marshal.glibmarshal.c', 'marshal.list')
+
 if ARGUMENTS.get('debug', 0):
   env['CCFLAGS'] += ' -fprofile-arcs -ftest-coverage'
   env['LIBS'] += ['bz2', 'gcov']
@@ -164,6 +207,7 @@
                    graph_utils.cc
                    gzip.cc
                    libcurl_http_fetcher.cc
+                   marshal.glibmarshal.c
                    omaha_hash_calculator.cc
                    omaha_request_prep_action.cc
                    omaha_response_handler_action.cc
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
index 88d6dc4..fe332c0 100644
--- a/UpdateEngine.conf
+++ b/UpdateEngine.conf
@@ -16,6 +16,9 @@
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
            send_member="GetStatus"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="CheckForUpdate"/>
   </policy>
   <policy context="default">
     <deny send_destination="org.chromium.UpdateEngine" />
diff --git a/dbus_service.cc b/dbus_service.cc
index 8dc239f..12398cd 100644
--- a/dbus_service.cc
+++ b/dbus_service.cc
@@ -5,6 +5,7 @@
 #include "update_engine/dbus_service.h"
 #include <string>
 #include "base/logging.h"
+#include "update_engine/marshal.glibmarshal.h"
 
 using std::string;
 
@@ -14,10 +15,28 @@
   G_OBJECT_CLASS(update_engine_service_parent_class)->finalize(object);
 }
 
+static guint status_update_signal = 0;
+
 static void update_engine_service_class_init(UpdateEngineServiceClass* klass) {
   GObjectClass *object_class;
   object_class = G_OBJECT_CLASS(klass);
   object_class->finalize = update_engine_service_finalize;
+  
+  status_update_signal = g_signal_new(
+      "status_update",
+      G_OBJECT_CLASS_TYPE(klass),
+      G_SIGNAL_RUN_LAST,
+      0,  // 0 == no class method associated
+      NULL,  // Accumulator
+      NULL,  // Accumulator data
+      update_engine_VOID__INT64_DOUBLE_STRING_STRING_INT64,
+      G_TYPE_NONE,  // Return type
+      5,  // param count:
+      G_TYPE_INT64,
+      G_TYPE_DOUBLE,
+      G_TYPE_STRING,
+      G_TYPE_STRING,
+      G_TYPE_INT64);
 }
 
 static void update_engine_service_init(UpdateEngineService* object) {
@@ -49,3 +68,26 @@
   return TRUE;
 }
 
+gboolean update_engine_service_check_for_update(UpdateEngineService* self,
+                                                GError **error) {
+  self->update_attempter_->CheckForUpdate();
+  return TRUE;
+}
+
+gboolean update_engine_service_emit_status_update(
+    UpdateEngineService* self,
+    gint64 last_checked_time,
+    gdouble progress,
+    const gchar* current_operation,
+    const gchar* new_version,
+    gint64 new_size) {
+  g_signal_emit(self,
+                status_update_signal,
+                0,
+                last_checked_time,
+                progress,
+                current_operation,
+                new_version,
+                new_size);
+  return TRUE;
+}
diff --git a/dbus_service.h b/dbus_service.h
index 1b2604e..40cb987 100644
--- a/dbus_service.h
+++ b/dbus_service.h
@@ -15,17 +15,17 @@
 
 // Type macros:
 #define UPDATE_ENGINE_TYPE_SERVICE (update_engine_service_get_type())
-#define UPDATE_ENGINE_SERVICE(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST((obj), UPDATE_ENGINE_TYPE_SERVICE, \
+#define UPDATE_ENGINE_SERVICE(obj)                                      \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), UPDATE_ENGINE_TYPE_SERVICE,        \
                               UpdateEngineService))
-#define UPDATE_ENGINE_IS_SERVICE(obj) \
+#define UPDATE_ENGINE_IS_SERVICE(obj)                                   \
   (G_TYPE_CHECK_INSTANCE_TYPE((obj), UPDATE_ENGINE_TYPE_SERVICE))
-#define UPDATE_ENGINE_SERVICE_CLASS(klass) \
+#define UPDATE_ENGINE_SERVICE_CLASS(klass)                      \
   (G_TYPE_CHECK_CLASS_CAST((klass), UPDATE_ENGINE_TYPE_SERVICE, \
                            UpdateEngineService))
-#define UPDATE_ENGINE_IS_SERVICE_CLASS(klass) \
+#define UPDATE_ENGINE_IS_SERVICE_CLASS(klass)                           \
   (G_TYPE_CHECK_CLASS_TYPE((klass), UPDATE_ENGINE_TYPE_SERVICE))
-#define UPDATE_ENGINE_SERVICE_GET_CLASS(obj) \
+#define UPDATE_ENGINE_SERVICE_GET_CLASS(obj)                    \
   (G_TYPE_INSTANCE_GET_CLASS((obj), UPDATE_ENGINE_TYPE_SERVICE, \
                              UpdateEngineService))
 
@@ -47,12 +47,23 @@
 // Methods
 
 gboolean update_engine_service_get_status(UpdateEngineService* self,
-  int64_t* last_checked_time,
-  double* progress,
-  gchar** current_operation,
-  gchar** new_version,
-  int64_t* new_size,
-  GError **error);
+                                          int64_t* last_checked_time,
+                                          double* progress,
+                                          gchar** current_operation,
+                                          gchar** new_version,
+                                          int64_t* new_size,
+                                          GError **error);
+
+gboolean update_engine_service_check_for_update(UpdateEngineService* self,
+                                                GError **error);
+
+gboolean update_engine_service_emit_status_update(
+    UpdateEngineService* self,
+    gint64 last_checked_time,
+    gdouble progress,
+    const gchar* current_operation,
+    const gchar* new_version,
+    gint64 new_size);
 
 G_END_DECLS
 
diff --git a/download_action.cc b/download_action.cc
index eee25cc..4787ee2 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -19,7 +19,9 @@
 
 DownloadAction::DownloadAction(HttpFetcher* http_fetcher)
     : writer_(NULL),
-      http_fetcher_(http_fetcher) {}
+      http_fetcher_(http_fetcher),
+      delegate_(NULL),
+      bytes_received_(0) {}
 
 DownloadAction::~DownloadAction() {}
 
@@ -29,6 +31,7 @@
   // Get the InstallPlan and read it
   CHECK(HasInputObject());
   install_plan_ = GetInputObject();
+  bytes_received_ = 0;
 
   install_plan_.Dump();
 
@@ -84,6 +87,9 @@
 void DownloadAction::ReceivedBytes(HttpFetcher *fetcher,
                                    const char* bytes,
                                    int length) {
+  bytes_received_ += length;
+  if (delegate_)
+    delegate_->BytesReceived(bytes_received_, install_plan_.size);
   int rc = writer_->Write(bytes, length);
   TEST_AND_RETURN(rc >= 0);
   omaha_hash_calculator_.Update(bytes, length);
@@ -94,9 +100,9 @@
   vector<string> command;
   command.push_back("/bin/sync");
   int rc;
-  LOG(INFO) << "FlushLinuxCaches/sync...";
+  LOG(INFO) << "FlushLinuxCaches-sync...";
   Subprocess::SynchronousExec(command, &rc);
-  LOG(INFO) << "FlushLinuxCaches/drop_caches...";
+  LOG(INFO) << "FlushLinuxCaches-drop_caches...";
 
   const char* const drop_cmd = "3\n";
   utils::WriteFile("/proc/sys/vm/drop_caches", drop_cmd, strlen(drop_cmd));
diff --git a/download_action.h b/download_action.h
index 0f375fa..e1e8aeb 100644
--- a/download_action.h
+++ b/download_action.h
@@ -33,6 +33,15 @@
 
 namespace chromeos_update_engine {
 
+class DownloadActionDelegate {
+ public:
+  // Called before any bytes are received and periodically after
+  // bytes are received.
+  // bytes_received is the number of bytes downloaded thus far.
+  // total is the number of bytes expected.
+  virtual void BytesReceived(uint64_t bytes_received, uint64_t total) = 0;
+};
+
 class DownloadAction;
 class NoneType;
 
@@ -66,11 +75,15 @@
   static std::string StaticType() { return "DownloadAction"; }
   std::string Type() const { return StaticType(); }
 
-  // Delegate methods (see http_fetcher.h)
+  // HttpFetcherDelegate methods (see http_fetcher.h)
   virtual void ReceivedBytes(HttpFetcher *fetcher,
                              const char* bytes, int length);
   virtual void TransferComplete(HttpFetcher *fetcher, bool successful);
 
+  void set_delegate(DownloadActionDelegate* delegate) {
+    delegate_ = delegate;
+  }
+
  private:
   // The InstallPlan passed in
   InstallPlan install_plan_;
@@ -93,6 +106,11 @@
 
   // Used to find the hash of the bytes downloaded
   OmahaHashCalculator omaha_hash_calculator_;
+  
+  // For reporting status to outsiders
+  DownloadActionDelegate* delegate_;
+  uint64_t bytes_received_;
+  
   DISALLOW_COPY_AND_ASSIGN(DownloadAction);
 };
 
diff --git a/download_action_unittest.cc b/download_action_unittest.cc
index 6353584..3b04eae 100644
--- a/download_action_unittest.cc
+++ b/download_action_unittest.cc
@@ -75,6 +75,7 @@
   // takes ownership of passed in HttpFetcher
   InstallPlan install_plan(true,
                            "",
+                           0,
                            OmahaHashCalculator::OmahaHashOfData(data),
                            output_temp_file.GetPath(),
                            "");
@@ -152,7 +153,7 @@
 
     // takes ownership of passed in HttpFetcher
     ObjectFeederAction<InstallPlan> feeder_action;
-    InstallPlan install_plan(true, "", "", temp_file.GetPath(), "");
+    InstallPlan install_plan(true, "", 0, "", temp_file.GetPath(), "");
     feeder_action.set_obj(install_plan);
     DownloadAction download_action(new MockHttpFetcher(&data[0], data.size()));
     download_action.SetTestFileWriter(&writer);
@@ -233,6 +234,7 @@
   // takes ownership of passed in HttpFetcher
   InstallPlan install_plan(true,
                            "",
+                           0,
                            OmahaHashCalculator::OmahaHashOfString("x"),
                            "/dev/null",
                            "/dev/null");
@@ -268,7 +270,7 @@
   DirectFileWriter writer;
 
   // takes ownership of passed in HttpFetcher
-  InstallPlan install_plan(true, "", "", path, "");
+  InstallPlan install_plan(true, "", 0, "", path, "");
   ObjectFeederAction<InstallPlan> feeder_action;
   feeder_action.set_obj(install_plan);
   DownloadAction download_action(new MockHttpFetcher("x", 1));
diff --git a/filesystem_copier_action_unittest.cc b/filesystem_copier_action_unittest.cc
index b5cd64d..e2bd42b 100644
--- a/filesystem_copier_action_unittest.cc
+++ b/filesystem_copier_action_unittest.cc
@@ -231,7 +231,7 @@
 
   ObjectFeederAction<InstallPlan> feeder_action;
   const char* kUrl = "http://some/url";
-  InstallPlan install_plan(true, kUrl, "", "", "");
+  InstallPlan install_plan(true, kUrl, 0, "", "", "");
   feeder_action.set_obj(install_plan);
   FilesystemCopierAction copier_action(false);
   ObjectCollectorAction<InstallPlan> collector_action;
@@ -256,7 +256,7 @@
   processor.set_delegate(&delegate);
 
   ObjectFeederAction<InstallPlan> feeder_action;
-  InstallPlan install_plan(false, "", "", "/no/such/file", "/no/such/file");
+  InstallPlan install_plan(false, "", 0, "", "/no/such/file", "/no/such/file");
   feeder_action.set_obj(install_plan);
   FilesystemCopierAction copier_action(false);
   ObjectCollectorAction<InstallPlan> collector_action;
diff --git a/install_plan.h b/install_plan.h
index fba89fe..9fc6ead 100644
--- a/install_plan.h
+++ b/install_plan.h
@@ -16,11 +16,13 @@
 struct InstallPlan {
   InstallPlan(bool is_full,
               const std::string& url,
+              uint64_t size,
               const std::string& hash,
               const std::string& install_path,
               const std::string& kernel_install_path)
       : is_full_update(is_full),
         download_url(url),
+        size(size),
         download_hash(hash),
         install_path(install_path),
         kernel_install_path(kernel_install_path) {}
@@ -28,6 +30,7 @@
 
   bool is_full_update;
   std::string download_url;  // url to download from
+  uint64_t size;  // size of the download url's data
   std::string download_hash;  // hash of the data at the url
   std::string install_path;  // path to install device
   std::string kernel_install_path;  // path to kernel install device
@@ -35,6 +38,7 @@
   bool operator==(const InstallPlan& that) const {
     return (is_full_update == that.is_full_update) &&
            (download_url == that.download_url) &&
+           (size == that.size) &&
            (download_hash == that.download_hash) &&
            (install_path == that.install_path) &&
            (kernel_install_path == that.kernel_install_path);
@@ -45,7 +49,9 @@
   void Dump() const {
     LOG(INFO) << "InstallPlan: "
               << (is_full_update ? "full_update" : "delta_update")
-              << ", url: " << download_url << ", hash: " << download_hash
+              << ", url: " << download_url
+              << ", size: " << size
+              << ", hash: " << download_hash
               << ", install_path: " << install_path
               << ", kernel_install_path: " << kernel_install_path;
   }
diff --git a/main.cc b/main.cc
index 4725967..f61ff34 100644
--- a/main.cc
+++ b/main.cc
@@ -26,16 +26,6 @@
 
 namespace chromeos_update_engine {
 
-gboolean SetupInMainLoop(void* arg) {
-  // TODO(adlr): Tell update_attempter to start working.
-  // Comment this in for that:
-  UpdateAttempter* update_attempter = reinterpret_cast<UpdateAttempter*>(arg);
-  LOG(INFO) << "Starting update!";
-  update_attempter->Update(false);
-
-  return FALSE;  // Don't call this callback function again
-}
-
 void SetupDbusService(UpdateEngineService* service) {
   DBusGConnection *bus;
   DBusGProxy *proxy;
@@ -82,18 +72,18 @@
   google::ParseCommandLineFlags(&argc, &argv, true);
   CommandLine::Init(argc, argv);
   logging::InitLogging("logfile.txt",
-                       FLAGS_logtostderr ?
-                         logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG :
-                         logging::LOG_ONLY_TO_FILE,
+                       (FLAGS_logtostderr ?
+                        logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG :
+                        logging::LOG_ONLY_TO_FILE),
                        logging::DONT_LOCK_LOG_FILE,
                        logging::APPEND_TO_OLD_LOG_FILE);
   LOG(INFO) << "Chrome OS Update Engine starting";
   
   // Create the single GMainLoop
-  GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+  GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
 
   // Create the update attempter:
-  chromeos_update_engine::UpdateAttempter update_attempter(loop);
+  chromeos_update_engine::UpdateAttempter update_attempter;
 
   // Create the dbus service object:
   dbus_g_object_type_install_info(UPDATE_ENGINE_TYPE_SERVICE,
@@ -101,16 +91,15 @@
   UpdateEngineService* service =
       UPDATE_ENGINE_SERVICE(g_object_new(UPDATE_ENGINE_TYPE_SERVICE, NULL));
   service->update_attempter_ = &update_attempter;
+  update_attempter.set_dbus_service(service);
   chromeos_update_engine::SetupDbusService(service);
 
-  // Set up init routine to run within the main loop.
-  g_timeout_add(0, &chromeos_update_engine::SetupInMainLoop, &update_attempter);
-
   // Run the main loop until exit time:
   g_main_loop_run(loop);
 
   // Cleanup:
   g_main_loop_unref(loop);
+  update_attempter.set_dbus_service(NULL);
   g_object_unref(G_OBJECT(service));
 
   LOG(INFO) << "Chrome OS Update Engine terminating";
diff --git a/marshal.list b/marshal.list
new file mode 100644
index 0000000..9dce1bd
--- /dev/null
+++ b/marshal.list
@@ -0,0 +1 @@
+VOID:INT64,DOUBLE,STRING,STRING,INT64
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index f90255a..486f19b 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -26,34 +26,22 @@
     LOG(INFO) << "There are no updates. Aborting.";
     return;
   }
-  InstallPlan install_plan;
-  install_plan.download_url = response.codebase;
-  install_plan.download_hash = response.hash;
+  install_plan_.download_url = response.codebase;
+  install_plan_.size = response.size;
+  install_plan_.download_hash = response.hash;
   TEST_AND_RETURN(GetInstallDev(
       (!boot_device_.empty() ? boot_device_ : utils::BootDevice()),
-      &install_plan.install_path));
-  install_plan.kernel_install_path =
-      utils::BootKernelDevice(install_plan.install_path);
+      &install_plan_.install_path));
+  install_plan_.kernel_install_path =
+      utils::BootKernelDevice(install_plan_.install_path);
 
-  // Get the filename part of the url. Assume that if it has kFullUpdateTag
-  // in the name, it's a full update.
-  string::size_type last_slash = response.codebase.rfind('/');
-  string filename;
-  if (last_slash == string::npos)
-    filename = response.codebase;
-  else
-    filename = response.codebase.substr(last_slash + 1);
-  install_plan.is_full_update = (filename.find(kFullUpdateTag) != string::npos);
+  install_plan_.is_full_update = true;  // TODO(adlr): know if update is a delta
 
-  if (filename.size() > 255) {
-    // Very long name. Let's shorten it
-    filename.resize(255);
-  }
   TEST_AND_RETURN(HasOutputPipe());
   if (HasOutputPipe())
-    SetOutputObject(install_plan);
+    SetOutputObject(install_plan_);
   LOG(INFO) << "Using this install plan:";
-  install_plan.Dump();
+  install_plan_.Dump();
   
   completer.set_success(true);
 }
diff --git a/omaha_response_handler_action.h b/omaha_response_handler_action.h
index b56d343..e79e32b 100644
--- a/omaha_response_handler_action.h
+++ b/omaha_response_handler_action.h
@@ -43,6 +43,7 @@
   }
   
   bool GotNoUpdateResponse() const { return got_no_update_response_; }
+  const InstallPlan& install_plan() const { return install_plan_; }
 
   // Debugging/logging
   static std::string StaticType() { return "OmahaResponseHandlerAction"; }
@@ -59,6 +60,9 @@
   // set to non-empty in unit tests
   std::string boot_device_;
   
+  // The install plan, if we have an update.
+  InstallPlan install_plan_;
+  
   // True only if we got a response and the response said no updates
   bool got_no_update_response_;
 
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index e997ea9..b396a9c 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -82,7 +82,7 @@
     UpdateCheckResponse in;
     in.update_exists = true;
     in.display_version = "a.b.c.d";
-    in.codebase = "http://foo/the_update_a.b.c.d_FULL_.tgz";
+    in.codebase = "http://foo/the_update_a.b.c.d.tgz";
     in.more_info_url = "http://more/info";
     in.hash = "HASH+";
     in.size = 12;
@@ -99,7 +99,7 @@
     UpdateCheckResponse in;
     in.update_exists = true;
     in.display_version = "a.b.c.d";
-    in.codebase = "http://foo/the_update_a.b.c.d_DELTA_.tgz";
+    in.codebase = "http://foo/the_update_a.b.c.d.tgz";
     in.more_info_url = "http://more/info";
     in.hash = "HASHj+";
     in.size = 12;
@@ -107,7 +107,7 @@
     in.prompt = true;
     InstallPlan install_plan;
     EXPECT_TRUE(DoTest(in, "/dev/sda5", &install_plan));
-    EXPECT_FALSE(install_plan.is_full_update);
+    EXPECT_TRUE(install_plan.is_full_update);
     EXPECT_EQ(in.codebase, install_plan.download_url);
     EXPECT_EQ(in.hash, install_plan.download_hash);
     EXPECT_EQ("/dev/sda3", install_plan.install_path);
@@ -124,7 +124,7 @@
     in.prompt = true;
     InstallPlan install_plan;
     EXPECT_TRUE(DoTest(in, "/dev/sda3", &install_plan));
-    EXPECT_FALSE(install_plan.is_full_update);
+    EXPECT_TRUE(install_plan.is_full_update);
     EXPECT_EQ(in.codebase, install_plan.download_url);
     EXPECT_EQ(in.hash, install_plan.download_hash);
     EXPECT_EQ("/dev/sda5", install_plan.install_path);
diff --git a/update_attempter.cc b/update_attempter.cc
index ad3bd9e..ebfcd1d 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -3,10 +3,18 @@
 // found in the LICENSE file.
 
 #include "update_engine/update_attempter.h"
+
+// From 'man clock_gettime': feature test macro: _POSIX_C_SOURCE >= 199309L
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 199309L
+#endif  // _POSIX_C_SOURCE
+#include <time.h>
+
 #include <tr1/memory>
 #include <string>
 #include <vector>
 #include <glib.h>
+#include "update_engine/dbus_service.h"
 #include "update_engine/download_action.h"
 #include "update_engine/filesystem_copier_action.h"
 #include "update_engine/libcurl_http_fetcher.h"
@@ -22,6 +30,59 @@
 
 namespace chromeos_update_engine {
 
+namespace {
+// Returns true on success.
+bool GetCPUClockTime(struct timespec* out) {
+  return clock_gettime(CLOCK_REALTIME, out) == 0;
+}
+// Returns stop - start.
+struct timespec CPUClockTimeElapsed(const struct timespec& start,
+                                    const struct timespec& stop) {
+  CHECK(start.tv_sec >= 0);
+  CHECK(stop.tv_sec >= 0);
+  CHECK(start.tv_nsec >= 0);
+  CHECK(stop.tv_nsec >= 0);
+
+  const int64_t kOneBillion = 1000000000L;
+  const int64_t start64 = start.tv_sec * kOneBillion + start.tv_nsec;
+  const int64_t stop64 = stop.tv_sec * kOneBillion + stop.tv_nsec;
+
+  const int64_t result64 = stop64 - start64;
+
+  struct timespec ret;
+  ret.tv_sec = result64 / kOneBillion;
+  ret.tv_nsec = result64 % kOneBillion;
+
+  return ret;
+}
+bool CPUClockTimeGreaterThanHalfSecond(const struct timespec& spec) {
+  if (spec.tv_sec >= 1)
+    return true;
+  return (spec.tv_nsec > 500000000);
+}
+}
+
+const char* UpdateStatusToString(UpdateStatus status) {
+  switch (status) {
+    case UPDATE_STATUS_IDLE:
+      return "UPDATE_STATUS_IDLE";
+    case UPDATE_STATUS_CHECKING_FOR_UPDATE:
+      return "UPDATE_STATUS_CHECKING_FOR_UPDATE";
+    case UPDATE_STATUS_UPDATE_AVAILABLE:
+      return "UPDATE_STATUS_UPDATE_AVAILABLE";
+    case UPDATE_STATUS_DOWNLOADING:
+      return "UPDATE_STATUS_DOWNLOADING";
+    case UPDATE_STATUS_VERIFYING:
+      return "UPDATE_STATUS_VERIFYING";
+    case UPDATE_STATUS_FINALIZING:
+      return "UPDATE_STATUS_FINALIZING";
+    case UPDATE_STATUS_UPDATED_NEED_REBOOT:
+      return "UPDATE_STATUS_UPDATED_NEED_REBOOT";
+    default:
+      return "unknown status";
+  }
+}
+
 void UpdateAttempter::Update(bool force_full_update) {
   full_update_ = force_full_update;
   CHECK(!processor_.IsRunning());
@@ -46,7 +107,8 @@
       new SetBootableFlagAction);
   shared_ptr<PostinstallRunnerAction> postinstall_runner_action_postcommit(
       new PostinstallRunnerAction(false));
-      
+  
+  download_action->set_delegate(this);
   response_handler_action_ = response_handler_action;
 
   actions_.push_back(shared_ptr<AbstractAction>(request_prep_action));
@@ -87,22 +149,63 @@
   BondActions(set_bootable_flag_action.get(),
               postinstall_runner_action_postcommit.get());
 
+  SetStatusAndNotify(UPDATE_STATUS_CHECKING_FOR_UPDATE);
   processor_.StartProcessing();
 }
 
-// Delegate method:
+void UpdateAttempter::CheckForUpdate() {
+  if (status_ != UPDATE_STATUS_IDLE) {
+    LOG(INFO) << "Check for update requested, but status is "
+              << UpdateStatusToString(status_) << ", so not checking.";
+    return;
+  }
+  Update(false);
+}
+
+// Delegate methods:
 void UpdateAttempter::ProcessingDone(const ActionProcessor* processor,
                                      bool success) {
   CHECK(response_handler_action_);
-  if (response_handler_action_->GotNoUpdateResponse()) {
-    // All done.
-    g_main_loop_quit(loop_);
-    return;
-  }
-  if (!success) {
+  LOG(INFO) << "Processing Done.";
+  if (success) {
+    SetStatusAndNotify(UPDATE_STATUS_UPDATED_NEED_REBOOT);
+  } else {
     LOG(INFO) << "Update failed.";
+    SetStatusAndNotify(UPDATE_STATUS_IDLE);
   }
-  g_main_loop_quit(loop_);
+}
+
+void UpdateAttempter::ProcessingStopped(const ActionProcessor* processor) {
+  download_progress_ = 0.0;
+  SetStatusAndNotify(UPDATE_STATUS_IDLE);
+}
+
+// Called whenever an action has finished processing, either successfully
+// or otherwise.
+void UpdateAttempter::ActionCompleted(ActionProcessor* processor,
+                                      AbstractAction* action,
+                                      bool success) {
+  // Reset download progress regardless of whether or not the download action
+  // succeeded.
+  const string type = action->Type();
+  if (type == DownloadAction::StaticType())
+    download_progress_ = 0.0;
+  if (!success)
+    return;
+  // Find out which action completed.
+  if (type == OmahaResponseHandlerAction::StaticType()) {
+    SetStatusAndNotify(UPDATE_STATUS_DOWNLOADING);
+    OmahaResponseHandlerAction* omaha_response_handler_action = 
+        dynamic_cast<OmahaResponseHandlerAction*>(action);
+    CHECK(omaha_response_handler_action);
+    const InstallPlan& plan = omaha_response_handler_action->install_plan();
+    last_checked_time_ = time(NULL);
+    // TODO(adlr): put version in InstallPlan
+    new_version_ = "0.0.0.0";
+    new_size_ = plan.size;
+  } else if (type == DownloadAction::StaticType()) {
+    SetStatusAndNotify(UPDATE_STATUS_FINALIZING);
+  }
 }
 
 // Stop updating. An attempt will be made to record status to the disk
@@ -118,19 +221,49 @@
   NOTIMPLEMENTED();
 }
 
+void UpdateAttempter::BytesReceived(uint64_t bytes_received, uint64_t total) {
+  if (status_ != UPDATE_STATUS_DOWNLOADING) {
+    LOG(ERROR) << "BytesReceived called while not downloading.";
+    return;
+  }
+  download_progress_ = static_cast<double>(bytes_received) /
+      static_cast<double>(total);
+  // We self throttle here
+  timespec now;
+  now.tv_sec = 0;
+  now.tv_nsec = 0;
+  if (GetCPUClockTime(&now) &&
+      CPUClockTimeGreaterThanHalfSecond(
+          CPUClockTimeElapsed(last_notify_time_, now))) {
+    SetStatusAndNotify(UPDATE_STATUS_DOWNLOADING);
+  }
+}
+
 bool UpdateAttempter::GetStatus(int64_t* last_checked_time,
                                 double* progress,
                                 std::string* current_operation,
                                 std::string* new_version,
                                 int64_t* new_size) {
-  // TODO(adlr): Return actual status.
-  *last_checked_time = 123;
-  *progress = 0.2223;
-  *current_operation = "DOWNLOADING";
-  *new_version = "0.2.3.8";
-  *new_size = 10002;
+  *last_checked_time = last_checked_time_;
+  *progress = download_progress_;
+  *current_operation = UpdateStatusToString(status_);
+  *new_version = new_version_;
+  *new_size = new_size_;
   return true;
 }
 
+void UpdateAttempter::SetStatusAndNotify(UpdateStatus status) {
+  status_ = status;
+  if (!dbus_service_)
+    return;
+  GetCPUClockTime(&last_notify_time_);
+  update_engine_service_emit_status_update(
+      dbus_service_,
+      last_checked_time_,
+      download_progress_,
+      UpdateStatusToString(status_),
+      new_version_.c_str(),
+      new_size_);
+}
 
 }  // namespace chromeos_update_engine
diff --git a/update_attempter.h b/update_attempter.h
index 83e4737..76fe3fb 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -5,24 +5,52 @@
 #ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_ATTEMPTER_H__
 #define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_ATTEMPTER_H__
 
+#include <time.h>
 #include <tr1/memory>
 #include <string>
 #include <vector>
 #include <glib.h>
 #include "update_engine/action_processor.h"
+#include "update_engine/download_action.h"
 #include "update_engine/omaha_response_handler_action.h"
 
+struct UpdateEngineService;
+
 namespace chromeos_update_engine {
 
-class UpdateAttempter : public ActionProcessorDelegate {
+enum UpdateStatus {
+  UPDATE_STATUS_IDLE = 0,
+  UPDATE_STATUS_CHECKING_FOR_UPDATE,
+  UPDATE_STATUS_UPDATE_AVAILABLE,
+  UPDATE_STATUS_DOWNLOADING,
+  UPDATE_STATUS_VERIFYING,
+  UPDATE_STATUS_FINALIZING,
+  UPDATE_STATUS_UPDATED_NEED_REBOOT
+};
+
+const char* UpdateStatusToString(UpdateStatus status);
+
+class UpdateAttempter : public ActionProcessorDelegate,
+                        public DownloadActionDelegate {
  public:
-  explicit UpdateAttempter(GMainLoop *loop)
-      : full_update_(false),
-        loop_(loop) {}
+  UpdateAttempter() : full_update_(false),
+                      dbus_service_(NULL),
+                      status_(UPDATE_STATUS_IDLE),
+                      download_progress_(0.0),
+                      last_checked_time_(0),
+                      new_version_("0.0.0.0"),
+                      new_size_(0) {
+    last_notify_time_.tv_sec = 0;
+    last_notify_time_.tv_nsec = 0;
+  }
   void Update(bool force_full_update);
   
-  // Delegate method:
+  // ActionProcessorDelegate methods:
   void ProcessingDone(const ActionProcessor* processor, bool success);
+  void ProcessingStopped(const ActionProcessor* processor);
+  void ActionCompleted(ActionProcessor* processor,
+                       AbstractAction* action,
+                       bool success);
   
   // Stop updating. An attempt will be made to record status to the disk
   // so that updates can be resumed later.
@@ -38,14 +66,40 @@
                  std::string* new_version,
                  int64_t* new_size);
 
+  void set_dbus_service(struct UpdateEngineService* dbus_service) {
+    dbus_service_ = dbus_service;
+  }
+
+  void CheckForUpdate();
+
+  // DownloadActionDelegate method
+  void BytesReceived(uint64_t bytes_received, uint64_t total);
+
  private:
+  // Sets the status to the given status and notifies a status update
+  // over dbus.
+  void SetStatusAndNotify(UpdateStatus status);
+  
+  struct timespec last_notify_time_;
+
   bool full_update_;
   std::vector<std::tr1::shared_ptr<AbstractAction> > actions_;
   ActionProcessor processor_;
-  GMainLoop *loop_;
+  
+  // If non-null, this UpdateAttempter will send status updates over this
+  // dbus service.
+  UpdateEngineService* dbus_service_;
 
   // pointer to the OmahaResponseHandlerAction in the actions_ vector;
   std::tr1::shared_ptr<OmahaResponseHandlerAction> response_handler_action_;
+
+  // For status:
+  UpdateStatus status_;
+  double download_progress_;
+  int64_t last_checked_time_;
+  std::string new_version_;
+  int64_t new_size_;
+
   DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
 };
 
diff --git a/update_engine.xml b/update_engine.xml
index 11c804f..f5faaeb 100644
--- a/update_engine.xml
+++ b/update_engine.xml
@@ -14,5 +14,14 @@
       <arg type="s" name="new_version" direction="out" />
       <arg type="x" name="new_size" direction="out" />
     </method>
+    <method name="CheckForUpdate">
+    </method>
+    <signal name="StatusUpdate">
+      <arg type="x" name="last_checked_time" />
+      <arg type="d" name="progress" />
+      <arg type="s" name="current_operation" />
+      <arg type="s" name="new_version" />
+      <arg type="x" name="new_size" />
+    </signal>
   </interface>
 </node>
diff --git a/update_engine_client.cc b/update_engine_client.cc
index bed7205..cfdbc8a 100644
--- a/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -5,6 +5,7 @@
 #include <gflags/gflags.h>
 #include <glib.h>
 
+#include "update_engine/marshal.glibmarshal.h"
 #include "update_engine/dbus_constants.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/utils.h"
@@ -23,13 +24,15 @@
             "Force an update, even over an expensive network.");
 DEFINE_bool(check_for_update, false,
             "Initiate check for updates.");
+DEFINE_bool(watch_for_updates, false,
+            "Listen for status updates and print them to the screen.");
 
 namespace {
 
-bool GetStatus() {
-  DBusGConnection *bus;
-  DBusGProxy *proxy;
-  GError *error = NULL;
+bool GetProxy(DBusGProxy** out_proxy) {
+  DBusGConnection* bus;
+  DBusGProxy* proxy;
+  GError* error = NULL;
 
   bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
   if (!bus) {
@@ -43,6 +46,30 @@
   if (!proxy) {
     LOG(FATAL) << "Error getting proxy: " << GetGErrorMessage(error);
   }
+  *out_proxy = proxy;
+  return true;
+}
+
+static void StatusUpdateSignalHandler(DBusGProxy* proxy,
+                                      int64_t last_checked_time,
+                                      double progress,
+                                      gchar* current_operation,
+                                      gchar* new_version,
+                                      int64_t new_size,
+                                      void* user_data) {
+  LOG(INFO) << "Got status update:";
+  LOG(INFO) << "  last_checked_time: " << last_checked_time;
+  LOG(INFO) << "  progress: " << progress;
+  LOG(INFO) << "  current_operation: " << current_operation;
+  LOG(INFO) << "  new_version: " << new_version;
+  LOG(INFO) << "  new_size: " << new_size;
+}
+
+bool GetStatus() {
+  DBusGProxy* proxy;
+  GError* error = NULL;
+  
+  CHECK(GetProxy(&proxy));
 
   gint64 last_checked_time = 0;
   gdouble progress = 0.0;
@@ -71,7 +98,52 @@
   return true;
 }
 
+// Should never return.
+void WatchForUpdates() {
+  DBusGProxy* proxy;
+  
+  CHECK(GetProxy(&proxy));
+  
+  // Register marshaller
+  dbus_g_object_register_marshaller(
+      update_engine_VOID__INT64_DOUBLE_STRING_STRING_INT64,
+      G_TYPE_NONE,
+      G_TYPE_INT64,
+      G_TYPE_DOUBLE,
+      G_TYPE_STRING,
+      G_TYPE_STRING,
+      G_TYPE_INT64,
+      G_TYPE_INVALID);
+  
+  // TODO(adlr): make StatusUpdate a const string
+  dbus_g_proxy_add_signal(proxy,
+                          "StatusUpdate",
+                          G_TYPE_INT64,
+                          G_TYPE_DOUBLE,
+                          G_TYPE_STRING,
+                          G_TYPE_STRING,
+                          G_TYPE_INT64,
+                          G_TYPE_INVALID);
+  GMainLoop* loop = g_main_loop_new (NULL, TRUE);
+  dbus_g_proxy_connect_signal(proxy,
+                              "StatusUpdate",
+                              G_CALLBACK(StatusUpdateSignalHandler),
+                              NULL,
+                              NULL);
+  g_main_loop_run(loop);
+  g_main_loop_unref(loop);
+}
+
 bool CheckForUpdates(bool force) {
+  DBusGProxy* proxy;
+  GError* error = NULL;
+  
+  CHECK(GetProxy(&proxy));
+
+  gboolean rc =
+      org_chromium_UpdateEngineInterface_check_for_update(proxy, &error);
+  CHECK_EQ(rc, TRUE) << "Error checking for update: "
+                     << GetGErrorMessage(error);
   return true;
 }
 
@@ -101,6 +173,11 @@
         << "Update check/initiate update failed.";
     return 0;
   }
+  if (FLAGS_watch_for_updates) {
+    LOG(INFO) << "Watching for status updates.";
+    WatchForUpdates();  // Should never return.
+    return 1;
+  }
   
   LOG(INFO) << "No flags specified. Exiting.";
   return 0;