[Android][Framework]Provision

Provision是什么?

Provision的作用很简单,就是一个系统初始化引导程序,源生的Android里面Provision只做了一件事,就是写入一个DEVICE_PROVISIONED标记。

这个标记作用很大,这个标记只会在系统 全新升级(双清)的时候写入一次,代表了Android系统升级准备完成,可以正常工作。

Provision在哪?

一般在package目录下:package/apps/Provision

Proision代码

Provision下只有一个Activity文件和一个Manifest文件

Manifest

<application>
  <activity android:name="DefaultActivity"
          android:excludeFromRecents="true">
      <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>
</application>

配置说明:

  • android:excludeFromRecents="true" 不在Recents界面显示。
  • <intent-filter android:priority="1"> 优先级高于Launcher,所以会先于Launcher启动(因为系统Launcher没有显式设置priority值,所以默认是0)
  • category.HOME 桌面程序标记,和Launcher属于一个级别

Activity

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.Secure.putInt(getContentResolver(), Settings.Secure.DEVICE_PROVISIONED, 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();
  }
}

源生代码很简单 做了两件事:

  1. 设置DEVICE_PROVISIONED标记
  2. 禁止Provision自己的Activity组件

对这个代码做点说明:

// Add a persistent setting to allow other apps to know the device has been provisioned.
Settings.Secure.putInt(getContentResolver(), Settings.Secure.DEVICE_PROVISIONED, 1);

注释表述很清楚:为设备写入持久的数据,并供其他App了解设备已经provision过。

class Settings The Settings provider contains global system-level device preferences. class Secure Secure system settings, containing system preferences that applications can read but are not allowed to write. These are for preferences that the user must explicitly modify through the system UI or specialized APIs for those values, not modified directly by applications. public static boolean putInt(ContentResolver cr, String name, int value) Convenience function for updating a single settings value as an integer. This will either create a new entry in the table if the given name does not exist, or modify the value of the existing row with that name. Note that internally setting values are always stored as strings, so this function converts the given value to a string before storing it.

  • @param cr The ContentResolver to access.
  • @param name The name of the setting to modify.
  • @param value The new value for the setting.
  • @return true if the value was set, false on database errors

禁止组件的功能

pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
    PackageManager.DONT_KILL_APP);

public abstract void setComponentEnabledSetting(ComponentName componentName, int newState, int flags); Set the enabled setting for a package component (activity, receiver, service, provider). This setting will override any enabled state which may have been set by the component in its manifest.

  • @param componentName The component to enable
  • @param newState The new enabled state for the component. The legal values for this state are:
    • COMPONENT_ENABLED_STATE_ENABLED
    • COMPONENT_ENABLED_STATE_DISABLED
    • COMPONENT_ENABLED_STATE_DEFAULT The last one removes the setting, thereby restoring the component’s state to whatever was set in it’s manifest (or enabled, by default).
  • @param flags Optional behavior flags: DONT_KILL_APP or 0.

功能禁止后,系统package信息会记录下来,保存在 /data/system/packages.xml

<package name="com.android.provision" codePath="/system/app/Provision.apk" nativeLibraryPath="/data/data/com.android.provision/lib"
flags="1" ft="11b7e237e00" it="11b7e237e00"
ut="11b7e237e00" version="15" userId="10005">
<sigs count="1">
<cert index="1" />
</sigs>
<disabled-components>
<item name="com.android.provision.DefaultActivity" />
</disabled-components>
</package>

这段代码就是记录的禁止DefaultActivity的信息。所以这个组件只会运行一次,之后就被禁止运行了。除非格式化/data/目录。

5X实现

/**
* Application that sets the provisioned bit, like SetupWizard does.
*/
public class DefaultActivity extends Activity {
  private static final String ORIGINAL_LAUNCHER_PACKAGENAME = "com.android.launcher3";
  private static final String ORIGINAL_LAUNCHER_CLASSNAME = "com.android.launcher3.Launcher";
  @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);
      //set Launcher3 as the preferred home activity
      setupDefaultLauncher(pm);
      // terminate the activity.
      finish();
  }

  private void setupDefaultLauncher(PackageManager pm){
      Intent queryIntent = new Intent();
      queryIntent.addCategory(Intent.CATEGORY_HOME);
      queryIntent.setAction(Intent.ACTION_MAIN);  // 查找符合 Launcher 的 activity

      List<ResolveInfo> homeActivities = pm.queryIntentActivities(queryIntent, 0);
      if(homeActivities == null) {
          return;
      }

      ComponentName defaultLauncher = new ComponentName(ORIGINAL_LAUNCHER_PACKAGENAME,
              ORIGINAL_LAUNCHER_CLASSNAME);
      int activityNum = homeActivities.size();
      ComponentName[] set = new ComponentName[activityNum];
      int defaultMatch = -1;
      for(int i = 0; i < activityNum; i++){ //从符合 Launcher 中查找 Launcher3
          ResolveInfo info = homeActivities.get(i);
          set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
          if(ORIGINAL_LAUNCHER_CLASSNAME.equals(info.activityInfo.name)
                  && ORIGINAL_LAUNCHER_PACKAGENAME.equals(info.activityInfo.packageName)){
              defaultMatch = info.match;
          }
      }

      //if Launcher3 is not found, do not set anything
      if(defaultMatch == -1){
          return;
      }
      IntentFilter filter = new IntentFilter();
      filter.addAction(Intent.ACTION_MAIN);
      filter.addCategory(Intent.CATEGORY_HOME);
      filter.addCategory(Intent.CATEGORY_DEFAULT);

      pm.addPreferredActivity(filter, defaultMatch, set, defaultLauncher);
  }
}

Reference Android 初始化Setup Wizard——Provision

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏腾讯BBTeam团队的专栏

sar - Linux 系统监控利器

sar可以从多方面对系统的活动进行报告,包括:文件的读写情况、系统调用的使用情况、磁盘I/O、CPU效率、内存使用状况、进程活动及IPC有关的活动等。

3206
来自专栏程序员互动联盟

【开发指南】如何为nexus 5编译固件

nexus 5是谷歌的亲儿子,而android的源码是开源的,那如果我有一个nexus 5手机,为何不自己为nexus 5编译软件呢? 开搞,本文假定已经有an...

39112
来自专栏俗人笔记

PHP中检测IP是否是内网IP的三个方法

2473
来自专栏coding

hexo搭建个人博客

搭建个人博客有很多种方式,最老牌的当属wordpress,功能丰富,但过于笨重。我想要的只是最简单的显示文章以及搜索功能,当然,样式要简洁漂亮,而且必须支持ma...

4747
来自专栏乐沙弥的世界

中小型数据库 RMAN CATALOG 备份恢复方案(一)

        对于数据库的稳定性,高可用,跨平台以及海量数据库的处理,Oracle 数据库通常是大型数据库和大企业的首选。尽管如此,仍然不乏很多中小企业想要品...

751
来自专栏Winter漫聊技术

Retrofit进阶

什么是Retrofit? 这类文章太多了,这里就不多做介绍,贴个官方链接: http://square.github.io/retrofit/

1072
来自专栏猿天地

Spring Cloud Gateway 限流操作

API网关作为所有请求的入口,请求量大,我们可以通过对并发访问的请求进行限速来保护系统的可用性。

1873
来自专栏hbbliyong

解决Electron加载带jquery的项目报错问题

<!-- Insert this line above script imports --> <script>if (typeof module === 'o...

3697
来自专栏Netkiller

PPTP VPN 服务器

本文节选自《Netkiller Linux 手札》 作者:netkiller 地址:www.netkiller.cn/linux/ 38.2. pptpd 38...

1.3K5
来自专栏Hadoop实操

Hive创建外部表CSV数据中列含有逗号问题处理

在不能修改示例数据的结构情况下,这里需要使用Hive提供的Serde,在Hive1.1版本中提供了多种Serde,此处的数据通过属于CSV格式,所以这里使用默认...

6116

扫码关注云+社区