Google开机向导解析

Google开机向导简介

以下涉及Google开机向导的代码,均来自于反编译自版本227.4976668的SetupWizard.apk。

Launcher

开机向导本质是个Launcher,查看其manifest XML文件可以发现,其MAIN activity声明了category.HOME,且优先级为 5


    
        
        
        
        ...
    

启动

当AMS准备好之后(systemReady),会调用startHomeActivityLocked()方法启动优先级最高的Launcher应用。当Google开机向导优先级最高时,就会启动com.google.android.setupwizard.SetupWizardActivity

进入Google开机向导后,首先判断系统是否需要进入开机向导,通过以下三个参数的值:

  • Settings$Global.DEVICE_PROVISIONED
  • Settings$Secure.USER_SETUP_COMPLETE
  • System property: ro.setupwizard.mode (默认配置为OPTIONAL)

其中DEVICE_PROVISIONED用来标识是否走完开机向导。
USER_SETUP_COMPLETEro.setupwizard.mode是为多用户系统服务的,所知不多,暂不深入分析。

DEVICE_PROVISIONED为0,表示需要正常进入开机向导流程。

Wizard script

Google开机向导定义了一个wizard script的XML格式,用来描述开机向导的流程,格式如下:



    
    
        
        
        
    
    ...

如上,Google开机向导的每一步均以WizardAction标签表示,id作为该步骤的唯一标识,内容可以是intent uri,也可以是另一个script uri;下一步的走向则由result标签决定,根据resultCode的不同,会走到不同的流程。

Google开机向导启动后,需要解析该wizard script,WizardAction标签的内容解析为WizardAction类对象,
所解析出来的这些WizardAction对象存在WizardScript对象的一个列表中:

public class WizardAction implements Parcelable {
    private final WizardBranchArray mBranches;    // 对应"result"标签,继承"SparseArray","resultCode"为key
    private final String mId;   // 对应`id`标签
    private final int mIndex;   // Action列表中的位置
    private final String mScriptUri;    // 所属script uri
    private final String mUri;    // Intent uri
    ...
}

First intent

获取到WizardAction列表之后,需要取出第一个可用的action,新建一个WizardStack对象,将第一个action放到栈顶,并构建相应的intent,跳转到该页面,如此便到了用户可见的第一个页面。

com.google.android.wizardmanager.WizardManager:

private static Intent buildIntent(Context context, Intent originalIntent, WizardStack stack, Callback callback) {
    WizardAction action = stack.peek();   // 获取栈顶的WizardAction
    Intent wizardIntent = action.getIntent();   // 由wizard script中的intent uri转化而来
    if (wizardIntent == null) {
        return null;
    }

    wizardIntent.putExtras(originalIntent);
    // 启动下一个界面时必须传入当前的WizardStack对象引用与当前的WizardAction对象引用
    Bundle args = new Bundle();
    WizardBundleHelper.putParcelableAsByteArray(args, "stack", stack);
    WizardBundleHelper.putParcelableAsByteArray(args, "action", action);
    wizardIntent.putExtra("wizardBundle", args);
    return wizardIntent;
}

Next intent

进入wizard script定义的第一个页面之后,怎么跳转到下一个页面呢?

Google 开机向导通过构建一个action为com.android.wizard.NEXT的nextIntent,并跳转到nextIntent。Android原生提供了帮助类WizardManagerHelper来构建next intent。Next intent中必不可少的参数是wizardBundle,如上代码所示,wizardBundle对应的参数包含了WizardStack对象引用与当前的WizardAction对象引用,如果缺少该参数,Google开机向导将不会执行下一步操作,直接返回,导致流程无法进行下去。而resultCode则用来标识下一步的action,从wizard script示例中可以看到,每个resultCode对应一个id,通过传入resultCode可以指定下一步的action,没有resultCode则执行默认的action。

com.android.setupwizardlib.util.WizardManagerHelper:

public static Intent getNextIntent(Intent originalIntent, int resultCode, Intent data) {
    Intent intent = new Intent("com.android.wizard.NEXT");
    intent.putExtra("wizardBundle", originalIntent.getBundleExtra("wizardBundle"));
    ...
    intent.putExtra("com.android.setupwizard.ResultCode", resultCode);
    ...
    return intent;
}

退出Google开机向导

Google开机向导启动时候做了很多初始化操作,例如调用StatusBarManager.disable()来禁用状态栏,具体可查看com.google.android.setupwizard.lifecycle包下面的一些实现。

相应的,在结束时应当将这些初始化操作复原,这些复原操作都是在退出时进行的。在Google开机向导的wizard script中可以看到最后的action是com.android.setupwizard.EXIT,查看对应的activity com.google.android.setupwizard.SetupWizardExitActivity

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  ...
    FrpHelper.get(this).removeFrpNotification();
    boolean isDeferredSetup = WizardManagerHelper.isDeferredSetupWizard(getIntent());
    ExitHelper.get(this).finishSetup(isDeferredSetup);
    ExitHelper.get(this).launchExitIntent(this, isDeferredSetup);
    ExitHelper.get(this).finishAllAppTasks(this);
}

可以看到,Google开机向导实现了一个工具类ExitHelper处理退出的一些操作。

你可能感兴趣的:(Google开机向导解析)