Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 347cf3ea authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Refactor bootanimation into a shared lib."

parents 47f4948d 2c9e94aa
Loading
Loading
Loading
Loading
+39 −9
Original line number Diff line number Diff line
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 \
    BootAnimation.cpp

LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
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)

LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code

# libbootanimation
# ===========================================================

include $(CLEAR_VARS)
LOCAL_MODULE := libbootanimation
LOCAL_CFLAGS += ${bootanimation_CommonCFlags}

LOCAL_SRC_FILES:= \
    BootAnimation.cpp

LOCAL_C_INCLUDES += \
    external/tinyalsa/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}
+9 −78
Original line number Diff line number Diff line
@@ -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;
}

// ---------------------------------------------------------------------------

}
+45 −41
Original line number Diff line number Diff line
@@ -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;
};

// ---------------------------------------------------------------------------
+105 −1
Original line number Diff line number Diff line
@@ -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;
}