Provision
的作用很简单,就是一个系统初始化引导程序,源生的Android里面Provision
只做了一件事,就是写入一个DEVICE_PROVISIONED
标记。
这个标记作用很大,这个标记只会在系统 全新升级(双清)的时候写入一次,代表了Android系统升级准备完成,可以正常工作。
一般在package
目录下:package/apps/Provision
。
Provision
下只有一个Activity
文件和一个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
属于一个级别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();
}
}
源生代码很简单 做了两件事:
DEVICE_PROVISIONED
标记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.
禁止组件的功能
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.
功能禁止后,系统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/
目录。
/**
* 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