Merge "Refactor bootanimation into a shared lib."

This commit is contained in:
TreeHugger Robot
2017-05-31 19:15:27 +00:00
committed by Android (Google) Code Review
4 changed files with 199 additions and 130 deletions

View File

@@ -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}

View File

@@ -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 = &part;
} 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;
}
// ---------------------------------------------------------------------------
}

View File

@@ -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;
};
// ---------------------------------------------------------------------------

View File

@@ -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 = &part;
}
}
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;
}