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