xref: /aosp_15_r20/frameworks/base/cmds/bootanimation/BootAnimation.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_NDEBUG 0
18 
19 #define LOG_TAG "BootAnimation"
20 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
21 
22 #include <filesystem>
23 #include <vector>
24 
25 #include <stdint.h>
26 #include <inttypes.h>
27 #include <sys/inotify.h>
28 #include <sys/poll.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <math.h>
32 #include <fcntl.h>
33 #include <utils/misc.h>
34 #include <utils/Trace.h>
35 #include <signal.h>
36 #include <time.h>
37 
38 #include <cutils/atomic.h>
39 #include <cutils/properties.h>
40 
41 #include <android/imagedecoder.h>
42 #include <androidfw/AssetManager.h>
43 #include <binder/IPCThreadState.h>
44 #include <utils/Errors.h>
45 #include <utils/Log.h>
46 #include <utils/SystemClock.h>
47 
48 #include <android-base/properties.h>
49 
50 #include <ui/DisplayMode.h>
51 #include <ui/PixelFormat.h>
52 #include <ui/Rect.h>
53 #include <ui/Region.h>
54 
55 #include <gui/ISurfaceComposer.h>
56 #include <gui/DisplayEventReceiver.h>
57 #include <gui/Surface.h>
58 #include <gui/SurfaceComposerClient.h>
59 #include <GLES2/gl2.h>
60 #include <GLES2/gl2ext.h>
61 #include <EGL/eglext.h>
62 
63 #include "BootAnimation.h"
64 
65 #include <com_android_graphics_bootanimation_flags.h>
66 
67 #define ANIM_PATH_MAX 255
68 #define STR(x)   #x
69 #define STRTO(x) STR(x)
70 
71 namespace android {
72 
73 using ui::DisplayMode;
74 
75 static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
76 static const char PRODUCT_BOOTANIMATION_DIR[] = "/product/media/";
77 static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "bootanimation-dark.zip";
78 static const char PRODUCT_BOOTANIMATION_FILE[] = "bootanimation.zip";
79 static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
80 static const char APEX_BOOTANIMATION_FILE[] = "/apex/com.android.bootanimation/etc/bootanimation.zip";
81 static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip";
82 static const char PRODUCT_SHUTDOWNANIMATION_FILE[] = "/product/media/shutdownanimation.zip";
83 static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip";
84 
85 static constexpr const char* PRODUCT_USERSPACE_REBOOT_ANIMATION_FILE = "/product/media/userspace-reboot.zip";
86 static constexpr const char* OEM_USERSPACE_REBOOT_ANIMATION_FILE = "/oem/media/userspace-reboot.zip";
87 static constexpr const char* SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE = "/system/media/userspace-reboot.zip";
88 
89 static const char BOOTANIM_DATA_DIR_PATH[] = "/data/misc/bootanim";
90 static const char BOOTANIM_TIME_DIR_NAME[] = "time";
91 static const char BOOTANIM_TIME_DIR_PATH[] = "/data/misc/bootanim/time";
92 static const char CLOCK_FONT_ASSET[] = "images/clock_font.png";
93 static const char CLOCK_FONT_ZIP_NAME[] = "clock_font.png";
94 static const char PROGRESS_FONT_ASSET[] = "images/progress_font.png";
95 static const char PROGRESS_FONT_ZIP_NAME[] = "progress_font.png";
96 static const char LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change";
97 static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/misc/bootanim/time/last_time_change";
98 static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
99 static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/misc/bootanim/time/time_is_accurate";
100 static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/misc/bootanim/time/time_format_12_hour";
101 // Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00.
102 static const long long ACCURATE_TIME_EPOCH = 946684800000;
103 static constexpr char FONT_BEGIN_CHAR = ' ';
104 static constexpr char FONT_END_CHAR = '~' + 1;
105 static constexpr size_t FONT_NUM_CHARS = FONT_END_CHAR - FONT_BEGIN_CHAR + 1;
106 static constexpr size_t FONT_NUM_COLS = 16;
107 static constexpr size_t FONT_NUM_ROWS = FONT_NUM_CHARS / FONT_NUM_COLS;
108 static const int TEXT_CENTER_VALUE = INT_MAX;
109 static const int TEXT_MISSING_VALUE = INT_MIN;
110 static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
111 static const char PROGRESS_PROP_NAME[] = "service.bootanim.progress";
112 static const char CLOCK_ENABLED_PROP_NAME[] = "persist.sys.bootanim.clock.enabled";
113 static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
114 static const int MAX_CHECK_EXIT_INTERVAL_US = 50000;
115 static constexpr size_t TEXT_POS_LEN_MAX = 16;
116 static const int DYNAMIC_COLOR_COUNT = 4;
117 static const char U_TEXTURE[] = "uTexture";
118 static const char U_FADE[] = "uFade";
119 static const char U_CROP_AREA[] = "uCropArea";
120 static const char U_START_COLOR_PREFIX[] = "uStartColor";
121 static const char U_END_COLOR_PREFIX[] = "uEndColor";
122 static const char U_COLOR_PROGRESS[] = "uColorProgress";
123 static const char A_UV[] = "aUv";
124 static const char A_POSITION[] = "aPosition";
125 static const char VERTEX_SHADER_SOURCE[] = R"(
126     precision mediump float;
127     attribute vec4 aPosition;
128     attribute highp vec2 aUv;
129     varying highp vec2 vUv;
130     void main() {
131         gl_Position = aPosition;
132         vUv = aUv;
133     })";
134 static const char IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE[] = R"(
135     precision mediump float;
136     const float cWhiteMaskThreshold = 0.05;
137     uniform sampler2D uTexture;
138     uniform float uFade;
139     uniform float uColorProgress;
140     uniform vec3 uStartColor0;
141     uniform vec3 uStartColor1;
142     uniform vec3 uStartColor2;
143     uniform vec3 uStartColor3;
144     uniform vec3 uEndColor0;
145     uniform vec3 uEndColor1;
146     uniform vec3 uEndColor2;
147     uniform vec3 uEndColor3;
148     varying highp vec2 vUv;
149     void main() {
150         vec4 mask = texture2D(uTexture, vUv);
151         float r = mask.r;
152         float g = mask.g;
153         float b = mask.b;
154         float a = mask.a;
155         // If all channels have values, render pixel as a shade of white.
156         float useWhiteMask = step(cWhiteMaskThreshold, r)
157             * step(cWhiteMaskThreshold, g)
158             * step(cWhiteMaskThreshold, b)
159             * step(cWhiteMaskThreshold, a);
160         vec3 color = r * mix(uStartColor0, uEndColor0, uColorProgress)
161                 + g * mix(uStartColor1, uEndColor1, uColorProgress)
162                 + b * mix(uStartColor2, uEndColor2, uColorProgress)
163                 + a * mix(uStartColor3, uEndColor3, uColorProgress);
164         color = mix(color, vec3((r + g + b + a) * 0.25), useWhiteMask);
165         gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade));
166     })";
167 static const char IMAGE_FRAG_SHADER_SOURCE[] = R"(
168     precision mediump float;
169     uniform sampler2D uTexture;
170     uniform float uFade;
171     varying highp vec2 vUv;
172     void main() {
173         vec4 color = texture2D(uTexture, vUv);
174         gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a;
175     })";
176 static const char TEXT_FRAG_SHADER_SOURCE[] = R"(
177     precision mediump float;
178     uniform sampler2D uTexture;
179     uniform vec4 uCropArea;
180     varying highp vec2 vUv;
181     void main() {
182         vec2 uv = vec2(mix(uCropArea.x, uCropArea.z, vUv.x),
183                        mix(uCropArea.y, uCropArea.w, vUv.y));
184         gl_FragColor = texture2D(uTexture, uv);
185     })";
186 
187 static GLfloat quadPositions[] = {
188     -0.5f, -0.5f,
189     +0.5f, -0.5f,
190     +0.5f, +0.5f,
191     +0.5f, +0.5f,
192     -0.5f, +0.5f,
193     -0.5f, -0.5f
194 };
195 static GLfloat quadUVs[] = {
196     0.0f, 1.0f,
197     1.0f, 1.0f,
198     1.0f, 0.0f,
199     1.0f, 0.0f,
200     0.0f, 0.0f,
201     0.0f, 1.0f
202 };
203 
204 // ---------------------------------------------------------------------------
205 
BootAnimation(sp<Callbacks> callbacks)206 BootAnimation::BootAnimation(sp<Callbacks> callbacks)
207         : Thread(false), mLooper(new Looper(false)), mClockEnabled(true), mTimeIsAccurate(false),
208         mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
209     ATRACE_CALL();
210     mSession = new SurfaceComposerClient();
211 
212     std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
213     if (powerCtl.empty()) {
214         mShuttingDown = false;
215     } else {
216         mShuttingDown = true;
217     }
218     ALOGD("%sAnimationStartTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
219             elapsedRealtime());
220 }
221 
~BootAnimation()222 BootAnimation::~BootAnimation() {
223     ATRACE_CALL();
224     if (mAnimation != nullptr) {
225         releaseAnimation(mAnimation);
226         mAnimation = nullptr;
227     }
228     ALOGD("%sAnimationStopTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
229             elapsedRealtime());
230 }
231 
onFirstRef()232 void BootAnimation::onFirstRef() {
233     ATRACE_CALL();
234     status_t err = mSession->linkToComposerDeath(this);
235     SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
236     if (err == NO_ERROR) {
237         // Load the animation content -- this can be slow (eg 200ms)
238         // called before waitForSurfaceFlinger() in main() to avoid wait
239         ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms",
240                 mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
241         preloadAnimation();
242         ALOGD("%sAnimationPreloadTiming stop time: %" PRId64 "ms",
243                 mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
244     }
245 }
246 
session() const247 sp<SurfaceComposerClient> BootAnimation::session() const {
248     return mSession;
249 }
250 
binderDied(const wp<IBinder> &)251 void BootAnimation::binderDied(const wp<IBinder>&) {
252     ATRACE_CALL();
253     // woah, surfaceflinger died!
254     SLOGD("SurfaceFlinger died, exiting...");
255 
256     // calling requestExit() is not enough here because the Surface code
257     // might be blocked on a condition variable that will never be updated.
258     kill( getpid(), SIGKILL );
259     requestExit();
260 }
261 
decodeImage(const void * encodedData,size_t dataLength,AndroidBitmapInfo * outInfo,bool premultiplyAlpha)262 static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitmapInfo* outInfo,
263     bool premultiplyAlpha) {
264     ATRACE_CALL();
265     AImageDecoder* decoder = nullptr;
266     AImageDecoder_createFromBuffer(encodedData, dataLength, &decoder);
267     if (!decoder) {
268         return nullptr;
269     }
270 
271     const AImageDecoderHeaderInfo* info = AImageDecoder_getHeaderInfo(decoder);
272     outInfo->width = AImageDecoderHeaderInfo_getWidth(info);
273     outInfo->height = AImageDecoderHeaderInfo_getHeight(info);
274     outInfo->format = AImageDecoderHeaderInfo_getAndroidBitmapFormat(info);
275     outInfo->stride = AImageDecoder_getMinimumStride(decoder);
276     outInfo->flags = 0;
277 
278     if (!premultiplyAlpha) {
279         AImageDecoder_setUnpremultipliedRequired(decoder, true);
280     }
281 
282     const size_t size = outInfo->stride * outInfo->height;
283     void* pixels = malloc(size);
284     int result = AImageDecoder_decodeImage(decoder, pixels, outInfo->stride, size);
285     AImageDecoder_delete(decoder);
286 
287     if (result != ANDROID_IMAGE_DECODER_SUCCESS) {
288         free(pixels);
289         return nullptr;
290     }
291     return pixels;
292 }
293 
initTexture(Texture * texture,AssetManager & assets,const char * name,bool premultiplyAlpha)294 status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
295         const char* name, bool premultiplyAlpha) {
296     ATRACE_CALL();
297     Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);
298     if (asset == nullptr)
299         return NO_INIT;
300 
301     AndroidBitmapInfo bitmapInfo;
302     void* pixels = decodeImage(asset->getBuffer(false), asset->getLength(), &bitmapInfo,
303         premultiplyAlpha);
304     auto pixelDeleter = std::unique_ptr<void, decltype(free)*>{ pixels, free };
305 
306     asset->close();
307     delete asset;
308 
309     if (!pixels) {
310         return NO_INIT;
311     }
312 
313     const int w = bitmapInfo.width;
314     const int h = bitmapInfo.height;
315 
316     texture->w = w;
317     texture->h = h;
318 
319     glGenTextures(1, &texture->name);
320     glBindTexture(GL_TEXTURE_2D, texture->name);
321 
322     switch (bitmapInfo.format) {
323         case ANDROID_BITMAP_FORMAT_A_8:
324             glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA,
325                     GL_UNSIGNED_BYTE, pixels);
326             break;
327         case ANDROID_BITMAP_FORMAT_RGBA_4444:
328             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
329                     GL_UNSIGNED_SHORT_4_4_4_4, pixels);
330             break;
331         case ANDROID_BITMAP_FORMAT_RGBA_8888:
332             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
333                     GL_UNSIGNED_BYTE, pixels);
334             break;
335         case ANDROID_BITMAP_FORMAT_RGB_565:
336             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
337                     GL_UNSIGNED_SHORT_5_6_5, pixels);
338             break;
339         default:
340             break;
341     }
342 
343     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
344     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
345     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
346     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
347 
348     return NO_ERROR;
349 }
350 
initTexture(FileMap * map,int * width,int * height,bool premultiplyAlpha)351 status_t BootAnimation::initTexture(FileMap* map, int* width, int* height,
352     bool premultiplyAlpha) {
353     ATRACE_CALL();
354     AndroidBitmapInfo bitmapInfo;
355     void* pixels = decodeImage(map->getDataPtr(), map->getDataLength(), &bitmapInfo,
356         premultiplyAlpha);
357     auto pixelDeleter = std::unique_ptr<void, decltype(free)*>{ pixels, free };
358 
359     // FileMap memory is never released until application exit.
360     // Release it now as the texture is already loaded and the memory used for
361     // the packed resource can be released.
362     delete map;
363 
364     if (!pixels) {
365         return NO_INIT;
366     }
367 
368     const int w = bitmapInfo.width;
369     const int h = bitmapInfo.height;
370 
371     int tw = 1 << (31 - __builtin_clz(w));
372     int th = 1 << (31 - __builtin_clz(h));
373     if (tw < w) tw <<= 1;
374     if (th < h) th <<= 1;
375 
376     switch (bitmapInfo.format) {
377         case ANDROID_BITMAP_FORMAT_RGBA_8888:
378             if (!mUseNpotTextures && (tw != w || th != h)) {
379                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
380                         GL_UNSIGNED_BYTE, nullptr);
381                 glTexSubImage2D(GL_TEXTURE_2D, 0,
382                         0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
383             } else {
384                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
385                         GL_UNSIGNED_BYTE, pixels);
386             }
387             break;
388 
389         case ANDROID_BITMAP_FORMAT_RGB_565:
390             if (!mUseNpotTextures && (tw != w || th != h)) {
391                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
392                         GL_UNSIGNED_SHORT_5_6_5, nullptr);
393                 glTexSubImage2D(GL_TEXTURE_2D, 0,
394                         0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels);
395             } else {
396                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
397                         GL_UNSIGNED_SHORT_5_6_5, pixels);
398             }
399             break;
400         default:
401             break;
402     }
403 
404     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
405     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
406     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
407     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
408 
409     *width = w;
410     *height = h;
411 
412     return NO_ERROR;
413 }
414 
415 class BootAnimation::DisplayEventCallback : public LooperCallback {
416     BootAnimation* mBootAnimation;
417 
418 public:
DisplayEventCallback(BootAnimation * bootAnimation)419     DisplayEventCallback(BootAnimation* bootAnimation) {
420         ATRACE_CALL();
421         mBootAnimation = bootAnimation;
422     }
423 
handleEvent(int,int events,void *)424     int handleEvent(int /* fd */, int events, void* /* data */) {
425         ATRACE_CALL();
426         if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
427             ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x",
428                     events);
429             return 0; // remove the callback
430         }
431 
432         if (!(events & Looper::EVENT_INPUT)) {
433             ALOGW("Received spurious callback for unhandled poll event.  events=0x%x", events);
434             return 1; // keep the callback
435         }
436 
437         constexpr int kBufferSize = 100;
438         DisplayEventReceiver::Event buffer[kBufferSize];
439         ssize_t numEvents;
440         do {
441             numEvents = mBootAnimation->mDisplayEventReceiver->getEvents(buffer, kBufferSize);
442             for (size_t i = 0; i < static_cast<size_t>(numEvents); i++) {
443                 const auto& event = buffer[i];
444                 if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) {
445                     SLOGV("Hotplug received");
446 
447                     if (!event.hotplug.connected) {
448                         // ignore hotplug disconnect
449                         continue;
450                     }
451                     auto token = SurfaceComposerClient::getPhysicalDisplayToken(
452                         event.header.displayId);
453 
454                     auto& firstDisplay = mBootAnimation->mDisplays.front();
455                     if (token != firstDisplay.displayToken) {
456                         // ignore hotplug of a secondary display
457                         continue;
458                     }
459 
460                     DisplayMode displayMode;
461                     const status_t error = SurfaceComposerClient::getActiveDisplayMode(
462                                                firstDisplay.displayToken, &displayMode);
463                     if (error != NO_ERROR) {
464                         SLOGE("Can't get active display mode.");
465                     }
466                     mBootAnimation->resizeSurface(displayMode.resolution.getWidth(),
467                                                   displayMode.resolution.getHeight(),
468                                                   firstDisplay);
469                 }
470             }
471         } while (numEvents > 0);
472 
473         return 1;  // keep the callback
474     }
475 };
476 
getEglConfig(const EGLDisplay & display)477 EGLConfig BootAnimation::getEglConfig(const EGLDisplay& display) {
478     const EGLint attribs[] = {
479         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
480         EGL_RED_SIZE,   8,
481         EGL_GREEN_SIZE, 8,
482         EGL_BLUE_SIZE,  8,
483         EGL_DEPTH_SIZE, 0,
484         EGL_NONE
485     };
486     EGLint numConfigs;
487     EGLConfig config;
488     eglChooseConfig(display, attribs, &config, 1, &numConfigs);
489     return config;
490 }
491 
limitSurfaceSize(int width,int height) const492 ui::Size BootAnimation::limitSurfaceSize(int width, int height) const {
493     ui::Size limited(width, height);
494     bool wasLimited = false;
495     const float aspectRatio = float(width) / float(height);
496     if (mMaxWidth != 0 && width > mMaxWidth) {
497         limited.height = mMaxWidth / aspectRatio;
498         limited.width = mMaxWidth;
499         wasLimited = true;
500     }
501     if (mMaxHeight != 0 && limited.height > mMaxHeight) {
502         limited.height = mMaxHeight;
503         limited.width = mMaxHeight * aspectRatio;
504         wasLimited = true;
505     }
506     SLOGV_IF(wasLimited, "Surface size has been limited to [%dx%d] from [%dx%d]",
507              limited.width, limited.height, width, height);
508     return limited;
509 }
510 
readyToRun()511 status_t BootAnimation::readyToRun() {
512     ATRACE_CALL();
513     mAssets.addDefaultAssets();
514     return initDisplaysAndSurfaces();
515 }
516 
initDisplaysAndSurfaces()517 status_t BootAnimation::initDisplaysAndSurfaces() {
518     std::vector<PhysicalDisplayId> displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
519     if (displayIds.empty()) {
520         SLOGE("Failed to get ID for any displays");
521         return NAME_NOT_FOUND;
522     }
523 
524     // If Multi-Display isn't explicitly enabled, ignore all displays after the first one
525     if (!com::android::graphics::bootanimation::flags::multidisplay()) {
526         displayIds.erase(displayIds.begin() + 1, displayIds.end());
527     }
528 
529     for (const auto id : displayIds) {
530         if (const auto token = SurfaceComposerClient::getPhysicalDisplayToken(id)) {
531             mDisplays.push_back({.displayToken = token});
532         } else {
533             SLOGE("Failed to get display token for a display");
534             SLOGE("Failed to get display token for display %" PRIu64, id.value);
535             return NAME_NOT_FOUND;
536         }
537     }
538 
539     // Initialize EGL
540     mEgl = eglGetDisplay(EGL_DEFAULT_DISPLAY);
541     eglInitialize(mEgl, nullptr, nullptr);
542     EGLConfig config = getEglConfig(mEgl);
543     EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
544     mEglContext = eglCreateContext(mEgl, config, nullptr, contextAttributes);
545 
546     mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0);
547     mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0);
548 
549     for (size_t displayIdx = 0; displayIdx < mDisplays.size(); displayIdx++) {
550         auto& display = mDisplays[displayIdx];
551         DisplayMode displayMode;
552         const status_t error =
553                 SurfaceComposerClient::getActiveDisplayMode(display.displayToken, &displayMode);
554         if (error != NO_ERROR) {
555             return error;
556         }
557         ui::Size resolution = displayMode.resolution;
558         // Clamp each surface to max size
559         resolution = limitSurfaceSize(resolution.width, resolution.height);
560         // Create the native surface
561         display.surfaceControl =
562                 session()->createSurface(String8("BootAnimation"), resolution.width,
563                                          resolution.height, PIXEL_FORMAT_RGB_565,
564                                          ISurfaceComposerClient::eOpaque);
565         // Attach surface to layerstack, and associate layerstack with physical display
566         configureDisplayAndLayerStack(display, ui::LayerStack::fromValue(displayIdx));
567         display.surface = display.surfaceControl->getSurface();
568         display.eglSurface = eglCreateWindowSurface(mEgl, config, display.surface.get(), nullptr);
569 
570         EGLint w, h;
571         eglQuerySurface(mEgl, display.eglSurface, EGL_WIDTH, &w);
572         eglQuerySurface(mEgl, display.eglSurface, EGL_HEIGHT, &h);
573         if (eglMakeCurrent(mEgl, display.eglSurface, display.eglSurface,
574                            mEglContext) == EGL_FALSE) {
575             return NO_INIT;
576         }
577         display.initWidth = display.width = w;
578         display.initHeight = display.height = h;
579         mTargetInset = -1;
580 
581         // Rotate the boot animation according to the value specified in the sysprop
582         // ro.bootanim.set_orientation_<display_id>. Four values are supported: ORIENTATION_0,
583         // ORIENTATION_90, ORIENTATION_180 and ORIENTATION_270.
584         // If the value isn't specified or is ORIENTATION_0, nothing will be changed.
585         // This is needed to support boot animation in orientations different from the natural
586         // device orientation. For example, on tablets that may want to keep natural orientation
587         // portrait for applications compatibility and to have the boot animation in landscape.
588         rotateAwayFromNaturalOrientationIfNeeded(display);
589 
590         projectSceneToWindow(display);
591     } // end iteration over all display tokens
592 
593     // Register a display event receiver
594     mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>();
595     status_t status = mDisplayEventReceiver->initCheck();
596     SLOGE_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver failed with status: %d",
597              status);
598     mLooper->addFd(mDisplayEventReceiver->getFd(), 0, Looper::EVENT_INPUT,
599                    new DisplayEventCallback(this), nullptr);
600 
601     return NO_ERROR;
602 }
603 
configureDisplayAndLayerStack(const Display & display,ui::LayerStack layerStack)604 void BootAnimation::configureDisplayAndLayerStack(const Display& display,
605                                                   ui::LayerStack layerStack) {
606     SurfaceComposerClient::Transaction t;
607     t.setDisplayLayerStack(display.displayToken, layerStack);
608     t.setLayerStack(display.surfaceControl, layerStack)
609      .setLayer(display.surfaceControl, 0x40000000)
610      .apply();
611 }
612 
rotateAwayFromNaturalOrientationIfNeeded(Display & display)613 void BootAnimation::rotateAwayFromNaturalOrientationIfNeeded(Display& display) {
614     ATRACE_CALL();
615     const auto orientation = parseOrientationProperty();
616 
617     if (orientation == ui::ROTATION_0) {
618         // Do nothing if the sysprop isn't set or is set to ROTATION_0.
619         return;
620     }
621 
622     if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
623         std::swap(display.width, display.height);
624         std::swap(display.initWidth, display.initHeight);
625         display.surfaceControl->updateDefaultBufferSize(display.width, display.height);
626     }
627 
628     Rect displayRect(0, 0, display.width, display.height);
629     Rect layerStackRect(0, 0, display.width, display.height);
630 
631     SurfaceComposerClient::Transaction t;
632     t.setDisplayProjection(display.displayToken, orientation, layerStackRect, displayRect);
633     t.apply();
634 }
635 
parseOrientationProperty()636 ui::Rotation BootAnimation::parseOrientationProperty() {
637     ATRACE_CALL();
638     const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
639     if (displayIds.size() == 0) {
640         return ui::ROTATION_0;
641     }
642     const auto displayId = displayIds[0];
643     const auto syspropName = [displayId] {
644         std::stringstream ss;
645         ss << "ro.bootanim.set_orientation_" << displayId.value;
646         return ss.str();
647     }();
648     auto syspropValue = android::base::GetProperty(syspropName, "");
649     if (syspropValue == "") {
650         syspropValue = android::base::GetProperty("ro.bootanim.set_orientation_logical_0", "");
651     }
652 
653     if (syspropValue == "ORIENTATION_90") {
654         return ui::ROTATION_90;
655     } else if (syspropValue == "ORIENTATION_180") {
656         return ui::ROTATION_180;
657     } else if (syspropValue == "ORIENTATION_270") {
658         return ui::ROTATION_270;
659     }
660     return ui::ROTATION_0;
661 }
662 
projectSceneToWindow(const Display & display)663 void BootAnimation::projectSceneToWindow(const Display& display) {
664     ATRACE_CALL();
665     glViewport(0, 0, display.width, display.height);
666     glScissor(0, 0, display.width, display.height);
667 }
668 
resizeSurface(int newWidth,int newHeight,Display & display)669 void BootAnimation::resizeSurface(int newWidth, int newHeight, Display& display) {
670     ATRACE_CALL();
671     // We assume this function is called on the animation thread.
672     if (newWidth == display.width && newHeight == display.height) {
673         return;
674     }
675 
676     eglMakeCurrent(mEgl, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
677     eglDestroySurface(mEgl, display.eglSurface);
678 
679     const auto limitedSize = limitSurfaceSize(newWidth, newHeight);
680     display.width = limitedSize.width;
681     display.height = limitedSize.height;
682 
683     display.surfaceControl->updateDefaultBufferSize(display.width, display.height);
684     EGLConfig config = getEglConfig(mEgl);
685     EGLSurface eglSurface = eglCreateWindowSurface(mEgl, config, display.surface.get(), nullptr);
686     if (eglMakeCurrent(mEgl, eglSurface, eglSurface, mEglContext) == EGL_FALSE) {
687         SLOGE("Can't make the new eglSurface current. Error %d", eglGetError());
688         return;
689     }
690 
691     projectSceneToWindow(display);
692 
693     display.eglSurface = eglSurface;
694 }
695 
preloadAnimation()696 bool BootAnimation::preloadAnimation() {
697     ATRACE_CALL();
698     findBootAnimationFile();
699     if (!mZipFileName.empty()) {
700         mAnimation = loadAnimation(mZipFileName);
701         return (mAnimation != nullptr);
702     }
703 
704     return false;
705 }
706 
findBootAnimationFileInternal(const std::vector<std::string> & files)707 bool BootAnimation::findBootAnimationFileInternal(const std::vector<std::string> &files) {
708     ATRACE_CALL();
709     for (const std::string& f : files) {
710         if (access(f.c_str(), R_OK) == 0) {
711             mZipFileName = f.c_str();
712             return true;
713         }
714     }
715     return false;
716 }
717 
findBootAnimationFile()718 void BootAnimation::findBootAnimationFile() {
719     ATRACE_CALL();
720     const bool playDarkAnim = android::base::GetIntProperty("ro.boot.theme", 0) == 1;
721     const std::string productBootanimationFile = PRODUCT_BOOTANIMATION_DIR +
722         android::base::GetProperty("ro.product.bootanim.file", playDarkAnim ?
723         PRODUCT_BOOTANIMATION_DARK_FILE : PRODUCT_BOOTANIMATION_FILE);
724     static const std::vector<std::string> bootFiles = {
725         APEX_BOOTANIMATION_FILE, productBootanimationFile,
726         OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE
727     };
728     static const std::vector<std::string> shutdownFiles = {
729         PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE, ""
730     };
731     static const std::vector<std::string> userspaceRebootFiles = {
732         PRODUCT_USERSPACE_REBOOT_ANIMATION_FILE, OEM_USERSPACE_REBOOT_ANIMATION_FILE,
733         SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE,
734     };
735 
736     if (android::base::GetBoolProperty("sys.init.userspace_reboot.in_progress", false)) {
737         findBootAnimationFileInternal(userspaceRebootFiles);
738     } else if (mShuttingDown) {
739         findBootAnimationFileInternal(shutdownFiles);
740     } else {
741         findBootAnimationFileInternal(bootFiles);
742     }
743 }
744 
compileShader(GLenum shaderType,const GLchar * source)745 GLuint compileShader(GLenum shaderType, const GLchar *source) {
746     ATRACE_CALL();
747     GLuint shader = glCreateShader(shaderType);
748     glShaderSource(shader, 1, &source, 0);
749     glCompileShader(shader);
750     GLint isCompiled = 0;
751     glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
752     if (isCompiled == GL_FALSE) {
753         SLOGE("Compile shader failed. Shader type: %d", shaderType);
754         GLint maxLength = 0;
755         glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
756         std::vector<GLchar> errorLog(maxLength);
757         glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);
758         SLOGE("Shader compilation error: %s", &errorLog[0]);
759         return 0;
760     }
761     return shader;
762 }
763 
linkShader(GLuint vertexShader,GLuint fragmentShader)764 GLuint linkShader(GLuint vertexShader, GLuint fragmentShader) {
765     ATRACE_CALL();
766     GLuint program = glCreateProgram();
767     glAttachShader(program, vertexShader);
768     glAttachShader(program, fragmentShader);
769     glLinkProgram(program);
770     GLint isLinked = 0;
771     glGetProgramiv(program, GL_LINK_STATUS, (int *)&isLinked);
772     if (isLinked == GL_FALSE) {
773         SLOGE("Linking shader failed. Shader handles: vert %d, frag %d",
774             vertexShader, fragmentShader);
775         return 0;
776     }
777     return program;
778 }
779 
initShaders()780 void BootAnimation::initShaders() {
781     ATRACE_CALL();
782     bool dynamicColoringEnabled = mAnimation != nullptr && mAnimation->dynamicColoringEnabled;
783     GLuint vertexShader = compileShader(GL_VERTEX_SHADER, (const GLchar *)VERTEX_SHADER_SOURCE);
784     GLuint imageFragmentShader =
785         compileShader(GL_FRAGMENT_SHADER, dynamicColoringEnabled
786             ? (const GLchar *)IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE
787             : (const GLchar *)IMAGE_FRAG_SHADER_SOURCE);
788     GLuint textFragmentShader =
789         compileShader(GL_FRAGMENT_SHADER, (const GLchar *)TEXT_FRAG_SHADER_SOURCE);
790 
791     // Initialize image shader.
792     mImageShader = linkShader(vertexShader, imageFragmentShader);
793     GLint positionLocation = glGetAttribLocation(mImageShader, A_POSITION);
794     GLint uvLocation = glGetAttribLocation(mImageShader, A_UV);
795     mImageTextureLocation = glGetUniformLocation(mImageShader, U_TEXTURE);
796     mImageFadeLocation = glGetUniformLocation(mImageShader, U_FADE);
797     glEnableVertexAttribArray(positionLocation);
798     glVertexAttribPointer(positionLocation, 2,  GL_FLOAT, GL_FALSE, 0, quadPositions);
799     glVertexAttribPointer(uvLocation, 2, GL_FLOAT, GL_FALSE, 0, quadUVs);
800     glEnableVertexAttribArray(uvLocation);
801 
802     // Initialize text shader.
803     mTextShader = linkShader(vertexShader, textFragmentShader);
804     positionLocation = glGetAttribLocation(mTextShader, A_POSITION);
805     uvLocation = glGetAttribLocation(mTextShader, A_UV);
806     mTextTextureLocation = glGetUniformLocation(mTextShader, U_TEXTURE);
807     mTextCropAreaLocation = glGetUniformLocation(mTextShader, U_CROP_AREA);
808     glEnableVertexAttribArray(positionLocation);
809     glVertexAttribPointer(positionLocation, 2,  GL_FLOAT, GL_FALSE, 0, quadPositions);
810     glVertexAttribPointer(uvLocation, 2, GL_FLOAT, GL_FALSE, 0, quadUVs);
811     glEnableVertexAttribArray(uvLocation);
812 }
813 
threadLoop()814 bool BootAnimation::threadLoop() {
815     ATRACE_CALL();
816     bool result;
817     initShaders();
818 
819     // We have no bootanimation file, so we use the stock android logo
820     // animation.
821     if (mZipFileName.empty()) {
822         ALOGD("No animation file");
823         result = android(mDisplays.front());
824     } else {
825         result = movie();
826     }
827 
828     mCallbacks->shutdown();
829     eglMakeCurrent(mEgl, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
830     eglDestroyContext(mEgl, mEglContext);
831     for (auto& display : mDisplays) {
832         eglDestroySurface(mEgl, display.eglSurface);
833         display.surface.clear();
834         display.surfaceControl.clear();
835     }
836     eglTerminate(mEgl);
837     eglReleaseThread();
838     IPCThreadState::self()->stopProcess();
839     return result;
840 }
841 
android(const Display & display)842 bool BootAnimation::android(const Display& display) {
843     ATRACE_CALL();
844     glActiveTexture(GL_TEXTURE0);
845 
846     SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
847             elapsedRealtime());
848     initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
849     initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
850 
851     mCallbacks->init({});
852 
853     // clear screen
854     glDisable(GL_DITHER);
855     glDisable(GL_SCISSOR_TEST);
856     glUseProgram(mImageShader);
857 
858     glClearColor(0,0,0,1);
859     glClear(GL_COLOR_BUFFER_BIT);
860     eglSwapBuffers(mEgl, display.eglSurface);
861 
862     // Blend state
863     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
864 
865     const nsecs_t startTime = systemTime();
866     do {
867         processDisplayEvents();
868         const GLint xc = (display.width  - mAndroid[0].w) / 2;
869         const GLint yc = (display.height - mAndroid[0].h) / 2;
870         const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);
871         glScissor(updateRect.left, display.height - updateRect.bottom, updateRect.width(),
872                   updateRect.height());
873 
874         nsecs_t now = systemTime();
875         double time = now - startTime;
876         float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
877         GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
878         GLint x = xc - offset;
879 
880         glDisable(GL_SCISSOR_TEST);
881         glClear(GL_COLOR_BUFFER_BIT);
882 
883         glEnable(GL_SCISSOR_TEST);
884         glDisable(GL_BLEND);
885         glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
886         drawTexturedQuad(x,                 yc, mAndroid[1].w, mAndroid[1].h, display);
887         drawTexturedQuad(x + mAndroid[1].w, yc, mAndroid[1].w, mAndroid[1].h, display);
888 
889         glEnable(GL_BLEND);
890         glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
891         drawTexturedQuad(xc, yc, mAndroid[0].w, mAndroid[0].h, display);
892 
893         EGLBoolean res = eglSwapBuffers(mEgl, display.eglSurface);
894         if (res == EGL_FALSE)
895             break;
896 
897         // 12fps: don't animate too fast to preserve CPU
898         const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
899         if (sleepTime > 0)
900             usleep(sleepTime);
901 
902         checkExit();
903     } while (!exitPending());
904 
905     glDeleteTextures(1, &mAndroid[0].name);
906     glDeleteTextures(1, &mAndroid[1].name);
907     return false;
908 }
909 
checkExit()910 void BootAnimation::checkExit() {
911     ATRACE_CALL();
912     // Allow SurfaceFlinger to gracefully request shutdown
913     char value[PROPERTY_VALUE_MAX];
914     property_get(EXIT_PROP_NAME, value, "0");
915     int exitnow = atoi(value);
916     if (exitnow) {
917         requestExit();
918     }
919 }
920 
validClock(const Animation::Part & part)921 bool BootAnimation::validClock(const Animation::Part& part) {
922     ATRACE_CALL();
923     return part.clockPosX != TEXT_MISSING_VALUE && part.clockPosY != TEXT_MISSING_VALUE;
924 }
925 
parseTextCoord(const char * str,int * dest)926 bool parseTextCoord(const char* str, int* dest) {
927     ATRACE_CALL();
928     if (strcmp("c", str) == 0) {
929         *dest = TEXT_CENTER_VALUE;
930         return true;
931     }
932 
933     char* end;
934     int val = (int) strtol(str, &end, 0);
935     if (end == str || *end != '\0' || val == INT_MAX || val == INT_MIN) {
936         return false;
937     }
938     *dest = val;
939     return true;
940 }
941 
942 // Parse two position coordinates. If only string is non-empty, treat it as the y value.
parsePosition(const char * str1,const char * str2,int * x,int * y)943 void parsePosition(const char* str1, const char* str2, int* x, int* y) {
944     ATRACE_CALL();
945     bool success = false;
946     if (strlen(str1) == 0) {  // No values were specified
947         // success = false
948     } else if (strlen(str2) == 0) {  // we have only one value
949         if (parseTextCoord(str1, y)) {
950             *x = TEXT_CENTER_VALUE;
951             success = true;
952         }
953     } else {
954         if (parseTextCoord(str1, x) && parseTextCoord(str2, y)) {
955             success = true;
956         }
957     }
958 
959     if (!success) {
960         *x = TEXT_MISSING_VALUE;
961         *y = TEXT_MISSING_VALUE;
962     }
963 }
964 
965 // Parse a color represented as an HTML-style 'RRGGBB' string: each pair of
966 // characters in str is a hex number in [0, 255], which are converted to
967 // floating point values in the range [0.0, 1.0] and placed in the
968 // corresponding elements of color.
969 //
970 // If the input string isn't valid, parseColor returns false and color is
971 // left unchanged.
parseColor(const char str[7],float color[3])972 static bool parseColor(const char str[7], float color[3]) {
973     ATRACE_CALL();
974     float tmpColor[3];
975     for (int i = 0; i < 3; i++) {
976         int val = 0;
977         for (int j = 0; j < 2; j++) {
978             val *= 16;
979             char c = str[2*i + j];
980             if      (c >= '0' && c <= '9') val += c - '0';
981             else if (c >= 'A' && c <= 'F') val += (c - 'A') + 10;
982             else if (c >= 'a' && c <= 'f') val += (c - 'a') + 10;
983             else                           return false;
984         }
985         tmpColor[i] = static_cast<float>(val) / 255.0f;
986     }
987     memcpy(color, tmpColor, sizeof(tmpColor));
988     return true;
989 }
990 
991 // Parse a color represented as a signed decimal int string.
992 // E.g. "-2757722" (whose hex 2's complement is 0xFFD5EBA6).
993 // If the input color string is empty, set color with values in defaultColor.
parseColorDecimalString(const std::string & colorString,float color[3],float defaultColor[3])994 static void parseColorDecimalString(const std::string& colorString,
995     float color[3], float defaultColor[3]) {
996     ATRACE_CALL();
997     if (colorString == "") {
998         memcpy(color, defaultColor, sizeof(float) * 3);
999         return;
1000     }
1001     int colorInt = atoi(colorString.c_str());
1002     color[0] = ((float)((colorInt >> 16) & 0xFF)) / 0xFF; // r
1003     color[1] = ((float)((colorInt >> 8) & 0xFF)) / 0xFF; // g
1004     color[2] = ((float)(colorInt & 0xFF)) / 0xFF; // b
1005 }
1006 
readFile(ZipFileRO * zip,const char * name,String8 & outString)1007 static bool readFile(ZipFileRO* zip, const char* name, String8& outString) {
1008     ATRACE_CALL();
1009     ZipEntryRO entry = zip->findEntryByName(name);
1010     SLOGE_IF(!entry, "couldn't find %s", name);
1011     if (!entry) {
1012         return false;
1013     }
1014 
1015     FileMap* entryMap = zip->createEntryFileMap(entry);
1016     zip->releaseEntry(entry);
1017     SLOGE_IF(!entryMap, "entryMap is null");
1018     if (!entryMap) {
1019         return false;
1020     }
1021 
1022     outString = String8((char const*)entryMap->getDataPtr(), entryMap->getDataLength());
1023     delete entryMap;
1024     return true;
1025 }
1026 
1027 // The font image should be a 96x2 array of character images.  The
1028 // columns are the printable ASCII characters 0x20 - 0x7f.  The
1029 // top row is regular text; the bottom row is bold.
initFont(Font * font,const char * fallback)1030 status_t BootAnimation::initFont(Font* font, const char* fallback) {
1031     ATRACE_CALL();
1032     status_t status = NO_ERROR;
1033 
1034     if (font->map != nullptr) {
1035         glGenTextures(1, &font->texture.name);
1036         glBindTexture(GL_TEXTURE_2D, font->texture.name);
1037 
1038         status = initTexture(font->map, &font->texture.w, &font->texture.h);
1039 
1040         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1041         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1042         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1043         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1044     } else if (fallback != nullptr) {
1045         status = initTexture(&font->texture, mAssets, fallback);
1046     } else {
1047         return NO_INIT;
1048     }
1049 
1050     if (status == NO_ERROR) {
1051         font->char_width = font->texture.w / FONT_NUM_COLS;
1052         font->char_height = font->texture.h / FONT_NUM_ROWS / 2;  // There are bold and regular rows
1053     }
1054 
1055     return status;
1056 }
1057 
drawText(const char * str,const Font & font,bool bold,int * x,int * y,const Display & display)1058 void BootAnimation::drawText(const char* str, const Font& font, bool bold,
1059                              int* x, int* y, const Display& display) {
1060     ATRACE_CALL();
1061     glEnable(GL_BLEND);  // Allow us to draw on top of the animation
1062     glBindTexture(GL_TEXTURE_2D, font.texture.name);
1063     glUseProgram(mTextShader);
1064     glUniform1i(mTextTextureLocation, 0);
1065 
1066     const int len = strlen(str);
1067     const int strWidth = font.char_width * len;
1068 
1069     if (*x == TEXT_CENTER_VALUE) {
1070         *x = (display.width - strWidth) / 2;
1071     } else if (*x < 0) {
1072         *x = display.width + *x - strWidth;
1073     }
1074     if (*y == TEXT_CENTER_VALUE) {
1075         *y = (display.height - font.char_height) / 2;
1076     } else if (*y < 0) {
1077         *y = display.height + *y - font.char_height;
1078     }
1079 
1080     for (int i = 0; i < len; i++) {
1081         char c = str[i];
1082 
1083         if (c < FONT_BEGIN_CHAR || c > FONT_END_CHAR) {
1084             c = '?';
1085         }
1086 
1087         // Crop the texture to only the pixels in the current glyph
1088         const int charPos = (c - FONT_BEGIN_CHAR);  // Position in the list of valid characters
1089         const int row = charPos / FONT_NUM_COLS;
1090         const int col = charPos % FONT_NUM_COLS;
1091         // Bold fonts are expected in the second half of each row.
1092         float v0 = (row + (bold ? 0.5f : 0.0f)) / FONT_NUM_ROWS;
1093         float u0 = ((float)col) / FONT_NUM_COLS;
1094         float v1 = v0 + 1.0f / FONT_NUM_ROWS / 2;
1095         float u1 = u0 + 1.0f / FONT_NUM_COLS;
1096         glUniform4f(mTextCropAreaLocation, u0, v0, u1, v1);
1097         drawTexturedQuad(*x, *y, font.char_width, font.char_height, display);
1098 
1099         *x += font.char_width;
1100     }
1101 
1102     glDisable(GL_BLEND);  // Return to the animation's default behaviour
1103     glBindTexture(GL_TEXTURE_2D, 0);
1104 }
1105 
1106 // We render 12 or 24 hour time.
drawClock(const Font & font,const int xPos,const int yPos,const Display & display)1107 void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos,
1108                               const Display& display) {
1109     ATRACE_CALL();
1110     static constexpr char TIME_FORMAT_12[] = "%l:%M";
1111     static constexpr char TIME_FORMAT_24[] = "%H:%M";
1112     static constexpr int TIME_LENGTH = 6;
1113 
1114     time_t rawtime;
1115     time(&rawtime);
1116     struct tm* timeInfo = localtime(&rawtime);
1117 
1118     char timeBuff[TIME_LENGTH];
1119     const char* timeFormat = mTimeFormat12Hour ? TIME_FORMAT_12 : TIME_FORMAT_24;
1120     size_t length = strftime(timeBuff, TIME_LENGTH, timeFormat, timeInfo);
1121 
1122     if (length != TIME_LENGTH - 1) {
1123         SLOGE("Couldn't format time; abandoning boot animation clock");
1124         mClockEnabled = false;
1125         return;
1126     }
1127 
1128     char* out = timeBuff[0] == ' ' ? &timeBuff[1] : &timeBuff[0];
1129     int x = xPos;
1130     int y = yPos;
1131     drawText(out, font, false, &x, &y, display);
1132 }
1133 
drawProgress(int percent,const Font & font,const int xPos,const int yPos,const Display & display)1134 void BootAnimation::drawProgress(int percent, const Font& font, const int xPos, const int yPos,
1135                                  const Display& display) {
1136     ATRACE_CALL();
1137     static constexpr int PERCENT_LENGTH = 5;
1138 
1139     char percentBuff[PERCENT_LENGTH];
1140     // ';' has the ascii code just after ':', and the font resource contains '%'
1141     // for that ascii code.
1142     sprintf(percentBuff, "%d;", percent);
1143     int x = xPos;
1144     int y = yPos;
1145     drawText(percentBuff, font, false, &x, &y, display);
1146 }
1147 
parseAnimationDesc(Animation & animation)1148 bool BootAnimation::parseAnimationDesc(Animation& animation)  {
1149     ATRACE_CALL();
1150     String8 desString;
1151 
1152     if (!readFile(animation.zip, "desc.txt", desString)) {
1153         return false;
1154     }
1155     char const* s = desString.c_str();
1156     std::string dynamicColoringPartName = "";
1157     bool postDynamicColoring = false;
1158 
1159     // Parse the description file
1160     for (;;) {
1161         const char* endl = strstr(s, "\n");
1162         if (endl == nullptr) break;
1163         String8 line(s, endl - s);
1164         const char* l = line.c_str();
1165         int fps = 0;
1166         int width = 0;
1167         int height = 0;
1168         int count = 0;
1169         int pause = 0;
1170         int progress = 0;
1171         int framesToFadeCount = 0;
1172         int colorTransitionStart = 0;
1173         int colorTransitionEnd = 0;
1174         char path[ANIM_ENTRY_NAME_MAX];
1175         char color[7] = "000000"; // default to black if unspecified
1176         char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
1177         char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
1178         char dynamicColoringPartNameBuffer[ANIM_ENTRY_NAME_MAX];
1179         char pathType;
1180         // start colors default to black if unspecified
1181         char start_color_0[7] = "000000";
1182         char start_color_1[7] = "000000";
1183         char start_color_2[7] = "000000";
1184         char start_color_3[7] = "000000";
1185 
1186         int nextReadPos;
1187 
1188         if (strlen(l) == 0) {
1189             s = ++endl;
1190             continue;
1191         }
1192 
1193         int topLineNumbers = sscanf(l, "%d %d %d %d", &width, &height, &fps, &progress);
1194         if (topLineNumbers == 3 || topLineNumbers == 4) {
1195             // SLOGD("> w=%d, h=%d, fps=%d, progress=%d", width, height, fps, progress);
1196             animation.width = width;
1197             animation.height = height;
1198             animation.fps = fps;
1199             if (topLineNumbers == 4) {
1200               animation.progressEnabled = (progress != 0);
1201             } else {
1202               animation.progressEnabled = false;
1203             }
1204         } else if (sscanf(l, "dynamic_colors %" STRTO(ANIM_PATH_MAX) "s #%6s #%6s #%6s #%6s %d %d",
1205             dynamicColoringPartNameBuffer,
1206             start_color_0, start_color_1, start_color_2, start_color_3,
1207             &colorTransitionStart, &colorTransitionEnd)) {
1208             animation.dynamicColoringEnabled = true;
1209             parseColor(start_color_0, animation.startColors[0]);
1210             parseColor(start_color_1, animation.startColors[1]);
1211             parseColor(start_color_2, animation.startColors[2]);
1212             parseColor(start_color_3, animation.startColors[3]);
1213             animation.colorTransitionStart = colorTransitionStart;
1214             animation.colorTransitionEnd = colorTransitionEnd;
1215             dynamicColoringPartName = std::string(dynamicColoringPartNameBuffer);
1216         } else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
1217                           &pathType, &count, &pause, path, &nextReadPos) >= 4) {
1218             if (pathType == 'f') {
1219                 sscanf(l + nextReadPos, " %d #%6s %16s %16s", &framesToFadeCount, color, clockPos1,
1220                        clockPos2);
1221             } else {
1222                 sscanf(l + nextReadPos, " #%6s %16s %16s", color, clockPos1, clockPos2);
1223             }
1224             // SLOGD("> type=%c, count=%d, pause=%d, path=%s, framesToFadeCount=%d, color=%s, "
1225             //       "clockPos1=%s, clockPos2=%s",
1226             //       pathType, count, pause, path, framesToFadeCount, color, clockPos1, clockPos2);
1227             Animation::Part part;
1228             if (path == dynamicColoringPartName) {
1229                 // Part is specified to use dynamic coloring.
1230                 part.useDynamicColoring = true;
1231                 part.postDynamicColoring = false;
1232                 postDynamicColoring = true;
1233             } else {
1234                 // Part does not use dynamic coloring.
1235                 part.useDynamicColoring = false;
1236                 part.postDynamicColoring =  postDynamicColoring;
1237             }
1238             part.playUntilComplete = pathType == 'c';
1239             part.framesToFadeCount = framesToFadeCount;
1240             part.count = count;
1241             part.pause = pause;
1242             part.path = path;
1243             part.audioData = nullptr;
1244             part.animation = nullptr;
1245             if (!parseColor(color, part.backgroundColor)) {
1246                 SLOGE("> invalid color '#%s'", color);
1247                 part.backgroundColor[0] = 0.0f;
1248                 part.backgroundColor[1] = 0.0f;
1249                 part.backgroundColor[2] = 0.0f;
1250             }
1251             parsePosition(clockPos1, clockPos2, &part.clockPosX, &part.clockPosY);
1252             animation.parts.add(part);
1253         }
1254         else if (strcmp(l, "$SYSTEM") == 0) {
1255             // SLOGD("> SYSTEM");
1256             Animation::Part part;
1257             part.playUntilComplete = false;
1258             part.framesToFadeCount = 0;
1259             part.count = 1;
1260             part.pause = 0;
1261             part.audioData = nullptr;
1262             part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
1263             if (part.animation != nullptr)
1264                 animation.parts.add(part);
1265         }
1266         s = ++endl;
1267     }
1268 
1269     return true;
1270 }
1271 
preloadZip(Animation & animation)1272 bool BootAnimation::preloadZip(Animation& animation) {
1273     ATRACE_CALL();
1274     const size_t numParts = animation.parts.size();
1275     void *cookie = nullptr;
1276     ZipFileRO* zip = animation.zip;
1277     if (!zip->startIteration(&cookie)) {
1278         return false;
1279     }
1280 
1281     ZipEntryRO entry;
1282     char name[ANIM_ENTRY_NAME_MAX];
1283     while ((entry = zip->nextEntry(cookie)) != nullptr) {
1284         const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
1285         if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
1286             SLOGE("Error fetching entry file name");
1287             continue;
1288         }
1289 
1290         const std::filesystem::path entryName(name);
1291         const std::filesystem::path path(entryName.parent_path());
1292         const std::filesystem::path leaf(entryName.filename());
1293         if (!leaf.empty()) {
1294             if (entryName == CLOCK_FONT_ZIP_NAME) {
1295                 FileMap* map = zip->createEntryFileMap(entry);
1296                 if (map) {
1297                     animation.clockFont.map = map;
1298                 }
1299                 continue;
1300             }
1301 
1302             if (entryName == PROGRESS_FONT_ZIP_NAME) {
1303                 FileMap* map = zip->createEntryFileMap(entry);
1304                 if (map) {
1305                     animation.progressFont.map = map;
1306                 }
1307                 continue;
1308             }
1309 
1310             for (size_t partIdx = 0; partIdx < numParts; partIdx++) {
1311                 if (path.string() == animation.parts[partIdx].path.c_str()) {
1312                     uint16_t method;
1313                     // supports only stored png files
1314                     if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr,
1315                             nullptr, nullptr)) {
1316                         if (method == ZipFileRO::kCompressStored) {
1317                             FileMap* map = zip->createEntryFileMap(entry);
1318                             if (map) {
1319                                 Animation::Part& part(animation.parts.editItemAt(partIdx));
1320                                 if (leaf == "audio.wav") {
1321                                     // a part may have at most one audio file
1322                                     part.audioData = (uint8_t *)map->getDataPtr();
1323                                     part.audioLength = map->getDataLength();
1324                                 } else if (leaf == "trim.txt") {
1325                                     part.trimData = String8((char const*)map->getDataPtr(),
1326                                                         map->getDataLength());
1327                                 } else {
1328                                     Animation::Frame frame;
1329                                     frame.name = leaf.c_str();
1330                                     frame.map = map;
1331                                     frame.trimWidth = animation.width;
1332                                     frame.trimHeight = animation.height;
1333                                     frame.trimX = 0;
1334                                     frame.trimY = 0;
1335                                     part.frames.add(frame);
1336                                 }
1337                             }
1338                         } else {
1339                             SLOGE("bootanimation.zip is compressed; must be only stored");
1340                         }
1341                     }
1342                 }
1343             }
1344         }
1345     }
1346 
1347     // If there is trimData present, override the positioning defaults.
1348     for (Animation::Part& part : animation.parts) {
1349         const char* trimDataStr = part.trimData.c_str();
1350         const size_t numFramesInPart = part.frames.size();
1351         for (size_t frameIdxInPart = 0; frameIdxInPart < numFramesInPart; frameIdxInPart++) {
1352             const char* endl = strstr(trimDataStr, "\n");
1353             // No more trimData for this part.
1354             if (endl == nullptr) {
1355                 break;
1356             }
1357             String8 line(trimDataStr, endl - trimDataStr);
1358             const char* lineStr = line.c_str();
1359             trimDataStr = ++endl;
1360             int width = 0, height = 0, x = 0, y = 0;
1361             if (sscanf(lineStr, "%dx%d+%d+%d", &width, &height, &x, &y) == 4) {
1362                 Animation::Frame& frame(part.frames.editItemAt(frameIdxInPart));
1363                 frame.trimWidth = width;
1364                 frame.trimHeight = height;
1365                 frame.trimX = x;
1366                 frame.trimY = y;
1367             } else {
1368                 SLOGE("Error parsing trim.txt, line: %s", lineStr);
1369                 break;
1370             }
1371         }
1372     }
1373 
1374     zip->endIteration(cookie);
1375 
1376     return true;
1377 }
1378 
movie()1379 bool BootAnimation::movie() {
1380     ATRACE_CALL();
1381     if (mAnimation == nullptr) {
1382         mAnimation = loadAnimation(mZipFileName);
1383     }
1384 
1385     if (mAnimation == nullptr)
1386         return false;
1387 
1388     // mCallbacks->init() may get called recursively,
1389     // this loop is needed to get the same results
1390     for (const Animation::Part& part : mAnimation->parts) {
1391         if (part.animation != nullptr) {
1392             mCallbacks->init(part.animation->parts);
1393         }
1394     }
1395     mCallbacks->init(mAnimation->parts);
1396 
1397     bool anyPartHasClock = false;
1398     for (size_t i=0; i < mAnimation->parts.size(); i++) {
1399         if(validClock(mAnimation->parts[i])) {
1400             anyPartHasClock = true;
1401             break;
1402         }
1403     }
1404     if (!anyPartHasClock) {
1405         mClockEnabled = false;
1406     } else if (!android::base::GetBoolProperty(CLOCK_ENABLED_PROP_NAME, false)) {
1407         mClockEnabled = false;
1408     }
1409 
1410     // Check if npot textures are supported
1411     mUseNpotTextures = false;
1412     String8 gl_extensions;
1413     const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
1414     if (!exts) {
1415         glGetError();
1416     } else {
1417         gl_extensions = exts;
1418         if ((gl_extensions.find("GL_ARB_texture_non_power_of_two") != -1) ||
1419             (gl_extensions.find("GL_OES_texture_npot") != -1)) {
1420             mUseNpotTextures = true;
1421         }
1422     }
1423 
1424     // Blend required to draw time on top of animation frames.
1425     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1426     glDisable(GL_DITHER);
1427     glDisable(GL_SCISSOR_TEST);
1428     glDisable(GL_BLEND);
1429 
1430     glEnable(GL_TEXTURE_2D);
1431     glBindTexture(GL_TEXTURE_2D, 0);
1432     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1433     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1434     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1435     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1436     bool clockFontInitialized = false;
1437     if (mClockEnabled) {
1438         clockFontInitialized =
1439             (initFont(&mAnimation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
1440         mClockEnabled = clockFontInitialized;
1441     }
1442 
1443     initFont(&mAnimation->progressFont, PROGRESS_FONT_ASSET);
1444 
1445     if (mClockEnabled && !updateIsTimeAccurate()) {
1446         mTimeCheckThread = new TimeCheckThread(this);
1447         mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
1448     }
1449 
1450     if (mAnimation->dynamicColoringEnabled) {
1451         initDynamicColors();
1452     }
1453 
1454     playAnimation(*mAnimation);
1455 
1456     if (mTimeCheckThread != nullptr) {
1457         mTimeCheckThread->requestExit();
1458         mTimeCheckThread = nullptr;
1459     }
1460 
1461     if (clockFontInitialized) {
1462         glDeleteTextures(1, &mAnimation->clockFont.texture.name);
1463     }
1464 
1465     releaseAnimation(mAnimation);
1466     mAnimation = nullptr;
1467 
1468     return false;
1469 }
1470 
shouldStopPlayingPart(const Animation::Part & part,const int fadedFramesCount,const int lastDisplayedProgress)1471 bool BootAnimation::shouldStopPlayingPart(const Animation::Part& part,
1472                                           const int fadedFramesCount,
1473                                           const int lastDisplayedProgress) {
1474     ATRACE_CALL();
1475     // stop playing only if it is time to exit and it's a partial part which has been faded out
1476     return exitPending() && !part.playUntilComplete && fadedFramesCount >= part.framesToFadeCount &&
1477         (lastDisplayedProgress == 0 || lastDisplayedProgress == 100);
1478 }
1479 
1480 // Linear mapping from range <a1, a2> to range <b1, b2>
mapLinear(float x,float a1,float a2,float b1,float b2)1481 float mapLinear(float x, float a1, float a2, float b1, float b2) {
1482     return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
1483 }
1484 
drawTexturedQuad(float xStart,float yStart,float width,float height,const Display & display)1485 void BootAnimation::drawTexturedQuad(float xStart, float yStart,
1486                                      float width, float height,
1487                                      const Display& display) {
1488     ATRACE_CALL();
1489     // Map coordinates from screen space to world space.
1490     float x0 = mapLinear(xStart, 0, display.width, -1, 1);
1491     float y0 = mapLinear(yStart, 0, display.height, -1, 1);
1492     float x1 = mapLinear(xStart + width, 0, display.width, -1, 1);
1493     float y1 = mapLinear(yStart + height, 0, display.height, -1, 1);
1494     // Update quad vertex positions.
1495     quadPositions[0] = x0;
1496     quadPositions[1] = y0;
1497     quadPositions[2] = x1;
1498     quadPositions[3] = y0;
1499     quadPositions[4] = x1;
1500     quadPositions[5] = y1;
1501     quadPositions[6] = x1;
1502     quadPositions[7] = y1;
1503     quadPositions[8] = x0;
1504     quadPositions[9] = y1;
1505     quadPositions[10] = x0;
1506     quadPositions[11] = y0;
1507     glDrawArrays(GL_TRIANGLES, 0,
1508         sizeof(quadPositions) / sizeof(quadPositions[0]) / 2);
1509 }
1510 
initDynamicColors()1511 void BootAnimation::initDynamicColors() {
1512     ATRACE_CALL();
1513     for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) {
1514         const auto syspropName = "persist.bootanim.color" + std::to_string(i + 1);
1515         const auto syspropValue = android::base::GetProperty(syspropName, "");
1516         if (syspropValue != "") {
1517             SLOGI("Loaded dynamic color: %s -> %s", syspropName.c_str(), syspropValue.c_str());
1518             mDynamicColorsApplied = true;
1519         }
1520         parseColorDecimalString(syspropValue,
1521             mAnimation->endColors[i], mAnimation->startColors[i]);
1522     }
1523     glUseProgram(mImageShader);
1524     SLOGI("Dynamically coloring boot animation. Sysprops loaded? %i", mDynamicColorsApplied);
1525     for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) {
1526         float *startColor = mAnimation->startColors[i];
1527         float *endColor = mAnimation->endColors[i];
1528         glUniform3f(glGetUniformLocation(mImageShader,
1529             (U_START_COLOR_PREFIX + std::to_string(i)).c_str()),
1530             startColor[0], startColor[1], startColor[2]);
1531         glUniform3f(glGetUniformLocation(mImageShader,
1532             (U_END_COLOR_PREFIX + std::to_string(i)).c_str()),
1533             endColor[0], endColor[1], endColor[2]);
1534     }
1535     mImageColorProgressLocation = glGetUniformLocation(mImageShader, U_COLOR_PROGRESS);
1536 }
1537 
playAnimation(const Animation & animation)1538 bool BootAnimation::playAnimation(const Animation& animation) {
1539     ATRACE_CALL();
1540     const size_t numParts = animation.parts.size();
1541     nsecs_t frameDuration = s2ns(1) / animation.fps;
1542 
1543     SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
1544             elapsedRealtime());
1545 
1546     int fadedFramesCount = 0;
1547     int lastDisplayedProgress = 0;
1548     int colorTransitionStart = animation.colorTransitionStart;
1549     int colorTransitionEnd = animation.colorTransitionEnd;
1550     for (size_t partIdx = 0; partIdx < numParts; partIdx++) {
1551         const Animation::Part& part(animation.parts[partIdx]);
1552         const size_t numFramesInPart = part.frames.size();
1553         glBindTexture(GL_TEXTURE_2D, 0);
1554 
1555         // Handle animation package
1556         if (part.animation != nullptr) {
1557             playAnimation(*part.animation);
1558             if (exitPending())
1559                 break;
1560             continue; //to next part
1561         }
1562 
1563         // process the part not only while the count allows but also if already fading
1564         for (int frameIdx = 0;
1565              !part.count || frameIdx < part.count || fadedFramesCount > 0;
1566              frameIdx++) {
1567             if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
1568 
1569             // It's possible that the sysprops were not loaded yet at this boot phase.
1570             // If that's the case, then we should keep trying until they are available.
1571             if (animation.dynamicColoringEnabled && !mDynamicColorsApplied
1572                 && (part.useDynamicColoring || part.postDynamicColoring)) {
1573                 SLOGD("Trying to load dynamic color sysprops.");
1574                 initDynamicColors();
1575                 if (mDynamicColorsApplied) {
1576                     // Sysprops were loaded. Next step is to adjust the animation if we loaded
1577                     // the colors after the animation should have started.
1578                     const int transitionLength = colorTransitionEnd - colorTransitionStart;
1579                     if (part.postDynamicColoring) {
1580                         colorTransitionStart = 0;
1581                         colorTransitionEnd = fmin(transitionLength, numFramesInPart - 1);
1582                     }
1583                 }
1584             }
1585 
1586             mCallbacks->playPart(partIdx, part, frameIdx);
1587 
1588             glClearColor(
1589                     part.backgroundColor[0],
1590                     part.backgroundColor[1],
1591                     part.backgroundColor[2],
1592                     1.0f);
1593 
1594             ALOGD("Playing files = %s/%s, Requested repeat = %d, playUntilComplete = %s",
1595                     animation.fileName.c_str(), part.path.c_str(), part.count,
1596                     part.playUntilComplete ? "true" : "false");
1597 
1598             // For the last animation, if we have progress indicator from
1599             // the system, display it.
1600             const bool displayProgress = animation.progressEnabled && (partIdx == (numParts - 1)) &&
1601                     android::base::GetIntProperty(PROGRESS_PROP_NAME, 0) != 0;
1602 
1603             for (size_t frameIdxInPart = 0; frameIdxInPart < numFramesInPart; frameIdxInPart++) {
1604                 if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
1605 
1606                 // Color progress is
1607                 // - the animation progress, normalized from
1608                 //   [colorTransitionStart,colorTransitionEnd] to [0, 1] for the dynamic coloring
1609                 //   part.
1610                 // - 0 for parts that come before,
1611                 // - 1 for parts that come after.
1612                 float colorProgress = part.useDynamicColoring
1613                     ? fmin(fmax(
1614                         (static_cast<float>(frameIdxInPart) - colorTransitionStart) /
1615                             fmax(colorTransitionEnd - colorTransitionStart, 1.0f), 0.0f), 1.0f)
1616                     : (part.postDynamicColoring ? 1 : 0);
1617                 processDisplayEvents();
1618 
1619                 const Animation::Frame& frame(part.frames[frameIdxInPart]);
1620                 nsecs_t lastFrame = systemTime();
1621 
1622                 if (frameIdx > 0) {
1623                     glBindTexture(GL_TEXTURE_2D, frame.tid);
1624                 } else {
1625                     if (part.count != 1) {
1626                         glGenTextures(1, &frame.tid);
1627                         glBindTexture(GL_TEXTURE_2D, frame.tid);
1628                     }
1629                     int w, h;
1630                     // Set decoding option to alpha unpremultiplied so that the R, G, B channels
1631                     // of transparent pixels are preserved.
1632                     initTexture(frame.map, &w, &h, false /* don't premultiply alpha */);
1633                 }
1634 
1635                 float fade = 0;
1636                 // if the part hasn't been stopped yet then continue fading if necessary
1637                 if (exitPending() && part.hasFadingPhase()) {
1638                     fade = static_cast<float>(++fadedFramesCount) / part.framesToFadeCount;
1639                     if (fadedFramesCount >= part.framesToFadeCount) {
1640                         fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading
1641                     }
1642                 }
1643 
1644                 // Draw the current frame's texture on every physical display that is enabled.
1645                 for (const auto& display : mDisplays) {
1646                     eglMakeCurrent(mEgl, display.eglSurface, display.eglSurface, mEglContext);
1647 
1648                     const double ratioW =
1649                             static_cast<double>(display.width) / display.initWidth;
1650                     const double ratioH =
1651                             static_cast<double>(display.height) / display.initHeight;
1652                     const int animationX = (display.width - animation.width * ratioW) / 2;
1653                     const int animationY = (display.height - animation.height * ratioH) / 2;
1654 
1655                     const int trimWidth = frame.trimWidth * ratioW;
1656                     const int trimHeight = frame.trimHeight * ratioH;
1657                     const int trimX = frame.trimX * ratioW;
1658                     const int trimY = frame.trimY * ratioH;
1659                     const int xc = animationX + trimX;
1660                     const int yc = animationY + trimY;
1661                     projectSceneToWindow(display);
1662                     handleViewport(frameDuration, display);
1663                     glClear(GL_COLOR_BUFFER_BIT);
1664                     // specify the y center as ceiling((height - frame.trimHeight) / 2)
1665                     // which is equivalent to height - (yc + frame.trimHeight)
1666                     const int frameDrawY = display.height - (yc + trimHeight);
1667 
1668                     glUseProgram(mImageShader);
1669                     glUniform1i(mImageTextureLocation, 0);
1670                     glUniform1f(mImageFadeLocation, fade);
1671                     if (animation.dynamicColoringEnabled) {
1672                         glUniform1f(mImageColorProgressLocation, colorProgress);
1673                     }
1674                     glEnable(GL_BLEND);
1675                     drawTexturedQuad(xc, frameDrawY, trimWidth, trimHeight, display);
1676                     glDisable(GL_BLEND);
1677 
1678                     if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
1679                         drawClock(animation.clockFont, part.clockPosX, part.clockPosY, display);
1680                     }
1681 
1682                     if (displayProgress) {
1683                         int newProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
1684                         // In case the new progress jumped suddenly, still show an
1685                         // increment of 1.
1686                         if (lastDisplayedProgress != 100) {
1687                           // Artificially sleep 1/10th a second to slow down the animation.
1688                           usleep(100000);
1689                           if (lastDisplayedProgress < newProgress) {
1690                             lastDisplayedProgress++;
1691                           }
1692                         }
1693                         // Put the progress percentage right below the animation.
1694                         int posY = animation.height / 3;
1695                         int posX = TEXT_CENTER_VALUE;
1696                         drawProgress(lastDisplayedProgress,
1697                             animation.progressFont, posX, posY, display);
1698                     }
1699 
1700                     eglSwapBuffers(mEgl, display.eglSurface);
1701                 }
1702 
1703 
1704                 nsecs_t now = systemTime();
1705                 nsecs_t delay = frameDuration - (now - lastFrame);
1706                 //SLOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
1707                 lastFrame = now;
1708 
1709                 if (delay > 0) {
1710                     struct timespec spec;
1711                     spec.tv_sec  = (now + delay) / 1000000000;
1712                     spec.tv_nsec = (now + delay) % 1000000000;
1713                     int err;
1714                     do {
1715                         err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, nullptr);
1716                     } while (err == EINTR);
1717                 }
1718 
1719                 checkExit();
1720             }
1721 
1722             int pauseDuration = part.pause * ns2us(frameDuration);
1723             while(pauseDuration > 0 && !exitPending()){
1724                 if (pauseDuration > MAX_CHECK_EXIT_INTERVAL_US) {
1725                     usleep(MAX_CHECK_EXIT_INTERVAL_US);
1726                     pauseDuration -= MAX_CHECK_EXIT_INTERVAL_US;
1727                 } else {
1728                     usleep(pauseDuration);
1729                     break;
1730                 }
1731                 checkExit();
1732             }
1733 
1734             if (exitPending() && !part.count && mCurrentInset >= mTargetInset &&
1735                 !part.hasFadingPhase()) {
1736                 if (lastDisplayedProgress != 0 && lastDisplayedProgress != 100) {
1737                     android::base::SetProperty(PROGRESS_PROP_NAME, "100");
1738                     continue;
1739                 }
1740                 break; // exit the infinite non-fading part when it has been played at least once
1741             }
1742         }
1743     }
1744 
1745     // Free textures created for looping parts now that the animation is done.
1746     for (const Animation::Part& part : animation.parts) {
1747         if (part.count != 1) {
1748             for (const auto& frame : part.frames) {
1749                 glDeleteTextures(1, &frame.tid);
1750             }
1751         }
1752     }
1753 
1754     ALOGD("%sAnimationShownTiming End time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
1755             elapsedRealtime());
1756 
1757     return true;
1758 }
1759 
processDisplayEvents()1760 void BootAnimation::processDisplayEvents() {
1761     ATRACE_CALL();
1762     // This will poll mDisplayEventReceiver and if there are new events it'll call
1763     // displayEventCallback synchronously.
1764     mLooper->pollOnce(0);
1765 }
1766 
handleViewport(nsecs_t timestep,const Display & display)1767 void BootAnimation::handleViewport(nsecs_t timestep, const Display& display) {
1768     ATRACE_CALL();
1769     if (mShuttingDown || !display.surfaceControl || mTargetInset == 0) {
1770         return;
1771     }
1772     if (mTargetInset < 0) {
1773         // Poll the amount for the top display inset. This will return -1 until persistent properties
1774         // have been loaded.
1775         mTargetInset =
1776                 android::base::GetIntProperty("persist.sys.displayinset.top", -1 /* default */,
1777                                               -1 /* min */, display.height / 2 /* max */);
1778     }
1779     if (mTargetInset <= 0) {
1780         return;
1781     }
1782 
1783     if (mCurrentInset < mTargetInset) {
1784         // After the device boots, the inset will effectively be cropped away. We animate this here.
1785         float fraction = static_cast<float>(mCurrentInset) / mTargetInset;
1786         int interpolatedInset = (cosf((fraction + 1) * M_PI) / 2.0f + 0.5f) * mTargetInset;
1787 
1788         SurfaceComposerClient::Transaction()
1789                 .setCrop(display.surfaceControl,
1790                          Rect(0, interpolatedInset, display.width, display.height))
1791                 .apply();
1792     } else {
1793         // At the end of the animation, we switch to the viewport that DisplayManager will apply
1794         // later. This changes the coordinate system, and means we must move the surface up by
1795         // the inset amount.
1796         Rect layerStackRect(0, 0,
1797                 display.width,
1798                 display.height - mTargetInset);
1799         Rect displayRect(0, mTargetInset,
1800                 display.width,
1801                 display.height);
1802         SurfaceComposerClient::Transaction t;
1803         t.setPosition(display.surfaceControl, 0, -mTargetInset)
1804                 .setCrop(display.surfaceControl,
1805                          Rect(0, mTargetInset,
1806                               display.width,
1807                               display.height));
1808         t.setDisplayProjection(display.displayToken,
1809                 ui::ROTATION_0, layerStackRect, displayRect);
1810         t.apply();
1811 
1812         mTargetInset = mCurrentInset = 0;
1813     }
1814 
1815     int delta = timestep * mTargetInset / ms2ns(200);
1816     mCurrentInset += delta;
1817 }
1818 
releaseAnimation(Animation * animation) const1819 void BootAnimation::releaseAnimation(Animation* animation) const {
1820     ATRACE_CALL();
1821     for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
1822          e = animation->parts.end(); it != e; ++it) {
1823         if (it->animation)
1824             releaseAnimation(it->animation);
1825     }
1826     if (animation->zip)
1827         delete animation->zip;
1828     delete animation;
1829 }
1830 
loadAnimation(const String8 & fn)1831 BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) {
1832     ATRACE_CALL();
1833     if (mLoadedFiles.indexOf(fn) >= 0) {
1834         SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
1835             fn.c_str());
1836         return nullptr;
1837     }
1838     ZipFileRO *zip = ZipFileRO::open(fn.c_str());
1839     if (zip == nullptr) {
1840         SLOGE("Failed to open animation zip \"%s\": %s",
1841             fn.c_str(), strerror(errno));
1842         return nullptr;
1843     }
1844 
1845     ALOGD("%s is loaded successfully", fn.c_str());
1846 
1847     Animation *animation =  new Animation;
1848     animation->fileName = fn;
1849     animation->zip = zip;
1850     animation->clockFont.map = nullptr;
1851     mLoadedFiles.add(animation->fileName);
1852 
1853     parseAnimationDesc(*animation);
1854     if (!preloadZip(*animation)) {
1855         releaseAnimation(animation);
1856         return nullptr;
1857     }
1858 
1859     mLoadedFiles.remove(fn);
1860     return animation;
1861 }
1862 
updateIsTimeAccurate()1863 bool BootAnimation::updateIsTimeAccurate() {
1864     ATRACE_CALL();
1865     static constexpr long long MAX_TIME_IN_PAST =   60000LL * 60LL * 24LL * 30LL;  // 30 days
1866     static constexpr long long MAX_TIME_IN_FUTURE = 60000LL * 90LL;  // 90 minutes
1867 
1868     if (mTimeIsAccurate) {
1869         return true;
1870     }
1871     if (mShuttingDown) return true;
1872     struct stat statResult;
1873 
1874     if(stat(TIME_FORMAT_12_HOUR_FLAG_FILE_PATH, &statResult) == 0) {
1875         mTimeFormat12Hour = true;
1876     }
1877 
1878     if(stat(ACCURATE_TIME_FLAG_FILE_PATH, &statResult) == 0) {
1879         mTimeIsAccurate = true;
1880         return true;
1881     }
1882 
1883     FILE* file = fopen(LAST_TIME_CHANGED_FILE_PATH, "r");
1884     if (file != nullptr) {
1885       long long lastChangedTime = 0;
1886       fscanf(file, "%lld", &lastChangedTime);
1887       fclose(file);
1888       if (lastChangedTime > 0) {
1889         struct timespec now;
1890         clock_gettime(CLOCK_REALTIME, &now);
1891         // Match the Java timestamp format
1892         long long rtcNow = (now.tv_sec * 1000LL) + (now.tv_nsec / 1000000LL);
1893         if (ACCURATE_TIME_EPOCH < rtcNow
1894             && lastChangedTime > (rtcNow - MAX_TIME_IN_PAST)
1895             && lastChangedTime < (rtcNow + MAX_TIME_IN_FUTURE)) {
1896             mTimeIsAccurate = true;
1897         }
1898       }
1899     }
1900 
1901     return mTimeIsAccurate;
1902 }
1903 
TimeCheckThread(BootAnimation * bootAnimation)1904 BootAnimation::TimeCheckThread::TimeCheckThread(BootAnimation* bootAnimation) : Thread(false),
1905     mInotifyFd(-1), mBootAnimWd(-1), mTimeWd(-1), mBootAnimation(bootAnimation) {}
1906 
~TimeCheckThread()1907 BootAnimation::TimeCheckThread::~TimeCheckThread() {
1908     ATRACE_CALL();
1909     // mInotifyFd may be -1 but that's ok since we're not at risk of attempting to close a valid FD.
1910     close(mInotifyFd);
1911 }
1912 
threadLoop()1913 bool BootAnimation::TimeCheckThread::threadLoop() {
1914     ATRACE_CALL();
1915     bool shouldLoop = doThreadLoop() && !mBootAnimation->mTimeIsAccurate
1916         && mBootAnimation->mClockEnabled;
1917     if (!shouldLoop) {
1918         close(mInotifyFd);
1919         mInotifyFd = -1;
1920     }
1921     return shouldLoop;
1922 }
1923 
doThreadLoop()1924 bool BootAnimation::TimeCheckThread::doThreadLoop() {
1925     ATRACE_CALL();
1926     static constexpr int BUFF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1));
1927 
1928     // Poll instead of doing a blocking read so the Thread can exit if requested.
1929     struct pollfd pfd = { mInotifyFd, POLLIN, 0 };
1930     ssize_t pollResult = poll(&pfd, 1, 1000);
1931 
1932     if (pollResult == 0) {
1933         return true;
1934     } else if (pollResult < 0) {
1935         SLOGE("Could not poll inotify events");
1936         return false;
1937     }
1938 
1939     char buff[BUFF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event))));;
1940     ssize_t length = read(mInotifyFd, buff, BUFF_LEN);
1941     if (length == 0) {
1942         return true;
1943     } else if (length < 0) {
1944         SLOGE("Could not read inotify events");
1945         return false;
1946     }
1947 
1948     const struct inotify_event *event;
1949     for (char* ptr = buff; ptr < buff + length; ptr += sizeof(struct inotify_event) + event->len) {
1950         event = (const struct inotify_event *) ptr;
1951         if (event->wd == mBootAnimWd && strcmp(BOOTANIM_TIME_DIR_NAME, event->name) == 0) {
1952             addTimeDirWatch();
1953         } else if (event->wd == mTimeWd && (strcmp(LAST_TIME_CHANGED_FILE_NAME, event->name) == 0
1954                 || strcmp(ACCURATE_TIME_FLAG_FILE_NAME, event->name) == 0)) {
1955             return !mBootAnimation->updateIsTimeAccurate();
1956         }
1957     }
1958 
1959     return true;
1960 }
1961 
addTimeDirWatch()1962 void BootAnimation::TimeCheckThread::addTimeDirWatch() {
1963         ATRACE_CALL();
1964         mTimeWd = inotify_add_watch(mInotifyFd, BOOTANIM_TIME_DIR_PATH,
1965                 IN_CLOSE_WRITE | IN_MOVED_TO | IN_ATTRIB);
1966         if (mTimeWd > 0) {
1967             // No need to watch for the time directory to be created if it already exists
1968             inotify_rm_watch(mInotifyFd, mBootAnimWd);
1969             mBootAnimWd = -1;
1970         }
1971 }
1972 
readyToRun()1973 status_t BootAnimation::TimeCheckThread::readyToRun() {
1974     ATRACE_CALL();
1975     mInotifyFd = inotify_init();
1976     if (mInotifyFd < 0) {
1977         SLOGE("Could not initialize inotify fd");
1978         return NO_INIT;
1979     }
1980 
1981     mBootAnimWd = inotify_add_watch(mInotifyFd, BOOTANIM_DATA_DIR_PATH, IN_CREATE | IN_ATTRIB);
1982     if (mBootAnimWd < 0) {
1983         close(mInotifyFd);
1984         mInotifyFd = -1;
1985         SLOGE("Could not add watch for %s: %s", BOOTANIM_DATA_DIR_PATH, strerror(errno));
1986         return NO_INIT;
1987     }
1988 
1989     addTimeDirWatch();
1990 
1991     if (mBootAnimation->updateIsTimeAccurate()) {
1992         close(mInotifyFd);
1993         mInotifyFd = -1;
1994         return ALREADY_EXISTS;
1995     }
1996 
1997     return NO_ERROR;
1998 }
1999 
2000 // ---------------------------------------------------------------------------
2001 
2002 } // namespace android
2003