Android系统启动之BOOT_COMPLETED广播

Posted 迷途小书童Eric

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android系统启动之BOOT_COMPLETED广播相关的知识,希望对你有一定的参考价值。

对于android系统启动的流程,大家应该很熟悉了。但系统启动是一个很复杂的过程,中间夹杂着很多细节。比如,开机动画什么退出?BOOT_COMPLETED广播什么时候发送?该广播有什么特点?等等。这篇文章就详细说一下。

大家知道,在开机启动流程最后,AMS会调用startHomeActivityLocked(),用来启动Launcher,Launcher启动后,会回调AMS的activityIdle,代码如下:

final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean
                          fromTimeout, Configuration config) 
    ...
    ActivityRecord r = ActivityRecord.forToken(token);
    if (r != null) 
        ...
        if (isFrontStack(r.task.stack) || fromTimeout) 
                booting = checkFinishBootingLocked();
        
    
    ...

因为系统刚启动,Launcher肯定是FrontStack,所以会调用checkFinishBootingLocked(),该函数代码如下:

private boolean checkFinishBootingLocked() 
    final boolean booting = mService.mBooting;
    boolean enableScreen = false;
    mService.mBooting = false;
    if (!mService.mBooted) 
        mService.mBooted = true;
        enableScreen = true;
    
    if (booting || enableScreen) 
        mService.postFinishBooting(booting, enableScreen);
    
    return booting;

这里有两个关键变量,mService.mBooting和mService.mBooted。
mService.mBooting是AMS在AMS.systemRead()中,调用startHomeActivityLocked()之前设置为ture的。因此这里肯定=true。
mService.mBooted设置的地方比较多,这里不一一列举了,但这时候因为没有系统还未启动完毕,因此该值为false。
所以,booting=true;enableScreen=true。
进一步调用mService.postFinishBooting(true, true)。该函数代码如下:

void postFinishBooting(boolean finishBooting, boolean enableScreen) 
    mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG,
            finishBooting? 1 : 0, enableScreen ? 1 : 0));

可见,是想AMS的MainHandler发送FINISH_BOOTING_MSG消息,并且该消息的两个参数arg1=1,arg2=1。

final class MainHandler extends Handler 
    public void handleMessage(Message msg) 
        ...
        case FINISH_BOOTING_MSG: 
            if (msg.arg1 != 0) 
                finishBooting();
            
            if (msg.arg2 != 0) 
                enableScreenAfterBoot();
            
            break;
        
           ...
    

FINISH_BOOTING_MSG的消息处理为:先调用finishBooting(),后调用enableScreenAfterBoot()。
下面一个一个分析,先分析finishBooting():

finishBooting()

final void finishBooting() 
    synchronized (this) 
        if (!mBootAnimationComplete) 
            mCallFinishBooting = true; 
            return;
        
        mCallFinishBooting = false;
    

刚进入该函数,就会判断mBootAnimationComplete。如果该值为false,将变量mCallFinishBooting设置为true,不做处理,直接返回。看来,如果要发送BOOT_COMPLETED广播,该变量必须等于true。
变量mBootAnimationComplete在初始化时,设置为false。那么,在哪里把修改为true呢?答案是在bootAnimationComplete()中。

public void bootAnimationComplete() 
    final boolean callFinishBooting;
    synchronized (this) 
        callFinishBooting = mCallFinishBooting;
        mBootAnimationComplete = true;
    
    if (callFinishBooting) 
        finishBooting();
    

结合finishBooting()的源码,可以得到以下逻辑:
- 如果AMS在调用finishBooting()时候,mBootAnimationComplete还没有被设置为true时,则不做处理,直接返回。
- 当其他地方调用bootAnimationComplete()时,发现callFinishBooting=true,会再一次调用finishBooting(),这时候该函数不会直接返回,而是继续往下执行(主要工作就是发送BOOT_COMPLETED广播)。
为什么这么做呢?看来是有件事很重要,必须处理完后,系统才能发送BOOT_COMPLETED广播。那么谁会调用bootAnimationComplete()呢?带着以上疑问,我们看一下FINISH_BOOTING_MSG消息处理的另外一个操作:enableScreenAfterBoot()。

enableScreenAfterBoot()

根据之前的描述,调用完finishBooting(),会调用enableScreenAfterBoot()。下面看一下该函数的代码:

void enableScreenAfterBoot() 
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
            SystemClock.uptimeMillis());
    mWindowManager.enableScreenAfterBoot();

    synchronized (this) 
        updateEventDispatchingLocked();
    

比较简单,进一步调用了WMS.enableScreenAfterBoot()。

public void enableScreenAfterBoot() 
    synchronized(mWindowMap) 
        ...
        if (mSystemBooted)  //保证该函数只调用一次
            return;
        
        mSystemBooted = true; 
        ...
        // If the screen still doesn't come up after 30 seconds, give
        // up and turn it on.
        mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30*1000); 
    

    ...
    performEnableScreen();  

该函数会延时30s后发送BOOT_TIMEOUT消息,这里先不说,后面会介绍该消息的作用。进一步看一下performEnableScreen():

public void performEnableScreen() 
    synchronized(mWindowMap) 
        ...
        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();
                
             catch (RemoteException ex) 
                Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!");
            
            mBootAnimationStopped = true;
        

        if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) 
            ...
            return;
        
    ...
    try 
        mActivityManager.bootAnimationComplete();
     catch (RemoteException e) 
    

    mPolicy.enableScreenAfterBoot();

    // Make sure the last requested orientation has been applied.
    updateRotationUnchecked(false, false);

可见,通过binder调用了SurfaceFlinger的bootFinished(),该函数会设置属性值“service.bootanim.exit”为1。同时,bootanimation播放开机动画时候,会循环检查该属性值是否为1,如果为1且动画播放完成,则会退出bootanimation。
之后,该函数又调用了checkBootAnimationCompleteLocked:

private boolean checkBootAnimationCompleteLocked() 
   if (SystemService.isRunning(BOOT_ANIMATION_SERVICE)) 
       mH.removeMessages(H.CHECK_IF_BOOT_ANIMATION_FINISHED);
       mH.sendEmptyMessageDelayed(H.CHECK_IF_BOOT_ANIMATION_FINISHED,
               BOOT_ANIMATION_POLL_INTERVAL);
       ...
       return false;
   
   ...
   return true;

这里的BOOT_ANIMATION_SERVICE即bootanim,也就是bootanimation开机动画进程。如果bootanimation进程退出了,则返回true,performEnableScreen()会接着往下进行;如果bootanimation进程没有退出,则会每隔0.2s检查bootanimation是否退出,直到该进程退出,然后再次调用performEnableScreen()。

接着看performEnableScreen()。经过上面的逻辑,如果bootanimation进程退出了,performEnableScreen()会继续往下执行,就会调用mActivityManager.bootAnimationComplete()。

到这里,就清楚了,在发送BOOT_COMPLETED之前,需要完成的工作就是通知bootanimation停止播放动画,且等待bootanim进程退出。

前面介绍enableScreenAfterBoot()时候,说过该函数会延时30s后发送BOOT_TIMEOUT消息。该消息的作用就是万一过了30s之后,bootanimaiton还没有退出,系统也会强制发送BOOT_COMPLETED广播,并进入桌面。

BOOT_COMPLETED广播

发送BOOT_COMPLETED广播的代码如下:

final void finishBooting() 
    ...
    Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
        intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
        intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
        broadcastIntentLocked(null, null, intent, null,
             new IIntentReceiver.Stub() 
                 @Override
                 public void performReceive(Intent intent, int resultCode,
                        String data, Bundle extras, boolean ordered,
                        boolean sticky, int sendingUser) 
                            synchronized (ActivityManagerService.this) 
                                       requestPssAllProcsLocked(SystemClock.uptimeMillis(),
                                                    true, false);
                                        
                                    
                                ,
           0, null, null,
           android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
           AppOpsManager.OP_NONE, true, false, MY_PID, Process.SYSTEM_UID,                              userId);

    ...

注意broadcastIntentLocked()中倒数第四和第五个参数。其中倒数第四个参数为false,代表该广播不是sticky广播,倒数第五个为true,表示该广播是串行广播。
串行广播意味着,BOOT_COMPLETED广播会根据优先级依次发送给接收者。当然,系统级服务或应用优先级高,会先收到并处理该广播。第三方App优先级低,接收到该广播的时间会晚一些。

以上是关于Android系统启动之BOOT_COMPLETED广播的主要内容,如果未能解决你的问题,请参考以下文章

Android 进阶——系统启动之SystemServer创建并启动Installer服务

Android 进阶——系统启动之SystemServer创建并启动Installer服务

Android 进阶——系统启动之SystemServer创建并启动Installer服务

Android 进阶——系统启动之SystemServer创建并启动Installer服务

Android 进阶——系统启动之核心SystemServer进程启动详解

Android 进阶——系统启动之核心SystemServer进程启动详解