安卓系统新手引导分析

源码修改之新手启动引导(setupwizard)

描述:为了提供更好的用户体验以及提供云服务(云端备份等),在用户初次使用安卓设备时,厂商基本都有一个新手引导程序存在。安卓源码也提供了新手引导的范例,我们可以遵循其设计规则定制自己的新手引导程序。

查找新手引导程序位置:

首次开机,进入到新手引导界面:

命令: <- dumpsys window(查看window视图的dump信息)

采集主要信息,我们发现 ***com.mstar.android.setupwizard.DefaultActivity*** 多次出现在调试窗口中,找到代码位置

位置:<-device/mstar/common/apps/MSetupWizard/
查看源码文件:MSetupWizard
  • xml文件:









    1. 其一,注意中的android:priority=”2”属性。其中的priority属性为优先级,默认为0,范围[-1000,1000],这里声明2

    2. 其二,注意属性,这个属性是让app作为launcher界面存在,默认的编写中是没有上面的priority属性的。

    3. 我们大胆猜测,这里新手引导也是作为launcher界面存在,系统在启动HOME界面时候会把它加载到Launcher列表中执行,因为其属性值比其他默认的Launcher都要高,拥有优先启动的权利。

  • 上面我们知道了这个app作为Launcher存在,但是为什么只启动一次呢?下面我们来看一下源码!

    private void finishSetupWizard() {
    
        // Add a persistent setting to allow other apps to know the device has been provisioned.
        Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
        Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);
    
        // remove this activity from the package manager.
        PackageManager pm = getPackageManager();
        ComponentName name = new ComponentName(this, DefaultActivity.class);
        pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
        finish();
    }
    
    1. 我们在查看代码finish之前发现了这段代码,意思就是 :

      1. 添加全局设置并且其他app可以知道设备已经注册过
      2. 从包管理器中移除这个app
      3. setComponentEnabledSetting禁用组件

  • 查找属性的Activity(原生的setupwizard)

代码路径:packages/apps/Provision/AndroidManifest.xml

<intent-filter android:priority="1">
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.HOME" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

对应的Activity:packages/apps/Provision/src/com/android/provision/DefaultActivity.java

public class DefaultActivity extends Activity {
    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        // Add a persistent setting to allow other apps to know the device has been provisioned.
        Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
        Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);
        // remove this activity from the package manager.
        PackageManager pm = getPackageManager();
        ComponentName name = new ComponentName(this, DefaultActivity.class);
        pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);
        // terminate the activity.
        finish();
    }
}

上面可以看出,这里是谷歌源码提供的setupwizard引导页,逻辑和上面的几乎一致。这里的表明优先级高于HOME,但是三方定制则将其改成了2,由此可见我们的猜想是正确的。

源码修改

  1. 屏蔽MStar的setupwizard逻辑

    • 注释或者删除MSetupWizard的mk文件,让其不参与编译

      # LOCAL_PATH:= $(call my-dir)
      # include $(CLEAR_VARS)
      # LOCAL_MODULE_TAGS := optional
      # LOCAL_SRC_FILES := $(call all-subdir-java-files)
      # LOCAL_PACKAGE_NAME := MSetupWizard
      # LOCAL_CERTIFICATE := platform           
      # LOCAL_OVERRIDES_PACKAGES := Provision SetupWizard
      # include $(BUILD_PACKAGE)
      
    • 注:
      (1)platform签名:

      AndroidManifest.xml的manifest节点中添加 android:sharedUserId="android.uid.system",
      Android.mk中增加  LOCAL_CERTIFICATE := platform
      

      (2)shared签名:

      AndroidManifest.xml的manifest节点中增加android:sharedUserId="android.uid.shared",
      Android.mk中增加LOCAL_CERTIFICATE := shared
      

      (3)media签名:

      AndroidManifest.xml的manifest节点中增加 android:sharedUserId="android.media",
      Android.mk中增加 LOCAL_CERTIFICATE := media
      
  2. 添加定制的setupwizard程序到系统

    • 在packages/app/下新建自己的目录,例如:M_Guide

    • 将apk文件上传到该目录下:M_Guide.apk

    • 编写对应的mk文件,例如:

      LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      LOCAL_MODULE := M_Guide
      LOCAL_MODULE_TAGS := optional
      LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
      LOCAL_MODULE_CLASS := APPS
      LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
      LOCAL_CERTIFICATE := platform
      LOCAL_PRIVILEGED_MODULE := false  <-- false表示生成在app下(系统不可删除)ture生成在priv-app(系统核心不可删除)
      LOCAL_BUILT_MODULE_STEM := package.apk
      LOCAL_OVERRIDES_PACKAGES := Provision SetupWizard   <--覆盖编译
      include $(BUILD_PREBUILT)
      

      注意这里的LOCAL_OVERRIDES_PACKAGES 这里是覆盖编译 Provision SetupWizard ,仿照的三方mmk文件的编写方式,我们需要替换其逻辑,就要将我们的apk编译时替换原生的,当然你也可以让其三方的编译(三方编译时会覆盖原生的),然后你自己再替换三方的MSetupWizard

    • 根目录下编译,生成M_Guide文件,例如:

      mmm packages/app/M_Guide

  3. 屏蔽三方的setupwizard程序,添加自己apk到系统

    • 查找源码包含 MSetupWizard 的mk文件,这个文件一般包含在三方厂商的定制代码中,如果没有可忽略

    • 添加自己的程序到device.mk文件,让其成为系统内置软件。例如:

      # Apps
      PRODUCT_PACKAGES += \
            M_Guide \            <--在这里添加自己的app程序
          livecap \
          MLeanbackTv \
          MTvPlayer \
          MTvHotkey \
          MTvMisc \
          MTvFactory \
          MTvTest \
          MBrowser3 \
          MLocalMM2 \
          MMCastDemo \
          Launcher3 \
      #    DMP \
          DMS
      
    • 为什么添加在这里,我们可以查看Launche3的对应mk文件,部分如下:

      LOCAL_SDK_VERSION := current
      LOCAL_PACKAGE_NAME := Launcher3
      LOCAL_PRIVILEGED_MODULE := true
      # MStar Android Patch Begin
      LOCAL_CERTIFICATE := platform
      # MStar Android Patch End
      LOCAL_OVERRIDES_PACKAGES := Home Launcher2    <--这里覆盖了原生的Launcher2
      include $(BUILD_PACKAGE)
      

      我们的基本逻辑和以上的相似,所以我们按照其添加方式进行编写

    • 删除out目录下的MSetupWizard文件,make snod打包到系统

    • 刷入系统验证(进入恢复出厂即可走此程序)

小结:

android源码提供的开机新手引导界面的设计逻辑如下:

  1. 系统的新手引导app以Launcher形式存在源码中
  2. 作为引导的app的优先级priority属性要相应提高(谷歌自己的默认1)
  3. 新手引导app程序中在finish之前,设置已注册设备以及调用setComponentEnabledSetting禁用当前组件

优点:源代码无需改动,即可添加替换新手引导程序,系统恢复出厂默认调用,深度解耦代码逻辑
缺点:不良厂商可能会修改priority属性优先级最高,已确保自己的Launcher始终处于默认状态

坚持原创技术分享,您的支持将鼓励我继续创作!