update_engine: support DLC update

All DLC update related code is hide behind USE_dlc flag so platform
update never touches this new code path unless enabled later.

In CheckForUpdate, update_engine calls dlcservice to get a list of DLC
module and set 'dlc_ids_' accordingly.

BUG=chromium:900653
TEST=unittest

Change-Id: I654e37effa7c1b70b25147a027f2b16abe6bf9e1
Reviewed-on: https://chromium-review.googlesource.com/1321009
Commit-Ready: Xiaochu Liu <xiaochu@chromium.org>
Tested-by: Xiaochu Liu <xiaochu@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/common/dlcservice.h b/common/dlcservice.h
new file mode 100644
index 0000000..9dae560
--- /dev/null
+++ b/common/dlcservice.h
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_DLCSERVICE_H_
+#define UPDATE_ENGINE_COMMON_DLCSERVICE_H_
+
+#include <memory>
+
+#include "update_engine/common/dlcservice_interface.h"
+
+namespace chromeos_update_engine {
+
+// This factory function creates a new DlcServiceInterface instance for the
+// current platform.
+std::unique_ptr<DlcServiceInterface> CreateDlcService();
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_DLCSERVICE_H_
diff --git a/common/dlcservice_interface.h b/common/dlcservice_interface.h
new file mode 100644
index 0000000..aa24105
--- /dev/null
+++ b/common/dlcservice_interface.h
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_DLCSERVICE_INTERFACE_H_
+#define UPDATE_ENGINE_COMMON_DLCSERVICE_INTERFACE_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+namespace chromeos_update_engine {
+
+// The abstract dlcservice interface defines the interaction with the
+// platform's dlcservice.
+class DlcServiceInterface {
+ public:
+  virtual ~DlcServiceInterface() = default;
+
+  // Returns true and a list of installed DLC module ids in |dlc_module_ids|.
+  // On failure it returns false.
+  virtual bool GetInstalled(std::vector<std::string>* dlc_module_ids) = 0;
+
+ protected:
+  DlcServiceInterface() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DlcServiceInterface);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_DLCSERVICE_INTERFACE_H_
diff --git a/common/dlcservice_stub.cc b/common/dlcservice_stub.cc
new file mode 100644
index 0000000..c5f9306
--- /dev/null
+++ b/common/dlcservice_stub.cc
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/common/dlcservice_stub.h"
+
+#include <memory>
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+std::unique_ptr<DlcServiceInterface> CreateDlcService() {
+  return std::make_unique<DlcServiceStub>();
+}
+
+bool DlcServiceStub::GetInstalled(std::vector<std::string>* dlc_module_ids) {
+  if (dlc_module_ids)
+    dlc_module_ids->clear();
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/common/dlcservice_stub.h b/common/dlcservice_stub.h
new file mode 100644
index 0000000..4e12c11
--- /dev/null
+++ b/common/dlcservice_stub.h
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_DLCSERVICE_STUB_H_
+#define UPDATE_ENGINE_COMMON_DLCSERVICE_STUB_H_
+
+#include <string>
+#include <vector>
+
+#include "update_engine/common/dlcservice_interface.h"
+
+namespace chromeos_update_engine {
+
+// An implementation of the DlcServiceInterface that does nothing.
+class DlcServiceStub : public DlcServiceInterface {
+ public:
+  DlcServiceStub() = default;
+  ~DlcServiceStub() = default;
+
+  // BootControlInterface overrides.
+  bool GetInstalled(std::vector<std::string>* dlc_module_ids) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DlcServiceStub);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_COMMON_DLCSERVICE_STUB_H_
diff --git a/dlcservice_chromeos.cc b/dlcservice_chromeos.cc
new file mode 100644
index 0000000..e95f08f
--- /dev/null
+++ b/dlcservice_chromeos.cc
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/dlcservice_chromeos.h"
+
+#include <dlcservice/dbus-proxies.h>
+#include <dlcservice/proto_bindings/dlcservice.pb.h>
+
+#include "update_engine/dbus_connection.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+std::unique_ptr<DlcServiceInterface> CreateDlcService() {
+  return std::make_unique<DlcServiceChromeOS>();
+}
+
+bool DlcServiceChromeOS::GetInstalled(vector<string>* dlc_module_ids) {
+  if (!dlc_module_ids)
+    return false;
+  org::chromium::DlcServiceInterfaceProxy dlcservice_proxy(
+      DBusConnection::Get()->GetDBus());
+  string dlc_module_list_str;
+  if (!dlcservice_proxy.GetInstalled(&dlc_module_list_str, nullptr)) {
+    LOG(ERROR) << "dlcservice does not return installed DLC module list.";
+    return false;
+  }
+  dlcservice::DlcModuleList dlc_module_list;
+  if (!dlc_module_list.ParseFromString(dlc_module_list_str)) {
+    LOG(ERROR) << "Errors parsing DlcModuleList protobuf.";
+    return false;
+  }
+  for (const auto& dlc_module_info : dlc_module_list.dlc_module_infos()) {
+    dlc_module_ids->emplace_back(dlc_module_info.dlc_id());
+  }
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/dlcservice_chromeos.h b/dlcservice_chromeos.h
new file mode 100644
index 0000000..8d103c1
--- /dev/null
+++ b/dlcservice_chromeos.h
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
+#define UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "update_engine/common/dlcservice_interface.h"
+
+namespace chromeos_update_engine {
+
+// The Chrome OS implementation of the DlcServiceInterface. This interface
+// interacts with dlcservice via D-Bus.
+class DlcServiceChromeOS : public DlcServiceInterface {
+ public:
+  DlcServiceChromeOS() = default;
+  ~DlcServiceChromeOS() = default;
+
+  // BootControlInterface overrides.
+  bool GetInstalled(std::vector<std::string>* dlc_module_ids) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DlcServiceChromeOS);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
diff --git a/fake_system_state.h b/fake_system_state.h
index 67ad3aa..627bd5c 100644
--- a/fake_system_state.h
+++ b/fake_system_state.h
@@ -100,6 +100,8 @@
     return power_manager_;
   }
 
+  inline DlcServiceInterface* dlcservice() override { return dlcservice_; }
+
   inline bool system_rebooted() override { return fake_system_rebooted_; }
 
   // Setters for the various members, can be used for overriding the default
@@ -165,6 +167,10 @@
     fake_system_rebooted_ = system_rebooted;
   }
 
+  inline void set_dlcservice(DlcServiceInterface* dlcservice) {
+    dlcservice_ = dlcservice;
+  }
+
   // Getters for the built-in default implementations. These return the actual
   // concrete type of each implementation. For additional safety, they will fail
   // whenever the requested default was overridden by a different
@@ -261,6 +267,7 @@
   P2PManager* p2p_manager_;
   chromeos_update_manager::UpdateManager* update_manager_;
   PowerManagerInterface* power_manager_{&mock_power_manager_};
+  DlcServiceInterface* dlcservice_;
 
   // Other object pointers (not preinitialized).
   const policy::DevicePolicy* device_policy_;
diff --git a/real_system_state.cc b/real_system_state.cc
index 9dab3a1..f576c6c 100644
--- a/real_system_state.cc
+++ b/real_system_state.cc
@@ -32,6 +32,7 @@
 #include "update_engine/common/boot_control.h"
 #include "update_engine/common/boot_control_stub.h"
 #include "update_engine/common/constants.h"
+#include "update_engine/common/dlcservice.h"
 #include "update_engine/common/hardware.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/metrics_reporter_omaha.h"
@@ -88,6 +89,12 @@
     return false;
   }
 
+  dlcservice_ = CreateDlcService();
+  if (!dlcservice_) {
+    LOG(ERROR) << "Error initializing the DlcServiceInterface.";
+    return false;
+  }
+
   // Initialize standard and powerwash-safe prefs.
   base::FilePath non_volatile_path;
   // TODO(deymo): Fall back to in-memory prefs if there's no physical directory
diff --git a/real_system_state.h b/real_system_state.h
index 5239160..4712008 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -31,6 +31,7 @@
 #include "update_engine/certificate_checker.h"
 #include "update_engine/common/boot_control_interface.h"
 #include "update_engine/common/clock.h"
+#include "update_engine/common/dlcservice_interface.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs.h"
 #include "update_engine/connection_manager_interface.h"
@@ -126,6 +127,10 @@
 
   inline bool system_rebooted() override { return system_rebooted_; }
 
+  inline DlcServiceInterface* dlcservice() override {
+    return dlcservice_.get();
+  }
+
  private:
   // Real DBus proxies using the DBus connection.
 #if USE_CHROME_KIOSK_APP
@@ -136,6 +141,9 @@
   // Interface for the power manager.
   std::unique_ptr<PowerManagerInterface> power_manager_;
 
+  // Interface for dlcservice.
+  std::unique_ptr<DlcServiceInterface> dlcservice_;
+
   // Interface for the clock.
   std::unique_ptr<BootControlInterface> boot_control_;
 
diff --git a/system_state.h b/system_state.h
index 1b0ad08..f46cbcf 100644
--- a/system_state.h
+++ b/system_state.h
@@ -37,6 +37,7 @@
 class BootControlInterface;
 class ClockInterface;
 class ConnectionManagerInterface;
+class DlcServiceInterface;
 class HardwareInterface;
 class MetricsReporterInterface;
 class OmahaRequestParams;
@@ -109,6 +110,9 @@
   // restarted. Important for tracking whether you are running instance of the
   // update engine on first boot or due to a crash/restart.
   virtual bool system_rebooted() = 0;
+
+  // Returns a pointer to the DlcServiceInterface singleton.
+  virtual DlcServiceInterface* dlcservice() = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/update_attempter.cc b/update_attempter.cc
index 581b901..f1ec174 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -44,6 +44,7 @@
 #include "update_engine/common/boot_control_interface.h"
 #include "update_engine/common/clock_interface.h"
 #include "update_engine/common/constants.h"
+#include "update_engine/common/dlcservice_interface.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/platform_constants.h"
 #include "update_engine/common/prefs_interface.h"
@@ -840,6 +841,9 @@
   }
 
   if (forced_update_pending_callback_.get()) {
+    if (!system_state_->dlcservice()->GetInstalled(&dlc_ids_)) {
+      dlc_ids_.clear();
+    }
     // Make sure that a scheduling request is made prior to calling the forced
     // update pending callback.
     ScheduleUpdates();
diff --git a/update_attempter.h b/update_attempter.h
index ebbd5ec..2d020fa 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -266,6 +266,7 @@
   FRIEND_TEST(UpdateAttempterTest, BootTimeInUpdateMarkerFile);
   FRIEND_TEST(UpdateAttempterTest, BroadcastCompleteDownloadTest);
   FRIEND_TEST(UpdateAttempterTest, ChangeToDownloadingOnReceivedBytesTest);
+  FRIEND_TEST(UpdateAttempterTest, CheckForUpdateAUDlcTest);
   FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventTest);
   FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventResumedTest);
   FRIEND_TEST(UpdateAttempterTest, DisableDeltaUpdateIfNeededTest);
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 0e1be2c..8fe2cb9 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -31,6 +31,7 @@
 #include <policy/mock_device_policy.h>
 #include <policy/mock_libpolicy.h>
 
+#include "update_engine/common/dlcservice_interface.h"
 #include "update_engine/common/fake_clock.h"
 #include "update_engine/common/fake_prefs.h"
 #include "update_engine/common/mock_action.h"
@@ -79,6 +80,15 @@
 
 namespace chromeos_update_engine {
 
+namespace {
+
+class MockDlcService : public DlcServiceInterface {
+ public:
+  MOCK_METHOD1(GetInstalled, bool(vector<string>*));
+};
+
+}  // namespace
+
 const char kRollbackVersion[] = "10575.39.2";
 
 // Test a subclass rather than the main class directly so that we can mock out
@@ -122,10 +132,13 @@
     // Override system state members.
     fake_system_state_.set_connection_manager(&mock_connection_manager);
     fake_system_state_.set_update_attempter(&attempter_);
+    fake_system_state_.set_dlcservice(&mock_dlcservice_);
     loop_.SetAsCurrent();
 
     certificate_checker_.Init();
 
+    attempter_.set_forced_update_pending_callback(
+        new base::Callback<void(bool, bool)>(base::Bind([](bool, bool) {})));
     // Finish initializing the attempter.
     attempter_.Init();
   }
@@ -200,6 +213,7 @@
   UpdateAttempterUnderTest attempter_{&fake_system_state_};
   OpenSSLWrapper openssl_wrapper_;
   CertificateChecker certificate_checker_;
+  MockDlcService mock_dlcservice_;
 
   NiceMock<MockActionProcessor>* processor_;
   NiceMock<MockPrefs>* prefs_;  // Shortcut to fake_system_state_->mock_prefs().
@@ -1161,6 +1175,21 @@
   EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
 }
 
+TEST_F(UpdateAttempterTest, CheckForUpdateAUDlcTest) {
+  fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+  fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
+
+  const string dlc_module_id = "a_dlc_module_id";
+  vector<string> dlc_module_ids = {dlc_module_id};
+  ON_CALL(mock_dlcservice_, GetInstalled(testing::_))
+      .WillByDefault(DoAll(testing::SetArgPointee<0>(dlc_module_ids),
+                           testing::Return(true)));
+
+  attempter_.CheckForUpdate("", "autest", UpdateAttemptFlags::kNone);
+  EXPECT_EQ(attempter_.dlc_ids_.size(), 1);
+  EXPECT_EQ(attempter_.dlc_ids_[0], dlc_module_id);
+}
+
 TEST_F(UpdateAttempterTest, CheckForUpdateAUTest) {
   fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
   fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
diff --git a/update_engine.gyp b/update_engine.gyp
index 7621153..c9e05d8 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -223,6 +223,13 @@
           'libupdate_engine-client',
           'vboot_host',
         ],
+        'conditions':[
+          ['USE_dlc == 1', {
+            'exported_deps' : [
+              'libdlcservice-client',
+            ],
+          }],
+        ],
         'deps': ['<@(exported_deps)'],
       },
       'all_dependent_settings': {
@@ -308,6 +315,16 @@
             'update_engine-dbus-kiosk-app-client',
           ],
         }],
+        ['USE_dlc == 1', {
+          'sources': [
+            'dlcservice_chromeos.cc',
+          ],
+        }],
+        ['USE_dlc == 0', {
+          'sources': [
+            'common/dlcservice_stub.cc',
+          ],
+        }],
       ],
     },
     # update_engine daemon.