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 #define LOG_TAG "BootAnimation"
19 
20 #include <vector>
21 
22 #include <stdint.h>
23 #include <inttypes.h>
24 #include <sys/inotify.h>
25 #include <sys/poll.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <math.h>
29 #include <fcntl.h>
30 #include <utils/misc.h>
31 #include <signal.h>
32 #include <time.h>
33 
34 #include <cutils/atomic.h>
35 #include <cutils/properties.h>
36 
37 #include <androidfw/AssetManager.h>
38 #include <binder/IPCThreadState.h>
39 #include <utils/Errors.h>
40 #include <utils/Log.h>
41 #include <utils/SystemClock.h>
42 
43 #include <android-base/properties.h>
44 
45 #include <ui/PixelFormat.h>
46 #include <ui/Rect.h>
47 #include <ui/Region.h>
48 #include <ui/DisplayInfo.h>
49 
50 #include <gui/ISurfaceComposer.h>
51 #include <gui/Surface.h>
52 #include <gui/SurfaceComposerClient.h>
53 
54 // TODO: Fix Skia.
55 #pragma GCC diagnostic push
56 #pragma GCC diagnostic ignored "-Wunused-parameter"
57 #include <SkBitmap.h>
58 #include <SkImage.h>
59 #include <SkStream.h>
60 #pragma GCC diagnostic pop
61 
62 #include <GLES/gl.h>
63 #include <GLES/glext.h>
64 #include <EGL/eglext.h>
65 
66 #include "BootAnimation.h"
67 
68 #define ANIM_PATH_MAX 255
69 #define STR(x)   #x
70 #define STRTO(x) STR(x)
71 
72 namespace android {
73 
74 static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
75 static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "/product/media/bootanimation-dark.zip";
76 static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
77 static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
78 static const char APEX_BOOTANIMATION_FILE[] = "/apex/com.android.bootanimation/etc/bootanimation.zip";
79 static const char PRODUCT_ENCRYPTED_BOOTANIMATION_FILE[] = "/product/media/bootanimation-encrypted.zip";
80 static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.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 SYSTEM_DATA_DIR_PATH[] = "/data/system";
90 static const char SYSTEM_TIME_DIR_NAME[] = "time";
91 static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/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 LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change";
95 static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change";
96 static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
97 static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/system/time/time_is_accurate";
98 static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/system/time/time_format_12_hour";
99 // Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00.
100 static const long long ACCURATE_TIME_EPOCH = 946684800000;
101 static constexpr char FONT_BEGIN_CHAR = ' ';
102 static constexpr char FONT_END_CHAR = '~' + 1;
103 static constexpr size_t FONT_NUM_CHARS = FONT_END_CHAR - FONT_BEGIN_CHAR + 1;
104 static constexpr size_t FONT_NUM_COLS = 16;
105 static constexpr size_t FONT_NUM_ROWS = FONT_NUM_CHARS / FONT_NUM_COLS;
106 static const int TEXT_CENTER_VALUE = INT_MAX;
107 static const int TEXT_MISSING_VALUE = INT_MIN;
108 static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
109 static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
110 static constexpr size_t TEXT_POS_LEN_MAX = 16;
111 
112 // ---------------------------------------------------------------------------
113 
BootAnimation(sp<Callbacks> callbacks)114 BootAnimation::BootAnimation(sp<Callbacks> callbacks)
115         : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
116         mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
117     mSession = new SurfaceComposerClient();
118 
119     std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
120     if (powerCtl.empty()) {
121         mShuttingDown = false;
122     } else {
123         mShuttingDown = true;
124     }
125     ALOGD("%sAnimationStartTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
126             elapsedRealtime());
127 }
128 
~BootAnimation()129 BootAnimation::~BootAnimation() {
130     if (mAnimation != nullptr) {
131         releaseAnimation(mAnimation);
132         mAnimation = nullptr;
133     }
134     ALOGD("%sAnimationStopTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
135             elapsedRealtime());
136 }
137 
onFirstRef()138 void BootAnimation::onFirstRef() {
139     status_t err = mSession->linkToComposerDeath(this);
140     SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
141     if (err == NO_ERROR) {
142         // Load the animation content -- this can be slow (eg 200ms)
143         // called before waitForSurfaceFlinger() in main() to avoid wait
144         ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms",
145                 mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
146         preloadAnimation();
147         ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms",
148                 mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
149     }
150 }
151 
session() const152 sp<SurfaceComposerClient> BootAnimation::session() const {
153     return mSession;
154 }
155 
binderDied(const wp<IBinder> &)156 void BootAnimation::binderDied(const wp<IBinder>&)
157 {
158     // woah, surfaceflinger died!
159     SLOGD("SurfaceFlinger died, exiting...");
160 
161     // calling requestExit() is not enough here because the Surface code
162     // might be blocked on a condition variable that will never be updated.
163     kill( getpid(), SIGKILL );
164     requestExit();
165 }
166 
initTexture(Texture * texture,AssetManager & assets,const char * name)167 status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
168         const char* name) {
169     Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);
170     if (asset == nullptr)
171         return NO_INIT;
172     SkBitmap bitmap;
173     sk_sp<SkData> data = SkData::MakeWithoutCopy(asset->getBuffer(false),
174             asset->getLength());
175     sk_sp<SkImage> image = SkImage::MakeFromEncoded(data);
176     image->asLegacyBitmap(&bitmap, SkImage::kRO_LegacyBitmapMode);
177     asset->close();
178     delete asset;
179 
180     const int w = bitmap.width();
181     const int h = bitmap.height();
182     const void* p = bitmap.getPixels();
183 
184     GLint crop[4] = { 0, h, w, -h };
185     texture->w = w;
186     texture->h = h;
187 
188     glGenTextures(1, &texture->name);
189     glBindTexture(GL_TEXTURE_2D, texture->name);
190 
191     switch (bitmap.colorType()) {
192         case kAlpha_8_SkColorType:
193             glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA,
194                     GL_UNSIGNED_BYTE, p);
195             break;
196         case kARGB_4444_SkColorType:
197             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
198                     GL_UNSIGNED_SHORT_4_4_4_4, p);
199             break;
200         case kN32_SkColorType:
201             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
202                     GL_UNSIGNED_BYTE, p);
203             break;
204         case kRGB_565_SkColorType:
205             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
206                     GL_UNSIGNED_SHORT_5_6_5, p);
207             break;
208         default:
209             break;
210     }
211 
212     glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
213     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
214     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
215     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
216     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
217 
218     return NO_ERROR;
219 }
220 
initTexture(FileMap * map,int * width,int * height)221 status_t BootAnimation::initTexture(FileMap* map, int* width, int* height)
222 {
223     SkBitmap bitmap;
224     sk_sp<SkData> data = SkData::MakeWithoutCopy(map->getDataPtr(),
225             map->getDataLength());
226     sk_sp<SkImage> image = SkImage::MakeFromEncoded(data);
227     image->asLegacyBitmap(&bitmap, SkImage::kRO_LegacyBitmapMode);
228 
229     // FileMap memory is never released until application exit.
230     // Release it now as the texture is already loaded and the memory used for
231     // the packed resource can be released.
232     delete map;
233 
234     const int w = bitmap.width();
235     const int h = bitmap.height();
236     const void* p = bitmap.getPixels();
237 
238     GLint crop[4] = { 0, h, w, -h };
239     int tw = 1 << (31 - __builtin_clz(w));
240     int th = 1 << (31 - __builtin_clz(h));
241     if (tw < w) tw <<= 1;
242     if (th < h) th <<= 1;
243 
244     switch (bitmap.colorType()) {
245         case kN32_SkColorType:
246             if (!mUseNpotTextures && (tw != w || th != h)) {
247                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
248                         GL_UNSIGNED_BYTE, nullptr);
249                 glTexSubImage2D(GL_TEXTURE_2D, 0,
250                         0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p);
251             } else {
252                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
253                         GL_UNSIGNED_BYTE, p);
254             }
255             break;
256 
257         case kRGB_565_SkColorType:
258             if (!mUseNpotTextures && (tw != w || th != h)) {
259                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
260                         GL_UNSIGNED_SHORT_5_6_5, nullptr);
261                 glTexSubImage2D(GL_TEXTURE_2D, 0,
262                         0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p);
263             } else {
264                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
265                         GL_UNSIGNED_SHORT_5_6_5, p);
266             }
267             break;
268         default:
269             break;
270     }
271 
272     glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
273 
274     *width = w;
275     *height = h;
276 
277     return NO_ERROR;
278 }
279 
readyToRun()280 status_t BootAnimation::readyToRun() {
281     mAssets.addDefaultAssets();
282 
283     mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
284     if (mDisplayToken == nullptr)
285         return -1;
286 
287     DisplayInfo dinfo;
288     status_t status = SurfaceComposerClient::getDisplayInfo(mDisplayToken, &dinfo);
289     if (status)
290         return -1;
291 
292     // create the native surface
293     sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
294             dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
295 
296     SurfaceComposerClient::Transaction t;
297     t.setLayer(control, 0x40000000)
298         .apply();
299 
300     sp<Surface> s = control->getSurface();
301 
302     // initialize opengl and egl
303     const EGLint attribs[] = {
304             EGL_RED_SIZE,   8,
305             EGL_GREEN_SIZE, 8,
306             EGL_BLUE_SIZE,  8,
307             EGL_DEPTH_SIZE, 0,
308             EGL_NONE
309     };
310     EGLint w, h;
311     EGLint numConfigs;
312     EGLConfig config;
313     EGLSurface surface;
314     EGLContext context;
315 
316     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
317 
318     eglInitialize(display, nullptr, nullptr);
319     eglChooseConfig(display, attribs, &config, 1, &numConfigs);
320     surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
321     context = eglCreateContext(display, config, nullptr, nullptr);
322     eglQuerySurface(display, surface, EGL_WIDTH, &w);
323     eglQuerySurface(display, surface, EGL_HEIGHT, &h);
324 
325     if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
326         return NO_INIT;
327 
328     mDisplay = display;
329     mContext = context;
330     mSurface = surface;
331     mWidth = w;
332     mHeight = h;
333     mFlingerSurfaceControl = control;
334     mFlingerSurface = s;
335     mTargetInset = -1;
336 
337     return NO_ERROR;
338 }
339 
preloadAnimation()340 bool BootAnimation::preloadAnimation() {
341     findBootAnimationFile();
342     if (!mZipFileName.isEmpty()) {
343         mAnimation = loadAnimation(mZipFileName);
344         return (mAnimation != nullptr);
345     }
346 
347     return false;
348 }
349 
findBootAnimationFileInternal(const std::vector<std::string> & files)350 bool BootAnimation::findBootAnimationFileInternal(const std::vector<std::string> &files) {
351     for (const std::string& f : files) {
352         if (access(f.c_str(), R_OK) == 0) {
353             mZipFileName = f.c_str();
354             return true;
355         }
356     }
357     return false;
358 }
359 
findBootAnimationFile()360 void BootAnimation::findBootAnimationFile() {
361     // If the device has encryption turned on or is in process
362     // of being encrypted we show the encrypted boot animation.
363     char decrypt[PROPERTY_VALUE_MAX];
364     property_get("vold.decrypt", decrypt, "");
365 
366     bool encryptedAnimation = atoi(decrypt) != 0 ||
367         !strcmp("trigger_restart_min_framework", decrypt);
368 
369     if (!mShuttingDown && encryptedAnimation) {
370         static const std::vector<std::string> encryptedBootFiles = {
371             PRODUCT_ENCRYPTED_BOOTANIMATION_FILE, SYSTEM_ENCRYPTED_BOOTANIMATION_FILE,
372         };
373         if (findBootAnimationFileInternal(encryptedBootFiles)) {
374             return;
375         }
376     }
377 
378     const bool playDarkAnim = android::base::GetIntProperty("ro.boot.theme", 0) == 1;
379     static const std::vector<std::string> bootFiles = {
380         APEX_BOOTANIMATION_FILE, playDarkAnim ? PRODUCT_BOOTANIMATION_DARK_FILE : PRODUCT_BOOTANIMATION_FILE,
381         OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE
382     };
383     static const std::vector<std::string> shutdownFiles = {
384         PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE, ""
385     };
386     static const std::vector<std::string> userspaceRebootFiles = {
387         PRODUCT_USERSPACE_REBOOT_ANIMATION_FILE, OEM_USERSPACE_REBOOT_ANIMATION_FILE,
388         SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE,
389     };
390 
391     if (android::base::GetBoolProperty("sys.init.userspace_reboot.in_progress", false)) {
392         findBootAnimationFileInternal(userspaceRebootFiles);
393     } else if (mShuttingDown) {
394         findBootAnimationFileInternal(shutdownFiles);
395     } else {
396         findBootAnimationFileInternal(bootFiles);
397     }
398 }
399 
threadLoop()400 bool BootAnimation::threadLoop()
401 {
402     bool r;
403     // We have no bootanimation file, so we use the stock android logo
404     // animation.
405     if (mZipFileName.isEmpty()) {
406         r = android();
407     } else {
408         r = movie();
409     }
410 
411     eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
412     eglDestroyContext(mDisplay, mContext);
413     eglDestroySurface(mDisplay, mSurface);
414     mFlingerSurface.clear();
415     mFlingerSurfaceControl.clear();
416     eglTerminate(mDisplay);
417     eglReleaseThread();
418     IPCThreadState::self()->stopProcess();
419     return r;
420 }
421 
android()422 bool BootAnimation::android()
423 {
424     SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
425             elapsedRealtime());
426     initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
427     initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
428 
429     mCallbacks->init({});
430 
431     // clear screen
432     glShadeModel(GL_FLAT);
433     glDisable(GL_DITHER);
434     glDisable(GL_SCISSOR_TEST);
435     glClearColor(0,0,0,1);
436     glClear(GL_COLOR_BUFFER_BIT);
437     eglSwapBuffers(mDisplay, mSurface);
438 
439     glEnable(GL_TEXTURE_2D);
440     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
441 
442     const GLint xc = (mWidth  - mAndroid[0].w) / 2;
443     const GLint yc = (mHeight - mAndroid[0].h) / 2;
444     const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);
445 
446     glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
447             updateRect.height());
448 
449     // Blend state
450     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
451     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
452 
453     const nsecs_t startTime = systemTime();
454     do {
455         nsecs_t now = systemTime();
456         double time = now - startTime;
457         float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
458         GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
459         GLint x = xc - offset;
460 
461         glDisable(GL_SCISSOR_TEST);
462         glClear(GL_COLOR_BUFFER_BIT);
463 
464         glEnable(GL_SCISSOR_TEST);
465         glDisable(GL_BLEND);
466         glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
467         glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);
468         glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);
469 
470         glEnable(GL_BLEND);
471         glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
472         glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
473 
474         EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
475         if (res == EGL_FALSE)
476             break;
477 
478         // 12fps: don't animate too fast to preserve CPU
479         const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
480         if (sleepTime > 0)
481             usleep(sleepTime);
482 
483         checkExit();
484     } while (!exitPending());
485 
486     glDeleteTextures(1, &mAndroid[0].name);
487     glDeleteTextures(1, &mAndroid[1].name);
488     return false;
489 }
490 
checkExit()491 void BootAnimation::checkExit() {
492     // Allow surface flinger to gracefully request shutdown
493     char value[PROPERTY_VALUE_MAX];
494     property_get(EXIT_PROP_NAME, value, "0");
495     int exitnow = atoi(value);
496     if (exitnow) {
497         requestExit();
498         mCallbacks->shutdown();
499     }
500 }
501 
validClock(const Animation::Part & part)502 bool BootAnimation::validClock(const Animation::Part& part) {
503     return part.clockPosX != TEXT_MISSING_VALUE && part.clockPosY != TEXT_MISSING_VALUE;
504 }
505 
parseTextCoord(const char * str,int * dest)506 bool parseTextCoord(const char* str, int* dest) {
507     if (strcmp("c", str) == 0) {
508         *dest = TEXT_CENTER_VALUE;
509         return true;
510     }
511 
512     char* end;
513     int val = (int) strtol(str, &end, 0);
514     if (end == str || *end != '\0' || val == INT_MAX || val == INT_MIN) {
515         return false;
516     }
517     *dest = val;
518     return true;
519 }
520 
521 // 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)522 void parsePosition(const char* str1, const char* str2, int* x, int* y) {
523     bool success = false;
524     if (strlen(str1) == 0) {  // No values were specified
525         // success = false
526     } else if (strlen(str2) == 0) {  // we have only one value
527         if (parseTextCoord(str1, y)) {
528             *x = TEXT_CENTER_VALUE;
529             success = true;
530         }
531     } else {
532         if (parseTextCoord(str1, x) && parseTextCoord(str2, y)) {
533             success = true;
534         }
535     }
536 
537     if (!success) {
538         *x = TEXT_MISSING_VALUE;
539         *y = TEXT_MISSING_VALUE;
540     }
541 }
542 
543 // Parse a color represented as an HTML-style 'RRGGBB' string: each pair of
544 // characters in str is a hex number in [0, 255], which are converted to
545 // floating point values in the range [0.0, 1.0] and placed in the
546 // corresponding elements of color.
547 //
548 // If the input string isn't valid, parseColor returns false and color is
549 // left unchanged.
parseColor(const char str[7],float color[3])550 static bool parseColor(const char str[7], float color[3]) {
551     float tmpColor[3];
552     for (int i = 0; i < 3; i++) {
553         int val = 0;
554         for (int j = 0; j < 2; j++) {
555             val *= 16;
556             char c = str[2*i + j];
557             if      (c >= '0' && c <= '9') val += c - '0';
558             else if (c >= 'A' && c <= 'F') val += (c - 'A') + 10;
559             else if (c >= 'a' && c <= 'f') val += (c - 'a') + 10;
560             else                           return false;
561         }
562         tmpColor[i] = static_cast<float>(val) / 255.0f;
563     }
564     memcpy(color, tmpColor, sizeof(tmpColor));
565     return true;
566 }
567 
568 
readFile(ZipFileRO * zip,const char * name,String8 & outString)569 static bool readFile(ZipFileRO* zip, const char* name, String8& outString)
570 {
571     ZipEntryRO entry = zip->findEntryByName(name);
572     SLOGE_IF(!entry, "couldn't find %s", name);
573     if (!entry) {
574         return false;
575     }
576 
577     FileMap* entryMap = zip->createEntryFileMap(entry);
578     zip->releaseEntry(entry);
579     SLOGE_IF(!entryMap, "entryMap is null");
580     if (!entryMap) {
581         return false;
582     }
583 
584     outString.setTo((char const*)entryMap->getDataPtr(), entryMap->getDataLength());
585     delete entryMap;
586     return true;
587 }
588 
589 // The font image should be a 96x2 array of character images.  The
590 // columns are the printable ASCII characters 0x20 - 0x7f.  The
591 // top row is regular text; the bottom row is bold.
initFont(Font * font,const char * fallback)592 status_t BootAnimation::initFont(Font* font, const char* fallback) {
593     status_t status = NO_ERROR;
594 
595     if (font->map != nullptr) {
596         glGenTextures(1, &font->texture.name);
597         glBindTexture(GL_TEXTURE_2D, font->texture.name);
598 
599         status = initTexture(font->map, &font->texture.w, &font->texture.h);
600 
601         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
602         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
603         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
604         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
605     } else if (fallback != nullptr) {
606         status = initTexture(&font->texture, mAssets, fallback);
607     } else {
608         return NO_INIT;
609     }
610 
611     if (status == NO_ERROR) {
612         font->char_width = font->texture.w / FONT_NUM_COLS;
613         font->char_height = font->texture.h / FONT_NUM_ROWS / 2;  // There are bold and regular rows
614     }
615 
616     return status;
617 }
618 
drawText(const char * str,const Font & font,bool bold,int * x,int * y)619 void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) {
620     glEnable(GL_BLEND);  // Allow us to draw on top of the animation
621     glBindTexture(GL_TEXTURE_2D, font.texture.name);
622 
623     const int len = strlen(str);
624     const int strWidth = font.char_width * len;
625 
626     if (*x == TEXT_CENTER_VALUE) {
627         *x = (mWidth - strWidth) / 2;
628     } else if (*x < 0) {
629         *x = mWidth + *x - strWidth;
630     }
631     if (*y == TEXT_CENTER_VALUE) {
632         *y = (mHeight - font.char_height) / 2;
633     } else if (*y < 0) {
634         *y = mHeight + *y - font.char_height;
635     }
636 
637     int cropRect[4] = { 0, 0, font.char_width, -font.char_height };
638 
639     for (int i = 0; i < len; i++) {
640         char c = str[i];
641 
642         if (c < FONT_BEGIN_CHAR || c > FONT_END_CHAR) {
643             c = '?';
644         }
645 
646         // Crop the texture to only the pixels in the current glyph
647         const int charPos = (c - FONT_BEGIN_CHAR);  // Position in the list of valid characters
648         const int row = charPos / FONT_NUM_COLS;
649         const int col = charPos % FONT_NUM_COLS;
650         cropRect[0] = col * font.char_width;  // Left of column
651         cropRect[1] = row * font.char_height * 2; // Top of row
652         // Move down to bottom of regular (one char_heigh) or bold (two char_heigh) line
653         cropRect[1] += bold ? 2 * font.char_height : font.char_height;
654         glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);
655 
656         glDrawTexiOES(*x, *y, 0, font.char_width, font.char_height);
657 
658         *x += font.char_width;
659     }
660 
661     glDisable(GL_BLEND);  // Return to the animation's default behaviour
662     glBindTexture(GL_TEXTURE_2D, 0);
663 }
664 
665 // We render 12 or 24 hour time.
drawClock(const Font & font,const int xPos,const int yPos)666 void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos) {
667     static constexpr char TIME_FORMAT_12[] = "%l:%M";
668     static constexpr char TIME_FORMAT_24[] = "%H:%M";
669     static constexpr int TIME_LENGTH = 6;
670 
671     time_t rawtime;
672     time(&rawtime);
673     struct tm* timeInfo = localtime(&rawtime);
674 
675     char timeBuff[TIME_LENGTH];
676     const char* timeFormat = mTimeFormat12Hour ? TIME_FORMAT_12 : TIME_FORMAT_24;
677     size_t length = strftime(timeBuff, TIME_LENGTH, timeFormat, timeInfo);
678 
679     if (length != TIME_LENGTH - 1) {
680         SLOGE("Couldn't format time; abandoning boot animation clock");
681         mClockEnabled = false;
682         return;
683     }
684 
685     char* out = timeBuff[0] == ' ' ? &timeBuff[1] : &timeBuff[0];
686     int x = xPos;
687     int y = yPos;
688     drawText(out, font, false, &x, &y);
689 }
690 
parseAnimationDesc(Animation & animation)691 bool BootAnimation::parseAnimationDesc(Animation& animation)
692 {
693     String8 desString;
694 
695     if (!readFile(animation.zip, "desc.txt", desString)) {
696         return false;
697     }
698     char const* s = desString.string();
699 
700     // Parse the description file
701     for (;;) {
702         const char* endl = strstr(s, "\n");
703         if (endl == nullptr) break;
704         String8 line(s, endl - s);
705         const char* l = line.string();
706         int fps = 0;
707         int width = 0;
708         int height = 0;
709         int count = 0;
710         int pause = 0;
711         char path[ANIM_ENTRY_NAME_MAX];
712         char color[7] = "000000"; // default to black if unspecified
713         char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
714         char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
715 
716         char pathType;
717         if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
718             // SLOGD("> w=%d, h=%d, fps=%d", width, height, fps);
719             animation.width = width;
720             animation.height = height;
721             animation.fps = fps;
722         } else if (sscanf(l, " %c %d %d %" STRTO(ANIM_PATH_MAX) "s #%6s %16s %16s",
723                           &pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) {
724             //SLOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s",
725             //    pathType, count, pause, path, color, clockPos1, clockPos2);
726             Animation::Part part;
727             part.playUntilComplete = pathType == 'c';
728             part.count = count;
729             part.pause = pause;
730             part.path = path;
731             part.audioData = nullptr;
732             part.animation = nullptr;
733             if (!parseColor(color, part.backgroundColor)) {
734                 SLOGE("> invalid color '#%s'", color);
735                 part.backgroundColor[0] = 0.0f;
736                 part.backgroundColor[1] = 0.0f;
737                 part.backgroundColor[2] = 0.0f;
738             }
739             parsePosition(clockPos1, clockPos2, &part.clockPosX, &part.clockPosY);
740             animation.parts.add(part);
741         }
742         else if (strcmp(l, "$SYSTEM") == 0) {
743             // SLOGD("> SYSTEM");
744             Animation::Part part;
745             part.playUntilComplete = false;
746             part.count = 1;
747             part.pause = 0;
748             part.audioData = nullptr;
749             part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
750             if (part.animation != nullptr)
751                 animation.parts.add(part);
752         }
753         s = ++endl;
754     }
755 
756     return true;
757 }
758 
preloadZip(Animation & animation)759 bool BootAnimation::preloadZip(Animation& animation)
760 {
761     // read all the data structures
762     const size_t pcount = animation.parts.size();
763     void *cookie = nullptr;
764     ZipFileRO* zip = animation.zip;
765     if (!zip->startIteration(&cookie)) {
766         return false;
767     }
768 
769     ZipEntryRO entry;
770     char name[ANIM_ENTRY_NAME_MAX];
771     while ((entry = zip->nextEntry(cookie)) != nullptr) {
772         const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
773         if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
774             SLOGE("Error fetching entry file name");
775             continue;
776         }
777 
778         const String8 entryName(name);
779         const String8 path(entryName.getPathDir());
780         const String8 leaf(entryName.getPathLeaf());
781         if (leaf.size() > 0) {
782             if (entryName == CLOCK_FONT_ZIP_NAME) {
783                 FileMap* map = zip->createEntryFileMap(entry);
784                 if (map) {
785                     animation.clockFont.map = map;
786                 }
787                 continue;
788             }
789 
790             for (size_t j = 0; j < pcount; j++) {
791                 if (path == animation.parts[j].path) {
792                     uint16_t method;
793                     // supports only stored png files
794                     if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr, nullptr)) {
795                         if (method == ZipFileRO::kCompressStored) {
796                             FileMap* map = zip->createEntryFileMap(entry);
797                             if (map) {
798                                 Animation::Part& part(animation.parts.editItemAt(j));
799                                 if (leaf == "audio.wav") {
800                                     // a part may have at most one audio file
801                                     part.audioData = (uint8_t *)map->getDataPtr();
802                                     part.audioLength = map->getDataLength();
803                                 } else if (leaf == "trim.txt") {
804                                     part.trimData.setTo((char const*)map->getDataPtr(),
805                                                         map->getDataLength());
806                                 } else {
807                                     Animation::Frame frame;
808                                     frame.name = leaf;
809                                     frame.map = map;
810                                     frame.trimWidth = animation.width;
811                                     frame.trimHeight = animation.height;
812                                     frame.trimX = 0;
813                                     frame.trimY = 0;
814                                     part.frames.add(frame);
815                                 }
816                             }
817                         } else {
818                             SLOGE("bootanimation.zip is compressed; must be only stored");
819                         }
820                     }
821                 }
822             }
823         }
824     }
825 
826     // If there is trimData present, override the positioning defaults.
827     for (Animation::Part& part : animation.parts) {
828         const char* trimDataStr = part.trimData.string();
829         for (size_t frameIdx = 0; frameIdx < part.frames.size(); frameIdx++) {
830             const char* endl = strstr(trimDataStr, "\n");
831             // No more trimData for this part.
832             if (endl == nullptr) {
833                 break;
834             }
835             String8 line(trimDataStr, endl - trimDataStr);
836             const char* lineStr = line.string();
837             trimDataStr = ++endl;
838             int width = 0, height = 0, x = 0, y = 0;
839             if (sscanf(lineStr, "%dx%d+%d+%d", &width, &height, &x, &y) == 4) {
840                 Animation::Frame& frame(part.frames.editItemAt(frameIdx));
841                 frame.trimWidth = width;
842                 frame.trimHeight = height;
843                 frame.trimX = x;
844                 frame.trimY = y;
845             } else {
846                 SLOGE("Error parsing trim.txt, line: %s", lineStr);
847                 break;
848             }
849         }
850     }
851 
852     zip->endIteration(cookie);
853 
854     return true;
855 }
856 
movie()857 bool BootAnimation::movie()
858 {
859     if (mAnimation == nullptr) {
860         mAnimation = loadAnimation(mZipFileName);
861     }
862 
863     if (mAnimation == nullptr)
864         return false;
865 
866     // mCallbacks->init() may get called recursively,
867     // this loop is needed to get the same results
868     for (const Animation::Part& part : mAnimation->parts) {
869         if (part.animation != nullptr) {
870             mCallbacks->init(part.animation->parts);
871         }
872     }
873     mCallbacks->init(mAnimation->parts);
874 
875     bool anyPartHasClock = false;
876     for (size_t i=0; i < mAnimation->parts.size(); i++) {
877         if(validClock(mAnimation->parts[i])) {
878             anyPartHasClock = true;
879             break;
880         }
881     }
882     if (!anyPartHasClock) {
883         mClockEnabled = false;
884     }
885 
886     // Check if npot textures are supported
887     mUseNpotTextures = false;
888     String8 gl_extensions;
889     const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
890     if (!exts) {
891         glGetError();
892     } else {
893         gl_extensions.setTo(exts);
894         if ((gl_extensions.find("GL_ARB_texture_non_power_of_two") != -1) ||
895             (gl_extensions.find("GL_OES_texture_npot") != -1)) {
896             mUseNpotTextures = true;
897         }
898     }
899 
900     // Blend required to draw time on top of animation frames.
901     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
902     glShadeModel(GL_FLAT);
903     glDisable(GL_DITHER);
904     glDisable(GL_SCISSOR_TEST);
905     glDisable(GL_BLEND);
906 
907     glBindTexture(GL_TEXTURE_2D, 0);
908     glEnable(GL_TEXTURE_2D);
909     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
910     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
911     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
912     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
913     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
914 
915     bool clockFontInitialized = false;
916     if (mClockEnabled) {
917         clockFontInitialized =
918             (initFont(&mAnimation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
919         mClockEnabled = clockFontInitialized;
920     }
921 
922     if (mClockEnabled && !updateIsTimeAccurate()) {
923         mTimeCheckThread = new TimeCheckThread(this);
924         mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
925     }
926 
927     playAnimation(*mAnimation);
928 
929     if (mTimeCheckThread != nullptr) {
930         mTimeCheckThread->requestExit();
931         mTimeCheckThread = nullptr;
932     }
933 
934     if (clockFontInitialized) {
935         glDeleteTextures(1, &mAnimation->clockFont.texture.name);
936     }
937 
938     releaseAnimation(mAnimation);
939     mAnimation = nullptr;
940 
941     return false;
942 }
943 
playAnimation(const Animation & animation)944 bool BootAnimation::playAnimation(const Animation& animation)
945 {
946     const size_t pcount = animation.parts.size();
947     nsecs_t frameDuration = s2ns(1) / animation.fps;
948     const int animationX = (mWidth - animation.width) / 2;
949     const int animationY = (mHeight - animation.height) / 2;
950 
951     SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
952             elapsedRealtime());
953     for (size_t i=0 ; i<pcount ; i++) {
954         const Animation::Part& part(animation.parts[i]);
955         const size_t fcount = part.frames.size();
956         glBindTexture(GL_TEXTURE_2D, 0);
957 
958         // Handle animation package
959         if (part.animation != nullptr) {
960             playAnimation(*part.animation);
961             if (exitPending())
962                 break;
963             continue; //to next part
964         }
965 
966         for (int r=0 ; !part.count || r<part.count ; r++) {
967             // Exit any non playuntil complete parts immediately
968             if(exitPending() && !part.playUntilComplete)
969                 break;
970 
971             mCallbacks->playPart(i, part, r);
972 
973             glClearColor(
974                     part.backgroundColor[0],
975                     part.backgroundColor[1],
976                     part.backgroundColor[2],
977                     1.0f);
978 
979             for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
980                 const Animation::Frame& frame(part.frames[j]);
981                 nsecs_t lastFrame = systemTime();
982 
983                 if (r > 0) {
984                     glBindTexture(GL_TEXTURE_2D, frame.tid);
985                 } else {
986                     if (part.count != 1) {
987                         glGenTextures(1, &frame.tid);
988                         glBindTexture(GL_TEXTURE_2D, frame.tid);
989                         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
990                         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
991                     }
992                     int w, h;
993                     initTexture(frame.map, &w, &h);
994                 }
995 
996                 const int xc = animationX + frame.trimX;
997                 const int yc = animationY + frame.trimY;
998                 Region clearReg(Rect(mWidth, mHeight));
999                 clearReg.subtractSelf(Rect(xc, yc, xc+frame.trimWidth, yc+frame.trimHeight));
1000                 if (!clearReg.isEmpty()) {
1001                     Region::const_iterator head(clearReg.begin());
1002                     Region::const_iterator tail(clearReg.end());
1003                     glEnable(GL_SCISSOR_TEST);
1004                     while (head != tail) {
1005                         const Rect& r2(*head++);
1006                         glScissor(r2.left, mHeight - r2.bottom, r2.width(), r2.height());
1007                         glClear(GL_COLOR_BUFFER_BIT);
1008                     }
1009                     glDisable(GL_SCISSOR_TEST);
1010                 }
1011                 // specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
1012                 // which is equivalent to mHeight - (yc + frame.trimHeight)
1013                 glDrawTexiOES(xc, mHeight - (yc + frame.trimHeight),
1014                               0, frame.trimWidth, frame.trimHeight);
1015                 if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
1016                     drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
1017                 }
1018                 handleViewport(frameDuration);
1019 
1020                 eglSwapBuffers(mDisplay, mSurface);
1021 
1022                 nsecs_t now = systemTime();
1023                 nsecs_t delay = frameDuration - (now - lastFrame);
1024                 //SLOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
1025                 lastFrame = now;
1026 
1027                 if (delay > 0) {
1028                     struct timespec spec;
1029                     spec.tv_sec  = (now + delay) / 1000000000;
1030                     spec.tv_nsec = (now + delay) % 1000000000;
1031                     int err;
1032                     do {
1033                         err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, nullptr);
1034                     } while (err<0 && errno == EINTR);
1035                 }
1036 
1037                 checkExit();
1038             }
1039 
1040             usleep(part.pause * ns2us(frameDuration));
1041 
1042             // For infinite parts, we've now played them at least once, so perhaps exit
1043             if(exitPending() && !part.count && mCurrentInset >= mTargetInset)
1044                 break;
1045         }
1046 
1047     }
1048 
1049     // Free textures created for looping parts now that the animation is done.
1050     for (const Animation::Part& part : animation.parts) {
1051         if (part.count != 1) {
1052             const size_t fcount = part.frames.size();
1053             for (size_t j = 0; j < fcount; j++) {
1054                 const Animation::Frame& frame(part.frames[j]);
1055                 glDeleteTextures(1, &frame.tid);
1056             }
1057         }
1058     }
1059 
1060     return true;
1061 }
1062 
handleViewport(nsecs_t timestep)1063 void BootAnimation::handleViewport(nsecs_t timestep) {
1064     if (mShuttingDown || !mFlingerSurfaceControl || mTargetInset == 0) {
1065         return;
1066     }
1067     if (mTargetInset < 0) {
1068         // Poll the amount for the top display inset. This will return -1 until persistent properties
1069         // have been loaded.
1070         mTargetInset = android::base::GetIntProperty("persist.sys.displayinset.top",
1071                 -1 /* default */, -1 /* min */, mHeight / 2 /* max */);
1072     }
1073     if (mTargetInset <= 0) {
1074         return;
1075     }
1076 
1077     if (mCurrentInset < mTargetInset) {
1078         // After the device boots, the inset will effectively be cropped away. We animate this here.
1079         float fraction = static_cast<float>(mCurrentInset) / mTargetInset;
1080         int interpolatedInset = (cosf((fraction + 1) * M_PI) / 2.0f + 0.5f) * mTargetInset;
1081 
1082         SurfaceComposerClient::Transaction()
1083                 .setCrop(mFlingerSurfaceControl, Rect(0, interpolatedInset, mWidth, mHeight))
1084                 .apply();
1085     } else {
1086         // At the end of the animation, we switch to the viewport that DisplayManager will apply
1087         // later. This changes the coordinate system, and means we must move the surface up by
1088         // the inset amount.
1089         Rect layerStackRect(0, 0, mWidth, mHeight - mTargetInset);
1090         Rect displayRect(0, mTargetInset, mWidth, mHeight);
1091 
1092         SurfaceComposerClient::Transaction t;
1093         t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset)
1094                 .setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight));
1095         t.setDisplayProjection(mDisplayToken, 0 /* orientation */, layerStackRect, displayRect);
1096         t.apply();
1097 
1098         mTargetInset = mCurrentInset = 0;
1099     }
1100 
1101     int delta = timestep * mTargetInset / ms2ns(200);
1102     mCurrentInset += delta;
1103 }
1104 
releaseAnimation(Animation * animation) const1105 void BootAnimation::releaseAnimation(Animation* animation) const
1106 {
1107     for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
1108          e = animation->parts.end(); it != e; ++it) {
1109         if (it->animation)
1110             releaseAnimation(it->animation);
1111     }
1112     if (animation->zip)
1113         delete animation->zip;
1114     delete animation;
1115 }
1116 
loadAnimation(const String8 & fn)1117 BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
1118 {
1119     if (mLoadedFiles.indexOf(fn) >= 0) {
1120         SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
1121             fn.string());
1122         return nullptr;
1123     }
1124     ZipFileRO *zip = ZipFileRO::open(fn);
1125     if (zip == nullptr) {
1126         SLOGE("Failed to open animation zip \"%s\": %s",
1127             fn.string(), strerror(errno));
1128         return nullptr;
1129     }
1130 
1131     Animation *animation =  new Animation;
1132     animation->fileName = fn;
1133     animation->zip = zip;
1134     animation->clockFont.map = nullptr;
1135     mLoadedFiles.add(animation->fileName);
1136 
1137     parseAnimationDesc(*animation);
1138     if (!preloadZip(*animation)) {
1139         return nullptr;
1140     }
1141 
1142 
1143     mLoadedFiles.remove(fn);
1144     return animation;
1145 }
1146 
updateIsTimeAccurate()1147 bool BootAnimation::updateIsTimeAccurate() {
1148     static constexpr long long MAX_TIME_IN_PAST =   60000LL * 60LL * 24LL * 30LL;  // 30 days
1149     static constexpr long long MAX_TIME_IN_FUTURE = 60000LL * 90LL;  // 90 minutes
1150 
1151     if (mTimeIsAccurate) {
1152         return true;
1153     }
1154     if (mShuttingDown) return true;
1155     struct stat statResult;
1156 
1157     if(stat(TIME_FORMAT_12_HOUR_FLAG_FILE_PATH, &statResult) == 0) {
1158         mTimeFormat12Hour = true;
1159     }
1160 
1161     if(stat(ACCURATE_TIME_FLAG_FILE_PATH, &statResult) == 0) {
1162         mTimeIsAccurate = true;
1163         return true;
1164     }
1165 
1166     FILE* file = fopen(LAST_TIME_CHANGED_FILE_PATH, "r");
1167     if (file != nullptr) {
1168       long long lastChangedTime = 0;
1169       fscanf(file, "%lld", &lastChangedTime);
1170       fclose(file);
1171       if (lastChangedTime > 0) {
1172         struct timespec now;
1173         clock_gettime(CLOCK_REALTIME, &now);
1174         // Match the Java timestamp format
1175         long long rtcNow = (now.tv_sec * 1000LL) + (now.tv_nsec / 1000000LL);
1176         if (ACCURATE_TIME_EPOCH < rtcNow
1177             && lastChangedTime > (rtcNow - MAX_TIME_IN_PAST)
1178             && lastChangedTime < (rtcNow + MAX_TIME_IN_FUTURE)) {
1179             mTimeIsAccurate = true;
1180         }
1181       }
1182     }
1183 
1184     return mTimeIsAccurate;
1185 }
1186 
TimeCheckThread(BootAnimation * bootAnimation)1187 BootAnimation::TimeCheckThread::TimeCheckThread(BootAnimation* bootAnimation) : Thread(false),
1188     mInotifyFd(-1), mSystemWd(-1), mTimeWd(-1), mBootAnimation(bootAnimation) {}
1189 
~TimeCheckThread()1190 BootAnimation::TimeCheckThread::~TimeCheckThread() {
1191     // mInotifyFd may be -1 but that's ok since we're not at risk of attempting to close a valid FD.
1192     close(mInotifyFd);
1193 }
1194 
threadLoop()1195 bool BootAnimation::TimeCheckThread::threadLoop() {
1196     bool shouldLoop = doThreadLoop() && !mBootAnimation->mTimeIsAccurate
1197         && mBootAnimation->mClockEnabled;
1198     if (!shouldLoop) {
1199         close(mInotifyFd);
1200         mInotifyFd = -1;
1201     }
1202     return shouldLoop;
1203 }
1204 
doThreadLoop()1205 bool BootAnimation::TimeCheckThread::doThreadLoop() {
1206     static constexpr int BUFF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1));
1207 
1208     // Poll instead of doing a blocking read so the Thread can exit if requested.
1209     struct pollfd pfd = { mInotifyFd, POLLIN, 0 };
1210     ssize_t pollResult = poll(&pfd, 1, 1000);
1211 
1212     if (pollResult == 0) {
1213         return true;
1214     } else if (pollResult < 0) {
1215         SLOGE("Could not poll inotify events");
1216         return false;
1217     }
1218 
1219     char buff[BUFF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event))));;
1220     ssize_t length = read(mInotifyFd, buff, BUFF_LEN);
1221     if (length == 0) {
1222         return true;
1223     } else if (length < 0) {
1224         SLOGE("Could not read inotify events");
1225         return false;
1226     }
1227 
1228     const struct inotify_event *event;
1229     for (char* ptr = buff; ptr < buff + length; ptr += sizeof(struct inotify_event) + event->len) {
1230         event = (const struct inotify_event *) ptr;
1231         if (event->wd == mSystemWd && strcmp(SYSTEM_TIME_DIR_NAME, event->name) == 0) {
1232             addTimeDirWatch();
1233         } else if (event->wd == mTimeWd && (strcmp(LAST_TIME_CHANGED_FILE_NAME, event->name) == 0
1234                 || strcmp(ACCURATE_TIME_FLAG_FILE_NAME, event->name) == 0)) {
1235             return !mBootAnimation->updateIsTimeAccurate();
1236         }
1237     }
1238 
1239     return true;
1240 }
1241 
addTimeDirWatch()1242 void BootAnimation::TimeCheckThread::addTimeDirWatch() {
1243         mTimeWd = inotify_add_watch(mInotifyFd, SYSTEM_TIME_DIR_PATH,
1244                 IN_CLOSE_WRITE | IN_MOVED_TO | IN_ATTRIB);
1245         if (mTimeWd > 0) {
1246             // No need to watch for the time directory to be created if it already exists
1247             inotify_rm_watch(mInotifyFd, mSystemWd);
1248             mSystemWd = -1;
1249         }
1250 }
1251 
readyToRun()1252 status_t BootAnimation::TimeCheckThread::readyToRun() {
1253     mInotifyFd = inotify_init();
1254     if (mInotifyFd < 0) {
1255         SLOGE("Could not initialize inotify fd");
1256         return NO_INIT;
1257     }
1258 
1259     mSystemWd = inotify_add_watch(mInotifyFd, SYSTEM_DATA_DIR_PATH, IN_CREATE | IN_ATTRIB);
1260     if (mSystemWd < 0) {
1261         close(mInotifyFd);
1262         mInotifyFd = -1;
1263         SLOGE("Could not add watch for %s", SYSTEM_DATA_DIR_PATH);
1264         return NO_INIT;
1265     }
1266 
1267     addTimeDirWatch();
1268 
1269     if (mBootAnimation->updateIsTimeAccurate()) {
1270         close(mInotifyFd);
1271         mInotifyFd = -1;
1272         return ALREADY_EXISTS;
1273     }
1274 
1275     return NO_ERROR;
1276 }
1277 
1278 // ---------------------------------------------------------------------------
1279 
1280 }
1281 ; // namespace android
1282