Add {Get,Set}CohortHint interface.

Export the "cohort hint" getter and setter in the client interfaces
(D-Bus and Binder). The cohort hint is sent to Omaha on every update
check request but can be ignored and/or reset by Omaha on every
response.

Other minor linter fixes to the affected files.

Bug: 31740109
Test: Build with D-Bus and with Binder.

Change-Id: I93214f6ffb8662c238b3351e52bf2bdf23e46a9c
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
index 9238e3a..58cca09 100644
--- a/UpdateEngine.conf
+++ b/UpdateEngine.conf
@@ -38,6 +38,12 @@
            send_member="GetChannel"/>
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
+           send_member="SetCohortHint"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetCohortHint"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
            send_member="SetP2PUpdatePermission"/>
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
diff --git a/binder_bindings/android/brillo/IUpdateEngine.aidl b/binder_bindings/android/brillo/IUpdateEngine.aidl
index 6a3295a..b1a1b4f 100644
--- a/binder_bindings/android/brillo/IUpdateEngine.aidl
+++ b/binder_bindings/android/brillo/IUpdateEngine.aidl
@@ -28,6 +28,8 @@
   void RebootIfNeeded();
   void SetChannel(in String target_channel, in boolean powewash);
   String GetChannel(in boolean get_current_channel);
+  void SetCohortHint(in String cohort_hint);
+  String GetCohortHint();
   void SetP2PUpdatePermission(in boolean enabled);
   boolean GetP2PUpdatePermission();
   void SetUpdateOverCellularPermission(in boolean enabled);
diff --git a/binder_service_brillo.cc b/binder_service_brillo.cc
index 3947ae1..5e74159 100644
--- a/binder_service_brillo.cc
+++ b/binder_service_brillo.cc
@@ -118,6 +118,22 @@
   return ret;
 }
 
+Status BinderUpdateEngineBrilloService::SetCohortHint(
+    const String16& in_cohort_hint) {
+  return CallCommonHandler(&UpdateEngineService::SetCohortHint,
+                           NormalString(in_cohort_hint));
+}
+
+Status BinderUpdateEngineBrilloService::GetCohortHint(
+    String16* out_cohort_hint) {
+  string cohort_hint;
+  auto ret =
+      CallCommonHandler(&UpdateEngineService::GetCohortHint, &cohort_hint);
+
+  *out_cohort_hint = String16(cohort_hint.c_str());
+  return ret;
+}
+
 Status BinderUpdateEngineBrilloService::SetP2PUpdatePermission(bool enabled) {
   return CallCommonHandler(&UpdateEngineService::SetP2PUpdatePermission,
                            enabled);
diff --git a/binder_service_brillo.h b/binder_service_brillo.h
index b3bb81f..fad0f8c 100644
--- a/binder_service_brillo.h
+++ b/binder_service_brillo.h
@@ -19,6 +19,7 @@
 
 #include <utils/Errors.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -68,6 +69,10 @@
                                      bool powerwash) override;
   android::binder::Status GetChannel(bool get_current_channel,
                                      android::String16* out_channel) override;
+  android::binder::Status SetCohortHint(
+      const android::String16& cohort_hint) override;
+  android::binder::Status GetCohortHint(
+      android::String16* out_cohort_hint) override;
   android::binder::Status SetP2PUpdatePermission(bool enabled) override;
   android::binder::Status GetP2PUpdatePermission(
       bool* out_p2p_permission) override;
diff --git a/client_library/client_binder.cc b/client_library/client_binder.cc
index 6a61722..e98c225 100644
--- a/client_library/client_binder.cc
+++ b/client_library/client_binder.cc
@@ -73,6 +73,20 @@
   return true;
 }
 
+bool BinderUpdateEngineClient::SetCohortHint(const string& in_cohort_hint) {
+  return service_->SetCohortHint(String16{in_cohort_hint.c_str()}).isOk();
+}
+
+bool BinderUpdateEngineClient::GetCohortHint(string* out_cohort_hint) const {
+  String16 out_as_string16;
+
+  if (!service_->GetCohortHint(&out_as_string16).isOk())
+    return false;
+
+  *out_cohort_hint = String8{out_as_string16}.string();
+  return true;
+}
+
 bool BinderUpdateEngineClient::SetUpdateOverCellularPermission(bool allowed) {
   return service_->SetUpdateOverCellularPermission(allowed).isOk();
 }
diff --git a/client_library/client_binder.h b/client_library/client_binder.h
index cd857e0..b1b34da 100644
--- a/client_library/client_binder.h
+++ b/client_library/client_binder.h
@@ -53,6 +53,9 @@
                  std::string* out_new_version,
                  int64_t* out_new_size) const override;
 
+  bool SetCohortHint(const std::string& in_cohort_hint) override;
+  bool GetCohortHint(std::string* out_cohort_hint) const override;
+
   bool SetUpdateOverCellularPermission(bool allowed) override;
   bool GetUpdateOverCellularPermission(bool* allowed) const override;
 
diff --git a/client_library/client_dbus.cc b/client_library/client_dbus.cc
index 5cb63a4..426412c 100644
--- a/client_library/client_dbus.cc
+++ b/client_library/client_dbus.cc
@@ -72,6 +72,14 @@
   return StringToUpdateStatus(status_as_string, out_update_status);
 }
 
+bool DBusUpdateEngineClient::SetCohortHint(const string& cohort_hint) {
+  return proxy_->SetCohortHint(cohort_hint, nullptr);
+}
+
+bool DBusUpdateEngineClient::GetCohortHint(string* cohort_hint) const {
+  return proxy_->GetCohortHint(cohort_hint, nullptr);
+}
+
 bool DBusUpdateEngineClient::SetUpdateOverCellularPermission(bool allowed) {
   return proxy_->SetUpdateOverCellularPermission(allowed, nullptr);
 }
diff --git a/client_library/client_dbus.h b/client_library/client_dbus.h
index a2de594..cec1665 100644
--- a/client_library/client_dbus.h
+++ b/client_library/client_dbus.h
@@ -47,6 +47,9 @@
                  std::string* out_new_version,
                  int64_t* out_new_size) const override;
 
+  bool SetCohortHint(const std::string& cohort_hint) override;
+  bool GetCohortHint(std::string* cohort_hint) const override;
+
   bool SetUpdateOverCellularPermission(bool allowed) override;
   bool GetUpdateOverCellularPermission(bool* allowed) const override;
 
diff --git a/client_library/include/update_engine/client.h b/client_library/include/update_engine/client.h
index 7956dbd..be87c76 100644
--- a/client_library/include/update_engine/client.h
+++ b/client_library/include/update_engine/client.h
@@ -67,6 +67,10 @@
                          std::string* out_new_version,
                          int64_t* out_new_size) const = 0;
 
+  // Getter and setter for the cohort hint.
+  virtual bool SetCohortHint(const std::string& cohort_hint) = 0;
+  virtual bool GetCohortHint(std::string* cohort_hint) const = 0;
+
   // Getter and setter for the updates over cellular connections.
   virtual bool SetUpdateOverCellularPermission(bool allowed) = 0;
   virtual bool GetUpdateOverCellularPermission(bool* allowed) const = 0;
diff --git a/common_service.cc b/common_service.cc
index e284a93..c2473b2 100644
--- a/common_service.cc
+++ b/common_service.cc
@@ -187,6 +187,37 @@
   return true;
 }
 
+bool UpdateEngineService::SetCohortHint(ErrorPtr* error,
+                                        string in_cohort_hint) {
+  PrefsInterface* prefs = system_state_->prefs();
+
+  // It is ok to override the cohort hint with an invalid value since it is
+  // stored in stateful partition. The code reading it should sanitize it
+  // anyway.
+  if (!prefs->SetString(kPrefsOmahaCohortHint, in_cohort_hint)) {
+    LogAndSetError(
+        error,
+        FROM_HERE,
+        StringPrintf("Error setting the cohort hint value to \"%s\".",
+                     in_cohort_hint.c_str()));
+    return false;
+  }
+  return true;
+}
+
+bool UpdateEngineService::GetCohortHint(ErrorPtr* error,
+                                        string* out_cohort_hint) {
+  PrefsInterface* prefs = system_state_->prefs();
+
+  *out_cohort_hint = "";
+  if (prefs->Exists(kPrefsOmahaCohortHint) &&
+      !prefs->GetString(kPrefsOmahaCohortHint, out_cohort_hint)) {
+    LogAndSetError(error, FROM_HERE, "Error getting the cohort hint.");
+    return false;
+  }
+  return true;
+}
+
 bool UpdateEngineService::SetP2PUpdatePermission(ErrorPtr* error,
                                                  bool in_enabled) {
   PrefsInterface* prefs = system_state_->prefs();
diff --git a/common_service.h b/common_service.h
index 1d380bc..69368fb 100644
--- a/common_service.h
+++ b/common_service.h
@@ -92,6 +92,15 @@
                   bool in_get_current_channel,
                   std::string* out_channel);
 
+  // Sets the current "cohort hint" value to |in_cohort_hint|. The cohort hint
+  // is sent back to Omaha on every request and can be used as a hint of what
+  // cohort should we be put on.
+  bool SetCohortHint(brillo::ErrorPtr* error, std::string in_cohort_hint);
+
+  // Return the current cohort hint. This value can be set with SetCohortHint()
+  // and can also be updated from Omaha on every update check request.
+  bool GetCohortHint(brillo::ErrorPtr* error, std::string* out_cohort_hint);
+
   // Enables or disables the sharing and consuming updates over P2P feature
   // according to the |enabled| argument passed.
   bool SetP2PUpdatePermission(brillo::ErrorPtr* error, bool in_enabled);
diff --git a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
index aa99508..848f775 100644
--- a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
+++ b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
@@ -44,6 +44,12 @@
       <arg type="b" name="get_current_channel" direction="in" />
       <arg type="s" name="channel" direction="out" />
     </method>
+    <method name="SetCohortHint">
+      <arg type="s" name="cohort_hint" direction="in" />
+    </method>
+    <method name="GetCohortHint">
+      <arg type="s" name="cohort_hint" direction="out" />
+    </method>
     <method name="SetP2PUpdatePermission">
       <annotation name="org.freedesktop.DBus.GLib.CSymbol"
         value="update_engine_service_set_p2p_update_permission" />
diff --git a/dbus_service.cc b/dbus_service.cc
index d1e6d9e..0a7ad5b 100644
--- a/dbus_service.cc
+++ b/dbus_service.cc
@@ -98,6 +98,16 @@
   return common_->GetChannel(error, in_get_current_channel, out_channel);
 }
 
+bool DBusUpdateEngineService::GetCohortHint(ErrorPtr* error,
+                                            string* out_cohort_hint) {
+  return common_->GetCohortHint(error, out_cohort_hint);
+}
+
+bool DBusUpdateEngineService::SetCohortHint(ErrorPtr* error,
+                                            const string& in_cohort_hint) {
+  return common_->SetCohortHint(error, in_cohort_hint);
+}
+
 bool DBusUpdateEngineService::SetP2PUpdatePermission(ErrorPtr* error,
                                                      bool in_enabled) {
   return common_->SetP2PUpdatePermission(error, in_enabled);
diff --git a/dbus_service.h b/dbus_service.h
index 8b25d43..62984f6 100644
--- a/dbus_service.h
+++ b/dbus_service.h
@@ -19,6 +19,7 @@
 
 #include <inttypes.h>
 
+#include <memory>
 #include <string>
 
 #include <base/memory/ref_counted.h>
@@ -91,6 +92,12 @@
                   bool in_get_current_channel,
                   std::string* out_channel) override;
 
+  bool SetCohortHint(brillo::ErrorPtr* error,
+                     const std::string& in_cohort_hint) override;
+
+  bool GetCohortHint(brillo::ErrorPtr* error,
+                     std::string* out_cohort_hint) override;
+
   // Enables or disables the sharing and consuming updates over P2P feature
   // according to the |enabled| argument passed.
   bool SetP2PUpdatePermission(brillo::ErrorPtr* error,
diff --git a/update_engine_client.cc b/update_engine_client.cc
index 55d7e64..44897e0 100644
--- a/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -233,6 +233,8 @@
                 "target channel is more stable than the current channel unless "
                 "--nopowerwash is specified.");
   DEFINE_bool(check_for_update, false, "Initiate check for updates.");
+  DEFINE_string(
+      cohort_hint, "", "Set the current cohort hint to the passed value.");
   DEFINE_bool(follow, false,
               "Wait for any update operations to complete."
               "Exit status is 0 if the update succeeded, and 1 otherwise.");
@@ -259,6 +261,7 @@
               "Shows whether rollback partition "
               "is available.");
   DEFINE_bool(show_channel, false, "Show the current and target channels.");
+  DEFINE_bool(show_cohort_hint, false, "Show the current cohort hint.");
   DEFINE_bool(show_p2p_update, false,
               "Show the current setting for peer-to-peer update sharing.");
   DEFINE_bool(show_update_over_cellular, false,
@@ -333,6 +336,27 @@
               << (allowed ? "ENABLED" : "DISABLED");
   }
 
+  // Change/show the cohort hint.
+  bool set_cohort_hint =
+      base::CommandLine::ForCurrentProcess()->HasSwitch("cohort_hint");
+  if (set_cohort_hint) {
+    LOG(INFO) << "Setting cohort hint to: \"" << FLAGS_cohort_hint << "\"";
+    if (!client_->SetCohortHint(FLAGS_cohort_hint)) {
+      LOG(ERROR) << "Error setting the cohort hint.";
+      return 1;
+    }
+  }
+
+  if (FLAGS_show_cohort_hint || set_cohort_hint) {
+    string cohort_hint;
+    if (!client_->GetCohortHint(&cohort_hint)) {
+      LOG(ERROR) << "Error getting the cohort hint.";
+      return 1;
+    }
+
+    LOG(INFO) << "Current cohort hint: \"" << cohort_hint << "\"";
+  }
+
   if (!FLAGS_powerwash && !FLAGS_rollback && FLAGS_channel.empty()) {
     LOG(ERROR) << "powerwash flag only makes sense rollback or channel change";
     return 1;