blob: 665b4d9183099234499e9ca18424166692129173 [file] [log] [blame]
/*
* Copyright (C) 2017 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 "BootAction.h"
#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 <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");
// Wait for peripheral manager to come up.
while (client == nullptr) {
client = APeripheralManagerClient_new();
if (client == nullptr) {
ALOGV("peripheralmanager is not up, sleeping before we check again.");
usleep(250000);
}
}
ALOGD("Peripheralmanager is up.");
APeripheralManagerClient_delete(client);
float brightness = -1.0f;
float volume = -1.0f;
std::vector<ABootActionParameter> 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) {
ALOGE("Unable to load library at %s :: %s",
libraryPath.c_str(), dlerror());
return false;
}
void* loaded = nullptr;
if (!loadSymbol("boot_action_init", &loaded) || loaded == nullptr) {
return false;
}
mLibInit = reinterpret_cast<libInit>(loaded);
loaded = nullptr;
if (!loadSymbol("boot_action_shutdown", &loaded) || loaded == nullptr) {
return false;
}
mLibShutdown = reinterpret_cast<libShutdown>(loaded);
// StartPart is considered optional, if it isn't exported by the library
// we will still call init and shutdown.
loaded = nullptr;
if (!loadSymbol("boot_action_start_part", &loaded) || loaded == nullptr) {
ALOGI("No boot_action_start_part found, action will not be told when "
"Animation parts change.");
} else {
mLibStartPart = reinterpret_cast<libStartPart>(loaded);
}
ALOGD("Entering boot_action_init");
bool result = mLibInit(parameters.data(), parameters.size());
ALOGD("Returned from boot_action_init");
return result;
}
void BootAction::startPart(int partNumber, int playNumber) {
if (mLibStartPart == nullptr) return;
ALOGD("Entering boot_action_start_part");
mLibStartPart(partNumber, playNumber);
ALOGD("Returned from boot_action_start_part");
}
void BootAction::shutdown() {
ALOGD("Entering boot_action_shutdown");
mLibShutdown();
ALOGD("Returned from boot_action_shutdown");
}
bool BootAction::loadSymbol(const char* symbol, void** loaded) {
*loaded = dlsym(mLibHandle, symbol);
if (loaded == nullptr) {
ALOGE("Unable to load symbol : %s :: %s", symbol, dlerror());
return false;
}
return true;
}
} // namespace android