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