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