Add a feature to get the last UpdateAttempt ErrorCode from update_engine

For autotest, update_engine test failures are always hard to debug,
since the error message is not clear. Add a new flag 'last_attempt_error'
to get the last UpdateAttempt ErrorCode from update_engine.

Bug:25598547
Test:emerge-peppy update_engine
     emerge-peppy update_engine_client
     cros flash a test image to DUT.
     (on the DUT):update_engine_client --last_attempt_error
     Compare the results with the update_engine logs, matched.

Change-Id: Id12681097ed30b0826cad68809f17f934a07e5b2
diff --git a/Android.mk b/Android.mk
index 0b30720..07d8f64 100644
--- a/Android.mk
+++ b/Android.mk
@@ -465,7 +465,8 @@
 LOCAL_SHARED_LIBRARIES += \
     libupdate_engine_client
 LOCAL_SRC_FILES := \
-    update_engine_client.cc
+    update_engine_client.cc \
+    common/error_code_utils.cc
 else  # !defined(BRILLO)
 #TODO(deymo): Remove external/cros/system_api/dbus once the strings are moved
 # out of the DBus interface.
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
index 8a91607..9cf6042 100644
--- a/UpdateEngine.conf
+++ b/UpdateEngine.conf
@@ -54,6 +54,9 @@
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
            send_member="GetPrevVersion"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetLastAttemptError"/>
     <allow send_interface="org.chromium.UpdateEngineLibcrosProxyResolvedInterface" />
   </policy>
   <policy user="power">
diff --git a/binder_bindings/android/brillo/IUpdateEngine.aidl b/binder_bindings/android/brillo/IUpdateEngine.aidl
index 9399ce3..1c0a3e5 100644
--- a/binder_bindings/android/brillo/IUpdateEngine.aidl
+++ b/binder_bindings/android/brillo/IUpdateEngine.aidl
@@ -36,4 +36,5 @@
   String GetPrevVersion();
   String GetRollbackPartition();
   void RegisterStatusCallback(in IUpdateEngineStatusCallback callback);
+  int GetLastAttemptError();
 }
diff --git a/binder_service_brillo.cc b/binder_service_brillo.cc
index 45ac343..6a6a16e 100644
--- a/binder_service_brillo.cc
+++ b/binder_service_brillo.cc
@@ -186,6 +186,12 @@
   return Status::ok();
 }
 
+Status BinderUpdateEngineBrilloService::GetLastAttemptError(
+    int* out_last_attempt_error) {
+  return CallCommonHandler(&UpdateEngineService::GetLastAttemptError,
+                           out_last_attempt_error);
+}
+
 void BinderUpdateEngineBrilloService::UnregisterStatusCallback(
     IUpdateEngineStatusCallback* callback) {
   auto it = callbacks_.begin();
diff --git a/binder_service_brillo.h b/binder_service_brillo.h
index 178305b..497b1b0 100644
--- a/binder_service_brillo.h
+++ b/binder_service_brillo.h
@@ -84,6 +84,8 @@
   android::binder::Status RegisterStatusCallback(
       const android::sp<android::brillo::IUpdateEngineStatusCallback>& callback)
       override;
+  android::binder::Status GetLastAttemptError(
+      int* out_last_attempt_error) override;
 
  private:
   // Generic function for dispatching to the common service.
diff --git a/client_library/client_binder.cc b/client_library/client_binder.cc
index 6f9b7b6..321dfc4 100644
--- a/client_library/client_binder.cc
+++ b/client_library/client_binder.cc
@@ -215,5 +215,16 @@
   return true;
 }
 
+bool BinderUpdateEngineClient::GetLastAttemptError(
+    int32_t* last_attempt_error) const {
+  int out_as_int;
+
+  if (!service_->GetLastAttemptError(&out_as_int).isOk())
+    return false;
+
+  *last_attempt_error = out_as_int;
+  return true;
+}
+
 }  // namespace internal
 }  // namespace update_engine
diff --git a/client_library/client_binder.h b/client_library/client_binder.h
index 562cee4..72f80dd 100644
--- a/client_library/client_binder.h
+++ b/client_library/client_binder.h
@@ -80,6 +80,8 @@
   bool RegisterStatusUpdateHandler(StatusUpdateHandler* handler) override;
   bool UnregisterStatusUpdateHandler(StatusUpdateHandler* handler) override;
 
+  bool GetLastAttemptError(int32_t* last_attempt_error) const override;
+
  private:
   class StatusUpdateCallback :
       public android::brillo::BnUpdateEngineStatusCallback {
diff --git a/client_library/client_dbus.cc b/client_library/client_dbus.cc
index 270a987..0d6b783 100644
--- a/client_library/client_dbus.cc
+++ b/client_library/client_dbus.cc
@@ -225,5 +225,10 @@
                             nullptr);
 }
 
+bool DBusUpdateEngineClient::GetLastAttemptError(
+    int32_t* last_attempt_error) const {
+  return proxy_->GetLastAttemptError(last_attempt_error, nullptr);
+}
+
 }  // namespace internal
 }  // namespace update_engine
diff --git a/client_library/client_dbus.h b/client_library/client_dbus.h
index 507fb5c..02a7e84 100644
--- a/client_library/client_dbus.h
+++ b/client_library/client_dbus.h
@@ -73,6 +73,8 @@
   bool RegisterStatusUpdateHandler(StatusUpdateHandler* handler) override;
   bool UnregisterStatusUpdateHandler(StatusUpdateHandler* handler) override;
 
+  bool GetLastAttemptError(int32_t* last_attempt_error) const override;
+
  private:
   void DBusStatusHandlersRegistered(const std::string& interface,
                                     const std::string& signal_name,
diff --git a/client_library/include/update_engine/client.h b/client_library/include/update_engine/client.h
index dc0fb8c..62ac5fb 100644
--- a/client_library/include/update_engine/client.h
+++ b/client_library/include/update_engine/client.h
@@ -112,6 +112,9 @@
   // Unregister a status update handler
   virtual bool UnregisterStatusUpdateHandler(StatusUpdateHandler* handler) = 0;
 
+  // Get the last UpdateAttempt error code.
+  virtual bool GetLastAttemptError(int32_t* last_attempt_error) const = 0;
+
  protected:
   // Use CreateInstance().
   UpdateEngineClient() = default;
diff --git a/common_service.cc b/common_service.cc
index 3c77c93..f0b818f 100644
--- a/common_service.cc
+++ b/common_service.cc
@@ -35,6 +35,7 @@
 #include "update_engine/omaha_request_params.h"
 #include "update_engine/p2p_manager.h"
 #include "update_engine/update_attempter.h"
+#include "update_engine/payload_state_interface.h"
 
 using base::StringPrintf;
 using brillo::ErrorPtr;
@@ -318,4 +319,10 @@
   return true;
 }
 
+bool UpdateEngineService::GetLastAttemptError(ErrorPtr* /* error */,
+                                              int32_t* out_last_attempt_error) {
+  ErrorCode error_code = system_state_->payload_state()->GetAttemptErrorCode();
+  *out_last_attempt_error = static_cast<int>(error_code);
+  return true;
+}
 }  // namespace chromeos_update_engine
diff --git a/common_service.h b/common_service.h
index e08b4fd..4ad8862 100644
--- a/common_service.h
+++ b/common_service.h
@@ -127,6 +127,10 @@
   bool GetRollbackPartition(brillo::ErrorPtr* error,
                             std::string* out_rollback_partition_name);
 
+  // Returns the last UpdateAttempt error.
+  bool GetLastAttemptError(brillo::ErrorPtr* error,
+                           int32_t* out_last_attempt_error);
+
  private:
   SystemState* system_state_;
 };
diff --git a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
index bc4ec36..02287de 100644
--- a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
+++ b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
@@ -80,5 +80,8 @@
     <method name="GetRollbackPartition">
       <arg type="s" name="rollback_partition_name" direction="out" />
     </method>
+    <method name="GetLastAttemptError">
+      <arg type="i" name="last_attempt_error" direction="out" />
+    </method>
   </interface>
 </node>
diff --git a/dbus_service.cc b/dbus_service.cc
index 0ab0ac0..392555f 100644
--- a/dbus_service.cc
+++ b/dbus_service.cc
@@ -132,6 +132,11 @@
   return common_->GetRollbackPartition(error, out_rollback_partition_name);
 }
 
+bool DBusUpdateEngineService::GetLastAttemptError(
+    ErrorPtr* error, int32_t* out_last_attempt_error){
+ return common_->GetLastAttemptError(error, out_last_attempt_error);
+}
+
 UpdateEngineAdaptor::UpdateEngineAdaptor(SystemState* system_state,
                                          const scoped_refptr<dbus::Bus>& bus)
     : org::chromium::UpdateEngineInterfaceAdaptor(&dbus_service_),
diff --git a/dbus_service.h b/dbus_service.h
index b0c68e5..1486e3c 100644
--- a/dbus_service.h
+++ b/dbus_service.h
@@ -129,6 +129,10 @@
   bool GetRollbackPartition(brillo::ErrorPtr* error,
                             std::string* out_rollback_partition_name) override;
 
+  // Returns the last UpdateAttempt error. If not updated yet, default success
+  // ErrorCode will be returned.
+  bool GetLastAttemptError(brillo::ErrorPtr* error,
+                           int32_t* out_last_attempt_error) override;
  private:
   std::unique_ptr<UpdateEngineService> common_;
 };
diff --git a/payload_state.cc b/payload_state.cc
index 4b5b5fd..04b6579 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -62,6 +62,7 @@
       url_switch_count_(0),
       attempt_num_bytes_downloaded_(0),
       attempt_connection_type_(metrics::ConnectionType::kUnknown),
+      attempt_error_code_(ErrorCode::kSuccess),
       attempt_type_(AttemptType::kUpdate) {
   for (int i = 0; i <= kNumDownloadSources; i++)
     total_bytes_downloaded_[i] = current_bytes_downloaded_[i] = 0;
@@ -232,6 +233,7 @@
                                      metrics::RollbackResult::kSuccess);
       break;
   }
+  attempt_error_code_ = ErrorCode::kSuccess;
 
   // Reset the number of responses seen since it counts from the last
   // successful update, e.g. now.
@@ -265,6 +267,8 @@
       break;
   }
 
+  attempt_error_code_ = base_error;
+
   switch (base_error) {
     // Errors which are good indicators of a problem with a particular URL or
     // the protocol used in the URL or entities in the communication channel
diff --git a/payload_state.h b/payload_state.h
index bec5823..46711b6 100644
--- a/payload_state.h
+++ b/payload_state.h
@@ -147,6 +147,10 @@
     return p2p_url_;
   }
 
+  inline ErrorCode GetAttemptErrorCode() const override {
+    return attempt_error_code_;
+  }
+
  private:
   enum class AttemptType {
     kUpdate,
@@ -559,6 +563,9 @@
   // The connection type when the attempt started.
   metrics::ConnectionType attempt_connection_type_;
 
+  // The attempt error code when the attempt finished.
+  ErrorCode attempt_error_code_;
+
   // Whether we're currently rolling back.
   AttemptType attempt_type_;
 
diff --git a/payload_state_interface.h b/payload_state_interface.h
index 40a13dd..68798ee 100644
--- a/payload_state_interface.h
+++ b/payload_state_interface.h
@@ -192,6 +192,7 @@
   // Sets/gets the P2P download URL, if one is to be used.
   virtual void SetP2PUrl(const std::string& url) = 0;
   virtual std::string GetP2PUrl() const = 0;
+  virtual ErrorCode GetAttemptErrorCode() const = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/update_engine.gyp b/update_engine.gyp
index ab20243..7da34fb 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -334,7 +334,8 @@
       ],
       'sources': [
         'update_engine_client.cc',
-      ],
+        'common/error_code_utils.cc',
+     ],
     },
     # server-side code. This is used for delta_generator and unittests but not
     # for any client code.
diff --git a/update_engine_client.cc b/update_engine_client.cc
index eabc546..22fe6a6 100644
--- a/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -29,12 +29,15 @@
 #include <brillo/daemons/daemon.h>
 #include <brillo/flag_helper.h>
 
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/error_code_utils.h"
 #include "update_engine/client.h"
 #include "update_engine/status_update_handler.h"
 #include "update_engine/update_status.h"
 #include "update_engine/update_status_utils.h"
 
 using chromeos_update_engine::UpdateStatusToString;
+using chromeos_update_engine::ErrorCode;
 using std::string;
 using std::unique_ptr;
 using std::vector;
@@ -262,6 +265,7 @@
               "Listen for status updates and print them to the screen.");
   DEFINE_bool(prev_version, false,
               "Show the previous OS version used before the update reboot.");
+  DEFINE_bool(last_attempt_error, false, "Show the last attempt error.");
 
   // Boilerplate init commands.
   base::CommandLine::Init(argc_, argv_);
@@ -509,6 +513,19 @@
     return kContinueRunning;
   }
 
+  if (FLAGS_last_attempt_error) {
+    int last_attempt_error;
+    if (!client_->GetLastAttemptError(&last_attempt_error)) {
+      LOG(ERROR) << "Error getting last attempt error.";
+    } else {
+      ErrorCode code = static_cast<ErrorCode>(last_attempt_error);
+      string error_msg = chromeos_update_engine::utils::ErrorCodeToString(code);
+      printf("ERROR_CODE=%i\n"
+             "ERROR_MESSAGE=%s\n",
+             last_attempt_error, error_msg.c_str());
+    }
+ }
+
   return 0;
 }