今天遇到一个应用无法打开的问题,跟踪代码发现app在onCreate的时候会检查
inKeyguardRestrictedInputMode() 这是KeyguardManager的方法
解释为:
/**
* If keyguard screen is showing or in restricted key input mode (i.e. in
* keyguard password emergency screen). When in such mode, certain keys,
* such as the Home key and the right soft keys, don't work.
*
* @return true if in keyguard restricted input mode.
*
* @see android.view.WindowManagerPolicy#inKeyguardRestrictedKeyInputMode
*/
public boolean inKeyguardRestrictedInputMode() {
try {
return mWM.inKeyguardRestrictedInputMode();
} catch (RemoteException ex) {
return false;
}
}
进一步追踪到:
/**
* Given the state of the keyguard, is the input restricted?
* Input is restricted when the keyguard is showing, or when the keyguard
* was suppressed by an app that disabled the keyguard or we haven't been provisioned yet.
*/
public boolean isInputRestricted() {
return mShowing || mNeedToReshowWhenReenabled || !mUpdateMonitor.isDeviceProvisioned();
}
发现最终决定于mUpdateMonitor.isDeviceProvisioned,再追:
/**
* @return Whether the device is provisioned (whether they have gone through
* the setup wizard)
*/
public boolean isDeviceProvisioned() {
return mDeviceProvisioned;
}
那mDeviceProvisioned是怎么来的呢,再查:
private boolean isDeviceProvisionedInSettingsDb() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0) != 0;
}
ok, 是读的数据库里面的Settings.Global.DEVICE_PROVISIONED 字段,那是谁会去改写这个呢,终于在Provision里面找到了:
Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
看来只要Provision运行过了,这个字段就可以正确,最终发现果然是这个apk没有运行。
以前没怎么关注过这个apk,顿时来了兴趣。
看看他的Manifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.provision">
<original-package android:name="com.android.provision" />
<!-- For miscellaneous settings -->
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<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>
</manifest>
居然是category.HOME, 换句话说它是个launcher,而且android:priority="1" ,那他应该会是我们常见的launcher才对啊,不过它没有UI,奇怪了。
再看看代码。
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();
}
}
原来它除了写Settings.Global.DEVICE_PROVISIONED 和Settings.Secure.USER_SETUP_COMPLETE 之外还会把 自己COMPONENT_ENABLED_STATE_DISABLED,
看来确实是系统启动之后先调的它,但是它把自己disable了,我们常见的launcher才会机会起来,ok,我猜是这样的。
那luncher是怎么起来的呢?貌似要去看framework了。let’s go!
找到了 ActivityManagerService里面有个startHomeActivityLocked,应该是它干的,至于为什么就比较复杂了,有机会详细追下。
其中我关注的代码;
Intent intent = getHomeIntent();
ActivityInfo aInfo =
resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
resolveActivityInfo 会根据intent返回一个ActivityInfo,就知道要启动哪个了。怎么resolve?
PackageManagerService给出了:
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "resolve intent");
List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
return chooseBestActivity(intent, resolvedType, flags, query, userId);
}
queryIntentActivities 然后 chooseBestActivity , ok 明显了, 在Provision没有disable的时候,choose的就是它,要是disable了才轮到真正的launcher。
好复杂!