android系统的开机动画可分为三个部分,kernel启动,init进程启动,android系统服务启动。这三个开机动画都是在一个叫做 帧缓冲区(frame buffer)的硬件设备上进行渲染绘制的。
在Linux内核中,每一个硬件设备都有一个主设备号和一个从设备号,它们用来唯一地标识一个硬件设备。对于帧缓冲区硬件设备来说,它们的主设备号定义为FB_MAJOR(29),而从设备号则与注册的顺序有关,它们的值依次等于0,1,2等。
每一个被注册的帧缓冲区硬件设备在/dev/graphics目录下都有一个对应的设备文件fb<minor>,其中,<minor>表示一个从设备号。例如,第一个被注册的帧缓冲区硬件设备在/dev/graphics目录下都有一个对应的设备文件fb0。用户空间的应用程序通过这个
设备文件就可以操作帧缓冲区硬件设备了,即将要显示的画面渲染到帧缓冲区硬件设备上去
# CONFIG_FRAMEBUFFER_CONSOLE is not set # CONFIG_LOGO is not set
VERSION = 3 PATCHLEVEL = 1 SUBLEVEL = 10 EXTRAVERSION = NAME = "Divemaster Edition"
const struct linux_logo * __init_refok fb_find_logo(int depth) { const struct linux_logo *logo = NULL; if (nologo) return NULL; if (depth >= 1) { #ifdef CONFIG_LOGO_LINUX_MONO /* Generic Linux logo */ logo = &logo_linux_mono; #endif #ifdef CONFIG_LOGO_SUPERH_MONO /* SuperH Linux logo */ logo = &logo_superh_mono; #endif } if (depth >= 4) { #ifdef CONFIG_LOGO_LINUX_VGA16 /* Generic Linux logo */ logo = &logo_linux_vga16; #endif #ifdef CONFIG_LOGO_BLACKFIN_VGA16 /* Blackfin processor logo */ logo = &logo_blackfin_vga16; #endif #ifdef CONFIG_LOGO_SUPERH_VGA16 /* SuperH Linux logo */ logo = &logo_superh_vga16; #endif } if (depth >= 8) { #ifdef CONFIG_LOGO_LINUX_CLUT224 /* Generic Linux logo */ logo = &logo_linux_clut224; #endif #ifdef CONFIG_LOGO_BLACKFIN_CLUT224 /* Blackfin Linux logo */ logo = &logo_blackfin_clut224; #endif #ifdef CONFIG_LOGO_DEC_CLUT224 /* DEC Linux logo on MIPS/MIPS64 or ALPHA */ logo = &logo_dec_clut224; #endif #ifdef CONFIG_LOGO_MAC_CLUT224 /* Macintosh Linux logo on m68k */ if (MACH_IS_MAC) logo = &logo_mac_clut224; #endif #ifdef CONFIG_LOGO_PARISC_CLUT224 /* PA-RISC Linux logo */ logo = &logo_parisc_clut224; #endif #ifdef CONFIG_LOGO_SGI_CLUT224 /* SGI Linux logo on MIPS/MIPS64 and VISWS */ logo = &logo_sgi_clut224; #endif #ifdef CONFIG_LOGO_SUN_CLUT224 /* Sun Linux logo */ logo = &logo_sun_clut224; #endif #ifdef CONFIG_LOGO_SUPERH_CLUT224 /* SuperH Linux logo */ logo = &logo_superh_clut224; #endif #ifdef CONFIG_LOGO_M32R_CLUT224 /* M32R Linux logo */ logo = &logo_m32r_clut224; #endif } return logo; }
extern const struct linux_logo logo_linux_mono; extern const struct linux_logo logo_linux_vga16; extern const struct linux_logo logo_linux_clut224; extern const struct linux_logo logo_blackfin_vga16; extern const struct linux_logo logo_blackfin_clut224; extern const struct linux_logo logo_dec_clut224; extern const struct linux_logo logo_mac_clut224; extern const struct linux_logo logo_parisc_clut224; extern const struct linux_logo logo_sgi_clut224; extern const struct linux_logo logo_sun_clut224; extern const struct linux_logo logo_superh_mono; extern const struct linux_logo logo_superh_vga16; extern const struct linux_logo logo_superh_clut224; extern const struct linux_logo logo_m32r_clut224; extern const struct linux_logo logo_spe_clut224;
int fb_show_logo(struct fb_info *info, int rotate) { int y; y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, num_online_cpus()); y = fb_show_extra_logos(info, y, rotate); return y; }
static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, int rotate, unsigned int num) { unsigned int x; if (rotate == FB_ROTATE_UR) { for (x = 0; x < num && image->dx + image->width <= info->var.xres; x++) { info->fbops->fb_imageblit(info, image); image->dx += image->width + 8; } } else if (rotate == FB_ROTATE_UD) { for (x = 0; x < num && image->dx >= 0; x++) { info->fbops->fb_imageblit(info, image); image->dx -= image->width + 8; } } else if (rotate == FB_ROTATE_CW) { for (x = 0; x < num && image->dy + image->height <= info->var.yres; x++) { info->fbops->fb_imageblit(info, image); image->dy += image->height + 8; } } else if (rotate == FB_ROTATE_CCW) { for (x = 0; x < num && image->dy >= 0; x++) { info->fbops->fb_imageblit(info, image); image->dy -= image->height + 8; } } }其中:
int main(int argc, char **argv) { int fd_count = 0; struct pollfd ufds[4]; char *tmpdev; char* debuggable; char tmp[32]; int property_set_fd_init = 0; int signal_fd_init = 0; int keychord_fd_init = 0; bool is_charger = false; if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); if (!strcmp(basename(argv[0]), "watchdogd")) return watchdogd_main(argc, argv); ... queue_builtin_action(console_init_action, "console_init"); ... }
static int console_init_action(int nargs, char **args) { int fd; char tmp[PROP_VALUE_MAX]; if (console[0]) { snprintf(tmp, sizeof(tmp), "/dev/%s", console); console_name = strdup(tmp); } fd = open(console_name, O_RDWR); if (fd >= 0) have_console = 1; close(fd); if( load_565rle_image(INIT_IMAGE_FILE) ) { fd = open("/dev/tty0", O_WRONLY); if (fd >= 0) { const char *msg; msg = "\n" "\n" "\n" "\n" "\n" "\n" "\n" // console is 40 cols x 30 lines "\n" "\n" "\n" "\n" "\n" "\n" "\n" " A N D R O I D "; write(fd, msg, strlen(msg)); close(fd); } } return 0; }
#define INIT_IMAGE_FILE "/initlogo.rle"
int load_565rle_image(char *fn) { struct FB fb; struct stat s; unsigned short *data, *bits, *ptr; unsigned count, max; int fd; if (vt_set_mode(1)) return -1; fd = open(fn, O_RDONLY); if (fd < 0) { ERROR("cannot open '%s'\n", fn); goto fail_restore_text; } if (fstat(fd, &s) < 0) { goto fail_close_file; } data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); if (data == MAP_FAILED) goto fail_close_file; if (fb_open(&fb)) goto fail_unmap_data; max = fb_width(&fb) * fb_height(&fb); ptr = data; count = s.st_size; bits = fb.bits; while (count > 3) { unsigned n = ptr[0]; if (n > max) break; android_memset16(bits, ptr[1], n << 1); bits += n; max -= n; ptr += 2; count -= 4; } munmap(data, s.st_size); fb_update(&fb); fb_close(&fb); close(fd); unlink(fn); return 0; fail_unmap_data: munmap(data, s.st_size); fail_close_file: close(fd); fail_restore_text: vt_set_mode(0); return -1; }
static int vt_set_mode(int graphics) { int fd, r; fd = open("/dev/tty0", O_RDWR | O_SYNC); if (fd < 0) return -1; r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT)); close(fd); return r; }
static void fb_update(struct FB *fb) { fb->vi.yoffset = 1; ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); fb->vi.yoffset = 0; ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); }通过帧缓冲区设备渲染。
service bootanim /system/bin/bootanimation class main user graphics group graphics disabled oneshot
static void instantiate() { publish(); } static status_t publish(bool allowIsolated = false) { sp<IServiceManager> sm(defaultServiceManager()); return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated); }
SurfaceFlinger::SurfaceFlinger() : BnSurfaceComposer(), Thread(false), mTransactionFlags(0), mTransactionPending(false), mAnimTransactionPending(false), mLayersRemoved(false), mRepaintEverything(0), mBootTime(systemTime()), mVisibleRegionsDirty(false), mHwWorkListDirty(false), mDebugRegion(0), mDebugDDMS(0), mDebugDisableHWC(0), mDebugDisableTransformHint(0), mDebugInSwapBuffers(0), mLastSwapBufferTime(0), mDebugInTransaction(0), mLastTransactionTime(0), mBootFinished(false) { ALOGI("SurfaceFlinger is starting"); // debugging stuff... char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.showupdates", value, "0"); mDebugRegion = atoi(value); property_get("debug.sf.ddms", value, "0"); mDebugDDMS = atoi(value); if (mDebugDDMS) { if (!startDdmConnection()) { // start failed, and DDMS debugging not enabled mDebugDDMS = 0; } } ALOGI_IF(mDebugRegion, "showupdates enabled"); ALOGI_IF(mDebugDDMS, "DDMS debugging enabled"); }
void SurfaceFlinger::onFirstRef() { mEventQueue.init(this); run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY); // Wait for the main thread to be done with its initialization mReadyToRunBarrier.wait(); }
status_t SurfaceFlinger::readyToRun() { ALOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); ... // initialize OpenGL ES DisplayDevice::makeCurrent(mEGLDisplay, hw, mEGLContext); initializeGL(mEGLDisplay); // start the EventThread mEventThread = new EventThread(this); mEventQueue.setEventThread(mEventThread); ... // start boot animation startBootAnim(); return NO_ERROR; }
void SurfaceFlinger::startBootAnim() { // start boot animation property_set("service.bootanim.exit", "0"); property_set("ctl.start", "bootanim"); }
int main(int argc, char** argv) { #if defined(HAVE_PTHREADS) setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY); #endif char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.nobootanimation", value, "0"); int noBootAnimation = atoi(value); ALOGI_IF(noBootAnimation, "boot animation disabled"); if (!noBootAnimation) { ALOGD("boot animation start"); sp<ProcessState> proc(ProcessState::self()); ProcessState::self()->startThreadPool(); // create the boot animation object sp<BootAnimation> boot = new BootAnimation(); IPCThreadState::self()->joinThreadPool(); } return 0; }
void BootAnimation::onFirstRef() { status_t err = mSession->linkToComposerDeath(this); ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err)); if (err == NO_ERROR) { run("BootAnimation", PRIORITY_DISPLAY); } }
BootAnimation::BootAnimation() : Thread(false) { mSession = new SurfaceComposerClient(); }
status_t BootAnimation::readyToRun() { mAssets.addDefaultAssets(); ... mAndroidAnimation = true; // If the device has encryption turned on or is in process // of being encrypted we show the encrypted boot animation. char decrypt[PROPERTY_VALUE_MAX]; property_get("vold.decrypt", decrypt, ""); bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt); if ((encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) && (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) || ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) && (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) || ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) && (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) { mAndroidAnimation = false; } return NO_ERROR; }
#define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip" #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip" #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
bool BootAnimation::threadLoop() { bool r; if (mAndroidAnimation) { r = android(); } else { r = movie(); } eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(mDisplay, mContext); eglDestroySurface(mDisplay, mSurface); mFlingerSurface.clear(); mFlingerSurfaceControl.clear(); eglTerminate(mDisplay); IPCThreadState::self()->stopProcess(); return r; }
bool BootAnimation::android() { initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png"); initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png"); // clear screen glShadeModel(GL_FLAT); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); ... }
bool BootAnimation::movie() { ZipFileRO& zip(mZip); size_t numEntries = zip.getNumEntries(); ZipEntryRO desc = zip.findEntryByName("desc.txt"); FileMap* descMap = zip.createEntryFileMap(desc); ALOGE_IF(!descMap, "descMap is null"); if (!descMap) { return false; } String8 desString((char const*)descMap->getDataPtr(), descMap->getDataLength()); char const* s = desString.string(); Animation animation; // Parse the description file for (;;) { const char* endl = strstr(s, "\n"); if (!endl) break; String8 line(s, endl - s); const char* l = line.string(); int fps, width, height, count, pause; char path[256]; char pathType; if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps); // ALOGD("jscese> w=%d, h=%d", mWidth, mHeight); animation.width =width; animation.height =height; animation.fps = fps; } else if (sscanf(l, " %c %d %d %s", &pathType, &count, &pause, path) == 4) { //LOGD("> type=%c, count=%d, pause=%d, path=%s", pathType, count, pause, path); Animation::Part part; part.playUntilComplete = pathType == 'c'; part.count = count; part.pause = pause; part.path = path; animation.parts.add(part); } s = ++endl; } ... }
1920 1080 24 p 1 0 part1 p 0 10 part2
zip -r -0 bootanimation.zip bootanimation/ desc.txt
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) { final long origId = Binder.clearCallingIdentity(); ActivityRecord r = mMainStack.activityIdleInternal(token, false, config); ... }
final ActivityRecord activityIdleInternal(IBinder token, boolean fromTimeout, Configuration config) { if (localLOGV) Slog.v(TAG, "Activity idle: " + token); ActivityRecord res = null; ... if (mMainStack) { if (!mService.mBooted) { mService.mBooted = true; enableScreen = true; } } ... if (enableScreen) { mService.enableScreenAfterBoot(); } ... }
void enableScreenAfterBoot() { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN, SystemClock.uptimeMillis()); mWindowManager.enableScreenAfterBoot(); synchronized (this) { updateEventDispatchingLocked(); } }
public void performEnableScreen() { synchronized(mWindowMap) { if (DEBUG_BOOT) { RuntimeException here = new RuntimeException("here"); here.fillInStackTrace(); ... try { IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger"); if (surfaceFlinger != null) { //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!"); Parcel data = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED data, null, 0); data.recycle(); } ... }
void SurfaceFlinger::bootFinished() { const nsecs_t now = systemTime(); ... property_set("service.bootanim.exit", "1"); }