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