#define LOG_TAG "BootAction"
+#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 <dlfcn.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");
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) {
}
ALOGD("Entering boot_action_init");
- bool result = mLibInit();
+ bool result = mLibInit(parameters.data(), parameters.size());
ALOGD("Returned from boot_action_init");
return result;
}
#ifndef _BOOTANIMATION_BOOTACTION_H
#define _BOOTANIMATION_BOOTACTION_H
+#include <map>
#include <string>
+#include <base/json/json_value_converter.h>
#include <utils/RefBase.h>
+using base::JSONValueConverter;
+
namespace android {
class BootAction : public RefBase {
public:
+ struct BootParameter {
+ const char* key;
+ const char* value;
+ };
+
+ struct SavedBootParameters {
+ int brightness;
+ int volume;
+ ScopedVector<std::string> param_names;
+ ScopedVector<std::string> param_values;
+ static void RegisterJSONConverter(
+ JSONValueConverter<SavedBootParameters>* converter);
+ };
+
~BootAction();
+ // Rename next_boot.json to last_boot.json so that we don't repeat
+ // parameters if there is a crash before the framework comes up.
+ // TODO(b/65462981): Is this what we want to do? Should we swap in the
+ // framework instead?
+ static void swapBootConfigs();
+
// libraryPath is a fully qualified path to the target .so library.
bool init(const std::string& libraryPath);
void shutdown();
private:
- typedef bool (*libInit)();
+ typedef bool (*libInit)(const BootParameter* parameters, size_t num_parameters);
typedef void (*libStartPart)(int partNumber, int playNumber);
typedef void (*libShutdown)();