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