Convert BootControl HAL to binder.

The new HAL specification uses HIDL to communicate between the caller
and the HAL code itself, which is now what the BootControlAndroid
implementation does. Nevertheless, since update_engine_sideload uses
the boot_control HAL directly as a static library this patch keeps the
old implementation around, now called BootControlRecovery for this
purpose.

Bug: 31863957
Test: Applied an update on master.

Change-Id: Ib2f0c4f0c616e76d19cc78b9b7e44bc55aec8ffa
diff --git a/Android.mk b/Android.mk
index 263f840..a56a81b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -235,6 +235,41 @@
 LOCAL_SRC_FILES := $(ue_libpayload_consumer_src_files)
 include $(BUILD_STATIC_LIBRARY)
 
+# libupdate_engine_boot_control (type: static_library)
+# ========================================================
+# A BootControl class implementation using Android's HIDL boot_control HAL.
+ue_libupdate_engine_boot_control_exported_static_libraries := \
+    update_metadata-protos \
+    $(ue_update_metadata_protos_exported_static_libraries)
+
+ue_libupdate_engine_boot_control_exported_shared_libraries := \
+    libhwbinder \
+    libhidl \
+    libutils \
+    android.hardware.boot@1.0 \
+    $(ue_update_metadata_protos_exported_shared_libraries)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libupdate_engine_boot_control
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(ue_common_cflags)
+LOCAL_CPPFLAGS := $(ue_common_cppflags)
+LOCAL_LDFLAGS := $(ue_common_ldflags)
+LOCAL_C_INCLUDES := \
+    $(ue_common_c_includes) \
+    bootable/recovery
+LOCAL_STATIC_LIBRARIES := \
+    $(ue_common_static_libraries) \
+    $(ue_libupdate_engine_boot_control_exported_static_libraries)
+LOCAL_SHARED_LIBRARIES := \
+    $(ue_common_shared_libraries) \
+    $(ue_libupdate_engine_boot_control_exported_shared_libraries)
+LOCAL_SRC_FILES := \
+    boot_control_android.cc
+include $(BUILD_STATIC_LIBRARY)
+
 ifeq ($(local_use_omaha),1)
 
 # libupdate_engine (type: static_library)
@@ -250,17 +285,19 @@
     libbz \
     libfs_mgr \
     $(ue_libpayload_consumer_exported_static_libraries) \
-    $(ue_update_metadata_protos_exported_static_libraries)
+    $(ue_update_metadata_protos_exported_static_libraries) \
+    libupdate_engine_boot_control \
+    $(ue_libupdate_engine_boot_control_exported_static_libraries)
 ue_libupdate_engine_exported_shared_libraries := \
     libmetrics \
     libexpat \
     libbrillo-policy \
-    libhardware \
     libcurl \
     libcutils \
     libssl \
     $(ue_libpayload_consumer_exported_shared_libraries) \
-    $(ue_update_metadata_protos_exported_shared_libraries)
+    $(ue_update_metadata_protos_exported_shared_libraries) \
+    $(ue_libupdate_engine_boot_control_exported_shared_libraries)
 ifeq ($(local_use_dbus),1)
 ue_libupdate_engine_exported_static_libraries += \
     update_engine-dbus-adaptor \
@@ -315,7 +352,6 @@
     $(ue_libpayload_consumer_exported_shared_libraries:-host=) \
     $(ue_update_metadata_protos_exported_shared_libraries)
 LOCAL_SRC_FILES := \
-    boot_control_android.cc \
     certificate_checker.cc \
     common_service.cc \
     connection_utils.cc \
@@ -397,16 +433,18 @@
 ue_libupdate_engine_android_exported_static_libraries := \
     libpayload_consumer \
     libfs_mgr \
-    $(ue_libpayload_consumer_exported_static_libraries)
+    $(ue_libpayload_consumer_exported_static_libraries) \
+    libupdate_engine_boot_control \
+    $(ue_libupdate_engine_boot_control_exported_static_libraries)
 ue_libupdate_engine_android_exported_shared_libraries := \
     $(ue_libpayload_consumer_exported_shared_libraries) \
+    $(ue_libupdate_engine_boot_control_exported_shared_libraries) \
     libandroid \
     libbinder \
     libbinderwrapper \
     libbrillo-binder \
     libcutils \
     libcurl \
-    libhardware \
     libssl \
     libutils
 
@@ -436,7 +474,6 @@
     binder_bindings/android/os/IUpdateEngine.aidl \
     binder_bindings/android/os/IUpdateEngineCallback.aidl \
     binder_service_android.cc \
-    boot_control_android.cc \
     certificate_checker.cc \
     daemon.cc \
     daemon_state_android.cc \
@@ -522,7 +559,7 @@
 LOCAL_C_INCLUDES += \
     external/cros/system_api/dbus
 LOCAL_SRC_FILES := \
-    boot_control_android.cc \
+    boot_control_recovery.cc \
     hardware_android.cc \
     network_selector_stub.cc \
     proxy_resolver.cc \
diff --git a/boot_control_android.cc b/boot_control_android.cc
index a7d7456..71ccd73 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -28,13 +28,17 @@
 
 using std::string;
 
-#ifdef _UE_SIDELOAD
-// When called from update_engine_sideload, we don't attempt to dynamically load
-// the right boot_control HAL, instead we use the only HAL statically linked in
-// via the PRODUCT_STATIC_BOOT_CONTROL_HAL make variable and access the module
-// struct directly.
-extern const hw_module_t HAL_MODULE_INFO_SYM;
-#endif  // _UE_SIDELOAD
+using android::hardware::Return;
+using android::hardware::boot::V1_0::BoolResult;
+using android::hardware::boot::V1_0::CommandResult;
+using android::hardware::boot::V1_0::IBootControl;
+using android::hardware::hidl_string;
+
+namespace {
+auto StoreResultCallback(CommandResult* dest) {
+  return [dest](const CommandResult& result) { *dest = result; };
+}
+}  // namespace
 
 namespace chromeos_update_engine {
 
@@ -52,43 +56,25 @@
 }  // namespace boot_control
 
 bool BootControlAndroid::Init() {
-  const hw_module_t* hw_module;
-  int ret;
-
-#ifdef _UE_SIDELOAD
-  // For update_engine_sideload, we simulate the hw_get_module() by accessing it
-  // from the current process directly.
-  hw_module = &HAL_MODULE_INFO_SYM;
-  ret = 0;
-  if (!hw_module ||
-      strcmp(BOOT_CONTROL_HARDWARE_MODULE_ID, hw_module->id) != 0) {
-    ret = -EINVAL;
-  }
-#else  // !_UE_SIDELOAD
-  ret = hw_get_module(BOOT_CONTROL_HARDWARE_MODULE_ID, &hw_module);
-#endif  // _UE_SIDELOAD
-  if (ret != 0) {
-    LOG(ERROR) << "Error loading boot_control HAL implementation.";
+  module_ = IBootControl::getService("bootctrl");
+  if (module_ == nullptr) {
+    LOG(ERROR) << "Error getting bootctrl HIDL module.\n";
     return false;
   }
 
-  module_ = reinterpret_cast<boot_control_module_t*>(const_cast<hw_module_t*>(hw_module));
-  module_->init(module_);
+  LOG(INFO) << "Loaded boot HIDL HAL version "
+            << module_->getInterfaceVersion().get_major() << "."
+            << module_->getInterfaceVersion().get_minor();
 
-  LOG(INFO) << "Loaded boot_control HAL "
-            << "'" << hw_module->name << "' "
-            << "version " << (hw_module->module_api_version>>8) << "."
-            << (hw_module->module_api_version&0xff) << " "
-            << "authored by '" << hw_module->author << "'.";
   return true;
 }
 
 unsigned int BootControlAndroid::GetNumSlots() const {
-  return module_->getNumberSlots(module_);
+  return module_->getNumberSlots();
 }
 
 BootControlInterface::Slot BootControlAndroid::GetCurrentSlot() const {
-  return module_->getCurrentSlot(module_);
+  return module_->getCurrentSlot();
 }
 
 bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
@@ -123,8 +109,13 @@
     return false;
   }
 
-  const char* suffix = module_->getSuffix(module_, slot);
-  if (suffix == nullptr) {
+  string suffix;
+  auto store_suffix_cb = [&suffix](hidl_string cb_suffix) {
+    suffix = cb_suffix.c_str();
+  };
+  Return<void> ret = module_->getSuffix(slot, store_suffix_cb);
+
+  if (!ret.getStatus().isOk()) {
     LOG(ERROR) << "boot_control impl returned no suffix for slot "
                << SlotName(slot);
     return false;
@@ -141,42 +132,65 @@
 }
 
 bool BootControlAndroid::IsSlotBootable(Slot slot) const {
-  int ret = module_->isSlotBootable(module_, slot);
-  if (ret < 0) {
+  Return<BoolResult> ret = module_->isSlotBootable(slot);
+  if (!ret.getStatus().isOk()) {
     LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
-               << " is bootable: " << strerror(-ret);
+               << " is bootable: "
+               << ret.getStatus().exceptionMessage().string();
     return false;
   }
-  return ret == 1;
+  if (ret == BoolResult::INVALID_SLOT) {
+    LOG(ERROR) << "Invalid slot: " << SlotName(slot);
+    return false;
+  }
+  return ret == BoolResult::TRUE;
 }
 
 bool BootControlAndroid::MarkSlotUnbootable(Slot slot) {
-  int ret = module_->setSlotAsUnbootable(module_, slot);
-  if (ret < 0) {
-    LOG(ERROR) << "Unable to mark slot " << SlotName(slot)
-               << " as bootable: " << strerror(-ret);
+  CommandResult result;
+  auto ret = module_->setSlotAsUnbootable(slot, StoreResultCallback(&result));
+  if (!ret.getStatus().isOk()) {
+    LOG(ERROR) << "Unable to call MarkSlotUnbootable for slot "
+               << SlotName(slot) << ": "
+               << ret.getStatus().exceptionMessage().string();
     return false;
   }
-  return ret == 0;
+  if (!result.success) {
+    LOG(ERROR) << "Unable to mark slot " << SlotName(slot)
+               << " as unbootable: " << result.errMsg.c_str();
+  }
+  return result.success;
 }
 
 bool BootControlAndroid::SetActiveBootSlot(Slot slot) {
-  int ret = module_->setActiveBootSlot(module_, slot);
-  if (ret < 0) {
-    LOG(ERROR) << "Unable to set the active slot to slot " << SlotName(slot)
-               << ": " << strerror(-ret);
+  CommandResult result;
+  auto ret = module_->setActiveBootSlot(slot, StoreResultCallback(&result));
+  if (!ret.getStatus().isOk()) {
+    LOG(ERROR) << "Unable to call SetActiveBootSlot for slot " << SlotName(slot)
+               << ": " << ret.getStatus().exceptionMessage().string();
+    return false;
   }
-  return ret == 0;
+  if (!result.success) {
+    LOG(ERROR) << "Unable to set the active slot to slot " << SlotName(slot)
+               << ": " << result.errMsg.c_str();
+  }
+  return result.success;
 }
 
 bool BootControlAndroid::MarkBootSuccessfulAsync(
     base::Callback<void(bool)> callback) {
-  int ret = module_->markBootSuccessful(module_);
-  if (ret < 0) {
-    LOG(ERROR) << "Unable to mark boot successful: " << strerror(-ret);
+  CommandResult result;
+  auto ret = module_->markBootSuccessful(StoreResultCallback(&result));
+  if (!ret.getStatus().isOk()) {
+    LOG(ERROR) << "Unable to call MarkBootSuccessful: "
+               << ret.getStatus().exceptionMessage().string();
+    return false;
+  }
+  if (!result.success) {
+    LOG(ERROR) << "Unable to mark boot successful: " << result.errMsg.c_str();
   }
   return brillo::MessageLoop::current()->PostTask(
-             FROM_HERE, base::Bind(callback, ret == 0)) !=
+             FROM_HERE, base::Bind(callback, result.success)) !=
          brillo::MessageLoop::kTaskIdNull;
 }
 
diff --git a/boot_control_android.h b/boot_control_android.h
index a5a6255..1de0e41 100644
--- a/boot_control_android.h
+++ b/boot_control_android.h
@@ -19,8 +19,7 @@
 
 #include <string>
 
-#include <hardware/boot_control.h>
-#include <hardware/hardware.h>
+#include <android/hardware/boot/1.0/IBootControl.h>
 
 #include "update_engine/common/boot_control.h"
 
@@ -49,9 +48,7 @@
   bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
 
  private:
-  // NOTE: There is no way to release/unload HAL implementations so
-  // this is essentially leaked on object destruction.
-  boot_control_module_t* module_;
+  ::android::sp<::android::hardware::boot::V1_0::IBootControl> module_;
 
   DISALLOW_COPY_AND_ASSIGN(BootControlAndroid);
 };
diff --git a/boot_control_recovery.cc b/boot_control_recovery.cc
new file mode 100644
index 0000000..39b5ff1
--- /dev/null
+++ b/boot_control_recovery.cc
@@ -0,0 +1,182 @@
+//
+// Copyright (C) 2015 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/boot_control_recovery.h"
+
+#include <base/bind.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <brillo/make_unique_ptr.h>
+#include <brillo/message_loops/message_loop.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/utils_android.h"
+
+using std::string;
+
+#ifndef _UE_SIDELOAD
+#error "BootControlRecovery should only be used for update_engine_sideload."
+#endif
+
+// When called from update_engine_sideload, we don't attempt to dynamically load
+// the right boot_control HAL, instead we use the only HAL statically linked in
+// via the PRODUCT_STATIC_BOOT_CONTROL_HAL make variable and access the module
+// struct directly.
+extern const hw_module_t HAL_MODULE_INFO_SYM;
+
+namespace chromeos_update_engine {
+
+namespace boot_control {
+
+// Factory defined in boot_control.h.
+std::unique_ptr<BootControlInterface> CreateBootControl() {
+  std::unique_ptr<BootControlRecovery> boot_control(new BootControlRecovery());
+  if (!boot_control->Init()) {
+    return nullptr;
+  }
+  return std::move(boot_control);
+}
+
+}  // namespace boot_control
+
+bool BootControlRecovery::Init() {
+  const hw_module_t* hw_module;
+  int ret;
+
+  // For update_engine_sideload, we simulate the hw_get_module() by accessing it
+  // from the current process directly.
+  hw_module = &HAL_MODULE_INFO_SYM;
+  ret = 0;
+  if (!hw_module ||
+      strcmp(BOOT_CONTROL_HARDWARE_MODULE_ID, hw_module->id) != 0) {
+    ret = -EINVAL;
+  }
+  if (ret != 0) {
+    LOG(ERROR) << "Error loading boot_control HAL implementation.";
+    return false;
+  }
+
+  module_ = reinterpret_cast<boot_control_module_t*>(
+      const_cast<hw_module_t*>(hw_module));
+  module_->init(module_);
+
+  LOG(INFO) << "Loaded boot_control HAL "
+            << "'" << hw_module->name << "' "
+            << "version " << (hw_module->module_api_version >> 8) << "."
+            << (hw_module->module_api_version & 0xff) << " "
+            << "authored by '" << hw_module->author << "'.";
+  return true;
+}
+
+unsigned int BootControlRecovery::GetNumSlots() const {
+  return module_->getNumberSlots(module_);
+}
+
+BootControlInterface::Slot BootControlRecovery::GetCurrentSlot() const {
+  return module_->getCurrentSlot(module_);
+}
+
+bool BootControlRecovery::GetPartitionDevice(const string& partition_name,
+                                             Slot slot,
+                                             string* device) const {
+  // We can't use fs_mgr to look up |partition_name| because fstab
+  // doesn't list every slot partition (it uses the slotselect option
+  // to mask the suffix).
+  //
+  // We can however assume that there's an entry for the /misc mount
+  // point and use that to get the device file for the misc
+  // partition. This helps us locate the disk that |partition_name|
+  // resides on. From there we'll assume that a by-name scheme is used
+  // so we can just replace the trailing "misc" by the given
+  // |partition_name| and suffix corresponding to |slot|, e.g.
+  //
+  //   /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+  //   /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
+  //
+  // If needed, it's possible to relax the by-name assumption in the
+  // future by trawling /sys/block looking for the appropriate sibling
+  // of misc and then finding an entry in /dev matching the sysfs
+  // entry.
+
+  base::FilePath misc_device;
+  if (!utils::DeviceForMountPoint("/misc", &misc_device))
+    return false;
+
+  if (!utils::IsSymlink(misc_device.value().c_str())) {
+    LOG(ERROR) << "Device file " << misc_device.value() << " for /misc "
+               << "is not a symlink.";
+    return false;
+  }
+
+  const char* suffix = module_->getSuffix(module_, slot);
+  if (suffix == nullptr) {
+    LOG(ERROR) << "boot_control impl returned no suffix for slot "
+               << SlotName(slot);
+    return false;
+  }
+
+  base::FilePath path = misc_device.DirName().Append(partition_name + suffix);
+  if (!base::PathExists(path)) {
+    LOG(ERROR) << "Device file " << path.value() << " does not exist.";
+    return false;
+  }
+
+  *device = path.value();
+  return true;
+}
+
+bool BootControlRecovery::IsSlotBootable(Slot slot) const {
+  int ret = module_->isSlotBootable(module_, slot);
+  if (ret < 0) {
+    LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
+               << " is bootable: " << strerror(-ret);
+    return false;
+  }
+  return ret == 1;
+}
+
+bool BootControlRecovery::MarkSlotUnbootable(Slot slot) {
+  int ret = module_->setSlotAsUnbootable(module_, slot);
+  if (ret < 0) {
+    LOG(ERROR) << "Unable to mark slot " << SlotName(slot)
+               << " as bootable: " << strerror(-ret);
+    return false;
+  }
+  return ret == 0;
+}
+
+bool BootControlRecovery::SetActiveBootSlot(Slot slot) {
+  int ret = module_->setActiveBootSlot(module_, slot);
+  if (ret < 0) {
+    LOG(ERROR) << "Unable to set the active slot to slot " << SlotName(slot)
+               << ": " << strerror(-ret);
+  }
+  return ret == 0;
+}
+
+bool BootControlRecovery::MarkBootSuccessfulAsync(
+    base::Callback<void(bool)> callback) {
+  int ret = module_->markBootSuccessful(module_);
+  if (ret < 0) {
+    LOG(ERROR) << "Unable to mark boot successful: " << strerror(-ret);
+  }
+  return brillo::MessageLoop::current()->PostTask(
+             FROM_HERE, base::Bind(callback, ret == 0)) !=
+         brillo::MessageLoop::kTaskIdNull;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/boot_control_recovery.h b/boot_control_recovery.h
new file mode 100644
index 0000000..3a83caa
--- /dev/null
+++ b/boot_control_recovery.h
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2015 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_BOOT_CONTROL_RECOVERY_H_
+#define UPDATE_ENGINE_BOOT_CONTROL_RECOVERY_H_
+
+#include <string>
+
+#include <hardware/boot_control.h>
+#include <hardware/hardware.h>
+
+#include "update_engine/common/boot_control.h"
+
+namespace chromeos_update_engine {
+
+// The Android recovery implementation of the BootControlInterface. This
+// implementation uses the legacy libhardware's boot_control HAL to access the
+// bootloader by linking against it statically. This should only be used in
+// recovery.
+class BootControlRecovery : public BootControlInterface {
+ public:
+  BootControlRecovery() = default;
+  ~BootControlRecovery() = default;
+
+  // Load boot_control HAL implementation using libhardware and
+  // initializes it. Returns false if an error occurred.
+  bool Init();
+
+  // BootControlInterface overrides.
+  unsigned int GetNumSlots() const override;
+  BootControlInterface::Slot GetCurrentSlot() const override;
+  bool GetPartitionDevice(const std::string& partition_name,
+                          BootControlInterface::Slot slot,
+                          std::string* device) const override;
+  bool IsSlotBootable(BootControlInterface::Slot slot) const override;
+  bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
+  bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
+  bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
+
+ private:
+  // NOTE: There is no way to release/unload HAL implementations so
+  // this is essentially leaked on object destruction.
+  boot_control_module_t* module_;
+
+  DISALLOW_COPY_AND_ASSIGN(BootControlRecovery);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_BOOT_CONTROL_RECOVERY_H_