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
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 7ab402a..73ec63f 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -8,10 +8,6 @@
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 @@
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_SRC_FILES:= \
BootAnimation.cpp
+LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
+
LOCAL_C_INCLUDES += \
external/tinyalsa/include \
frameworks/wilhelm/include
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 6b2de4b..6526123 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -96,11 +96,9 @@
// ---------------------------------------------------------------------------
-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 @@
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 @@
int exitnow = atoi(value);
if (exitnow) {
requestExit();
+ mCallbacks->shutdown();
}
}
@@ -777,9 +778,7 @@
}
}
- if (mInitCallback != nullptr) {
- mInitCallback(animation.parts);
- }
+ mCallbacks->init(animation.parts);
zip->endIteration(cookie);
@@ -887,9 +886,7 @@
if(exitPending() && !part.playUntilComplete)
break;
- if (mPlayPartCallback != nullptr) {
- mPlayPartCallback(i, part, r);
- }
+ mCallbacks->playPart(i, part, r);
glClearColor(
part.backgroundColor[0],
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 3ebe7d6..56e131523 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -93,22 +93,27 @@
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 @@
String8 mZipFileName;
SortedVector<String8> mLoadedFiles;
sp<TimeCheckThread> mTimeCheckThread = nullptr;
- InitCallback mInitCallback = nullptr;
- PlayPartCallback mPlayPartCallback = nullptr;
+ sp<Callbacks> mCallbacks;
};
// ---------------------------------------------------------------------------
diff --git a/cmds/bootanimation/BootAnimationUtil.cpp b/cmds/bootanimation/BootAnimationUtil.cpp
new file mode 100644
index 0000000..377d6ce
--- /dev/null
+++ b/cmds/bootanimation/BootAnimationUtil.cpp
@@ -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
diff --git a/cmds/bootanimation/BootAnimationUtil.h b/cmds/bootanimation/BootAnimationUtil.h
new file mode 100644
index 0000000..60987cd1
--- /dev/null
+++ b/cmds/bootanimation/BootAnimationUtil.h
@@ -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
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index c11d905..daac588 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -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 @@
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 @@
{
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;
}
diff --git a/cmds/bootanimation/iot/BootAction.cpp b/cmds/bootanimation/iot/BootAction.cpp
new file mode 100644
index 0000000..8fda87e
--- /dev/null
+++ b/cmds/bootanimation/iot/BootAction.cpp
@@ -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
diff --git a/cmds/bootanimation/iot/BootAction.h b/cmds/bootanimation/iot/BootAction.h
new file mode 100644
index 0000000..31d0d1f
--- /dev/null
+++ b/cmds/bootanimation/iot/BootAction.h
@@ -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
diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp
new file mode 100644
index 0000000..d1ae786
--- /dev/null
+++ b/cmds/bootanimation/iot/iotbootanimation_main.cpp
@@ -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;
+}