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

Commit 9070c055 authored by Marin Shalamanov's avatar Marin Shalamanov
Browse files

Resize boot animation on display changes

Use DisplayEventReceiver to listen for display hotplug
events and resize the boot animation is display size
changes.

Bug: 156448328
Test: manually make sure the boot animation has correct
dimensions in the following cases
     I. With boot animation file
        1. scrcpy
        2. start boot animation with
              adb shell /system/bin/bootanimation
	   when no physical display is connected (= 480p
           fake HWC display). Use the virtual display from
           scrcpy to check the boot animation size.
        3. connect a 1080p display
           (HWC sends hotplug connect to change the display)
        4. disconnect the real display
     II. Same steps without boot animation file
         (android logo should be displayed)

Change-Id: Ib3f0383686399669e8de10456092baaec607fa01
parent 9d0e5417
Loading
Loading
Loading
Loading
+149 −55
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@
#include <ui/Region.h>

#include <gui/ISurfaceComposer.h>
#include <gui/DisplayEventReceiver.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>

@@ -106,7 +107,7 @@ static constexpr size_t TEXT_POS_LEN_MAX = 16;
// ---------------------------------------------------------------------------

BootAnimation::BootAnimation(sp<Callbacks> callbacks)
        : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
        : Thread(false), mLooper(new Looper(false)), mClockEnabled(true), mTimeIsAccurate(false),
        mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
    mSession = new SurfaceComposerClient();

@@ -147,8 +148,7 @@ sp<SurfaceComposerClient> BootAnimation::session() const {
    return mSession;
}

void BootAnimation::binderDied(const wp<IBinder>&)
{
void BootAnimation::binderDied(const wp<IBinder>&) {
    // woah, surfaceflinger died!
    SLOGD("SurfaceFlinger died, exiting...");

@@ -241,8 +241,7 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
    return NO_ERROR;
}

status_t BootAnimation::initTexture(FileMap* map, int* width, int* height)
{
status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) {
    AndroidBitmapInfo bitmapInfo;
    void* pixels = decodeImage(map->getDataPtr(), map->getDataLength(), &bitmapInfo);
    auto pixelDeleter = std::unique_ptr<void, decltype(free)*>{ pixels, free };
@@ -301,6 +300,78 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height)
    return NO_ERROR;
}

class BootAnimation::DisplayEventCallback : public LooperCallback {
    BootAnimation* mBootAnimation;

public:
    DisplayEventCallback(BootAnimation* bootAnimation) {
        mBootAnimation = bootAnimation;
    }

    int handleEvent(int /* fd */, int events, void* /* data */) {
        if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
            ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x",
                    events);
            return 0; // remove the callback
        }

        if (!(events & Looper::EVENT_INPUT)) {
            ALOGW("Received spurious callback for unhandled poll event.  events=0x%x", events);
            return 1; // keep the callback
        }

        constexpr int kBufferSize = 100;
        DisplayEventReceiver::Event buffer[kBufferSize];
        ssize_t numEvents;
        do {
            numEvents = mBootAnimation->mDisplayEventReceiver->getEvents(buffer, kBufferSize);
            for (size_t i = 0; i < static_cast<size_t>(numEvents); i++) {
                const auto& event = buffer[i];
                if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) {
                    SLOGV("Hotplug received");

                    if (!event.hotplug.connected) {
                        // ignore hotplug disconnect
                        continue;
                    }
                    auto token = SurfaceComposerClient::getPhysicalDisplayToken(
                        event.header.displayId);

                    if (token != mBootAnimation->mDisplayToken) {
                        // ignore hotplug of a secondary display
                        continue;
                    }

                    DisplayConfig displayConfig;
                    const status_t error = SurfaceComposerClient::getActiveDisplayConfig(
                        mBootAnimation->mDisplayToken, &displayConfig);
                    if (error != NO_ERROR) {
                        SLOGE("Can't get active display configuration.");
                    }
                    mBootAnimation->resizeSurface(displayConfig.resolution.getWidth(),
                        displayConfig.resolution.getHeight());
                }
            }
        } while (numEvents > 0);

        return 1;  // keep the callback
    }
};

EGLConfig BootAnimation::getEglConfig(const EGLDisplay& display) {
    const EGLint attribs[] = {
        EGL_RED_SIZE,   8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE,  8,
        EGL_DEPTH_SIZE, 0,
        EGL_NONE
    };
    EGLint numConfigs;
    EGLConfig config;
    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
    return config;
}

status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();

@@ -369,25 +440,12 @@ status_t BootAnimation::readyToRun() {
    sp<Surface> s = control->getSurface();

    // initialize opengl and egl
    const EGLint attribs[] = {
            EGL_RED_SIZE,   8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE,  8,
            EGL_DEPTH_SIZE, 0,
            EGL_NONE
    };
    EGLint w, h;
    EGLint numConfigs;
    EGLConfig config;
    EGLSurface surface;
    EGLContext context;

    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

    eglInitialize(display, nullptr, nullptr);
    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
    surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
    context = eglCreateContext(display, config, nullptr, nullptr);
    EGLConfig config = getEglConfig(display);
    EGLSurface surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
    EGLContext context = eglCreateContext(display, config, nullptr, nullptr);
    EGLint w, h;
    eglQuerySurface(display, surface, EGL_WIDTH, &w);
    eglQuerySurface(display, surface, EGL_HEIGHT, &h);

@@ -403,9 +461,46 @@ status_t BootAnimation::readyToRun() {
    mFlingerSurface = s;
    mTargetInset = -1;

    // Register a display event receiver
    mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>();
    status_t status = mDisplayEventReceiver->initCheck();
    SLOGE_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver failed with status: %d",
            status);
    mLooper->addFd(mDisplayEventReceiver->getFd(), 0, Looper::EVENT_INPUT,
            new DisplayEventCallback(this), nullptr);

    return NO_ERROR;
}

void BootAnimation::resizeSurface(int newWidth, int newHeight) {
    // We assume this function is called on the animation thread.
    if (newWidth == mWidth && newHeight == mHeight) {
        return;
    }
    SLOGV("Resizing the boot animation surface to %d %d", newWidth, newHeight);

    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroySurface(mDisplay, mSurface);

    mWidth = newWidth;
    mHeight = newHeight;

    SurfaceComposerClient::Transaction t;
    t.setSize(mFlingerSurfaceControl, mWidth, mHeight);
    t.apply();

    EGLConfig config = getEglConfig(mDisplay);
    EGLSurface surface = eglCreateWindowSurface(mDisplay, config, mFlingerSurface.get(), nullptr);
    if (eglMakeCurrent(mDisplay, surface, surface, mContext) == EGL_FALSE) {
        SLOGE("Can't make the new surface current. Error %d", eglGetError());
        return;
    }
    glViewport(0, 0, mWidth, mHeight);
    glScissor(0, 0, mWidth, mHeight);

    mSurface = surface;
}

bool BootAnimation::preloadAnimation() {
    findBootAnimationFile();
    if (!mZipFileName.isEmpty()) {
@@ -466,15 +561,14 @@ void BootAnimation::findBootAnimationFile() {
    }
}

bool BootAnimation::threadLoop()
{
    bool r;
bool BootAnimation::threadLoop() {
    bool result;
    // We have no bootanimation file, so we use the stock android logo
    // animation.
    if (mZipFileName.isEmpty()) {
        r = android();
        result = android();
    } else {
        r = movie();
        result = movie();
    }

    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
@@ -485,11 +579,10 @@ bool BootAnimation::threadLoop()
    eglTerminate(mDisplay);
    eglReleaseThread();
    IPCThreadState::self()->stopProcess();
    return r;
    return result;
}

bool BootAnimation::android()
{
bool BootAnimation::android() {
    SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
            elapsedRealtime());
    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
@@ -508,19 +601,19 @@ bool BootAnimation::android()
    glEnable(GL_TEXTURE_2D);
    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    const GLint xc = (mWidth  - mAndroid[0].w) / 2;
    const GLint yc = (mHeight - mAndroid[0].h) / 2;
    const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);

    glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
            updateRect.height());

    // Blend state
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    const nsecs_t startTime = systemTime();
    do {
        processDisplayEvents();
        const GLint xc = (mWidth  - mAndroid[0].w) / 2;
        const GLint yc = (mHeight - mAndroid[0].h) / 2;
        const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);
        glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
                updateRect.height());

        nsecs_t now = systemTime();
        double time = now - startTime;
        float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
@@ -635,8 +728,7 @@ static bool parseColor(const char str[7], float color[3]) {
}


static bool readFile(ZipFileRO* zip, const char* name, String8& outString)
{
static bool readFile(ZipFileRO* zip, const char* name, String8& outString) {
    ZipEntryRO entry = zip->findEntryByName(name);
    SLOGE_IF(!entry, "couldn't find %s", name);
    if (!entry) {
@@ -757,8 +849,7 @@ void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos)
    drawText(out, font, false, &x, &y);
}

bool BootAnimation::parseAnimationDesc(Animation& animation)
{
bool BootAnimation::parseAnimationDesc(Animation& animation)  {
    String8 desString;

    if (!readFile(animation.zip, "desc.txt", desString)) {
@@ -825,8 +916,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation)
    return true;
}

bool BootAnimation::preloadZip(Animation& animation)
{
bool BootAnimation::preloadZip(Animation& animation) {
    // read all the data structures
    const size_t pcount = animation.parts.size();
    void *cookie = nullptr;
@@ -923,8 +1013,7 @@ bool BootAnimation::preloadZip(Animation& animation)
    return true;
}

bool BootAnimation::movie()
{
bool BootAnimation::movie() {
    if (mAnimation == nullptr) {
        mAnimation = loadAnimation(mZipFileName);
    }
@@ -1010,12 +1099,9 @@ bool BootAnimation::movie()
    return false;
}

bool BootAnimation::playAnimation(const Animation& animation)
{
bool BootAnimation::playAnimation(const Animation& animation) {
    const size_t pcount = animation.parts.size();
    nsecs_t frameDuration = s2ns(1) / animation.fps;
    const int animationX = (mWidth - animation.width) / 2;
    const int animationY = (mHeight - animation.height) / 2;

    SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
            elapsedRealtime());
@@ -1046,6 +1132,11 @@ bool BootAnimation::playAnimation(const Animation& animation)
                    1.0f);

            for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
                processDisplayEvents();

                const int animationX = (mWidth - animation.width) / 2;
                const int animationY = (mHeight - animation.height) / 2;

                const Animation::Frame& frame(part.frames[j]);
                nsecs_t lastFrame = systemTime();

@@ -1129,6 +1220,12 @@ bool BootAnimation::playAnimation(const Animation& animation)
    return true;
}

void BootAnimation::processDisplayEvents() {
    // This will poll mDisplayEventReceiver and if there are new events it'll call
    // displayEventCallback synchronously.
    mLooper->pollOnce(0);
}

void BootAnimation::handleViewport(nsecs_t timestep) {
    if (mShuttingDown || !mFlingerSurfaceControl || mTargetInset == 0) {
        return;
@@ -1171,8 +1268,7 @@ void BootAnimation::handleViewport(nsecs_t timestep) {
    mCurrentInset += delta;
}

void BootAnimation::releaseAnimation(Animation* animation) const
{
void BootAnimation::releaseAnimation(Animation* animation) const {
    for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
         e = animation->parts.end(); it != e; ++it) {
        if (it->animation)
@@ -1183,8 +1279,7 @@ void BootAnimation::releaseAnimation(Animation* animation) const
    delete animation;
}

BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
{
BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) {
    if (mLoadedFiles.indexOf(fn) >= 0) {
        SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
            fn.string());
@@ -1346,5 +1441,4 @@ status_t BootAnimation::TimeCheckThread::readyToRun() {

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

}
; // namespace android
} // namespace android
+12 −0
Original line number Diff line number Diff line
@@ -18,11 +18,14 @@
#define ANDROID_BOOTANIMATION_H

#include <vector>
#include <queue>

#include <stdint.h>
#include <sys/types.h>

#include <androidfw/AssetManager.h>
#include <gui/DisplayEventReceiver.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
#include <binder/IBinder.h>

@@ -143,6 +146,13 @@ private:
        BootAnimation* mBootAnimation;
    };

    // Display event handling
    class DisplayEventCallback;
    std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver;
    sp<Looper> mLooper;
    int displayEventCallback(int fd, int events, void* data);
    void processDisplayEvents();

    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);
@@ -159,6 +169,8 @@ private:
    void findBootAnimationFile();
    bool findBootAnimationFileInternal(const std::vector<std::string>& files);
    bool preloadAnimation();
    EGLConfig getEglConfig(const EGLDisplay&);
    void resizeSurface(int newWidth, int newHeight);

    void checkExit();