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