summaryrefslogtreecommitdiff
path: root/cmds/bootanimation/BootAnimation.cpp
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2020-09-09 20:21:10 -0700
committerXin Li <delphij@google.com>2020-09-09 20:21:10 -0700
commitc64112eb974e9aa7638aead998f07a868acfb5a7 (patch)
tree503334edcee47bfd9f7a76d987d881992ecae9aa /cmds/bootanimation/BootAnimation.cpp
parent104d2f92b3911576c284ddb0adf78148359883d2 (diff)
parent14a6871e432e163533a320516ace97bd67d9c3a0 (diff)
Merge Android R
Bug: 168057903 Merged-In: Ice3e441cc9c0df8d0a6acc016bb74375e081bd67 Change-Id: I1d85742f594be2007c99841b290e502b6ede624e
Diffstat (limited to 'cmds/bootanimation/BootAnimation.cpp')
-rw-r--r--cmds/bootanimation/BootAnimation.cpp298
1 files changed, 230 insertions, 68 deletions
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 85db4b9f0f8f..bb2de17b42f3 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -42,12 +42,13 @@
#include <android-base/properties.h>
+#include <ui/DisplayConfig.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <ui/Region.h>
-#include <ui/DisplayInfo.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/DisplayEventReceiver.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -106,14 +107,15 @@ 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 DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays";
static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
static constexpr size_t TEXT_POS_LEN_MAX = 16;
// ---------------------------------------------------------------------------
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", "");
@@ -153,8 +155,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...");
@@ -218,8 +219,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) {
SkBitmap bitmap;
sk_sp<SkData> data = SkData::MakeWithoutCopy(map->getDataPtr(),
map->getDataLength());
@@ -277,48 +277,173 @@ 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;
+}
+
+ui::Size BootAnimation::limitSurfaceSize(int width, int height) const {
+ ui::Size limited(width, height);
+ bool wasLimited = false;
+ const float aspectRatio = float(width) / float(height);
+ if (mMaxWidth != 0 && width > mMaxWidth) {
+ limited.height = mMaxWidth / aspectRatio;
+ limited.width = mMaxWidth;
+ wasLimited = true;
+ }
+ if (mMaxHeight != 0 && limited.height > mMaxHeight) {
+ limited.height = mMaxHeight;
+ limited.width = mMaxHeight * aspectRatio;
+ wasLimited = true;
+ }
+ SLOGV_IF(wasLimited, "Surface size has been limited to [%dx%d] from [%dx%d]",
+ limited.width, limited.height, width, height);
+ return limited;
+}
+
status_t BootAnimation::readyToRun() {
mAssets.addDefaultAssets();
mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
if (mDisplayToken == nullptr)
- return -1;
-
- DisplayInfo dinfo;
- status_t status = SurfaceComposerClient::getDisplayInfo(mDisplayToken, &dinfo);
- if (status)
- return -1;
-
+ return NAME_NOT_FOUND;
+
+ DisplayConfig displayConfig;
+ const status_t error =
+ SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &displayConfig);
+ if (error != NO_ERROR)
+ return error;
+
+ mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0);
+ mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0);
+ ui::Size resolution = displayConfig.resolution;
+ resolution = limitSurfaceSize(resolution.width, resolution.height);
// create the native surface
sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
- dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
+ resolution.getWidth(), resolution.getHeight(), PIXEL_FORMAT_RGB_565);
SurfaceComposerClient::Transaction t;
+
+ // this guest property specifies multi-display IDs to show the boot animation
+ // multiple ids can be set with comma (,) as separator, for example:
+ // setprop persist.boot.animation.displays 19260422155234049,19261083906282754
+ Vector<uint64_t> physicalDisplayIds;
+ char displayValue[PROPERTY_VALUE_MAX] = "";
+ property_get(DISPLAYS_PROP_NAME, displayValue, "");
+ bool isValid = displayValue[0] != '\0';
+ if (isValid) {
+ char *p = displayValue;
+ while (*p) {
+ if (!isdigit(*p) && *p != ',') {
+ isValid = false;
+ break;
+ }
+ p ++;
+ }
+ if (!isValid)
+ SLOGE("Invalid syntax for the value of system prop: %s", DISPLAYS_PROP_NAME);
+ }
+ if (isValid) {
+ std::istringstream stream(displayValue);
+ for (PhysicalDisplayId id; stream >> id; ) {
+ physicalDisplayIds.add(id);
+ if (stream.peek() == ',')
+ stream.ignore();
+ }
+
+ // In the case of multi-display, boot animation shows on the specified displays
+ // in addition to the primary display
+ auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ constexpr uint32_t LAYER_STACK = 0;
+ for (auto id : physicalDisplayIds) {
+ if (std::find(ids.begin(), ids.end(), id) != ids.end()) {
+ sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(id);
+ if (token != nullptr)
+ t.setDisplayLayerStack(token, LAYER_STACK);
+ }
+ }
+ t.setLayerStack(control, LAYER_STACK);
+ }
+
t.setLayer(control, 0x40000000)
.apply();
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);
@@ -334,9 +459,47 @@ 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);
+
+ const auto limitedSize = limitSurfaceSize(newWidth, newHeight);
+ mWidth = limitedSize.width;
+ mHeight = limitedSize.height;
+
+ 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()) {
@@ -397,15 +560,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);
@@ -416,11 +578,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");
@@ -439,19 +600,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;
@@ -566,8 +727,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) {
@@ -688,8 +848,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)) {
@@ -756,8 +915,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;
@@ -854,8 +1012,7 @@ bool BootAnimation::preloadZip(Animation& animation)
return true;
}
-bool BootAnimation::movie()
-{
+bool BootAnimation::movie() {
if (mAnimation == nullptr) {
mAnimation = loadAnimation(mZipFileName);
}
@@ -941,12 +1098,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());
@@ -977,6 +1131,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();
@@ -1060,6 +1219,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;
@@ -1092,7 +1257,7 @@ void BootAnimation::handleViewport(nsecs_t timestep) {
SurfaceComposerClient::Transaction t;
t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset)
.setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight));
- t.setDisplayProjection(mDisplayToken, 0 /* orientation */, layerStackRect, displayRect);
+ t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, layerStackRect, displayRect);
t.apply();
mTargetInset = mCurrentInset = 0;
@@ -1102,8 +1267,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)
@@ -1114,8 +1278,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());
@@ -1136,10 +1299,10 @@ BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
parseAnimationDesc(*animation);
if (!preloadZip(*animation)) {
+ releaseAnimation(animation);
return nullptr;
}
-
mLoadedFiles.remove(fn);
return animation;
}
@@ -1260,7 +1423,7 @@ status_t BootAnimation::TimeCheckThread::readyToRun() {
if (mSystemWd < 0) {
close(mInotifyFd);
mInotifyFd = -1;
- SLOGE("Could not add watch for %s", SYSTEM_DATA_DIR_PATH);
+ SLOGE("Could not add watch for %s: %s", SYSTEM_DATA_DIR_PATH, strerror(errno));
return NO_INIT;
}
@@ -1277,5 +1440,4 @@ status_t BootAnimation::TimeCheckThread::readyToRun() {
// ---------------------------------------------------------------------------
-}
-; // namespace android
+} // namespace android