2011年04月21日
Androidのブートアニメーション
Androidをパワーオンリセットをするたびに目にするブートアニメーションですが、実はよくわかっていませんでした。これはいつだれがスタートして、いつだれがストップするのでしょうか?
ブートアニメーションのソースコード
frameworks/base/cmds/bootanimation
これを見るとANDROIDの文字のアニメーションはOpenGL ESで書かれていることがわかります。
また、/data/local か /system/media にbootanimation.zip というファイルがある場合はそれをムービーとして再生するようになっています。Nexus Oneの場合はそうなっていました。(そのファイルを取ってきてKZM-A9-Dualに入れたらNexus Oneと同じブートアニメーションになりました。)
bootanimation.zipの構造はこのページが詳しいです。
ブートアニメーションの開始と終了の謎
init.rcには以下のようなserviceのエントリがあります。
init.rc
service bootanim /system/bin/bootanimation user graphics group graphics disabled oneshot
しかし属性がdisableになっているので、自動的には起動しません。開始のトリガーはどうなっているのでしょうか?
実は、initの管理するpropertyで"ctl.start" と "ctl.stop"は特殊な意味を持っていて、これがサービスの開始と終了のコマンドになっています。(system/core/init/property_service.c と system/core/init/init.c を見てください。)
これを使っているのが以下のところです。
frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::readyToRun() { LOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); ... /* * We're now ready to accept clients... */ // start boot animation property_set("ctl.start", "bootanim"); return NO_ERROR; }
void SurfaceFlinger::bootFinished() { const nsecs_t now = systemTime(); const nsecs_t duration = now - mBootTime; LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); mBootFinished = true; property_set("ctl.stop", "bootanim"); }
readyToRun()はThread::readyToRun()を継承していてSurfaceFlingerのスレッドが開始されるときに一度だけ呼ばれます。
bootFinished()は以下の関数から呼ばれています。
frameworks/base/libs/surfaceflinger_client/ISurfaceComposer.cpp
status_t BnSurfaceComposer::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { ... case BOOT_FINISHED: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); } break; ...
このonTransactは以前にも見かけました。Binderを使ったプロセス間のメソッド呼び出しです。
どこか別のプロセスからbootFinished() を呼んでいるはずですが、ソースをgrepしても見つかりません。
PARTNER-Jetでブレークポイントをしかけてみると、確かにここは実行されていることが確認できました。
どこから呼ばれている?
やっと見つけました。
$ grep -r BOOT_FINISHED * ... frameworkds/base/include/surfaceflinger/ISurfaceComposer.h: BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION, ...
frameworks/base/services/java/com/android/server/WindowManagerService.java
performEnableScreen
... 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, data, null, 0); data.recycle(); } } catch (RemoteException ex) { Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!"); }
WindowManagerServiceでした。こんな変則的な呼び出し方をしていました。
さらにこのメソッドはActivityManagerService#enableScreenAfterBoot から呼ばれています。
おまけ
rootのshellで以下のコマンドを実行するとブートアニメーションが始まります。
# setprop ctl.start bootanim
止めるときは
# setprop ctl.stop bootanim