Allow IO During boot process, BootActions.
NOTE: this is only compiled into products with PRODUCT_IOT=true.
Introduce BootActions that a developer can provide to manipulate IO
before the android framework comes up on boot.
We will look for a configuration file at /oem/app/etc/boot_action.conf and
expect it to tell us the name of a shared library. We will then fetch
this library from /oem/app/lib/${arch}/ and load it. We expect it to export
boot_action_init(), boot_action_shutdown(), and optionally
boot_action_start_part(int partNumber, int playNumber).
We will then call boot_action_init() during boot after PeripheralManager
is up and call boot_action_shutdown() when the android framework is up
and we are going to start loading APKs.
We will also call boot_action_start_part(*) when each part of the boot
animation is started, use this if you want to synchronize the boot
action and the boot animation.
Boot actions run in a restricted environment and in general can only
make calls to PeripheralManager.
Bug: 37992717
Test: Pushed to local imx7d to test boot actions, pushed to bullhead test that animation+sound still works.
Change-Id: I9e53a17567f8028ea84486d637e1d231ee1125e1
This commit is contained in:
@@ -8,10 +8,6 @@ bootanimation_CommonCFlags += -Wall -Werror -Wunused -Wunreachable-code
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
bootanimation_main.cpp \
|
||||
audioplay.cpp \
|
||||
|
||||
LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
@@ -24,6 +20,29 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
liblog \
|
||||
libutils \
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
BootAnimationUtil.cpp \
|
||||
|
||||
ifeq ($(PRODUCT_IOT),true)
|
||||
LOCAL_SRC_FILES += \
|
||||
iot/iotbootanimation_main.cpp \
|
||||
iot/BootAction.cpp
|
||||
|
||||
LOCAL_SHARED_LIBRARIES += \
|
||||
libandroidthings \
|
||||
libbase \
|
||||
libbinder
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += cpufeatures
|
||||
|
||||
else
|
||||
|
||||
LOCAL_SRC_FILES += \
|
||||
bootanimation_main.cpp \
|
||||
audioplay.cpp \
|
||||
|
||||
endif # PRODUCT_IOT
|
||||
|
||||
LOCAL_MODULE:= bootanimation
|
||||
|
||||
LOCAL_INIT_RC := bootanim.rc
|
||||
@@ -45,6 +64,8 @@ LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
|
||||
LOCAL_SRC_FILES:= \
|
||||
BootAnimation.cpp
|
||||
|
||||
LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
external/tinyalsa/include \
|
||||
frameworks/wilhelm/include
|
||||
|
||||
@@ -96,11 +96,9 @@ static constexpr size_t TEXT_POS_LEN_MAX = 16;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
BootAnimation::BootAnimation(InitCallback initCallback,
|
||||
PlayPartCallback partCallback)
|
||||
BootAnimation::BootAnimation(sp<Callbacks> callbacks)
|
||||
: Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
|
||||
mTimeFormat12Hour(false), mTimeCheckThread(NULL),
|
||||
mInitCallback(initCallback), mPlayPartCallback(partCallback) {
|
||||
mTimeFormat12Hour(false), mTimeCheckThread(NULL), mCallbacks(callbacks) {
|
||||
mSession = new SurfaceComposerClient();
|
||||
|
||||
std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
|
||||
@@ -357,6 +355,8 @@ bool BootAnimation::android()
|
||||
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
|
||||
initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
|
||||
|
||||
mCallbacks->init({});
|
||||
|
||||
// clear screen
|
||||
glShadeModel(GL_FLAT);
|
||||
glDisable(GL_DITHER);
|
||||
@@ -424,6 +424,7 @@ void BootAnimation::checkExit() {
|
||||
int exitnow = atoi(value);
|
||||
if (exitnow) {
|
||||
requestExit();
|
||||
mCallbacks->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -777,9 +778,7 @@ bool BootAnimation::preloadZip(Animation& animation)
|
||||
}
|
||||
}
|
||||
|
||||
if (mInitCallback != nullptr) {
|
||||
mInitCallback(animation.parts);
|
||||
}
|
||||
mCallbacks->init(animation.parts);
|
||||
|
||||
zip->endIteration(cookie);
|
||||
|
||||
@@ -887,9 +886,7 @@ bool BootAnimation::playAnimation(const Animation& animation)
|
||||
if(exitPending() && !part.playUntilComplete)
|
||||
break;
|
||||
|
||||
if (mPlayPartCallback != nullptr) {
|
||||
mPlayPartCallback(i, part, r);
|
||||
}
|
||||
mCallbacks->playPart(i, part, r);
|
||||
|
||||
glClearColor(
|
||||
part.backgroundColor[0],
|
||||
|
||||
@@ -93,22 +93,27 @@ public:
|
||||
Font clockFont;
|
||||
};
|
||||
|
||||
// Callback will be called during initialization after we have loaded
|
||||
// the animation and be provided with all parts in animation.
|
||||
typedef std::function<void(const Vector<Animation::Part>& parts)> InitCallback;
|
||||
// All callbacks will be called from this class's internal thread.
|
||||
class Callbacks : public RefBase {
|
||||
public:
|
||||
// Will be called during initialization after we have loaded
|
||||
// the animation and be provided with all parts in animation.
|
||||
virtual void init(const Vector<Animation::Part>& /*parts*/) {}
|
||||
|
||||
// Callback will be called while animation is playing before each part is
|
||||
// played. It will be provided with the part and play count for it.
|
||||
// It will be provided with the partNumber for the part about to be played,
|
||||
// as well as a reference to the part itself. It will also be provided with
|
||||
// which play of that part is about to start, some parts are repeated
|
||||
// multiple times.
|
||||
typedef std::function<void(int partNumber, const Animation::Part& part, int playNumber)>
|
||||
PlayPartCallback;
|
||||
// Will be called while animation is playing before each part is
|
||||
// played. It will be provided with the part and play count for it.
|
||||
// It will be provided with the partNumber for the part about to be played,
|
||||
// as well as a reference to the part itself. It will also be provided with
|
||||
// which play of that part is about to start, some parts are repeated
|
||||
// multiple times.
|
||||
virtual void playPart(int /*partNumber*/, const Animation::Part& /*part*/,
|
||||
int /*playNumber*/) {}
|
||||
|
||||
// Callbacks may be null and will be called from this class's internal
|
||||
// thread.
|
||||
BootAnimation(InitCallback initCallback, PlayPartCallback partCallback);
|
||||
// Will be called when animation is done and thread is shutting down.
|
||||
virtual void shutdown() {}
|
||||
};
|
||||
|
||||
BootAnimation(sp<Callbacks> callbacks);
|
||||
|
||||
sp<SurfaceComposerClient> session() const;
|
||||
|
||||
@@ -170,8 +175,7 @@ private:
|
||||
String8 mZipFileName;
|
||||
SortedVector<String8> mLoadedFiles;
|
||||
sp<TimeCheckThread> mTimeCheckThread = nullptr;
|
||||
InitCallback mInitCallback = nullptr;
|
||||
PlayPartCallback mPlayPartCallback = nullptr;
|
||||
sp<Callbacks> mCallbacks;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
61
cmds/bootanimation/BootAnimationUtil.cpp
Normal file
61
cmds/bootanimation/BootAnimationUtil.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 "BootAnimationUtil.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
bool bootAnimationDisabled() {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get("debug.sf.nobootanimation", value, "0");
|
||||
if (atoi(value) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
property_get("ro.boot.quiescent", value, "0");
|
||||
return atoi(value) > 0;
|
||||
}
|
||||
|
||||
void waitForSurfaceFlinger() {
|
||||
// TODO: replace this with better waiting logic in future, b/35253872
|
||||
int64_t waitStartTime = elapsedRealtime();
|
||||
sp<IServiceManager> sm = defaultServiceManager();
|
||||
const String16 name("SurfaceFlinger");
|
||||
const int SERVICE_WAIT_SLEEP_MS = 100;
|
||||
const int LOG_PER_RETRIES = 10;
|
||||
int retry = 0;
|
||||
while (sm->checkService(name) == nullptr) {
|
||||
retry++;
|
||||
if ((retry % LOG_PER_RETRIES) == 0) {
|
||||
ALOGW("Waiting for SurfaceFlinger, waited for %" PRId64 " ms",
|
||||
elapsedRealtime() - waitStartTime);
|
||||
}
|
||||
usleep(SERVICE_WAIT_SLEEP_MS * 1000);
|
||||
};
|
||||
int64_t totalWaited = elapsedRealtime() - waitStartTime;
|
||||
if (totalWaited > SERVICE_WAIT_SLEEP_MS) {
|
||||
ALOGI("Waiting for SurfaceFlinger took %" PRId64 " ms", totalWaited);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
25
cmds/bootanimation/BootAnimationUtil.h
Normal file
25
cmds/bootanimation/BootAnimationUtil.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace android {
|
||||
|
||||
// Returns true if boot animation is disabled.
|
||||
bool bootAnimationDisabled();
|
||||
|
||||
// Waits until the surface flinger is up.
|
||||
void waitForSurfaceFlinger();
|
||||
|
||||
} // namespace android
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <android-base/properties.h>
|
||||
|
||||
#include "BootAnimation.h"
|
||||
#include "BootAnimationUtil.h"
|
||||
#include "audioplay.h"
|
||||
|
||||
using namespace android;
|
||||
@@ -95,6 +96,49 @@ bool playSoundsAllowed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
class AudioAnimationCallbacks : public android::BootAnimation::Callbacks {
|
||||
public:
|
||||
void init(const Vector<Animation::Part>& parts) override {
|
||||
const Animation::Part* partWithAudio = nullptr;
|
||||
for (const Animation::Part& part : parts) {
|
||||
if (part.audioData != nullptr) {
|
||||
partWithAudio = ∂
|
||||
}
|
||||
}
|
||||
|
||||
if (partWithAudio == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ALOGD("found audio.wav, creating playback engine");
|
||||
initAudioThread = new InitAudioThread(partWithAudio->audioData,
|
||||
partWithAudio->audioLength);
|
||||
initAudioThread->run("BootAnimation::InitAudioThread", PRIORITY_NORMAL);
|
||||
};
|
||||
|
||||
void playPart(int partNumber, const Animation::Part& part, int playNumber) override {
|
||||
// only play audio file the first time we animate the part
|
||||
if (playNumber == 0 && part.audioData && playSoundsAllowed()) {
|
||||
ALOGD("playing clip for part%d, size=%d",
|
||||
partNumber, part.audioLength);
|
||||
// Block until the audio engine is finished initializing.
|
||||
if (initAudioThread != nullptr) {
|
||||
initAudioThread->join();
|
||||
}
|
||||
audioplay::playClip(part.audioData, part.audioLength);
|
||||
}
|
||||
};
|
||||
|
||||
void shutdown() override {
|
||||
// we've finally played everything we're going to play
|
||||
audioplay::setPlaying(false);
|
||||
audioplay::destroy();
|
||||
};
|
||||
|
||||
private:
|
||||
sp<InitAudioThread> initAudioThread = nullptr;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -102,83 +146,19 @@ int main()
|
||||
{
|
||||
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
|
||||
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get("debug.sf.nobootanimation", value, "0");
|
||||
int noBootAnimation = atoi(value);
|
||||
if (!noBootAnimation) {
|
||||
property_get("ro.boot.quiescent", value, "0");
|
||||
noBootAnimation = atoi(value);
|
||||
}
|
||||
bool noBootAnimation = bootAnimationDisabled();
|
||||
ALOGI_IF(noBootAnimation, "boot animation disabled");
|
||||
if (!noBootAnimation) {
|
||||
|
||||
sp<ProcessState> proc(ProcessState::self());
|
||||
ProcessState::self()->startThreadPool();
|
||||
|
||||
// TODO: replace this with better waiting logic in future, b/35253872
|
||||
int64_t waitStartTime = elapsedRealtime();
|
||||
sp<IServiceManager> sm = defaultServiceManager();
|
||||
const String16 name("SurfaceFlinger");
|
||||
const int SERVICE_WAIT_SLEEP_MS = 100;
|
||||
const int LOG_PER_RETRIES = 10;
|
||||
int retry = 0;
|
||||
while (sm->checkService(name) == nullptr) {
|
||||
retry++;
|
||||
if ((retry % LOG_PER_RETRIES) == 0) {
|
||||
ALOGW("Waiting for SurfaceFlinger, waited for %" PRId64 " ms",
|
||||
elapsedRealtime() - waitStartTime);
|
||||
}
|
||||
usleep(SERVICE_WAIT_SLEEP_MS * 1000);
|
||||
};
|
||||
int64_t totalWaited = elapsedRealtime() - waitStartTime;
|
||||
if (totalWaited > SERVICE_WAIT_SLEEP_MS) {
|
||||
ALOGI("Waiting for SurfaceFlinger took %" PRId64 " ms", totalWaited);
|
||||
}
|
||||
|
||||
// TODO: Move audio code to a new class that just exports the callbacks.
|
||||
sp<InitAudioThread> initAudioThread = nullptr;
|
||||
|
||||
auto initCallback = [&](const Vector<Animation::Part>& parts) {
|
||||
const Animation::Part* partWithAudio = nullptr;
|
||||
for (const Animation::Part& part : parts) {
|
||||
if (part.audioData != nullptr) {
|
||||
partWithAudio = ∂
|
||||
}
|
||||
}
|
||||
|
||||
if (partWithAudio == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ALOGD("found audio.wav, creating playback engine");
|
||||
initAudioThread = new InitAudioThread(partWithAudio->audioData,
|
||||
partWithAudio->audioLength);
|
||||
initAudioThread->run("BootAnimation::InitAudioThread", PRIORITY_NORMAL);
|
||||
|
||||
};
|
||||
|
||||
auto partCallback = [&](int partNumber, const Animation::Part& part,
|
||||
int playNumber) {
|
||||
// only play audio file the first time we animate the part
|
||||
if (playNumber == 0 && part.audioData && playSoundsAllowed()) {
|
||||
ALOGD("playing clip for part%d, size=%d",
|
||||
partNumber, part.audioLength);
|
||||
// Block until the audio engine is finished initializing.
|
||||
if (initAudioThread != nullptr) {
|
||||
initAudioThread->join();
|
||||
}
|
||||
audioplay::playClip(part.audioData, part.audioLength);
|
||||
}
|
||||
};
|
||||
waitForSurfaceFlinger();
|
||||
|
||||
// create the boot animation object
|
||||
sp<BootAnimation> boot = new BootAnimation(initCallback, partCallback);
|
||||
sp<BootAnimation> boot = new BootAnimation(new AudioAnimationCallbacks());
|
||||
|
||||
IPCThreadState::self()->joinThreadPool();
|
||||
|
||||
// we've finally played everything we're going to play
|
||||
audioplay::setPlaying(false);
|
||||
audioplay::destroy();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
175
cmds/bootanimation/iot/BootAction.cpp
Normal file
175
cmds/bootanimation/iot/BootAction.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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 <android-base/strings.h>
|
||||
#include <cpu-features.h>
|
||||
#include <dlfcn.h>
|
||||
#include <pio/peripheral_manager_client.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
using android::base::Split;
|
||||
using android::base::Join;
|
||||
using android::base::StartsWith;
|
||||
using android::base::EndsWith;
|
||||
|
||||
namespace android {
|
||||
|
||||
BootAction::~BootAction() {
|
||||
if (mLibHandle != nullptr) {
|
||||
dlclose(mLibHandle);
|
||||
}
|
||||
}
|
||||
|
||||
bool BootAction::init(const std::string& libraryPath, const std::string& config) {
|
||||
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);
|
||||
|
||||
std::string path_to_lib = libraryPath;
|
||||
if (!parseConfig(config, &path_to_lib)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ALOGI("Loading boot action %s", path_to_lib.c_str());
|
||||
mLibHandle = dlopen(path_to_lib.c_str(), RTLD_NOW);
|
||||
if (mLibHandle == nullptr) {
|
||||
ALOGE("Unable to load library at %s :: %s",
|
||||
path_to_lib.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();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
bool BootAction::parseConfig(const std::string& config, std::string* path) {
|
||||
auto lines = Split(config, "\n");
|
||||
|
||||
if (lines.size() < 1) {
|
||||
ALOGE("Config format invalid, expected one line, found %d",
|
||||
lines.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t lineNumber = 0;
|
||||
auto& line1 = lines.at(lineNumber);
|
||||
while (StartsWith(line1, "#")) {
|
||||
if (lines.size() < ++lineNumber) {
|
||||
ALOGE("Config file contains no non-comment lines.");
|
||||
return false;
|
||||
}
|
||||
line1 = lines.at(lineNumber);
|
||||
}
|
||||
|
||||
const std::string libraryNameToken("LIBRARY_NAME=");
|
||||
if (!StartsWith(line1, libraryNameToken.c_str())) {
|
||||
ALOGE("Invalid config format, expected second line to start with %s "
|
||||
"Instead found: %s", libraryNameToken.c_str(), line1.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string libraryName = line1.substr(libraryNameToken.length());
|
||||
|
||||
*path += "/";
|
||||
*path += architectureDirectory();
|
||||
*path += "/";
|
||||
*path += libraryName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* BootAction::architectureDirectory() {
|
||||
switch(android_getCpuFamily()) {
|
||||
case ANDROID_CPU_FAMILY_ARM:
|
||||
return "arm";
|
||||
case ANDROID_CPU_FAMILY_X86:
|
||||
return "x86";
|
||||
case ANDROID_CPU_FAMILY_MIPS:
|
||||
return "mips";
|
||||
case ANDROID_CPU_FAMILY_ARM64:
|
||||
return "arm64";
|
||||
case ANDROID_CPU_FAMILY_X86_64:
|
||||
return "x86_64";
|
||||
case ANDROID_CPU_FAMILY_MIPS64:
|
||||
return "mips64";
|
||||
default:
|
||||
ALOGE("Unsupported cpu family: %d", android_getCpuFamily());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
63
cmds/bootanimation/iot/BootAction.h
Normal file
63
cmds/bootanimation/iot/BootAction.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _BOOTANIMATION_BOOTACTION_H
|
||||
#define _BOOTANIMATION_BOOTACTION_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class BootAction : public RefBase {
|
||||
public:
|
||||
~BootAction();
|
||||
// Parse the contents of the config file. We expect one line:
|
||||
// LIBRARY_NAME=
|
||||
//
|
||||
// LIBRARY_NAME is the name of the shared library that contains the boot action.
|
||||
bool init(const std::string& libraryPath, const std::string& config);
|
||||
|
||||
// The animation is going to start playing partNumber for the playCount'th
|
||||
// time, update the action as needed.
|
||||
// This is run in the same thread as the boot animation,
|
||||
// you must not block here.
|
||||
void startPart(int partNumber, int playCount);
|
||||
|
||||
// Shutdown the boot action, this will be called shortly before the
|
||||
// process is shut down to allow time for cleanup.
|
||||
void shutdown();
|
||||
|
||||
private:
|
||||
typedef bool (*libInit)();
|
||||
typedef void (*libStartPart)(int partNumber, int playNumber);
|
||||
typedef void (*libShutdown)();
|
||||
|
||||
bool parseConfig(const std::string& config, std::string* path);
|
||||
bool loadSymbol(const char* symbol, void** loaded);
|
||||
const char* architectureDirectory();
|
||||
|
||||
void* mLibHandle = nullptr;
|
||||
libInit mLibInit = nullptr;
|
||||
libStartPart mLibStartPart = nullptr;
|
||||
libShutdown mLibShutdown = nullptr;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
|
||||
#endif // _BOOTANIMATION_BOOTACTION_H
|
||||
93
cmds/bootanimation/iot/iotbootanimation_main.cpp
Normal file
93
cmds/bootanimation/iot/iotbootanimation_main.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "IotBootAnimation"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <binder/ProcessState.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <sys/resource.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/threads.h>
|
||||
#include <BootAnimation.h>
|
||||
|
||||
#include "BootAction.h"
|
||||
#include "BootAnimationUtil.h"
|
||||
|
||||
using namespace android;
|
||||
using android::base::ReadFileToString;
|
||||
|
||||
// Create a typedef for readability.
|
||||
typedef android::BootAnimation::Animation Animation;
|
||||
|
||||
namespace {
|
||||
|
||||
class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {public:
|
||||
void init(const Vector<Animation::Part>&) override {
|
||||
// Create and initialize BootActions if we have a boot_action.conf.
|
||||
std::string bootActionConf;
|
||||
if (ReadFileToString("/oem/app/etc/boot_action.conf", &bootActionConf)) {
|
||||
mBootAction = new BootAction();
|
||||
if (!mBootAction->init("/oem/app/lib", bootActionConf)) {
|
||||
mBootAction = NULL;
|
||||
}
|
||||
} else {
|
||||
ALOGI("No boot actions specified");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void playPart(int partNumber, const Animation::Part&, int playNumber) override {
|
||||
if (mBootAction != nullptr) {
|
||||
mBootAction->startPart(partNumber, playNumber);
|
||||
}
|
||||
};
|
||||
|
||||
void shutdown() override {
|
||||
if (mBootAction != nullptr) {
|
||||
mBootAction->shutdown();
|
||||
// Give it two seconds to shut down.
|
||||
sleep(2);
|
||||
mBootAction = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
sp<BootAction> mBootAction = nullptr;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
|
||||
|
||||
if (bootAnimationDisabled()) {
|
||||
ALOGI("boot animation disabled");
|
||||
return 0;
|
||||
}
|
||||
|
||||
waitForSurfaceFlinger();
|
||||
|
||||
sp<ProcessState> proc(ProcessState::self());
|
||||
ProcessState::self()->startThreadPool();
|
||||
|
||||
sp<BootAnimation> boot = new BootAnimation(new BootActionAnimationCallbacks());
|
||||
|
||||
IPCThreadState::self()->joinThreadPool();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user