Refactor bootanimation into a shared lib.
We would like to reuse the animation parts of it in Android things.
This refactors the audio part into the _main and gets callbacks from
the BootAnimation class at interesting times. This will be the same
approach we take to integrate with it.
BUG: 37992717
Test: Built locally and pushed to a bullhead, works with sound.
Change-Id: I5eaca07c25eeb5edeab07d7ae7a29945e0e2cd37
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 0e2c13e..7ab402a 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -1,15 +1,50 @@
+bootanimation_CommonCFlags = -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+bootanimation_CommonCFlags += -Wall -Werror -Wunused -Wunreachable-code
+
+
+# bootanimation executable
+# =========================================================
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
bootanimation_main.cpp \
audioplay.cpp \
+
+LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
+
+LOCAL_SHARED_LIBRARIES := \
+ libOpenSLES \
+ libandroidfw \
+ libbase \
+ libbinder \
+ libbootanimation \
+ libcutils \
+ liblog \
+ libutils \
+
+LOCAL_MODULE:= bootanimation
+
+LOCAL_INIT_RC := bootanim.rc
+
+ifdef TARGET_32_BIT_SURFACEFLINGER
+LOCAL_32_BIT_ONLY := true
+endif
+
+include $(BUILD_EXECUTABLE)
+
+
+# libbootanimation
+# ===========================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbootanimation
+LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
+
+LOCAL_SRC_FILES:= \
BootAnimation.cpp
-LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
LOCAL_C_INCLUDES += \
external/tinyalsa/include \
frameworks/wilhelm/include
@@ -25,16 +60,11 @@
libEGL \
libGLESv1_CM \
libgui \
- libOpenSLES \
libtinyalsa \
libbase
-LOCAL_MODULE:= bootanimation
-
-LOCAL_INIT_RC := bootanim.rc
-
ifdef TARGET_32_BIT_SURFACEFLINGER
LOCAL_32_BIT_ONLY := true
endif
-include $(BUILD_EXECUTABLE)
+include ${BUILD_SHARED_LIBRARY}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 4f772c3..6b2de4b 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -62,7 +62,6 @@
#include <EGL/eglext.h>
#include "BootAnimation.h"
-#include "audioplay.h"
namespace android {
@@ -92,26 +91,18 @@
static const int TEXT_CENTER_VALUE = INT_MAX;
static const int TEXT_MISSING_VALUE = INT_MIN;
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
-static const char PLAY_SOUND_PROP_NAME[] = "persist.sys.bootanim.play_sound";
static const int ANIM_ENTRY_NAME_MAX = 256;
static constexpr size_t TEXT_POS_LEN_MAX = 16;
-static const char BOOT_COMPLETED_PROP_NAME[] = "sys.boot_completed";
-static const char BOOTREASON_PROP_NAME[] = "ro.boot.bootreason";
-// bootreasons list in "system/core/bootstat/bootstat.cpp".
-static const std::vector<std::string> PLAY_SOUND_BOOTREASON_BLACKLIST {
- "kernel_panic",
- "Panic",
- "Watchdog",
-};
// ---------------------------------------------------------------------------
-BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
- mTimeFormat12Hour(false), mTimeCheckThread(NULL) {
+BootAnimation::BootAnimation(InitCallback initCallback,
+ PlayPartCallback partCallback)
+ : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
+ mTimeFormat12Hour(false), mTimeCheckThread(NULL),
+ mInitCallback(initCallback), mPlayPartCallback(partCallback) {
mSession = new SurfaceComposerClient();
- // If the system has already booted, the animation is not being used for a boot.
- mSystemBoot = !android::base::GetBoolProperty(BOOT_COMPLETED_PROP_NAME, false);
std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
if (powerCtl.empty()) {
mShuttingDown = false;
@@ -142,7 +133,6 @@
// might be blocked on a condition variable that will never be updated.
kill( getpid(), SIGKILL );
requestExit();
- audioplay::destroy();
}
status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
@@ -704,7 +694,6 @@
return false;
}
- Animation::Part* partWithAudio = NULL;
ZipEntryRO entry;
char name[ANIM_ENTRY_NAME_MAX];
while ((entry = zip->nextEntry(cookie)) != NULL) {
@@ -739,7 +728,6 @@
// a part may have at most one audio file
part.audioData = (uint8_t *)map->getDataPtr();
part.audioLength = map->getDataLength();
- partWithAudio = ∂
} else if (leaf == "trim.txt") {
part.trimData.setTo((char const*)map->getDataPtr(),
map->getDataLength());
@@ -789,13 +777,8 @@
}
}
- // Create and initialize audioplay if there is a wav file in any of the animations.
- // Do it on a separate thread so we don't hold up the animation intro.
- if (partWithAudio != NULL) {
- ALOGD("found audio.wav, creating playback engine");
- mInitAudioThread = new InitAudioThread(partWithAudio->audioData,
- partWithAudio->audioLength);
- mInitAudioThread->run("BootAnimation::InitAudioThread", PRIORITY_NORMAL);
+ if (mInitCallback != nullptr) {
+ mInitCallback(animation.parts);
}
zip->endIteration(cookie);
@@ -868,11 +851,6 @@
mTimeCheckThread = nullptr;
}
- // We should have joined mInitAudioThread thread in playAnimation
- if (mInitAudioThread != nullptr) {
- mInitAudioThread = nullptr;
- }
-
releaseAnimation(animation);
if (clockFontInitialized) {
@@ -909,14 +887,8 @@
if(exitPending() && !part.playUntilComplete)
break;
- // only play audio file the first time we animate the part
- if (r == 0 && part.audioData && playSoundsAllowed()) {
- ALOGD("playing clip for part%d, size=%d", (int) i, part.audioLength);
- // Block until the audio engine is finished initializing.
- if (mInitAudioThread != nullptr) {
- mInitAudioThread->join();
- }
- audioplay::playClip(part.audioData, part.audioLength);
+ if (mPlayPartCallback != nullptr) {
+ mPlayPartCallback(i, part, r);
}
glClearColor(
@@ -1005,10 +977,6 @@
}
}
- // we've finally played everything we're going to play
- audioplay::setPlaying(false);
- audioplay::destroy();
-
return true;
}
@@ -1054,32 +1022,6 @@
return animation;
}
-bool BootAnimation::playSoundsAllowed() const {
- // Only play sounds for system boots, not runtime restarts.
- if (!mSystemBoot) {
- return false;
- }
- if (mShuttingDown) { // no audio while shutting down
- return false;
- }
- // Read the system property to see if we should play the sound.
- // If it's not present, default to allowed.
- if (!property_get_bool(PLAY_SOUND_PROP_NAME, 1)) {
- return false;
- }
-
- // Don't play sounds if this is a reboot due to an error.
- char bootreason[PROPERTY_VALUE_MAX];
- if (property_get(BOOTREASON_PROP_NAME, bootreason, nullptr) > 0) {
- for (const auto& str : PLAY_SOUND_BOOTREASON_BLACKLIST) {
- if (strcasecmp(str.c_str(), bootreason) == 0) {
- return false;
- }
- }
- }
- return true;
-}
-
bool BootAnimation::updateIsTimeAccurate() {
static constexpr long long MAX_TIME_IN_PAST = 60000LL * 60LL * 24LL * 30LL; // 30 days
static constexpr long long MAX_TIME_IN_FUTURE = 60000LL * 90LL; // 90 minutes
@@ -1211,17 +1153,6 @@
return NO_ERROR;
}
-BootAnimation::InitAudioThread::InitAudioThread(uint8_t* exampleAudioData, int exampleAudioLength)
- : Thread(false),
- mExampleAudioData(exampleAudioData),
- mExampleAudioLength(exampleAudioLength) {}
-
-bool BootAnimation::InitAudioThread::threadLoop() {
- audioplay::create(mExampleAudioData, mExampleAudioLength);
- // Exit immediately
- return false;
-}
-
// ---------------------------------------------------------------------------
}
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 181ef1c..3ebe7d6 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -39,44 +39,6 @@
class BootAnimation : public Thread, public IBinder::DeathRecipient
{
public:
- BootAnimation();
-
- sp<SurfaceComposerClient> session() const;
-
-private:
- virtual bool threadLoop();
- virtual status_t readyToRun();
- virtual void onFirstRef();
- virtual void binderDied(const wp<IBinder>& who);
-
- bool updateIsTimeAccurate();
-
- class TimeCheckThread : public Thread {
- public:
- TimeCheckThread(BootAnimation* bootAnimation);
- virtual ~TimeCheckThread();
- private:
- virtual status_t readyToRun();
- virtual bool threadLoop();
- bool doThreadLoop();
- void addTimeDirWatch();
-
- int mInotifyFd;
- int mSystemWd;
- int mTimeWd;
- BootAnimation* mBootAnimation;
- };
-
- class InitAudioThread : public Thread {
- public:
- InitAudioThread(uint8_t* exampleAudioData, int mExampleAudioLength);
- private:
- virtual bool threadLoop();
-
- uint8_t* mExampleAudioData;
- int mExampleAudioLength;
- };
-
struct Texture {
GLint w;
GLint h;
@@ -131,6 +93,49 @@
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;
+
+ // 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;
+
+ // Callbacks may be null and will be called from this class's internal
+ // thread.
+ BootAnimation(InitCallback initCallback, PlayPartCallback partCallback);
+
+ sp<SurfaceComposerClient> session() const;
+
+private:
+ virtual bool threadLoop();
+ virtual status_t readyToRun();
+ virtual void onFirstRef();
+ virtual void binderDied(const wp<IBinder>& who);
+
+ bool updateIsTimeAccurate();
+
+ class TimeCheckThread : public Thread {
+ public:
+ TimeCheckThread(BootAnimation* bootAnimation);
+ virtual ~TimeCheckThread();
+ private:
+ virtual status_t readyToRun();
+ virtual bool threadLoop();
+ bool doThreadLoop();
+ void addTimeDirWatch();
+
+ int mInotifyFd;
+ int mSystemWd;
+ int mTimeWd;
+ BootAnimation* mBootAnimation;
+ };
+
status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
status_t initTexture(FileMap* map, int* width, int* height);
status_t initFont(Font* font, const char* fallback);
@@ -144,7 +149,6 @@
void releaseAnimation(Animation*) const;
bool parseAnimationDesc(Animation&);
bool preloadZip(Animation &animation);
- bool playSoundsAllowed() const;
void checkExit();
@@ -162,12 +166,12 @@
bool mClockEnabled;
bool mTimeIsAccurate;
bool mTimeFormat12Hour;
- bool mSystemBoot;
bool mShuttingDown;
String8 mZipFileName;
SortedVector<String8> mLoadedFiles;
sp<TimeCheckThread> mTimeCheckThread = nullptr;
- sp<InitAudioThread> mInitAudioThread = nullptr;
+ InitCallback mInitCallback = nullptr;
+ PlayPartCallback mPlayPartCallback = nullptr;
};
// ---------------------------------------------------------------------------
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index 3689d5e..c11d905 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -27,13 +27,77 @@
#include <utils/Log.h>
#include <utils/SystemClock.h>
#include <utils/threads.h>
+#include <android-base/properties.h>
#include "BootAnimation.h"
+#include "audioplay.h"
using namespace android;
// ---------------------------------------------------------------------------
+namespace {
+
+// Create a typedef for readability.
+typedef android::BootAnimation::Animation Animation;
+
+static const char PLAY_SOUND_PROP_NAME[] = "persist.sys.bootanim.play_sound";
+static const char BOOT_COMPLETED_PROP_NAME[] = "sys.boot_completed";
+static const char POWER_CTL_PROP_NAME[] = "sys.powerctl";
+static const char BOOTREASON_PROP_NAME[] = "ro.boot.bootreason";
+static const std::vector<std::string> PLAY_SOUND_BOOTREASON_BLACKLIST {
+ "kernel_panic",
+ "Panic",
+ "Watchdog",
+};
+
+class InitAudioThread : public Thread {
+public:
+ InitAudioThread(uint8_t* exampleAudioData, int exampleAudioLength)
+ : Thread(false),
+ mExampleAudioData(exampleAudioData),
+ mExampleAudioLength(exampleAudioLength) {}
+private:
+ virtual bool threadLoop() {
+ audioplay::create(mExampleAudioData, mExampleAudioLength);
+ // Exit immediately
+ return false;
+ }
+
+ uint8_t* mExampleAudioData;
+ int mExampleAudioLength;
+};
+
+bool playSoundsAllowed() {
+ // Only play sounds for system boots, not runtime restarts.
+ if (android::base::GetBoolProperty(BOOT_COMPLETED_PROP_NAME, false)) {
+ return false;
+ }
+ // no audio while shutting down
+ if (!android::base::GetProperty(POWER_CTL_PROP_NAME, "").empty()) {
+ return false;
+ }
+ // Read the system property to see if we should play the sound.
+ // If it's not present, default to allowed.
+ if (!property_get_bool(PLAY_SOUND_PROP_NAME, 1)) {
+ return false;
+ }
+
+ // Don't play sounds if this is a reboot due to an error.
+ char bootreason[PROPERTY_VALUE_MAX];
+ if (property_get(BOOTREASON_PROP_NAME, bootreason, nullptr) > 0) {
+ for (const auto& str : PLAY_SOUND_BOOTREASON_BLACKLIST) {
+ if (strcasecmp(str.c_str(), bootreason) == 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+
int main()
{
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
@@ -71,10 +135,50 @@
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);
+ }
+ };
+
// create the boot animation object
- sp<BootAnimation> boot = new BootAnimation();
+ sp<BootAnimation> boot = new BootAnimation(initCallback, partCallback);
IPCThreadState::self()->joinThreadPool();
+
+ // we've finally played everything we're going to play
+ audioplay::setPlaying(false);
+ audioplay::destroy();
}
return 0;
}