天天看點

Android 6.0 開機動畫一

開機動畫是在程序bootanim,我們來看下init.rc中檔案,

[html] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. service bootanim /system/bin/bootanimation boot  
  2.     class core  
  3.     user graphics  
  4.     group graphics audio  
  5.     disabled  
  6.     oneshot  

 應用程式bootanimation的使用者和使用者組名稱分别被設定為graphics。注意, 用來啟動應用程式bootanimation的服務是disable的,即init程序在啟動的時候,不會主動将應用程式bootanimation啟動起來。當SurfaceFlinger服務啟動的時候,它會通過修改系統屬性ctl.start的值來通知init程序啟動應用程式bootanimation,以便可以顯示第三個開機畫面,而當System程序将系統中的關鍵服務都啟動起來之後,ActivityManagerService服務就會通知SurfaceFlinger服務來修改系統屬性ctl.stop的值,以便可以通知init程序停止執行應用程式bootanimation來顯示開機畫面。

在之前分析SurfaceFlinger的部落格中,在init函數最後會調用startBootAnim函數

[cpp] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. void SurfaceFlinger::init() {  
  2.     ......  
  3.     startBootAnim();  
  4. }  

而startBootAnim函數後面會設定屬性ctl.start"為bootanim,這樣bootanim程序就會啟動。

[cpp] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. void SurfaceFlinger::startBootAnim() {  
  2.     // start boot animation  
  3.     property_set("service.bootanim.exit", "0");  
  4.     property_set("ctl.start", "bootanim");  
  5. }  

一、bootanim程序

下面我們來看下bootanim的main函數

[cpp] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. int main(int argc,char **argv)  
  2. {  
  3.     setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);  
  4.     char value[PROPERTY_VALUE_MAX];  
  5.     property_get("debug.sf.nobootanimation", value, "0");  
  6.     if (strcmp(basename(argv[1]),"shutdown"))  
  7.         bootOrshutdown = 0;  
  8.     else  
  9.         bootOrshutdown = 1;  
  10.     int noBootAnimation = atoi(value);  
  11.     ALOGI_IF(noBootAnimation,  "boot animation disabled");  
  12.     if (!noBootAnimation) {  
  13.         sp<ProcessState> proc(ProcessState::self());  
  14.         ProcessState::self()->startThreadPool();  
  15.         // create the boot animation object  
  16.         sp<BootAnimation> boot = new BootAnimation();  
  17.         IPCThreadState::self()->joinThreadPool();  
  18.     }  
  19.     return 0;  
  20. }  

這裡先會判斷是開機還是關機,然後建立BootAnimation對象。

我們先來看下構造函數和onFirstRef函數,構造函數中建立了一個SurfaceComposerClient對象,onFirstRef啟動了線程。

[cpp] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. BootAnimation::BootAnimation() : Thread(false), mZip(NULL)  
  2. {  
  3.     mSession = new SurfaceComposerClient();  
  4. }  
  5. void BootAnimation::onFirstRef() {  
  6.     status_t err = mSession->linkToComposerDeath(this);  
  7.     ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));  
  8.     if (err == NO_ERROR) {  
  9.         run("BootAnimation", PRIORITY_DISPLAY);  
  10.     }  
  11. }  

readyToRun函數是線上程啟動時調用的。

[cpp] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. status_t BootAnimation::readyToRun() {  
  2.     mAssets.addDefaultAssets();  
  3.     sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(  
  4.             ISurfaceComposer::eDisplayIdMain));  
  5.     DisplayInfo dinfo;  
  6.     status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);  
  7.     if (status)  
  8.         return -1;  
  9.     // create the native surface  
  10.     sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),  
  11.             dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);  
  12.     SurfaceComposerClient::openGlobalTransaction();  
  13.     control->setLayer(0x40000000);  
  14.     SurfaceComposerClient::closeGlobalTransaction();  
  15.     sp<Surface> s = control->getSurface();  
  16.     // initialize opengl and egl  
  17.     const EGLint attribs[] = {  
  18.             EGL_RED_SIZE,   8,  
  19.             EGL_GREEN_SIZE, 8,  
  20.             EGL_BLUE_SIZE,  8,  
  21.             EGL_DEPTH_SIZE, 0,  
  22.             EGL_NONE  
  23.     };  
  24.     EGLint w, h;  
  25.     EGLint numConfigs;  
  26.     EGLConfig config;  
  27.     EGLSurface surface;  
  28.     EGLContext context;  
  29.     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);  
  30.     eglInitialize(display, 0, 0);  
  31.     eglChooseConfig(display, attribs, &config, 1, &numConfigs);  
  32.     surface = eglCreateWindowSurface(display, config, s.get(), NULL);  
  33.     context = eglCreateContext(display, config, NULL, NULL);  
  34.     eglQuerySurface(display, surface, EGL_WIDTH, &w);  
  35.     eglQuerySurface(display, surface, EGL_HEIGHT, &h);  
  36.     if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)  
  37.         return NO_INIT;  
  38.     mDisplay = display;  
  39.     mContext = context;  
  40.     mSurface = surface;  
  41.     mWidth = w;  
  42.     mHeight = h;  
  43.     mFlingerSurfaceControl = control;  
  44.     mFlingerSurface = s;  
  45.     ZipFileRO* zipFile = NULL;  
  46.     float volume;  
  47.     const char * file;  
  48.     if(1 == bootOrshutdown) {  
  49.         volume = 1.0;  
  50.         zipFile = ZipFileRO::open("/system/media/shutdownanimation.zip");  
  51.         file = "/system/media/shutdown.mp3";  
  52.     } else {  
  53.         volume = 0.4;  
  54.         zipFile = ZipFileRO::open("/system/media/bootanimation.zip");  
  55.         file = "/system/media/boot.mp3";  
  56.     }  
  57.     mZip = zipFile;  
  58.     #ifndef OPT_PROJ_TARGET_CUCC  
  59.         mp = new MediaPlayer();  
  60.         sp<IMediaHTTPService> bootmedia=NULL ;  
  61.         if (mp->setDataSource(bootmedia,file,NULL) == NO_ERROR) {  
  62.             mp->setAudioStreamType(AUDIO_STREAM_MUSIC);  
  63.             mp->prepare();  
  64.             mp->setVolume(volume, volume);  
  65.             mp->setListener(new BootMediaPlayerListener());  
  66.         } else {  
  67.             ALOGE("Failed to load Bootanimation sounds: %s", file);  
  68.         }  
  69.     #endif  
  70.     return NO_ERROR;  
  71. }  

   BootAnimation類的成員函數session用來傳回BootAnimation類的成員變量mSession所描述的一個SurfaceComposerClient對象。通過調用SurfaceComposerClient對象mSession的成員函數createSurface可以獲得一個SurfaceControl對象control。

        SurfaceComposerClient類的成員函數createSurface首先調用内部的Binder代理對象mClient來請求SurfaceFlinger傳回一個類型為SurfaceLayer的Binder代理對象,接着再使用這個Binder代理對象來建立一個SurfaceControl對象。建立出來的SurfaceControl對象的成員變量mSurface就指向了從SurfaceFlinger傳回來的類型為SurfaceLayer的Binder代理對象。有了這個Binder代理對象之後,SurfaceControl對象就可以和SurfaceFlinger服務通信了。

       調用SurfaceControl對象control的成員函數getSurface會傳回一個Surface對象s。這個Surface對象s内部也有一個類型為SurfaceLayer的Binder代理對象mSurface,這個Binder代理對象與前面所建立的SurfaceControl對象control的内部的Binder代理對象mSurface引用的是同一個SurfaceLayer對象。這樣,Surface對象s也可以通過其内部的Binder代理對象mSurface來和SurfaceFlinger服務通信。

       Surface類繼承了ANativeWindow類。ANativeWindow類是連接配接OpenGL和Android視窗系統的橋梁,即OpenGL需要通過ANativeWindow類來間接地操作Android視窗系統。這種橋梁關系是通過EGL庫來建立的,所有以egl為字首的函數名均為EGL庫提供的接口。

       為了能夠在OpenGL和Android視窗系統之間的建立一個橋梁,我們需要一個EGLDisplay對象display,一個EGLConfig對象config,一個EGLSurface對象surface,以及一個EGLContext對象context,其中,EGLDisplay對象display用來描述一個EGL顯示屏,EGLConfig對象config用來描述一個EGL幀緩沖區配置參數,EGLSurface對象surface用來描述一個EGL繪圖表面,EGLContext對象context用來描述一個EGL繪圖上下文(狀态),它們是分别通過調用egl庫函數eglGetDisplay、EGLUtils::selectConfigForNativeWindow、eglCreateWindowSurface和eglCreateContext來獲得的。注意,EGLConfig對象config、EGLSurface對象surface和EGLContext對象context都是用來描述EGLDisplay對象display的。有了這些對象之後,就可以調用函數eglMakeCurrent來設定目前EGL庫所使用的繪圖表面以及繪圖上下文。

       還有另外一個地方需要注意的是,每一個EGLSurface對象surface有一個關聯的ANativeWindow對象。這個ANativeWindow對象是通過函數eglCreateWindowSurface的第三個參數來指定的。在我們這個場景中,這個ANativeWindow對象正好對應于前面所建立的 Surface對象s。每當OpenGL需要繪圖的時候,它就會找到前面所設定的繪圖表面,即EGLSurface對象surface。有了EGLSurface對象surface之後,就可以找到與它關聯的ANativeWindow對象,即Surface對象s。有了Surface對象s之後,就可以通過其内部的Binder代理對象mSurface來請求SurfaceFlinger服務傳回幀緩沖區硬體裝置的一個圖形通路接口。這樣,OpenGL最終就可以将要繪制的圖形渲染到幀緩沖區硬體裝置中去,即顯示在實際螢幕上。螢幕的大小,即寬度和高度,可以通過函數eglQuerySurface來獲得。

下面我們再看下threadLoop函數:

[cpp] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. bool BootAnimation::threadLoop()  
  2. {  
  3.     bool r;  
  4.     // We have no bootanimation file, so we use the stock android logo  
  5.     // animation.  
  6.     if (mZip == NULL) {  
  7.         r = android();  
  8.     } else {  
  9.         AudioSystem::getDeviceConnectionState(0x4, "AudioPolicyThreadStart");  
  10.         usleep(100*1000);  
  11.         #ifndef OPT_PROJ_TARGET_CUCC  
  12.             mp->start();  
  13.         #endif  
  14.         r = movie();  
  15.     }  
  16.     ALOGD("add step to know whether leave the movie");  
  17.     eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);  
  18.     eglDestroyContext(mDisplay, mContext);  
  19.     eglDestroySurface(mDisplay, mSurface);  
  20.     mFlingerSurface.clear();  
  21.     mFlingerSurfaceControl.clear();  
  22.     eglTerminate(mDisplay);  
  23.     IPCThreadState::self()->stopProcess();  
  24.     ALOGD("add step to know whether finish all the steps");  
  25.     return r;  
  26. }  

當mZip為空就用android原生的啟動畫面,如果不是就用自定義的。

android方法是播放原生的,movie是播放自定義的。

我們看下android方法

[cpp] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. bool BootAnimation::android()  
  2. {  
  3.     initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");  
  4.     initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");  
  5.     // clear screen  
  6.     glShadeModel(GL_FLAT);  
  7.     glDisable(GL_DITHER);  
  8.     glDisable(GL_SCISSOR_TEST);  
  9.     glClearColor(0,0,0,1);  
  10.     glClear(GL_COLOR_BUFFER_BIT);  
  11.     eglSwapBuffers(mDisplay, mSurface);  
  12.     glEnable(GL_TEXTURE_2D);  
  13.     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);  
  14.     const GLint xc = (mWidth  - mAndroid[0].w) / 2;  
  15.     const GLint yc = (mHeight - mAndroid[0].h) / 2;  
  16.     const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);  
  17.     glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),  
  18.             updateRect.height());  
  19.     // Blend state  
  20.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);  
  21.     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);  
  22.     const nsecs_t startTime = systemTime();  
  23.     do {  
  24.         nsecs_t now = systemTime();  
  25.         double time = now - startTime;  
  26.         float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;  
  27.         GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;  
  28.         GLint x = xc - offset;  
  29.         glDisable(GL_SCISSOR_TEST);  
  30.         glClear(GL_COLOR_BUFFER_BIT);  
  31.         glEnable(GL_SCISSOR_TEST);  
  32.         glDisable(GL_BLEND);  
  33.         glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);  
  34.         glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);  
  35.         glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);  
  36.         glEnable(GL_BLEND);  
  37.         glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);  
  38.         glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);  
  39.         EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);  
  40.         if (res == EGL_FALSE)  
  41.             break;  
  42.         // 12fps: don't animate too fast to preserve CPU  
  43.         const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);  
  44.         if (sleepTime > 0)  
  45.             usleep(sleepTime);  
  46.         checkExit();  
  47.     } while (!exitPending());  
  48.     glDeleteTextures(1, &mAndroid[0].name);  
  49.     glDeleteTextures(1, &mAndroid[1].name);  
  50.     return false;  
  51. }  

二、WMS通知關閉開機動畫

當System程序将系統中的關鍵服務啟動起來之後,就會将應用程式啟動器(Launcher)啟動起來。 一個Activity元件在啟動起來之後,就會被記錄起來,等到它所運作在的主線程空閑的時候,這個主線程就會向ActivityManagerService發送一個Activity元件空閑的通知。由于應用程式Launcher是系統中第一個被啟動的應用程式,即它的根Activity元件是系統中第一個被啟動的Activity元件,是以,當ActivityManagerService接收到它的空閑通知的時候,就可以知道系統是剛剛啟動起來的。在這種情況下,ActivityManagerService就會停止顯示開機動畫,以便可以在螢幕中顯示應用程式Lancher的界面。

最終會調用AMS的enableScreenAfterBoot方法

[cpp] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. void enableScreenAfterBoot() {  
  2.     EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,  
  3.             SystemClock.uptimeMillis());  
  4.     mWindowManager.enableScreenAfterBoot();  
  5.     synchronized (this) {  
  6.         updateEventDispatchingLocked();  
  7.     }  
  8. }  

在WMS的enableScreenAfterBoot方法中又會調用performEnableScreen方法

[cpp] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. public void enableScreenAfterBoot() {  
  2.     synchronized(mWindowMap) {  
  3.         if (DEBUG_BOOT) {  
  4.             RuntimeException here = new RuntimeException("here");  
  5.             here.fillInStackTrace();  
  6.             Slog.i(TAG, "enableScreenAfterBoot: mDisplayEnabled=" + mDisplayEnabled  
  7.                     + " mForceDisplayEnabled=" + mForceDisplayEnabled  
  8.                     + " mShowingBootMessages=" + mShowingBootMessages  
  9.                     + " mSystemBooted=" + mSystemBooted, here);  
  10.         }  
  11.         if (mSystemBooted) {  
  12.             return;  
  13.         }  
  14.         mSystemBooted = true;  
  15.         hideBootMessagesLocked();  
  16.         // If the screen still doesn't come up after 30 seconds, give  
  17.         // up and turn it on.  
  18.         mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30*1000);  
  19.     }  
  20.     mPolicy.systemBooted();  
  21.     performEnableScreen();  
  22. }  

WMS的performEnableScreen方法,會通過Binder調用SurfaceFlinger的transact的FIRST_CALL_TRANSACTION

[cpp] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. public void performEnableScreen() {  
  2.     synchronized(mWindowMap) {  
  3.         if (mDisplayEnabled) {  
  4.             return;  
  5.         }  
  6.         if (!mSystemBooted && !mShowingBootMessages) {  
  7.             return;  
  8.         }  
  9.         // Don't enable the screen until all existing windows have been drawn.  
  10.         if (!mForceDisplayEnabled && checkWaitingForWindowsLocked()) {  
  11.             return;  
  12.         }  
  13.         if (!mBootAnimationStopped) {  
  14.             // Do this one time.  
  15.             try {  
  16.                 IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");  
  17.                 if (surfaceFlinger != null) {  
  18.                     //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");  
  19.                     Parcel data = Parcel.obtain();  
  20.                     data.writeInterfaceToken("android.ui.ISurfaceComposer");  
  21.                     surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED  
  22.                             data, null, 0);  
  23.                     data.recycle();  
  24.                 }  
  25.             }  

最後會在SurfaceFlinger的bootFinshed函數中設定service.bootanim.exit屬性為1,這個後面會決定bootanim程序什麼時候關閉。

[cpp] view plain copy

Android 6.0 開機動畫一
Android 6.0 開機動畫一
  1. void SurfaceFlinger::bootFinished()  
  2. {  
  3.     const nsecs_t now = systemTime();  
  4.     const nsecs_t duration = now - mBootTime;  
  5.     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );  
  6.     mBootFinished = true;  
  7.     // wait patiently for the window manager death  
  8.     const String16 name("window");  
  9.     sp<IBinder> window(defaultServiceManager()->getService(name));  
  10.     if (window != 0) {  
  11.         window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));  
  12.     }  
  13.     // stop boot animation  
  14.     // formerly we would just kill the process, but we now ask it to exit so it  
  15.     // can choose where to stop the animation.  
  16.     property_set("service.bootanim.exit", "1");  
  17.     char boot_exit_value[32];  
  18.     property_get("service.bootanim.exit", boot_exit_value, "0");  
  19.     ALOGD("The service.bootanim.exit property value is %d", atoi(boot_exit_value));  

繼續閱讀