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/types.h>
22#include <math.h>
23#include <fcntl.h>
24#include <utils/misc.h>
25#include <signal.h>
26#include <time.h>
27
28#include <cutils/properties.h>
29
30#include <androidfw/AssetManager.h>
31#include <binder/IPCThreadState.h>
32#include <utils/Atomic.h>
33#include <utils/Errors.h>
34#include <utils/Log.h>
35
36#include <ui/PixelFormat.h>
37#include <ui/Rect.h>
38#include <ui/Region.h>
39#include <ui/DisplayInfo.h>
40
41#include <gui/ISurfaceComposer.h>
42#include <gui/Surface.h>
43#include <gui/SurfaceComposerClient.h>
44
45// TODO: Fix Skia.
46#pragma GCC diagnostic push
47#pragma GCC diagnostic ignored "-Wunused-parameter"
48#include <SkBitmap.h>
49#include <SkStream.h>
50#include <SkImageDecoder.h>
51#pragma GCC diagnostic pop
52
53#include <GLES/gl.h>
54#include <GLES/glext.h>
55#include <EGL/eglext.h>
56
57#include "BootAnimation.h"
58#include "AudioPlayer.h"
59
60#define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
61#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
62#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
63#define EXIT_PROP_NAME "service.bootanim.exit"
64
65namespace android {
66
67static const int ANIM_ENTRY_NAME_MAX = 256;
68
69// ---------------------------------------------------------------------------
70
71BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true) {
72    mSession = new SurfaceComposerClient();
73}
74
75BootAnimation::~BootAnimation() {
76}
77
78void BootAnimation::onFirstRef() {
79    status_t err = mSession->linkToComposerDeath(this);
80    ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
81    if (err == NO_ERROR) {
82        run("BootAnimation", PRIORITY_DISPLAY);
83    }
84}
85
86sp<SurfaceComposerClient> BootAnimation::session() const {
87    return mSession;
88}
89
90
91void BootAnimation::binderDied(const wp<IBinder>&)
92{
93    // woah, surfaceflinger died!
94    ALOGD("SurfaceFlinger died, exiting...");
95
96    // calling requestExit() is not enough here because the Surface code
97    // might be blocked on a condition variable that will never be updated.
98    kill( getpid(), SIGKILL );
99    requestExit();
100    if (mAudioPlayer != NULL) {
101        mAudioPlayer->requestExit();
102    }
103}
104
105status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
106        const char* name) {
107    Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);
108    if (asset == NULL)
109        return NO_INIT;
110    SkBitmap bitmap;
111    SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(),
112            &bitmap, kUnknown_SkColorType, SkImageDecoder::kDecodePixels_Mode);
113    asset->close();
114    delete asset;
115
116    // ensure we can call getPixels(). No need to call unlock, since the
117    // bitmap will go out of scope when we return from this method.
118    bitmap.lockPixels();
119
120    const int w = bitmap.width();
121    const int h = bitmap.height();
122    const void* p = bitmap.getPixels();
123
124    GLint crop[4] = { 0, h, w, -h };
125    texture->w = w;
126    texture->h = h;
127
128    glGenTextures(1, &texture->name);
129    glBindTexture(GL_TEXTURE_2D, texture->name);
130
131    switch (bitmap.colorType()) {
132        case kAlpha_8_SkColorType:
133            glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA,
134                    GL_UNSIGNED_BYTE, p);
135            break;
136        case kARGB_4444_SkColorType:
137            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
138                    GL_UNSIGNED_SHORT_4_4_4_4, p);
139            break;
140        case kN32_SkColorType:
141            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
142                    GL_UNSIGNED_BYTE, p);
143            break;
144        case kRGB_565_SkColorType:
145            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
146                    GL_UNSIGNED_SHORT_5_6_5, p);
147            break;
148        default:
149            break;
150    }
151
152    glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
153    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
154    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
155    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
156    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
157    return NO_ERROR;
158}
159
160status_t BootAnimation::initTexture(const Animation::Frame& frame)
161{
162    //StopWatch watch("blah");
163
164    SkBitmap bitmap;
165    SkMemoryStream  stream(frame.map->getDataPtr(), frame.map->getDataLength());
166    SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
167    if (codec != NULL) {
168        codec->setDitherImage(false);
169        codec->decode(&stream, &bitmap,
170                kN32_SkColorType,
171                SkImageDecoder::kDecodePixels_Mode);
172        delete codec;
173    }
174
175    // FileMap memory is never released until application exit.
176    // Release it now as the texture is already loaded and the memory used for
177    // the packed resource can be released.
178    delete frame.map;
179
180    // ensure we can call getPixels(). No need to call unlock, since the
181    // bitmap will go out of scope when we return from this method.
182    bitmap.lockPixels();
183
184    const int w = bitmap.width();
185    const int h = bitmap.height();
186    const void* p = bitmap.getPixels();
187
188    GLint crop[4] = { 0, h, w, -h };
189    int tw = 1 << (31 - __builtin_clz(w));
190    int th = 1 << (31 - __builtin_clz(h));
191    if (tw < w) tw <<= 1;
192    if (th < h) th <<= 1;
193
194    switch (bitmap.colorType()) {
195        case kN32_SkColorType:
196            if (tw != w || th != h) {
197                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
198                        GL_UNSIGNED_BYTE, 0);
199                glTexSubImage2D(GL_TEXTURE_2D, 0,
200                        0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p);
201            } else {
202                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
203                        GL_UNSIGNED_BYTE, p);
204            }
205            break;
206
207        case kRGB_565_SkColorType:
208            if (tw != w || th != h) {
209                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
210                        GL_UNSIGNED_SHORT_5_6_5, 0);
211                glTexSubImage2D(GL_TEXTURE_2D, 0,
212                        0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p);
213            } else {
214                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
215                        GL_UNSIGNED_SHORT_5_6_5, p);
216            }
217            break;
218        default:
219            break;
220    }
221
222    glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
223
224    return NO_ERROR;
225}
226
227status_t BootAnimation::readyToRun() {
228    mAssets.addDefaultAssets();
229
230    sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
231            ISurfaceComposer::eDisplayIdMain));
232    DisplayInfo dinfo;
233    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
234    if (status)
235        return -1;
236
237    // create the native surface
238    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
239            dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
240
241    SurfaceComposerClient::openGlobalTransaction();
242    control->setLayer(0x40000000);
243    SurfaceComposerClient::closeGlobalTransaction();
244
245    sp<Surface> s = control->getSurface();
246
247    // initialize opengl and egl
248    const EGLint attribs[] = {
249            EGL_RED_SIZE,   8,
250            EGL_GREEN_SIZE, 8,
251            EGL_BLUE_SIZE,  8,
252            EGL_DEPTH_SIZE, 0,
253            EGL_NONE
254    };
255    EGLint w, h;
256    EGLint numConfigs;
257    EGLConfig config;
258    EGLSurface surface;
259    EGLContext context;
260
261    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
262
263    eglInitialize(display, 0, 0);
264    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
265    surface = eglCreateWindowSurface(display, config, s.get(), NULL);
266    context = eglCreateContext(display, config, NULL, NULL);
267    eglQuerySurface(display, surface, EGL_WIDTH, &w);
268    eglQuerySurface(display, surface, EGL_HEIGHT, &h);
269
270    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
271        return NO_INIT;
272
273    mDisplay = display;
274    mContext = context;
275    mSurface = surface;
276    mWidth = w;
277    mHeight = h;
278    mFlingerSurfaceControl = control;
279    mFlingerSurface = s;
280
281    // If the device has encryption turned on or is in process
282    // of being encrypted we show the encrypted boot animation.
283    char decrypt[PROPERTY_VALUE_MAX];
284    property_get("vold.decrypt", decrypt, "");
285
286    bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
287
288    if (encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) {
289        mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE;
290    }
291    else if (access(OEM_BOOTANIMATION_FILE, R_OK) == 0) {
292        mZipFileName = OEM_BOOTANIMATION_FILE;
293    }
294    else if (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) {
295        mZipFileName = SYSTEM_BOOTANIMATION_FILE;
296    }
297    return NO_ERROR;
298}
299
300bool BootAnimation::threadLoop()
301{
302    bool r;
303    // We have no bootanimation file, so we use the stock android logo
304    // animation.
305    if (mZipFileName.isEmpty()) {
306        r = android();
307    } else {
308        r = movie();
309    }
310
311    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
312    eglDestroyContext(mDisplay, mContext);
313    eglDestroySurface(mDisplay, mSurface);
314    mFlingerSurface.clear();
315    mFlingerSurfaceControl.clear();
316    eglTerminate(mDisplay);
317    IPCThreadState::self()->stopProcess();
318    return r;
319}
320
321bool BootAnimation::android()
322{
323    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
324    initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
325
326    // clear screen
327    glShadeModel(GL_FLAT);
328    glDisable(GL_DITHER);
329    glDisable(GL_SCISSOR_TEST);
330    glClearColor(0,0,0,1);
331    glClear(GL_COLOR_BUFFER_BIT);
332    eglSwapBuffers(mDisplay, mSurface);
333
334    glEnable(GL_TEXTURE_2D);
335    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
336
337    const GLint xc = (mWidth  - mAndroid[0].w) / 2;
338    const GLint yc = (mHeight - mAndroid[0].h) / 2;
339    const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);
340
341    glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
342            updateRect.height());
343
344    // Blend state
345    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
346    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
347
348    const nsecs_t startTime = systemTime();
349    do {
350        nsecs_t now = systemTime();
351        double time = now - startTime;
352        float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
353        GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
354        GLint x = xc - offset;
355
356        glDisable(GL_SCISSOR_TEST);
357        glClear(GL_COLOR_BUFFER_BIT);
358
359        glEnable(GL_SCISSOR_TEST);
360        glDisable(GL_BLEND);
361        glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
362        glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);
363        glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);
364
365        glEnable(GL_BLEND);
366        glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
367        glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
368
369        EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
370        if (res == EGL_FALSE)
371            break;
372
373        // 12fps: don't animate too fast to preserve CPU
374        const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
375        if (sleepTime > 0)
376            usleep(sleepTime);
377
378        checkExit();
379    } while (!exitPending());
380
381    glDeleteTextures(1, &mAndroid[0].name);
382    glDeleteTextures(1, &mAndroid[1].name);
383    return false;
384}
385
386
387void BootAnimation::checkExit() {
388    // Allow surface flinger to gracefully request shutdown
389    char value[PROPERTY_VALUE_MAX];
390    property_get(EXIT_PROP_NAME, value, "0");
391    int exitnow = atoi(value);
392    if (exitnow) {
393        requestExit();
394        if (mAudioPlayer != NULL) {
395            mAudioPlayer->requestExit();
396        }
397    }
398}
399
400// Parse a color represented as an HTML-style 'RRGGBB' string: each pair of
401// characters in str is a hex number in [0, 255], which are converted to
402// floating point values in the range [0.0, 1.0] and placed in the
403// corresponding elements of color.
404//
405// If the input string isn't valid, parseColor returns false and color is
406// left unchanged.
407static bool parseColor(const char str[7], float color[3]) {
408    float tmpColor[3];
409    for (int i = 0; i < 3; i++) {
410        int val = 0;
411        for (int j = 0; j < 2; j++) {
412            val *= 16;
413            char c = str[2*i + j];
414            if      (c >= '0' && c <= '9') val += c - '0';
415            else if (c >= 'A' && c <= 'F') val += (c - 'A') + 10;
416            else if (c >= 'a' && c <= 'f') val += (c - 'a') + 10;
417            else                           return false;
418        }
419        tmpColor[i] = static_cast<float>(val) / 255.0f;
420    }
421    memcpy(color, tmpColor, sizeof(tmpColor));
422    return true;
423}
424
425
426static bool readFile(ZipFileRO* zip, const char* name, String8& outString)
427{
428    ZipEntryRO entry = zip->findEntryByName(name);
429    ALOGE_IF(!entry, "couldn't find %s", name);
430    if (!entry) {
431        return false;
432    }
433
434    FileMap* entryMap = zip->createEntryFileMap(entry);
435    zip->releaseEntry(entry);
436    ALOGE_IF(!entryMap, "entryMap is null");
437    if (!entryMap) {
438        return false;
439    }
440
441    outString.setTo((char const*)entryMap->getDataPtr(), entryMap->getDataLength());
442    delete entryMap;
443    return true;
444}
445
446// The time glyphs are stored in a single image of height 64 pixels. Each digit is 40 pixels wide,
447// and the colon character is half that at 20 pixels. The glyph order is '0123456789:'.
448// We render 24 hour time.
449void BootAnimation::drawTime(const Texture& clockTex, const int yPos) {
450    static constexpr char TIME_FORMAT[] = "%H:%M";
451    static constexpr int TIME_LENGTH = sizeof(TIME_FORMAT);
452
453    static constexpr int DIGIT_HEIGHT = 64;
454    static constexpr int DIGIT_WIDTH = 40;
455    static constexpr int COLON_WIDTH = DIGIT_WIDTH / 2;
456    static constexpr int TIME_WIDTH = (DIGIT_WIDTH * 4) + COLON_WIDTH;
457
458    if (clockTex.h < DIGIT_HEIGHT || clockTex.w < (10 * DIGIT_WIDTH + COLON_WIDTH)) {
459        ALOGE("Clock texture is too small; abandoning boot animation clock");
460        mClockEnabled = false;
461        return;
462    }
463
464    time_t rawtime;
465    time(&rawtime);
466    struct tm* timeInfo = localtime(&rawtime);
467
468    char timeBuff[TIME_LENGTH];
469    size_t length = strftime(timeBuff, TIME_LENGTH, TIME_FORMAT, timeInfo);
470
471    if (length != TIME_LENGTH - 1) {
472        ALOGE("Couldn't format time; abandoning boot animation clock");
473        mClockEnabled = false;
474        return;
475    }
476
477    glEnable(GL_BLEND);  // Allow us to draw on top of the animation
478    glBindTexture(GL_TEXTURE_2D, clockTex.name);
479
480    int xPos = (mWidth - TIME_WIDTH) / 2;
481    int cropRect[4] = { 0, DIGIT_HEIGHT, DIGIT_WIDTH, -DIGIT_HEIGHT };
482
483    for (int i = 0; i < TIME_LENGTH - 1; i++) {
484        char c = timeBuff[i];
485        int width = DIGIT_WIDTH;
486        int pos = c - '0';  // Position in the character list
487        if (pos < 0 || pos > 10) {
488            continue;
489        }
490        if (c == ':') {
491            width = COLON_WIDTH;
492        }
493
494        // Crop the texture to only the pixels in the current glyph
495        int left = pos * DIGIT_WIDTH;
496        cropRect[0] = left;
497        cropRect[2] = width;
498        glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);
499
500        glDrawTexiOES(xPos, yPos, 0, width, DIGIT_HEIGHT);
501
502        xPos += width;
503    }
504
505    glDisable(GL_BLEND);  // Return to the animation's default behaviour
506    glBindTexture(GL_TEXTURE_2D, 0);
507}
508
509bool BootAnimation::parseAnimationDesc(Animation& animation)
510{
511    String8 desString;
512
513    if (!readFile(animation.zip, "desc.txt", desString)) {
514        return false;
515    }
516    char const* s = desString.string();
517
518    // Create and initialize an AudioPlayer if we have an audio_conf.txt file
519    String8 audioConf;
520    if (readFile(animation.zip, "audio_conf.txt", audioConf)) {
521        mAudioPlayer = new AudioPlayer;
522        if (!mAudioPlayer->init(audioConf.string())) {
523            ALOGE("mAudioPlayer.init failed");
524            mAudioPlayer = NULL;
525        }
526    }
527
528    // Parse the description file
529    for (;;) {
530        const char* endl = strstr(s, "\n");
531        if (endl == NULL) break;
532        String8 line(s, endl - s);
533        const char* l = line.string();
534        int fps = 0;
535        int width = 0;
536        int height = 0;
537        int count = 0;
538        int pause = 0;
539        int clockPosY = -1;
540        char path[ANIM_ENTRY_NAME_MAX];
541        char color[7] = "000000"; // default to black if unspecified
542
543        char pathType;
544        if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
545            // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps);
546            animation.width = width;
547            animation.height = height;
548            animation.fps = fps;
549        } else if (sscanf(l, " %c %d %d %s #%6s %d",
550                          &pathType, &count, &pause, path, color, &clockPosY) >= 4) {
551            // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPosY=%d", pathType, count, pause, path, color, clockPosY);
552            Animation::Part part;
553            part.playUntilComplete = pathType == 'c';
554            part.count = count;
555            part.pause = pause;
556            part.path = path;
557            part.clockPosY = clockPosY;
558            part.audioFile = NULL;
559            part.animation = NULL;
560            if (!parseColor(color, part.backgroundColor)) {
561                ALOGE("> invalid color '#%s'", color);
562                part.backgroundColor[0] = 0.0f;
563                part.backgroundColor[1] = 0.0f;
564                part.backgroundColor[2] = 0.0f;
565            }
566            animation.parts.add(part);
567        }
568        else if (strcmp(l, "$SYSTEM") == 0) {
569            // ALOGD("> SYSTEM");
570            Animation::Part part;
571            part.playUntilComplete = false;
572            part.count = 1;
573            part.pause = 0;
574            part.audioFile = NULL;
575            part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
576            if (part.animation != NULL)
577                animation.parts.add(part);
578        }
579        s = ++endl;
580    }
581
582    return true;
583}
584
585bool BootAnimation::preloadZip(Animation& animation)
586{
587    // read all the data structures
588    const size_t pcount = animation.parts.size();
589    void *cookie = NULL;
590    ZipFileRO* mZip = animation.zip;
591    if (!mZip->startIteration(&cookie)) {
592        return false;
593    }
594
595    ZipEntryRO entry;
596    char name[ANIM_ENTRY_NAME_MAX];
597    while ((entry = mZip->nextEntry(cookie)) != NULL) {
598        const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
599        if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
600            ALOGE("Error fetching entry file name");
601            continue;
602        }
603
604        const String8 entryName(name);
605        const String8 path(entryName.getPathDir());
606        const String8 leaf(entryName.getPathLeaf());
607        if (leaf.size() > 0) {
608            for (size_t j=0 ; j<pcount ; j++) {
609                if (path == animation.parts[j].path) {
610                    uint16_t method;
611                    // supports only stored png files
612                    if (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
613                        if (method == ZipFileRO::kCompressStored) {
614                            FileMap* map = mZip->createEntryFileMap(entry);
615                            if (map) {
616                                Animation::Part& part(animation.parts.editItemAt(j));
617                                if (leaf == "audio.wav") {
618                                    // a part may have at most one audio file
619                                    part.audioFile = map;
620                                } else {
621                                    Animation::Frame frame;
622                                    frame.name = leaf;
623                                    frame.map = map;
624                                    part.frames.add(frame);
625                                }
626                            }
627                        }
628                    }
629                }
630            }
631        }
632    }
633
634    mZip->endIteration(cookie);
635
636    return true;
637}
638
639bool BootAnimation::movie()
640{
641
642    Animation* animation = loadAnimation(mZipFileName);
643    if (animation == NULL)
644        return false;
645
646    // Blend required to draw time on top of animation frames.
647    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
648    glShadeModel(GL_FLAT);
649    glDisable(GL_DITHER);
650    glDisable(GL_SCISSOR_TEST);
651    glDisable(GL_BLEND);
652
653    glBindTexture(GL_TEXTURE_2D, 0);
654    glEnable(GL_TEXTURE_2D);
655    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
656    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
657    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
658    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
659    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
660
661    bool clockTextureInitialized = false;
662    if (mClockEnabled) {
663        clockTextureInitialized = (initTexture(&mClock, mAssets, "images/clock64.png") == NO_ERROR);
664        mClockEnabled = clockTextureInitialized;
665    }
666
667    playAnimation(*animation);
668    releaseAnimation(animation);
669
670    if (clockTextureInitialized) {
671        glDeleteTextures(1, &mClock.name);
672    }
673
674    return false;
675}
676
677bool BootAnimation::playAnimation(const Animation& animation)
678{
679    const size_t pcount = animation.parts.size();
680    const int xc = (mWidth - animation.width) / 2;
681    const int yc = ((mHeight - animation.height) / 2);
682    nsecs_t frameDuration = s2ns(1) / animation.fps;
683
684    Region clearReg(Rect(mWidth, mHeight));
685    clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));
686
687    for (size_t i=0 ; i<pcount ; i++) {
688        const Animation::Part& part(animation.parts[i]);
689        const size_t fcount = part.frames.size();
690        glBindTexture(GL_TEXTURE_2D, 0);
691
692        // Handle animation package
693        if (part.animation != NULL) {
694            playAnimation(*part.animation);
695            if (exitPending())
696                break;
697            continue; //to next part
698        }
699
700        for (int r=0 ; !part.count || r<part.count ; r++) {
701            // Exit any non playuntil complete parts immediately
702            if(exitPending() && !part.playUntilComplete)
703                break;
704
705            // only play audio file the first time we animate the part
706            if (r == 0 && mAudioPlayer != NULL && part.audioFile) {
707                mAudioPlayer->playFile(part.audioFile);
708            }
709
710            glClearColor(
711                    part.backgroundColor[0],
712                    part.backgroundColor[1],
713                    part.backgroundColor[2],
714                    1.0f);
715
716            for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
717                const Animation::Frame& frame(part.frames[j]);
718                nsecs_t lastFrame = systemTime();
719
720                if (r > 0) {
721                    glBindTexture(GL_TEXTURE_2D, frame.tid);
722                } else {
723                    if (part.count != 1) {
724                        glGenTextures(1, &frame.tid);
725                        glBindTexture(GL_TEXTURE_2D, frame.tid);
726                        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
727                        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
728                    }
729                    initTexture(frame);
730                }
731
732                if (!clearReg.isEmpty()) {
733                    Region::const_iterator head(clearReg.begin());
734                    Region::const_iterator tail(clearReg.end());
735                    glEnable(GL_SCISSOR_TEST);
736                    while (head != tail) {
737                        const Rect& r2(*head++);
738                        glScissor(r2.left, mHeight - r2.bottom,
739                                r2.width(), r2.height());
740                        glClear(GL_COLOR_BUFFER_BIT);
741                    }
742                    glDisable(GL_SCISSOR_TEST);
743                }
744                // specify the y center as ceiling((mHeight - animation.height) / 2)
745                // which is equivalent to mHeight - (yc + animation.height)
746                glDrawTexiOES(xc, mHeight - (yc + animation.height),
747                              0, animation.width, animation.height);
748                if (mClockEnabled && part.clockPosY >= 0) {
749                    drawTime(mClock, part.clockPosY);
750                }
751
752                eglSwapBuffers(mDisplay, mSurface);
753
754                nsecs_t now = systemTime();
755                nsecs_t delay = frameDuration - (now - lastFrame);
756                //ALOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
757                lastFrame = now;
758
759                if (delay > 0) {
760                    struct timespec spec;
761                    spec.tv_sec  = (now + delay) / 1000000000;
762                    spec.tv_nsec = (now + delay) % 1000000000;
763                    int err;
764                    do {
765                        err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
766                    } while (err<0 && errno == EINTR);
767                }
768
769                checkExit();
770            }
771
772            usleep(part.pause * ns2us(frameDuration));
773
774            // For infinite parts, we've now played them at least once, so perhaps exit
775            if(exitPending() && !part.count)
776                break;
777        }
778
779        // free the textures for this part
780        if (part.count != 1) {
781            for (size_t j=0 ; j<fcount ; j++) {
782                const Animation::Frame& frame(part.frames[j]);
783                glDeleteTextures(1, &frame.tid);
784            }
785        }
786    }
787    return true;
788}
789
790void BootAnimation::releaseAnimation(Animation* animation) const
791{
792    for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
793         e = animation->parts.end(); it != e; ++it) {
794        if (it->animation)
795            releaseAnimation(it->animation);
796    }
797    if (animation->zip)
798        delete animation->zip;
799    delete animation;
800}
801
802BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
803{
804    if (mLoadedFiles.indexOf(fn) >= 0) {
805        ALOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
806            fn.string());
807        return NULL;
808    }
809    ZipFileRO *zip = ZipFileRO::open(fn);
810    if (zip == NULL) {
811        ALOGE("Failed to open animation zip \"%s\": %s",
812            fn.string(), strerror(errno));
813        return NULL;
814    }
815
816    Animation *animation =  new Animation;
817    animation->fileName = fn;
818    animation->zip = zip;
819    mLoadedFiles.add(animation->fileName);
820
821    parseAnimationDesc(*animation);
822    preloadZip(*animation);
823
824    mLoadedFiles.remove(fn);
825    return animation;
826}
827// ---------------------------------------------------------------------------
828
829}
830; // namespace android
831