update_engine: add chrome os device requisition to the omaha request

Requisition type is not currently sent to Omaha and is thus unavailable
for AU rules. This adds a requisition attribute to the <app> if the
device has a requisition type. Currently, the types may be one of
remora, shark, or rialto.

TEST=unittest
BUG=b:132014633,b:133324571

Change-Id: I0e53d3a5749da4cbb95ce73cff35191066339009
Reviewed-on: https://chromium-review.googlesource.com/1604218
Commit-Ready: Matthew Ziegelbaum <ziegs@chromium.org>
Tested-by: Matthew Ziegelbaum <ziegs@chromium.org>
Legacy-Commit-Queue: Commit Bot <commit-bot@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 53b2dd5..6604534 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -77,6 +77,10 @@
 
   std::string GetECVersion() const override { return ec_version_; }
 
+  std::string GetDeviceRequisition() const override {
+    return device_requisition_;
+  }
+
   int GetMinKernelKeyVersion() const override {
     return min_kernel_key_version_;
   }
@@ -173,6 +177,10 @@
 
   void SetECVersion(const std::string& ec_version) { ec_version_ = ec_version; }
 
+  void SetDeviceRequisition(const std::string& requisition) {
+    device_requisition_ = requisition;
+  }
+
   void SetMinKernelKeyVersion(int min_kernel_key_version) {
     min_kernel_key_version_ = min_kernel_key_version;
   }
@@ -207,6 +215,7 @@
   std::string hardware_class_{"Fake HWID BLAH-1234"};
   std::string firmware_version_{"Fake Firmware v1.0.1"};
   std::string ec_version_{"Fake EC v1.0a"};
+  std::string device_requisition_{"fake_requisition"};
   int min_kernel_key_version_{kMinKernelKeyVersion};
   int min_firmware_key_version_{kMinFirmwareKeyVersion};
   int kernel_max_rollforward_{kKernelMaxRollforward};
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index 6c53540..da9f10e 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -70,6 +70,10 @@
   // running a custom chrome os ec.
   virtual std::string GetECVersion() const = 0;
 
+  // Returns the OEM device requisition or an empty string if the system does
+  // not have a requisition, or if not running Chrome OS.
+  virtual std::string GetDeviceRequisition() const = 0;
+
   // Returns the minimum kernel key version that verified boot on Chrome OS
   // will allow to boot. This is the value of crossystem tpm_kernver. Returns
   // -1 on error, or if not running on Chrome OS.
diff --git a/hardware_android.cc b/hardware_android.cc
index 80c7757..82f1b9a 100644
--- a/hardware_android.cc
+++ b/hardware_android.cc
@@ -121,6 +121,11 @@
   return GetProperty(kPropBootBaseband, "");
 }
 
+string HardwareAndroid::GetDeviceRequisition() const {
+  LOG(WARNING) << "STUB: Getting requisition is not supported.";
+  return "";
+}
+
 int HardwareAndroid::GetMinKernelKeyVersion() const {
   LOG(WARNING) << "STUB: No Kernel key version is available.";
   return -1;
diff --git a/hardware_android.h b/hardware_android.h
index c59a152..6edf468 100644
--- a/hardware_android.h
+++ b/hardware_android.h
@@ -42,6 +42,7 @@
   std::string GetHardwareClass() const override;
   std::string GetFirmwareVersion() const override;
   std::string GetECVersion() const override;
+  std::string GetDeviceRequisition() const override;
   int GetMinKernelKeyVersion() const override;
   int GetMinFirmwareKeyVersion() const override;
   int GetMaxFirmwareKeyRollforward() const override;
diff --git a/hardware_chromeos.cc b/hardware_chromeos.cc
index 60583e1..dd21c1b 100644
--- a/hardware_chromeos.cc
+++ b/hardware_chromeos.cc
@@ -81,6 +81,27 @@
 
 const char* kActivePingKey = "first_active_omaha_ping_sent";
 
+const char* kOemRequisitionKey = "oem_device_requisition";
+
+// Gets a string value from the vpd for a given key using the `vpd_get_value`
+// shell command. Returns true on success.
+int GetVpdValue(string key, string* result) {
+  int exit_code = 0;
+  string value;
+  vector<string> cmd = {"vpd_get_value", key};
+  if (!chromeos_update_engine::Subprocess::SynchronousExec(
+          cmd, &exit_code, &value) ||
+      exit_code) {
+    LOG(ERROR) << "Failed to get vpd key for " << value
+               << " with exit code: " << exit_code;
+    return false;
+  }
+
+  base::TrimWhitespaceASCII(value, base::TRIM_ALL, &value);
+  *result = value;
+  return true;
+}
+
 }  // namespace
 
 namespace chromeos_update_engine {
@@ -190,6 +211,11 @@
   return utils::ParseECVersion(input_line);
 }
 
+string HardwareChromeOS::GetDeviceRequisition() const {
+  string requisition;
+  return GetVpdValue(kOemRequisitionKey, &requisition) ? requisition : "";
+}
+
 int HardwareChromeOS::GetMinKernelKeyVersion() const {
   return VbGetSystemPropertyInt("tpm_kernver");
 }
@@ -311,17 +337,11 @@
 }
 
 bool HardwareChromeOS::GetFirstActiveOmahaPingSent() const {
-  int exit_code = 0;
   string active_ping_str;
-  vector<string> cmd = {"vpd_get_value", kActivePingKey};
-  if (!Subprocess::SynchronousExec(cmd, &exit_code, &active_ping_str) ||
-      exit_code) {
-    LOG(ERROR) << "Failed to get vpd key for " << kActivePingKey
-               << " with exit code: " << exit_code;
+  if (!GetVpdValue(kActivePingKey, &active_ping_str)) {
     return false;
   }
 
-  base::TrimWhitespaceASCII(active_ping_str, base::TRIM_ALL, &active_ping_str);
   int active_ping;
   if (active_ping_str.empty() ||
       !base::StringToInt(active_ping_str, &active_ping)) {
diff --git a/hardware_chromeos.h b/hardware_chromeos.h
index 04bdae3..230e864 100644
--- a/hardware_chromeos.h
+++ b/hardware_chromeos.h
@@ -47,6 +47,7 @@
   std::string GetHardwareClass() const override;
   std::string GetFirmwareVersion() const override;
   std::string GetECVersion() const override;
+  std::string GetDeviceRequisition() const override;
   int GetMinKernelKeyVersion() const override;
   int GetMinFirmwareKeyVersion() const override;
   int GetMaxFirmwareKeyRollforward() const override;
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index bfbf6a4..91de9d4 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -2762,4 +2762,18 @@
             response.past_rollback_key_version.kernel);
 }
 
+TEST_F(OmahaRequestActionTest, IncludeRequisitionTest) {
+  request_params_.set_device_requisition("remora");
+  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
+  ASSERT_TRUE(TestUpdateCheck());
+  EXPECT_NE(string::npos, post_str.find("requisition=\"remora\""));
+}
+
+TEST_F(OmahaRequestActionTest, NoIncludeRequisitionTest) {
+  request_params_.set_device_requisition("");
+  tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
+  ASSERT_TRUE(TestUpdateCheck());
+  EXPECT_EQ(string::npos, post_str.find("requisition"));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/omaha_request_builder_xml.cc b/omaha_request_builder_xml.cc
index aac0136..2cb002e 100644
--- a/omaha_request_builder_xml.cc
+++ b/omaha_request_builder_xml.cc
@@ -313,6 +313,13 @@
     }
   }
 
+  string requisition_arg;
+  if (!params->device_requisition().empty()) {
+    requisition_arg = "requisition=\"" +
+                      XmlEncodeWithDefault(params->device_requisition(), "") +
+                      "\" ";
+  }
+
   // clang-format off
   string app_xml = "    <app "
       "appid=\"" + XmlEncodeWithDefault(app_data.id, "") + "\" " +
@@ -329,6 +336,7 @@
       "fw_version=\"" + XmlEncodeWithDefault(params->fw_version(), "") + "\" " +
       "ec_version=\"" + XmlEncodeWithDefault(params->ec_version(), "") + "\" " +
       install_date_in_days_str +
+      requisition_arg +
       ">\n" +
          app_body +
       "    </app>\n";
diff --git a/omaha_request_params.cc b/omaha_request_params.cc
index 8c410f1..70867a3 100644
--- a/omaha_request_params.cc
+++ b/omaha_request_params.cc
@@ -95,6 +95,7 @@
     fw_version_ = system_state_->hardware()->GetFirmwareVersion();
     ec_version_ = system_state_->hardware()->GetECVersion();
   }
+  device_requisition_ = system_state_->hardware()->GetDeviceRequisition();
 
   if (image_props_.current_channel == mutable_image_props_.target_channel) {
     // deltas are only okay if the /.nodelta file does not exist.  if we don't
diff --git a/omaha_request_params.h b/omaha_request_params.h
index f3f68f4..7b281da 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -85,6 +85,7 @@
   inline std::string hwid() const { return hwid_; }
   inline std::string fw_version() const { return fw_version_; }
   inline std::string ec_version() const { return ec_version_; }
+  inline std::string device_requisition() const { return device_requisition_; }
 
   inline void set_app_version(const std::string& version) {
     image_props_.version = version;
@@ -265,6 +266,9 @@
   void set_is_powerwash_allowed(bool powerwash_allowed) {
     mutable_image_props_.is_powerwash_allowed = powerwash_allowed;
   }
+  void set_device_requisition(const std::string& requisition) {
+    device_requisition_ = requisition;
+  }
 
  private:
   FRIEND_TEST(OmahaRequestParamsTest, ChannelIndexTest);
@@ -334,6 +338,9 @@
   std::string hwid_;        // Hardware Qualification ID of the client
   std::string fw_version_;  // Chrome OS Firmware Version.
   std::string ec_version_;  // Chrome OS EC Version.
+  // TODO(b:133324571) tracks removal of this field once it is no longer
+  // needed in AU requests. Remove by October 1st 2019.
+  std::string device_requisition_;  // Chrome OS Requisition type.
   bool delta_okay_;         // If this client can accept a delta
   bool interactive_;        // Whether this is a user-initiated update check
 
diff --git a/omaha_request_params_unittest.cc b/omaha_request_params_unittest.cc
index 7332431..bfcbc32 100644
--- a/omaha_request_params_unittest.cc
+++ b/omaha_request_params_unittest.cc
@@ -258,4 +258,8 @@
   EXPECT_TRUE(params_.CollectECFWVersions());
 }
 
+TEST_F(OmahaRequestParamsTest, RequisitionIsSetTest) {
+  EXPECT_TRUE(params_.Init("", "", false));
+  EXPECT_EQ("fake_requisition", params_.device_requisition());
+}
 }  // namespace chromeos_update_engine