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)
Merged-In: Ib3f0383686399669e8de10456092baaec607fa01
Change-Id: Ib3f0383686399669e8de10456092baaec607fa01
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index a1278f3..8281f4e 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -48,6 +48,7 @@
#include <ui/Region.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/DisplayEventReceiver.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -113,8 +114,8 @@
// ---------------------------------------------------------------------------
BootAnimation::BootAnimation(sp<Callbacks> callbacks)
- : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
- mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
+ : Thread(false), mClockEnabled(true), mTimeIsAccurate(false), mTimeFormat12Hour(false),
+ mTimeCheckThread(nullptr), mCallbacks(callbacks), mLooper(new Looper(false)) {
mSession = new SurfaceComposerClient();
std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
@@ -154,8 +155,7 @@
return mSession;
}
-void BootAnimation::binderDied(const wp<IBinder>&)
-{
+void BootAnimation::binderDied(const wp<IBinder>&) {
// woah, surfaceflinger died!
SLOGD("SurfaceFlinger died, exiting...");
@@ -219,8 +219,7 @@
return NO_ERROR;
}
-status_t BootAnimation::initTexture(FileMap* map, int* width, int* height)
-{
+status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) {
SkBitmap bitmap;
sk_sp<SkData> data = SkData::MakeWithoutCopy(map->getDataPtr(),
map->getDataLength());
@@ -278,6 +277,78 @@
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();
@@ -346,25 +417,12 @@
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);
@@ -380,9 +438,46 @@
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()) {
@@ -443,15 +538,14 @@
}
}
-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);
@@ -462,11 +556,10 @@
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");
@@ -485,19 +578,19 @@
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;
@@ -612,8 +705,7 @@
}
-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) {
@@ -734,8 +826,7 @@
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)) {
@@ -802,8 +893,7 @@
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;
@@ -900,8 +990,7 @@
return true;
}
-bool BootAnimation::movie()
-{
+bool BootAnimation::movie() {
if (mAnimation == nullptr) {
mAnimation = loadAnimation(mZipFileName);
}
@@ -987,12 +1076,9 @@
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());
@@ -1023,6 +1109,11 @@
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();
@@ -1106,6 +1197,12 @@
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;
@@ -1148,8 +1245,7 @@
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)
@@ -1160,8 +1256,7 @@
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());
@@ -1323,5 +1418,4 @@
// ---------------------------------------------------------------------------
-}
-; // namespace android
+} // namespace android
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 574d65e..36cd91b 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -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>
@@ -145,6 +148,11 @@
BootAnimation* mBootAnimation;
};
+ // Display event handling
+ class DisplayEventCallback;
+ 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);
@@ -161,6 +169,8 @@
void findBootAnimationFile();
bool findBootAnimationFileInternal(const std::vector<std::string>& files);
bool preloadAnimation();
+ EGLConfig getEglConfig(const EGLDisplay&);
+ void resizeSurface(int newWidth, int newHeight);
void checkExit();
@@ -189,6 +199,8 @@
sp<TimeCheckThread> mTimeCheckThread = nullptr;
sp<Callbacks> mCallbacks;
Animation* mAnimation = nullptr;
+ std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver;
+ sp<Looper> mLooper;
};
// ---------------------------------------------------------------------------