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