系统设置的activity是 Settings,另外有40多个activity继承于它,比如设置的一级菜单: wifi,蓝牙,声音,显示,安全,应用程序,语言和时间,关于设备等等。实际上都是这一个acitivy。
这里从安全设置看起,SecuritySettings.Java
以资源文件R.xml.security_settings_* 填充【根据当前锁屏方式,拥有者信息,密码显示等具体情形,加载不同的资源或配置】,具体在createPreferenceHierarchy() 和 onResume中
以改锁屏方式为主线,点击锁屏项时,onPreferenceTreeClick #587
调用,
在key值为KEY_UNLOCK_SET_OR_CHANGE则跳转到fragmen ----
`startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment", `
--------------------------------
偶然间发现,设置未知来源的开关
Settings.Global.INSTALL_NON_MARKET_APPS的值 enabled ? 1 : 0)
0 :为不允许安装未知来源apk
1 :为允许安装未知来源apk
这个值存储在了setting provider中,目录/data/data/com.android.providers.settings/的db文件,表secure的install_non_market_apps字段
试了一下adb操作发现是可以控制的
adb shell settings put secure install_non_market_apps 0 //设置不允许安装未知来源
adb shell settings get secure install_non_market_apps //获取状态
--------------------------------
回到ChooseLockGeneric.java中
它有一个内部类 ChooseLockGenericFragment extends SettingsPreferenceFragment
在ChooseLockGenericFragment的updatePreferencesOrFinish()方法中有这样一行代码用于显示所有的解锁方式:
addPreferencesFromResource(R.xml.security_settings_picker);
/src/xml/security_settings_picker.xml这个文件,就是用来配置所有解锁方式的文件,来看看它的源码:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:key="unlock_set_off"
android:title="@string/unlock_set_unlock_off_title"
android:persistent="false"/>
<PreferenceScreen
android:key="unlock_set_none"
android:title="@string/unlock_set_unlock_none_title"
android:persistent="false"/>
............
<PreferenceScreen
android:key="unlock_set_fingerprint"
android:title="@string/unlock_set_unlock_fingerprint_title"
android:persistent="false"/>
</PreferenceScreen>
屏幕锁定方式一共包含无,滑动,人脸解锁,图案,PIN,密码6中方式
选择一种方式后,执行updateUnlockMethodAndFinish(方式,启用锁屏?)
继续看ChooseLockGenericFragment的 onPreferenceTreeClick()方法,这个方法就是处理每一项的点击事件的方法。
如果我们选择无,也就是没有锁屏,点亮屏幕直接进入主屏,走的就是这个if语句:
final String key = preference.getKey();
if (KEY_UNLOCK_SET_OFF.equals(key) ) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true);
}
这里直接将事件交给了 updateUnlockMethodAndFinish(int quality, boolean disabled)方法处理。
这个方法的第一个参数表示解锁方式的等级,在android.app.admin.DevicePolicyManager.java中定义了各个解锁方式对应的等级值,从小到大,表示解锁方式安全性的由弱到强:
无和滑动两个对应的值都是PASSWORD_QUALITY_UNSPECIFIED = 0
注意这个方法的第二个参数disabled是一个boolean值,如果不使用任何解锁方式,也就是无,那么updateUnlockMethodAndFinish中的这个值就应该传入true;否则都应该传入false,所以源码中只有处理无选项的updateUnlockMethodAndFinish方法传入了true,其他都传入false。
我们这里分析的是无选项,所以只看它对应的代码
......
} else if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
mChooseLockSettingsHelper.utils().clearLock(false);
mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled);
getActivity().setResult(Activity.RESULT_OK);
finish();
}
......
mChooseLockSettingsHelper.utils().clearLock(false);这行代码是用来清除所有的锁屏方式的,看一下它里面的代码:
com.android.internal.widget.LockPatternUtils.java
/**
* Clear any lock pattern or password.
*/
public void clearLock(boolean isFallback, int userHandle) {
if(!isFallback) deleteGallery(userHandle);
saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, isFallback,
userHandle);
setLockPatternEnabled(false, userHandle);
saveLockPattern(null, isFallback, userHandle);
setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
userHandle);
onAfterChangingPassword(userHandle);
}
而 mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled);这个disabled值就是上面传进来的true,也就是意味着不使用任何解锁方法
接着finish之后会回调 SecuritySettings中的onActivityResult()方法。
这个方法里面会调用 createPreferenceHierarchy()方法,而这个方法中的addPreferencesFromResource(resid);就是在SecuritySettings界面显示刚刚选择的解锁方式,如图:
我们来仔细看一下代码:
在com.android.settings.SecuritySettings$$getResIdForLockUnlockScreen()
if (!lockPatternUtils.isSecure()) {
// if there are multiple users, disable "None" setting
UserManager mUm = (UserManager) context. getSystemService(Context.USER_SERVICE);
List<UserInfo> users = mUm.getUsers(true);
final boolean singleUser = users.size() == 1;
if (singleUser && lockPatternUtils.isLockScreenDisabled()) {
resid = R.xml.security_settings_lockscreen;
} else {
resid = R.xml.security_settings_chooser;
}
R.xml.security_settings_lockscreen 是无对应的布局;
R.xml.security_settings_chooser 是滑动解锁对应的布局.
看一下mLockPatternUtils.isLockScreenDisabled()这个方法:
/**
* Determine if LockScreen can be disabled. This is used, for example, to tell if we should
* show LockScreen or go straight to the home screen.
*
* @return true if lock screen is can be disabled
*/
public boolean isLockScreenDisabled() {
if (!isSecure() && getLong(DISABLE_LOCKSCREEN_KEY, 0) != 0) {
// Check if the number of switchable users forces the lockscreen.
final List<UserInfo> users = UserManager.get(mContext).getUsers(true);
final int userCount = users.size();
int switchableUsers = 0;
for (int i = 0; i < userCount; i++) {
if (users.get(i).supportsSwitchTo()) {
switchableUsers++;
}
}
return switchableUsers < 2;
}
return false;
}
也就是说如果是isSecure()为false,并且getLong的值不为0,就显示无。
getLong的值在刚刚的setLockScreenDisabled(disabled)中已经设置为 1 了。所以就来看看isSecure()方法:
public boolean isSecure(int userId) {
long mode = getKeyguardStoredPasswordQuality(userId);
final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
|| mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
|| mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
|| mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
|| mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
final boolean secure =
isPattern && isLockPatternEnabled(userId) && savedPatternExists(userId)
|| isPassword && savedPasswordExists(userId);
return secure;
}
由于当前的mode是
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,所以isPattern为false,所以isSecure()肯定返回false了。
最终我们看到的就是无这个选项了。
另外,滑动解锁与无的唯一区别就是,调用 updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, true) 是传入的是true,也就是使用锁屏,而无传入的这个值是false。
private LockPatternUtils mLockPatternUtils;
mLockPatternUtils = new LockPatternUtils(getActivity());
if (KEY_SYSTEM_SAFE_NONE.equals(key)) { //达到设置锁定方式为无
mLockPatternUtils.clearLock(false);
mLockPatternUtils.setLockScreenDisabled(true);
}else if(KEY_SYSTEM_SAFE_SLIP.equals(key)){ ////达到设置锁定方式为滑动
mLockPatternUtils.clearLock(false);
mLockPatternUtils.setLockScreenDisabled(false);
}
-
pin码锁屏设置
之前的步骤都一样,所以我们从上面的ChooseLockGeneric.java开始分析。
继续看ChooseLockGenericFragment的 onPreferenceTreeClick()方法,
执行onPreferenceTreeClick方法之后,因为选择的是pin解锁,走的就是这个if语句:
} else if (KEY_UNLOCK_SET_PIN.equals(unlockMethod)) {
maybeEnableEncryption(
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, false);
} ...
看一下它调用调用updateUnlockMethodAndFinish()方法,走的是哪段代码:
final boolean isFallback = getActivity().getIntent()
.getBooleanExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false);
quality = upgradeQuality(quality, null);
final Context context = getActivity();
if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
int minLength = mDPM.getPasswordMinimumLength(null);
if (minLength < MIN_PASSWORD_LENGTH) {
minLength = MIN_PASSWORD_LENGTH;
}
final int maxLength = mDPM.getPasswordMaximumLength(quality);
Intent intent = getLockPasswordIntent(context, quality, isFallback, minLength,
maxLength, mRequirePassword, /* confirm credentials */false);
if (isFallback) {
startActivityForResult(intent, FALLBACK_REQUEST);
return;
} else {
mFinishPending = true;
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
}
} else if ...
来看一下这段代码中的if (isFallback) {...}部分:
首先值得一提的是,如果我们选择一种安全性比较弱(介于 UNSPECIFIED 和SOMETHING之间)的解锁方式,例如语音解锁,那么我们不仅要录如自己的语音命令,还要选择一个备用的解锁方式,如图:
这里的这个isFallback就是用来判断当前这个pin解锁方式,是直接选择的呢,还是其他解锁方式的备用解锁方式。如果是直接选择的,那就直接startActivity(intent);,否则就startActivityForResult(intent, FALLBACK_REQUEST);
姑且先考虑pin码直接解锁这种情形,所以走else部分.
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
}
以这种方式启动Activity的意思就是说,因为ChooseLockGeneric是由SecuritySettings启动的,所以ChooseLockGeneric启动ChooseLockPattern之后, ChooseLockPattern的setResult方法会将结果返回给SecuritySettings而不是ChooseLockGeneric.java,这是应该注意的。
接着就是绘制两次图案了,在ChooseLockPassword.java中的handNext()方法中有这样一行代码:
if (mFirstPin.equals(pin)) {
final boolean isFallback = getActivity().getIntent().getBooleanExtra(
LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false);
boolean wasSecureBefore = mLockPatternUtils.isSecure();
mLockPatternUtils.clearLock(isFallback);
final boolean required = getActivity().getIntent().getBooleanExtra(
EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
mLockPatternUtils.setCredentialRequiredToDecrypt(required);
mLockPatternUtils.saveLockPassword(pin, mRequestedQuality, isFallback);
getActivity().setResult(RESULT_FINISHED);
getActivity().finish();
mDone = true;
if (!wasSecureBefore) {
startActivity(getRedactionInterstitialIntent(getActivity()));
}
}
关键两句
mLockPatternUtils.setCredentialRequiredToDecrypt(required);
mLockPatternUtils.saveLockPassword(pin, mRequestedQuality, isFallback);
来看一下LockPatternUtils.java里的setCredentialRequiredToDecrypt()
public void setCredentialRequiredToDecrypt(boolean required) {
if (getCurrentUser() != UserHandle.USER_OWNER) {
Log.w(TAG, "Only device owner may call setCredentialRequiredForDecrypt()");
return;
}
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0);
}
验证pin密码
/**
* Check to see if a password matches the saved password. If no password exists,
* always returns true.
* @param password The password to check.
* @return Whether the password matches the stored one.
*/
public boolean checkPassword(String password) {
final int userId = getCurrentOrCallingUserId();
try {
return getLockSettings().checkPassword(password, userId);
} catch (RemoteException re) {
return true;
}
}
- 图案解锁方式
接着来看一下选择图案解锁方式的流程
. 之前的步骤都一样,所以我们还是从上面的ChooseLockGeneric.java开始分析。
执行onPreferenceTreeClick方法之后,因为选择的是图案解锁,走的就是这个if语句
else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
updateUnlockMethodAndFinish(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, false);
}
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING的值是0x10000
. 看一下它调用调用updateUnlockMethodAndFinish()方法,走的是哪段代码:
else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
Intent intent = new Intent(getActivity(), ChooseLockPattern.class);
intent.putExtra("key_lock_method", "pattern");
intent.putExtra(CONFIRM_CREDENTIALS, false);
intent.putExtra(IphoneLockPatternUtils.LOCKSCREEN_WEAK_FALLBACK,
isFallback); //M: modify for voice unlock
if (isFallback) {
//M: add for voice unlock @{
String isFallbackFor = getActivity().getIntent().
getStringExtra(IphoneLockPatternUtils.LOCKSCREEN_WEAK_FALLBACK_FOR);
String commandKey = getActivity().getIntent().
getStringExtra(IphoneLockPatternUtils.SETTINGS_COMMAND_KEY);
String commandValue = getActivity().getIntent().
getStringExtra(IphoneLockPatternUtils.SETTINGS_COMMAND_VALUE);
intent.putExtra(IphoneLockPatternUtils.SETTINGS_COMMAND_KEY, commandKey);
intent.putExtra(IphoneLockPatternUtils.SETTINGS_COMMAND_VALUE, commandValue);
intent.putExtra(IphoneLockPatternUtils.LOCKSCREEN_WEAK_FALLBACK_FOR, isFallbackFor);
//@}
startActivityForResult(intent, FALLBACK_REQUEST);
return;
} else {
mFinishPending = true;
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
}
}
来看一下这段代码中的if (isFallback) {...}
部分:
之前说到是,如果我们选择一种安全性比较弱(介于 UNSPECIFIED 和SOMETHING之间)的解锁方式,
这里的这个isFallback就是用来判断当前这个图案解锁方式,是直接选择的呢,还是其他解锁方式的备用解锁方式。如果是直接选择的,那就直接startActivity(intent);,否则就
startActivityForResult(intent, FALLBACK_REQUEST);
我们暂且先考虑直接选择图案解锁这种情形,也就是走else部分。
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
}
以这种方式启动Activity的意思就是说,因为ChooseLockGeneric是由SecuritySettings启动的,所以ChooseLockGeneric启动ChooseLockPattern之后, ChooseLockPattern的setResult方法会将结果返回给SecuritySettings而不是ChooseLockGeneric.java,这是应该注意的。
- 接着就是绘制两次图案了,在ChooseLockPattern中的saveChosenPatternAndFinish()方法中有这样一行代码:
utils.saveIphoneLockPattern(mChosenPattern, isFallback, isFallbackFor);
看一下这个方法的内部:
setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);其实就是用来标识图案解锁这种方式的,在下面的显示解锁方式时会用到。
- 最后还是来看一下SecuritySettings.java中的createPreferenceHierarchy()方法:
首先要判断mLockPatternUtils.getKeyguardStoredPasswordQuality()的值,来看一下这个方法的具体实现:
一目了然,这个getLong获取的值就是上面我们提到的标识图案解锁的值,所以必然走的是这个case:
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
resid = R.xml.security_settings_pattern;
break;
最终显示出来的就是我们选择了图案解锁。
- --THE END.
- 感谢wisim.me提供的源码解析启发