Pass saved parameters to boot action

Bug: http://b/65462981
Test: Parameters in next_boot.json are passed to
      boot action; next_boot.json is moved to
      last_boot.json to allow reading by
      DeviceManagementService.

Change-Id: Ie290711ea48a3a221cfad2e9266215b76631ecbd
diff --git a/cmds/bootanimation/iot/BootAction.cpp b/cmds/bootanimation/iot/BootAction.cpp
index 889eb2f..2bc7343 100644
--- a/cmds/bootanimation/iot/BootAction.cpp
+++ b/cmds/bootanimation/iot/BootAction.cpp
@@ -18,25 +18,96 @@
 
 #define LOG_TAG "BootAction"
 
-#include <android-base/strings.h>
-#include <cpu-features.h>
 #include <dlfcn.h>
+#include <fcntl.h>
+
+#include <map>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <base/json/json_parser.h>
+#include <base/json/json_value_converter.h>
+#include <cpu-features.h>
 #include <pio/peripheral_manager_client.h>
 #include <utils/Log.h>
 
+using android::base::ReadFileToString;
+using android::base::RemoveFileIfExists;
 using android::base::Split;
 using android::base::Join;
 using android::base::StartsWith;
 using android::base::EndsWith;
+using base::JSONReader;
+using base::Value;
 
 namespace android {
 
+// Brightness and volume are stored as integer strings in next_boot.json.
+// They are divided by this constant to produce the actual float values in
+// range [0.0, 1.0]. This constant must match its counterpart in
+// DeviceManager.
+constexpr const float kFloatScaleFactor = 1000.0f;
+
+constexpr const char* kNextBootFile = "/data/misc/bootanimation/next_boot.json";
+constexpr const char* kLastBootFile = "/data/misc/bootanimation/last_boot.json";
+
+bool loadParameters(BootAction::SavedBootParameters* parameters)
+{
+    std::string contents;
+    if (!ReadFileToString(kLastBootFile, &contents)) {
+        if (errno != ENOENT)
+            ALOGE("Unable to read from %s: %s", kLastBootFile, strerror(errno));
+
+        return false;
+    }
+
+    std::unique_ptr<Value> json = JSONReader::Read(contents);
+    if (json.get() == nullptr) return false;
+
+    JSONValueConverter<BootAction::SavedBootParameters> converter;
+    if (!converter.Convert(*(json.get()), parameters)) return false;
+
+    return true;
+}
+
+void BootAction::SavedBootParameters::RegisterJSONConverter(
+        JSONValueConverter<SavedBootParameters> *converter) {
+    converter->RegisterIntField("brightness", &SavedBootParameters::brightness);
+    converter->RegisterIntField("volume", &SavedBootParameters::volume);
+    converter->RegisterRepeatedString("param_names",
+                                      &SavedBootParameters::param_names);
+    converter->RegisterRepeatedString("param_values",
+                                      &SavedBootParameters::param_values);
+}
+
 BootAction::~BootAction() {
     if (mLibHandle != nullptr) {
         dlclose(mLibHandle);
     }
 }
 
+void BootAction::swapBootConfigs() {
+    // rename() will fail if next_boot.json doesn't exist, so delete
+    // last_boot.json manually first.
+    std::string err;
+    if (!RemoveFileIfExists(kLastBootFile, &err))
+        ALOGE("Unable to delete last boot file: %s", err.c_str());
+
+    if (rename(kNextBootFile, kLastBootFile) && errno != ENOENT)
+        ALOGE("Unable to swap boot files: %s", strerror(errno));
+
+    int fd = open(kNextBootFile, O_CREAT, DEFFILEMODE);
+    if (fd == -1) {
+        ALOGE("Unable to create next boot file: %s", strerror(errno));
+    } else {
+        // Make next_boot.json writible to everyone so DeviceManagementService
+        // can save parameters there.
+        if (fchmod(fd, DEFFILEMODE))
+            ALOGE("Unable to set next boot file permissions: %s", strerror(errno));
+        close(fd);
+    }
+}
+
 bool BootAction::init(const std::string& libraryPath) {
     APeripheralManagerClient* client = nullptr;
     ALOGD("Connecting to peripheralmanager");
@@ -51,6 +122,28 @@
     ALOGD("Peripheralmanager is up.");
     APeripheralManagerClient_delete(client);
 
+    float brightness = -1.0f;
+    float volume = -1.0f;
+    std::vector<BootParameter> parameters;
+    SavedBootParameters saved_parameters;
+
+    if (loadParameters(&saved_parameters)) {
+        // TODO(b/65462981): Do something with brightness and volume?
+        brightness = saved_parameters.brightness / kFloatScaleFactor;
+        volume = saved_parameters.volume / kFloatScaleFactor;
+
+        if (saved_parameters.param_names.size() == saved_parameters.param_values.size()) {
+            for (size_t i = 0; i < saved_parameters.param_names.size(); i++) {
+                parameters.push_back({
+                        .key = saved_parameters.param_names[i]->c_str(),
+                        .value = saved_parameters.param_values[i]->c_str()
+                });
+            }
+        } else {
+            ALOGW("Parameter names and values size mismatch");
+        }
+    }
+
     ALOGI("Loading boot action %s", libraryPath.c_str());
     mLibHandle = dlopen(libraryPath.c_str(), RTLD_NOW);
     if (mLibHandle == nullptr) {
@@ -82,7 +175,7 @@
     }
 
     ALOGD("Entering boot_action_init");
-    bool result = mLibInit();
+    bool result = mLibInit(parameters.data(), parameters.size());
     ALOGD("Returned from boot_action_init");
     return result;
 }