Merge "Refactor bootanimation into a shared lib."
This commit is contained in:
committed by
Android (Google) Code Review
commit
347cf3ea9e
@@ -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 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
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}
|
||||
|
||||
@@ -62,7 +62,6 @@
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include "BootAnimation.h"
|
||||
#include "audioplay.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -92,26 +91,18 @@ static constexpr size_t FONT_NUM_ROWS = FONT_NUM_CHARS / FONT_NUM_COLS;
|
||||
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 @@ void BootAnimation::binderDied(const wp<IBinder>&)
|
||||
// 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 @@ bool BootAnimation::preloadZip(Animation& animation)
|
||||
return false;
|
||||
}
|
||||
|
||||
Animation::Part* partWithAudio = NULL;
|
||||
ZipEntryRO entry;
|
||||
char name[ANIM_ENTRY_NAME_MAX];
|
||||
while ((entry = zip->nextEntry(cookie)) != NULL) {
|
||||
@@ -739,7 +728,6 @@ bool BootAnimation::preloadZip(Animation& animation)
|
||||
// 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 @@ bool BootAnimation::preloadZip(Animation& animation)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 @@ bool BootAnimation::movie()
|
||||
mTimeCheckThread = nullptr;
|
||||
}
|
||||
|
||||
// We should have joined mInitAudioThread thread in playAnimation
|
||||
if (mInitAudioThread != nullptr) {
|
||||
mInitAudioThread = nullptr;
|
||||
}
|
||||
|
||||
releaseAnimation(animation);
|
||||
|
||||
if (clockFontInitialized) {
|
||||
@@ -909,14 +887,8 @@ bool BootAnimation::playAnimation(const Animation& animation)
|
||||
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 @@ bool BootAnimation::playAnimation(const Animation& animation)
|
||||
}
|
||||
}
|
||||
|
||||
// we've finally played everything we're going to play
|
||||
audioplay::setPlaying(false);
|
||||
audioplay::destroy();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1054,32 +1022,6 @@ BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
|
||||
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 @@ status_t BootAnimation::TimeCheckThread::readyToRun() {
|
||||
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;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
}
|
||||
|
||||
@@ -39,44 +39,6 @@ class SurfaceControl;
|
||||
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 @@ private:
|
||||
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 @@ private:
|
||||
void releaseAnimation(Animation*) const;
|
||||
bool parseAnimationDesc(Animation&);
|
||||
bool preloadZip(Animation &animation);
|
||||
bool playSoundsAllowed() const;
|
||||
|
||||
void checkExit();
|
||||
|
||||
@@ -162,12 +166,12 @@ private:
|
||||
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;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -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 @@ int main()
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user