BootControlAndroid: Implement using libhardware.

This simply calls into the a boot_control HAL implementation loaded with
libhardware. I've tested it with the implementation located in
system/extras/boot_control_copy.

The only non-trivial routine here is GetPartitionDevice() which I've
hand-tested on a device using the following snippet of code:

  string names[] = {"boot", "system", "nope_enoent"};
  for (string name : names) {
    for (int slot = 0; slot <= 2; slot++) {
      string device;
      if (!GetPartitionDevice(name, slot, &device))
        device = "NOTHERE";
      LOG(INFO) << "slot:" << slot << " part:" << name << " -> " << device;
    }
  }

Change-Id: I1280325bd7cd4cf1cc9a33ef13c2f46f215da6e6
diff --git a/boot_control_android.cc b/boot_control_android.cc
index fa9defd..4bb3b92 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -16,45 +16,162 @@
 
 #include "update_engine/boot_control_android.h"
 
+#include <base/logging.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
 #include <chromeos/make_unique_ptr.h>
+#include <cutils/properties.h>
+#include <fs_mgr.h>
 
-#include "update_engine/boot_control.h"
+#include "update_engine/utils.h"
 
 using std::string;
 
+namespace {
+
+// Open the appropriate fstab file and fallback to /fstab.device if
+// that's what's being used.
+static struct fstab* OpenFSTab() {
+  char propbuf[PROPERTY_VALUE_MAX];
+  struct fstab* fstab;
+
+  property_get("ro.hardware", propbuf, "");
+  string fstab_name = string("/fstab.") + propbuf;
+  fstab = fs_mgr_read_fstab(fstab_name.c_str());
+  if (fstab != nullptr)
+    return fstab;
+
+  fstab = fs_mgr_read_fstab("/fstab.device");
+  return fstab;
+}
+
+}  // namespace
+
+
 namespace chromeos_update_engine {
 
 namespace boot_control {
 
 // Factory defined in boot_control.h.
 std::unique_ptr<BootControlInterface> CreateBootControl() {
-  return chromeos::make_unique_ptr(new BootControlAndroid());
+  std::unique_ptr<BootControlAndroid> boot_control(new BootControlAndroid());
+  if (!boot_control->Init()) {
+    return nullptr;
+  }
+  return chromeos::make_unique_ptr(boot_control.release());
 }
 
 }  // namespace boot_control
 
-// TODO(deymo): Read the values from the libhardware HAL.
+bool BootControlAndroid::Init() {
+  const hw_module_t* hw_module;
+  int ret;
+
+  ret = hw_get_module(BOOT_CONTROL_HARDWARE_MODULE_ID, &hw_module);
+  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 BootControlAndroid::GetNumSlots() const {
-  return 2;
+  return module_->getNumberSlots(module_);
 }
 
 BootControlInterface::Slot BootControlAndroid::GetCurrentSlot() const {
-  return 0;
+  return module_->getCurrentSlot(module_);
 }
 
 bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
-                                            unsigned int slot,
+                                            Slot slot,
                                             string* device) const {
-  return false;
+  struct fstab* fstab;
+  struct fstab_rec* record;
+
+  // 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.
+
+  fstab = OpenFSTab();
+  if (fstab == nullptr) {
+    LOG(ERROR) << "Error opening fstab file.";
+    return false;
+  }
+  record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+  if (record == nullptr) {
+    LOG(ERROR) << "Error finding /misc entry in fstab file.";
+    fs_mgr_free_fstab(fstab);
+    return false;
+  }
+
+  base::FilePath misc_device = base::FilePath(record->blk_device);
+  fs_mgr_free_fstab(fstab);
+
+  if (misc_device.BaseName() != base::FilePath("misc")) {
+    LOG(ERROR) << "Device file " << misc_device.value() << " for /misc "
+               << "is not in the expected format.";
+    return false;
+  }
+
+  const char* suffix = module_->getSuffix(module_, slot);
+  if (suffix == nullptr) {
+    LOG(ERROR) << "boot_control impl returned no suffix for slot " << 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 BootControlAndroid::IsSlotBootable(Slot slot) const {
-  return false;
+  int ret = module_->isSlotBootable(module_, slot);
+  if (ret < 0) {
+    LOG(ERROR) << "Unable to determine if slot " << slot
+               << " is bootable: " << strerror(-ret);
+    return false;
+  }
+  return ret == 1;
 }
 
 bool BootControlAndroid::MarkSlotUnbootable(Slot slot) {
-  return true;
+  int ret = module_->setSlotAsUnbootable(module_, slot);
+  if (ret < 0) {
+    LOG(ERROR) << "Unable to mark slot " << slot
+               << " as bootable: " << strerror(-ret);
+    return false;
+  }
+  return ret == 0;
 }
 
 }  // namespace chromeos_update_engine