開機動畫是在程序bootanim,我們來看下init.rc中檔案,
[html] view plain copy
- service bootanim /system/bin/bootanimation boot
- class core
- user graphics
- group graphics audio
- disabled
- 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
- void SurfaceFlinger::init() {
- ......
- startBootAnim();
- }
而startBootAnim函數後面會設定屬性ctl.start"為bootanim,這樣bootanim程序就會啟動。
[cpp] view plain copy
- void SurfaceFlinger::startBootAnim() {
- // start boot animation
- property_set("service.bootanim.exit", "0");
- property_set("ctl.start", "bootanim");
- }
一、bootanim程序
下面我們來看下bootanim的main函數
[cpp] view plain copy
- int main(int argc,char **argv)
- {
- setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.nobootanimation", value, "0");
- if (strcmp(basename(argv[1]),"shutdown"))
- bootOrshutdown = 0;
- else
- bootOrshutdown = 1;
- int noBootAnimation = atoi(value);
- ALOGI_IF(noBootAnimation, "boot animation disabled");
- if (!noBootAnimation) {
- sp<ProcessState> proc(ProcessState::self());
- ProcessState::self()->startThreadPool();
- // create the boot animation object
- sp<BootAnimation> boot = new BootAnimation();
- IPCThreadState::self()->joinThreadPool();
- }
- return 0;
- }
這裡先會判斷是開機還是關機,然後建立BootAnimation對象。
我們先來看下構造函數和onFirstRef函數,構造函數中建立了一個SurfaceComposerClient對象,onFirstRef啟動了線程。
[cpp] view plain copy
- BootAnimation::BootAnimation() : Thread(false), mZip(NULL)
- {
- mSession = new SurfaceComposerClient();
- }
- 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);
- }
- }
readyToRun函數是線上程啟動時調用的。
[cpp] view plain copy
- status_t BootAnimation::readyToRun() {
- mAssets.addDefaultAssets();
- sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
- DisplayInfo dinfo;
- status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
- if (status)
- return -1;
- // create the native surface
- sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
- dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
- SurfaceComposerClient::openGlobalTransaction();
- control->setLayer(0x40000000);
- SurfaceComposerClient::closeGlobalTransaction();
- sp<Surface> s = control->getSurface();
- // initialize opengl and egl
- const EGLint attribs[] = {
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_DEPTH_SIZE, 0,
- EGL_NONE
- };
- EGLint w, h;
- EGLint numConfigs;
- EGLConfig config;
- EGLSurface surface;
- EGLContext context;
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- eglInitialize(display, 0, 0);
- eglChooseConfig(display, attribs, &config, 1, &numConfigs);
- surface = eglCreateWindowSurface(display, config, s.get(), NULL);
- context = eglCreateContext(display, config, NULL, NULL);
- eglQuerySurface(display, surface, EGL_WIDTH, &w);
- eglQuerySurface(display, surface, EGL_HEIGHT, &h);
- if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
- return NO_INIT;
- mDisplay = display;
- mContext = context;
- mSurface = surface;
- mWidth = w;
- mHeight = h;
- mFlingerSurfaceControl = control;
- mFlingerSurface = s;
- ZipFileRO* zipFile = NULL;
- float volume;
- const char * file;
- if(1 == bootOrshutdown) {
- volume = 1.0;
- zipFile = ZipFileRO::open("/system/media/shutdownanimation.zip");
- file = "/system/media/shutdown.mp3";
- } else {
- volume = 0.4;
- zipFile = ZipFileRO::open("/system/media/bootanimation.zip");
- file = "/system/media/boot.mp3";
- }
- mZip = zipFile;
- #ifndef OPT_PROJ_TARGET_CUCC
- mp = new MediaPlayer();
- sp<IMediaHTTPService> bootmedia=NULL ;
- if (mp->setDataSource(bootmedia,file,NULL) == NO_ERROR) {
- mp->setAudioStreamType(AUDIO_STREAM_MUSIC);
- mp->prepare();
- mp->setVolume(volume, volume);
- mp->setListener(new BootMediaPlayerListener());
- } else {
- ALOGE("Failed to load Bootanimation sounds: %s", file);
- }
- #endif
- return NO_ERROR;
- }
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
- bool BootAnimation::threadLoop()
- {
- bool r;
- // We have no bootanimation file, so we use the stock android logo
- // animation.
- if (mZip == NULL) {
- r = android();
- } else {
- AudioSystem::getDeviceConnectionState(0x4, "AudioPolicyThreadStart");
- usleep(100*1000);
- #ifndef OPT_PROJ_TARGET_CUCC
- mp->start();
- #endif
- r = movie();
- }
- ALOGD("add step to know whether leave the 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();
- ALOGD("add step to know whether finish all the steps");
- return r;
- }
當mZip為空就用android原生的啟動畫面,如果不是就用自定義的。
android方法是播放原生的,movie是播放自定義的。
我們看下android方法
[cpp] view plain copy
- 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);
- glClearColor(0,0,0,1);
- glClear(GL_COLOR_BUFFER_BIT);
- eglSwapBuffers(mDisplay, mSurface);
- glEnable(GL_TEXTURE_2D);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- const GLint xc = (mWidth - mAndroid[0].w) / 2;
- const GLint yc = (mHeight - mAndroid[0].h) / 2;
- const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);
- glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
- updateRect.height());
- // Blend state
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- const nsecs_t startTime = systemTime();
- do {
- nsecs_t now = systemTime();
- double time = now - startTime;
- float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
- GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
- GLint x = xc - offset;
- glDisable(GL_SCISSOR_TEST);
- glClear(GL_COLOR_BUFFER_BIT);
- glEnable(GL_SCISSOR_TEST);
- glDisable(GL_BLEND);
- glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
- glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h);
- glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);
- glEnable(GL_BLEND);
- glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
- glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
- EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
- if (res == EGL_FALSE)
- break;
- // 12fps: don't animate too fast to preserve CPU
- const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
- if (sleepTime > 0)
- usleep(sleepTime);
- checkExit();
- } while (!exitPending());
- glDeleteTextures(1, &mAndroid[0].name);
- glDeleteTextures(1, &mAndroid[1].name);
- return false;
- }
二、WMS通知關閉開機動畫
當System程序将系統中的關鍵服務啟動起來之後,就會将應用程式啟動器(Launcher)啟動起來。 一個Activity元件在啟動起來之後,就會被記錄起來,等到它所運作在的主線程空閑的時候,這個主線程就會向ActivityManagerService發送一個Activity元件空閑的通知。由于應用程式Launcher是系統中第一個被啟動的應用程式,即它的根Activity元件是系統中第一個被啟動的Activity元件,是以,當ActivityManagerService接收到它的空閑通知的時候,就可以知道系統是剛剛啟動起來的。在這種情況下,ActivityManagerService就會停止顯示開機動畫,以便可以在螢幕中顯示應用程式Lancher的界面。
最終會調用AMS的enableScreenAfterBoot方法
[cpp] view plain copy
- void enableScreenAfterBoot() {
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
- SystemClock.uptimeMillis());
- mWindowManager.enableScreenAfterBoot();
- synchronized (this) {
- updateEventDispatchingLocked();
- }
- }
在WMS的enableScreenAfterBoot方法中又會調用performEnableScreen方法
[cpp] view plain copy
- public void enableScreenAfterBoot() {
- synchronized(mWindowMap) {
- if (DEBUG_BOOT) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "enableScreenAfterBoot: mDisplayEnabled=" + mDisplayEnabled
- + " mForceDisplayEnabled=" + mForceDisplayEnabled
- + " mShowingBootMessages=" + mShowingBootMessages
- + " mSystemBooted=" + mSystemBooted, here);
- }
- if (mSystemBooted) {
- return;
- }
- mSystemBooted = true;
- hideBootMessagesLocked();
- // If the screen still doesn't come up after 30 seconds, give
- // up and turn it on.
- mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30*1000);
- }
- mPolicy.systemBooted();
- performEnableScreen();
- }
WMS的performEnableScreen方法,會通過Binder調用SurfaceFlinger的transact的FIRST_CALL_TRANSACTION
[cpp] view plain copy
- public void performEnableScreen() {
- synchronized(mWindowMap) {
- if (mDisplayEnabled) {
- return;
- }
- if (!mSystemBooted && !mShowingBootMessages) {
- return;
- }
- // Don't enable the screen until all existing windows have been drawn.
- if (!mForceDisplayEnabled && checkWaitingForWindowsLocked()) {
- return;
- }
- if (!mBootAnimationStopped) {
- // Do this one time.
- 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();
- }
- }
最後會在SurfaceFlinger的bootFinshed函數中設定service.bootanim.exit屬性為1,這個後面會決定bootanim程序什麼時候關閉。
[cpp] view plain copy
- void SurfaceFlinger::bootFinished()
- {
- const nsecs_t now = systemTime();
- const nsecs_t duration = now - mBootTime;
- ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
- mBootFinished = true;
- // wait patiently for the window manager death
- const String16 name("window");
- sp<IBinder> window(defaultServiceManager()->getService(name));
- if (window != 0) {
- window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
- }
- // stop boot animation
- // formerly we would just kill the process, but we now ask it to exit so it
- // can choose where to stop the animation.
- property_set("service.bootanim.exit", "1");
- char boot_exit_value[32];
- property_get("service.bootanim.exit", boot_exit_value, "0");
- ALOGD("The service.bootanim.exit property value is %d", atoi(boot_exit_value));
- }