Initial implementation of sending an install success event to Omaha.

BUG=560
TEST=emerged,ran unit tests

Review URL: http://codereview.chromium.org/2981008
diff --git a/integration_unittest.cc b/integration_unittest.cc
index 7b7a35b..8a0ac61 100644
--- a/integration_unittest.cc
+++ b/integration_unittest.cc
@@ -105,7 +105,7 @@
 
   // Actions:
   OmahaRequestPrepAction request_prep_action(false);
-  OmahaRequestAction update_check_action(new LibcurlHttpFetcher);
+  OmahaRequestAction update_check_action(NULL, new LibcurlHttpFetcher);
   OmahaResponseHandlerAction response_handler_action;
   DownloadAction download_action(new LibcurlHttpFetcher);
   InstallAction install_action;
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 3c203e2..9d14a6d 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -10,6 +10,7 @@
 #include <libxml/xpath.h>
 #include <libxml/xpathInternals.h>
 
+#include "base/string_util.h"
 #include "chromeos/obsolete_logging.h"
 #include "update_engine/action_pipe.h"
 #include "update_engine/utils.h"
@@ -59,15 +60,26 @@
   }
 };
 
-// Returns a properly formatted omaha request for a request to Omaha.
-string FormatRequest(const OmahaRequestParams& params) {
+string FormatRequest(const OmahaEvent* event,
+                     const OmahaRequestParams& params) {
+  string body;
+  if (event == NULL) {
+    body = string(
+        "        <o:ping active=\"0\"></o:ping>\n"
+        "        <o:updatecheck></o:updatecheck>\n");
+  } else {
+    body = StringPrintf(
+        "        <o:event eventtype=\"%d\" eventresult=\"%d\" "
+        "errorcode=\"%d\"></o:event>\n",
+        event->type, event->result, event->error_code);
+  }
   return string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                 "<o:gupdate xmlns:o=\"http://www.google.com/update2/request\" "
                 "version=\"" + XmlEncode(kGupdateVersion) + "\" "
                 "updaterversion=\"" + XmlEncode(kGupdateVersion) + "\" "
                 "protocol=\"2.0\" "
-                "machineid=\"") + XmlEncode(params.machine_id) + "\" ismachine=\"1\" "
-      "userid=\"" + XmlEncode(params.user_id) + "\">\n"
+                "machineid=\"") + XmlEncode(params.machine_id) +
+      "\" ismachine=\"1\" userid=\"" + XmlEncode(params.user_id) + "\">\n"
       "    <o:os version=\"" + XmlEncode(params.os_version) + "\" platform=\"" +
       XmlEncode(params.os_platform) + "\" sp=\"" +
       XmlEncode(params.os_sp) + "\"></o:os>\n"
@@ -75,9 +87,7 @@
       XmlEncode(params.app_version) + "\" "
       "lang=\"" + XmlEncode(params.app_lang) + "\" track=\"" +
       XmlEncode(params.app_track) + "\" board=\"" +
-      XmlEncode(params.os_board) + "\">\n"
-      "        <o:ping active=\"0\"></o:ping>\n"
-      "        <o:updatecheck></o:updatecheck>\n"
+      XmlEncode(params.os_board) + "\">\n" + body +
       "    </o:app>\n"
       "</o:gupdate>\n";
 }
@@ -99,8 +109,10 @@
   return string(reinterpret_cast<const char *>(str.get()));
 }
 
-OmahaRequestAction::OmahaRequestAction(HttpFetcher* http_fetcher)
-    : http_fetcher_(http_fetcher) {}
+OmahaRequestAction::OmahaRequestAction(OmahaEvent* event,
+                                       HttpFetcher* http_fetcher)
+    : event_(event),
+      http_fetcher_(http_fetcher) {}
 
 OmahaRequestAction::~OmahaRequestAction() {}
 
@@ -108,9 +120,9 @@
   CHECK(HasInputObject());
   params_ = GetInputObject();
   http_fetcher_->set_delegate(this);
-  string request_post(FormatRequest(params_));
+  string request_post(FormatRequest(event_.get(), params_));
   http_fetcher_->SetPostData(request_post.data(), request_post.size());
-  LOG(INFO) << "Checking for update at " << params_.update_url;
+  LOG(INFO) << "Posting an Omaha request to " << params_.update_url;
   LOG(INFO) << "Request: " << request_post;
   http_fetcher_->BeginTransfer(params_.update_url);
 }
@@ -195,10 +207,18 @@
 void OmahaRequestAction::TransferComplete(HttpFetcher *fetcher,
                                           bool successful) {
   ScopedActionCompleter completer(processor_, this);
-  LOG(INFO) << "Update check response: " << string(response_buffer_.begin(),
-                                                   response_buffer_.end());
+  LOG(INFO) << "Omaha request response: " << string(response_buffer_.begin(),
+                                                    response_buffer_.end());
+
+  // Events are best effort transactions -- assume they always succeed.
+  if (IsEvent()) {
+    CHECK(!HasOutputPipe()) << "No output pipe allowed for event requests.";
+    completer.set_success(true);
+    return;
+  }
+
   if (!successful) {
-    LOG(ERROR) << "Update check network transfer failed.";
+    LOG(ERROR) << "Omaha request network transfer failed.";
     return;
   }
   if (!HasOutputPipe()) {
diff --git a/omaha_request_action.h b/omaha_request_action.h
index f8dddc8..582fe5f 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -93,28 +93,68 @@
 };
 COMPILE_ASSERT(sizeof(off_t) == 8, off_t_not_64bit);
 
+// This struct encapsulates the Omaha event information. For a
+// complete list of defined event types and results, see
+// http://code.google.com/p/omaha/wiki/ServerProtocol#event
+struct OmahaEvent {
+  enum Type {
+    kTypeUnknown = 0,
+    kTypeDownloadComplete = 1,
+    kTypeInstallComplete = 2,
+    kTypeUpdateComplete = 3,
+  };
+
+  enum Result {
+    kResultError = 0,
+    kResultSuccess = 1,
+  };
+
+  OmahaEvent()
+      : type(kTypeUnknown),
+        result(kResultError),
+        error_code(0) {}
+  OmahaEvent(Type in_type, Result in_result, int in_error_code)
+      : type(in_type),
+        result(in_result),
+        error_code(in_error_code) {}
+
+  Type type;
+  Result result;
+  int error_code;
+};
+
 class OmahaRequestAction;
 class NoneType;
 
 template<>
 class ActionTraits<OmahaRequestAction> {
  public:
-  // Takes parameters on the input pipe
+  // Takes parameters on the input pipe.
   typedef OmahaRequestParams InputObjectType;
-  // On success, puts the output path on output
+  // On UpdateCheck success, puts the Omaha response on output. Event
+  // requests do not have an output pipe.
   typedef OmahaResponse OutputObjectType;
 };
 
 class OmahaRequestAction : public Action<OmahaRequestAction>,
                            public HttpFetcherDelegate {
  public:
-  // The ctor takes in all the parameters that will be used for
-  // making the request to Omaha. For some of them we have constants
-  // that should be used.
+  // The ctor takes in all the parameters that will be used for making
+  // the request to Omaha. For some of them we have constants that
+  // should be used.
+  //
   // Takes ownership of the passed in HttpFetcher. Useful for testing.
+  //
+  // Takes ownership of the passed in OmahaEvent. If |event| is NULL,
+  // this is an UpdateCheck request, otherwise it's an Event request.
+  // Event requests always succeed.
+  //
   // A good calling pattern is:
-  // OmahaRequestAction(..., new WhateverHttpFetcher);
-  OmahaRequestAction(HttpFetcher* http_fetcher);
+  // OmahaRequestAction(new OmahaEvent(...), new WhateverHttpFetcher);
+  // or
+  // OmahaRequestAction(NULL, new WhateverHttpFetcher);
+  OmahaRequestAction(OmahaEvent* event,
+                     HttpFetcher* http_fetcher);
   virtual ~OmahaRequestAction();
   typedef ActionTraits<OmahaRequestAction>::InputObjectType InputObjectType;
   typedef ActionTraits<OmahaRequestAction>::OutputObjectType OutputObjectType;
@@ -130,10 +170,16 @@
                              const char* bytes, int length);
   virtual void TransferComplete(HttpFetcher *fetcher, bool successful);
 
+  // Returns true if this is an Event request, false if it's an UpdateCheck.
+  bool IsEvent() const { return event_.get() != NULL; }
+
  private:
   // These are data that are passed in the request to the Omaha server
   OmahaRequestParams params_;
 
+  // Pointer to the OmahaEvent info. This is an UpdateCheck request if NULL.
+  scoped_ptr<OmahaEvent> event_;
+
   // pointer to the HttpFetcher that does the http work
   scoped_ptr<HttpFetcher> http_fetcher_;
 
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index 778f408..0e06439 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -4,8 +4,11 @@
 
 #include <string>
 #include <vector>
+
 #include <glib.h>
-#include <gtest/gtest.h>
+
+#include "base/string_util.h"
+#include "gtest/gtest.h"
 #include "update_engine/action_pipe.h"
 #include "update_engine/mock_http_fetcher.h"
 #include "update_engine/omaha_hash_calculator.h"
@@ -118,16 +121,16 @@
 // OmahaRequestAction. out_response may be NULL.
 // out_post_data may be null; if non-null, the post-data received by the
 // mock HttpFetcher is returned.
-bool TestOmahaRequestAction(const OmahaRequestParams& params,
-                            const string& http_response,
-                            bool expected_success,
-                            OmahaResponse* out_response,
-                            vector<char> *out_post_data) {
-  GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
-  MockHttpFetcher *fetcher = new MockHttpFetcher(http_response.data(),
+bool TestUpdateCheck(const OmahaRequestParams& params,
+                     const string& http_response,
+                     bool expected_success,
+                     OmahaResponse* out_response,
+                     vector<char>* out_post_data) {
+  GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
+  MockHttpFetcher* fetcher = new MockHttpFetcher(http_response.data(),
                                                  http_response.size());
   ObjectFeederAction<OmahaRequestParams> feeder_action;
-  OmahaRequestAction action(fetcher);  // takes ownership of fetcher
+  OmahaRequestAction action(NULL, fetcher);  // takes ownership of fetcher
   OmahaRequestActionTestProcessorDelegate delegate;
   delegate.loop_ = loop;
   delegate.expected_success_ = expected_success;
@@ -153,6 +156,35 @@
   return collector_action.has_input_object_;
 }
 
+// Tests Event requests -- they should always succeed. |out_post_data|
+// may be null; if non-null, the post-data received by the mock
+// HttpFetcher is returned.
+void TestEvent(const OmahaRequestParams& params,
+               OmahaEvent* event,
+               const string& http_response,
+               vector<char>* out_post_data) {
+  GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
+  MockHttpFetcher* fetcher = new MockHttpFetcher(http_response.data(),
+                                                 http_response.size());
+  ObjectFeederAction<OmahaRequestParams> feeder_action;
+  OmahaRequestAction action(event, fetcher);  // takes ownership of fetcher
+  OmahaRequestActionTestProcessorDelegate delegate;
+  delegate.loop_ = loop;
+  ActionProcessor processor;
+  feeder_action.set_obj(params);
+  processor.set_delegate(&delegate);
+  processor.EnqueueAction(&feeder_action);
+  processor.EnqueueAction(&action);
+
+  BondActions(&feeder_action, &action);
+
+  g_timeout_add(0, &StartProcessorInRunLoop, &processor);
+  g_main_loop_run(loop);
+  g_main_loop_unref(loop);
+  if (out_post_data)
+    *out_post_data = fetcher->post_data();
+}
+
 TEST(OmahaRequestActionTest, NoUpdateTest) {
   OmahaRequestParams params("",  // machine_id
                             "",  // user_id
@@ -167,11 +199,11 @@
                             "");  // url
   OmahaResponse response;
   ASSERT_TRUE(
-      TestOmahaRequestAction(params,
-                             GetNoUpdateResponse(OmahaRequestParams::kAppId),
-                             true,
-                             &response,
-                             NULL));
+      TestUpdateCheck(params,
+                      GetNoUpdateResponse(OmahaRequestParams::kAppId),
+                      true,
+                      &response,
+                      NULL));
   EXPECT_FALSE(response.update_exists);
 }
 
@@ -189,18 +221,18 @@
                             "");  // url
   OmahaResponse response;
   ASSERT_TRUE(
-      TestOmahaRequestAction(params,
-                             GetUpdateResponse(OmahaRequestParams::kAppId,
-                                               "1.2.3.4",  // version
-                                               "http://more/info",
-                                               "true",  // prompt
-                                               "http://code/base",  // dl url
-                                               "HASH1234=",  // checksum
-                                               "false",  // needs admin
-                                               "123"),  // size
-                             true,
-                             &response,
-                             NULL));
+      TestUpdateCheck(params,
+                      GetUpdateResponse(OmahaRequestParams::kAppId,
+                                        "1.2.3.4",  // version
+                                        "http://more/info",
+                                        "true",  // prompt
+                                        "http://code/base",  // dl url
+                                        "HASH1234=",  // checksum
+                                        "false",  // needs admin
+                                        "123"),  // size
+                      true,
+                      &response,
+                      NULL));
   EXPECT_TRUE(response.update_exists);
   EXPECT_EQ("1.2.3.4", response.display_version);
   EXPECT_EQ("http://code/base", response.codebase);
@@ -229,7 +261,8 @@
 
   ObjectFeederAction<OmahaRequestParams> feeder_action;
   feeder_action.set_obj(params);
-  OmahaRequestAction action(new MockHttpFetcher(http_response.data(),
+  OmahaRequestAction action(NULL,
+                            new MockHttpFetcher(http_response.data(),
                                                 http_response.size()));
   OmahaRequestActionTestProcessorDelegate delegate;
   delegate.loop_ = loop;
@@ -259,11 +292,11 @@
                             "http://url");
   OmahaResponse response;
   ASSERT_FALSE(
-      TestOmahaRequestAction(params,
-                             "invalid xml>",
-                             false,
-                             &response,
-                             NULL));
+      TestUpdateCheck(params,
+                      "invalid xml>",
+                      false,
+                      &response,
+                      NULL));
   EXPECT_FALSE(response.update_exists);
 }
 
@@ -280,7 +313,7 @@
                             "unittest_track",
                             "http://url");
   OmahaResponse response;
-  ASSERT_FALSE(TestOmahaRequestAction(
+  ASSERT_FALSE(TestUpdateCheck(
       params,
       "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
       "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
@@ -305,7 +338,7 @@
                             "unittest_track",
                             "http://url");
   OmahaResponse response;
-  ASSERT_FALSE(TestOmahaRequestAction(
+  ASSERT_FALSE(TestUpdateCheck(
       params,
       "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
       "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
@@ -330,7 +363,7 @@
                             "unittest_track",
                             "http://url");
   OmahaResponse response;
-  ASSERT_FALSE(TestOmahaRequestAction(
+  ASSERT_FALSE(TestUpdateCheck(
       params,
       "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
       "xmlns=\"http://www.google.com/update2/response\" protocol=\"2.0\"><app "
@@ -355,24 +388,24 @@
                             "unittest_track",
                             "http://url");
   OmahaResponse response;
-  ASSERT_TRUE(TestOmahaRequestAction(params,
-                                     string("<?xml version=\"1.0\" "
-                                            "encoding=\"UTF-8\"?><gupdate "
-                                            "xmlns=\"http://www.google.com/"
-                                            "update2/response\" "
-                                            "protocol=\"2.0\"><app appid=\"") +
-                                     OmahaRequestParams::kAppId
-                                     + "\" status=\"ok\"><ping "
-                                     "status=\"ok\"/><updatecheck "
-                                     "DisplayVersion=\"1.2.3.4\" "
-                                     "Prompt=\"false\" "
-                                     "codebase=\"http://code/base\" "
-                                     "hash=\"HASH1234=\" needsadmin=\"true\" "
-                                     "size=\"123\" "
-                                     "status=\"ok\"/></app></gupdate>",
-                                     true,
-                                     &response,
-                                     NULL));
+  ASSERT_TRUE(TestUpdateCheck(params,
+                              string("<?xml version=\"1.0\" "
+                                     "encoding=\"UTF-8\"?><gupdate "
+                                     "xmlns=\"http://www.google.com/"
+                                     "update2/response\" "
+                                     "protocol=\"2.0\"><app appid=\"") +
+                              OmahaRequestParams::kAppId
+                              + "\" status=\"ok\"><ping "
+                              "status=\"ok\"/><updatecheck "
+                              "DisplayVersion=\"1.2.3.4\" "
+                              "Prompt=\"false\" "
+                              "codebase=\"http://code/base\" "
+                              "hash=\"HASH1234=\" needsadmin=\"true\" "
+                              "size=\"123\" "
+                              "status=\"ok\"/></app></gupdate>",
+                              true,
+                              &response,
+                              NULL));
   EXPECT_TRUE(response.update_exists);
   EXPECT_EQ("1.2.3.4", response.display_version);
   EXPECT_EQ("http://code/base", response.codebase);
@@ -419,7 +452,8 @@
 
   ObjectFeederAction<OmahaRequestParams> feeder_action;
   feeder_action.set_obj(params);
-  OmahaRequestAction action(new MockHttpFetcher(http_response.data(),
+  OmahaRequestAction action(NULL,
+                            new MockHttpFetcher(http_response.data(),
                                                 http_response.size()));
   TerminateEarlyTestProcessorDelegate delegate;
   delegate.loop_ = loop;
@@ -457,11 +491,11 @@
                             "http://url");
   OmahaResponse response;
   ASSERT_FALSE(
-      TestOmahaRequestAction(params,
-                             "invalid xml>",
-                             false,
-                             &response,
-                             &post_data));
+      TestUpdateCheck(params,
+                      "invalid xml>",
+                      false,
+                      &response,
+                      &post_data));
   // convert post_data to string
   string post_str(&post_data[0], post_data.size());
   EXPECT_NE(post_str.find("testthemachine&lt;id"), string::npos);
@@ -487,21 +521,21 @@
                             "http://url");
   OmahaResponse response;
   ASSERT_TRUE(
-      TestOmahaRequestAction(params,
-                             GetUpdateResponse(OmahaRequestParams::kAppId,
-                                               "1.2.3.4",  // version
-                                               "testthe&lt;url",  // more info
-                                               "true",  // prompt
-                                               "testthe&amp;code",  // dl url
-                                               "HASH1234=", // checksum
-                                               "false",  // needs admin
-                                               "123"),  // size
-                             true,
-                             &response,
-                             NULL));
+      TestUpdateCheck(params,
+                      GetUpdateResponse(OmahaRequestParams::kAppId,
+                                        "1.2.3.4",  // version
+                                        "testthe&lt;url",  // more info
+                                        "true",  // prompt
+                                        "testthe&amp;codebase",  // dl url
+                                        "HASH1234=", // checksum
+                                        "false",  // needs admin
+                                        "123"),  // size
+                      true,
+                      &response,
+                      NULL));
 
   EXPECT_EQ(response.more_info_url, "testthe<url");
-  EXPECT_EQ(response.codebase, "testthe&code");
+  EXPECT_EQ(response.codebase, "testthe&codebase");
 }
 
 TEST(OmahaRequestActionTest, ParseIntTest) {
@@ -518,21 +552,97 @@
                             "http://url");
   OmahaResponse response;
   ASSERT_TRUE(
-      TestOmahaRequestAction(params,
-                             GetUpdateResponse(OmahaRequestParams::kAppId,
-                                               "1.2.3.4",  // version
-                                               "theurl",  // more info
-                                               "true",  // prompt
-                                               "thecodebase",  // dl url
-                                               "HASH1234=", // checksum
-                                               "false",  // needs admin
-                                               // overflows int32:
-                                               "123123123123123"),  // size
-                             true,
-                             &response,
-                             NULL));
+      TestUpdateCheck(params,
+                      GetUpdateResponse(OmahaRequestParams::kAppId,
+                                        "1.2.3.4",  // version
+                                        "theurl",  // more info
+                                        "true",  // prompt
+                                        "thecodebase",  // dl url
+                                        "HASH1234=", // checksum
+                                        "false",  // needs admin
+                                        // overflows int32:
+                                        "123123123123123"),  // size
+                      true,
+                      &response,
+                      NULL));
 
   EXPECT_EQ(response.size, 123123123123123ll);
 }
 
+TEST(OmahaRequestActionTest, FormatUpdateCheckOutputTest) {
+  vector<char> post_data;
+  OmahaRequestParams params("machine_id",
+                            "user_id",
+                            OmahaRequestParams::kOsPlatform,
+                            OmahaRequestParams::kOsVersion,
+                            "service_pack",
+                            "x86-generic",
+                            OmahaRequestParams::kAppId,
+                            "0.1.0.0",
+                            "en-US",
+                            "unittest_track",
+                            "http://url");
+  OmahaResponse response;
+  ASSERT_FALSE(TestUpdateCheck(params,
+                               "invalid xml>",
+                               false,
+                               &response,
+                               &post_data));
+  // convert post_data to string
+  string post_str(&post_data[0], post_data.size());
+  EXPECT_NE(post_str.find("        <o:ping active=\"0\"></o:ping>\n"
+                          "        <o:updatecheck></o:updatecheck>\n"),
+            string::npos);
+  EXPECT_EQ(post_str.find("o:event"), string::npos);
+}
+
+TEST(OmahaRequestActionTest, FormatEventOutputTest) {
+  vector<char> post_data;
+  OmahaRequestParams params("machine_id",
+                            "user_id",
+                            OmahaRequestParams::kOsPlatform,
+                            OmahaRequestParams::kOsVersion,
+                            "service_pack",
+                            "x86-generic",
+                            OmahaRequestParams::kAppId,
+                            "0.1.0.0",
+                            "en-US",
+                            "unittest_track",
+                            "http://url");
+  TestEvent(params,
+            new OmahaEvent(OmahaEvent::kTypeDownloadComplete,
+                           OmahaEvent::kResultError,
+                           5),
+            "invalid xml>",
+            &post_data);
+  // convert post_data to string
+  string post_str(&post_data[0], post_data.size());
+  string expected_event = StringPrintf(
+      "        <o:event eventtype=\"%d\" eventresult=\"%d\" "
+      "errorcode=\"%d\"></o:event>\n",
+      OmahaEvent::kTypeDownloadComplete,
+      OmahaEvent::kResultError,
+      5);
+  EXPECT_NE(post_str.find(expected_event), string::npos);
+  EXPECT_EQ(post_str.find("o:updatecheck"), string::npos);
+}
+
+TEST(OmahaRequestActionTest, IsEventTest) {
+  string http_response("doesn't matter");
+
+  OmahaRequestAction update_check_action(
+      NULL,
+      new MockHttpFetcher(http_response.data(),
+                          http_response.size()));
+  EXPECT_FALSE(update_check_action.IsEvent());
+
+  OmahaRequestAction event_action(
+      new OmahaEvent(OmahaEvent::kTypeInstallComplete,
+                     OmahaEvent::kResultError,
+                     0),
+      new MockHttpFetcher(http_response.data(),
+                          http_response.size()));
+  EXPECT_TRUE(event_action.IsEvent());
+}
+
 }  // namespace chromeos_update_engine
diff --git a/omaha_request_prep_action.cc b/omaha_request_prep_action.cc
index 365c463..09ae3bd 100644
--- a/omaha_request_prep_action.cc
+++ b/omaha_request_prep_action.cc
@@ -39,16 +39,16 @@
   const string board(GetLsbValue("CHROMEOS_RELEASE_BOARD", ""));
 
   OmahaRequestParams out(machine_id,  // machine_id
-                        machine_id,  // user_id (use machine_id)
-                        OmahaRequestParams::kOsPlatform,
-                        OmahaRequestParams::kOsVersion,
-                        sp,  // e.g. 0.2.3.3_i686
-                        board,  // e.g. x86-generic
-                        OmahaRequestParams::kAppId,
-                        version,  // app version (from lsb-release)
-                        "en-US",  // lang
-                        track,  // track
-                        update_url);
+                         machine_id,  // user_id (use machine_id)
+                         OmahaRequestParams::kOsPlatform,
+                         OmahaRequestParams::kOsVersion,
+                         sp,  // e.g. 0.2.3.3_i686
+                         board,  // e.g. x86-generic
+                         OmahaRequestParams::kAppId,
+                         version,  // app version (from lsb-release)
+                         "en-US",  // lang
+                         track,  // track
+                         update_url);
 
   CHECK(HasOutputPipe());
   SetOutputObject(out);
diff --git a/update_attempter.cc b/update_attempter.cc
index a8a3207..f034b0b 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -100,10 +100,10 @@
   processor_.set_delegate(this);
 
   // Actions:
-  shared_ptr<OmahaRequestPrepAction> request_prep_action(
+  shared_ptr<OmahaRequestPrepAction> update_check_prep_action(
       new OmahaRequestPrepAction(force_full_update));
   shared_ptr<OmahaRequestAction> update_check_action(
-      new OmahaRequestAction(new LibcurlHttpFetcher));
+      new OmahaRequestAction(NULL, new LibcurlHttpFetcher));
   shared_ptr<OmahaResponseHandlerAction> response_handler_action(
       new OmahaResponseHandlerAction);
   shared_ptr<FilesystemCopierAction> filesystem_copier_action(
@@ -118,11 +118,18 @@
       new SetBootableFlagAction);
   shared_ptr<PostinstallRunnerAction> postinstall_runner_action_postcommit(
       new PostinstallRunnerAction(false));
+  shared_ptr<OmahaRequestPrepAction> install_success_prep_action(
+      new OmahaRequestPrepAction(false));
+  shared_ptr<OmahaRequestAction> install_success_action(
+      new OmahaRequestAction(new OmahaEvent(OmahaEvent::kTypeInstallComplete,
+                                            OmahaEvent::kResultSuccess,
+                                            0),
+                             new LibcurlHttpFetcher));
 
   download_action->set_delegate(this);
   response_handler_action_ = response_handler_action;
 
-  actions_.push_back(shared_ptr<AbstractAction>(request_prep_action));
+  actions_.push_back(shared_ptr<AbstractAction>(update_check_prep_action));
   actions_.push_back(shared_ptr<AbstractAction>(update_check_action));
   actions_.push_back(shared_ptr<AbstractAction>(response_handler_action));
   actions_.push_back(shared_ptr<AbstractAction>(filesystem_copier_action));
@@ -134,6 +141,8 @@
   actions_.push_back(shared_ptr<AbstractAction>(set_bootable_flag_action));
   actions_.push_back(shared_ptr<AbstractAction>(
       postinstall_runner_action_postcommit));
+  actions_.push_back(shared_ptr<AbstractAction>(install_success_prep_action));
+  actions_.push_back(shared_ptr<AbstractAction>(install_success_action));
 
   // Enqueue the actions
   for (vector<shared_ptr<AbstractAction> >::iterator it = actions_.begin();
@@ -143,7 +152,7 @@
 
   // Bond them together. We have to use the leaf-types when calling
   // BondActions().
-  BondActions(request_prep_action.get(),
+  BondActions(update_check_prep_action.get(),
               update_check_action.get());
   BondActions(update_check_action.get(),
               response_handler_action.get());
@@ -159,6 +168,8 @@
               set_bootable_flag_action.get());
   BondActions(set_bootable_flag_action.get(),
               postinstall_runner_action_postcommit.get());
+  BondActions(install_success_prep_action.get(),
+              install_success_action.get());
 
   SetStatusAndNotify(UPDATE_STATUS_CHECKING_FOR_UPDATE);
   processor_.StartProcessing();