系統(tǒng)設置的activity是 Settings,另外有40多個activity繼承于它蚊伞,比如設置的一級菜單: wifi席赂,藍牙,聲音时迫,顯示氧枣,安全,應用程序别垮,語言和時間,關于設備等等扎谎。實際上都是這一個acitivy碳想。
這里從安全設置看起,SecuritySettings.Java
以資源文件R.xml.security_settings_* 填充【根據(jù)當前鎖屏方式毁靶,擁有者信息胧奔,密碼顯示等具體情形,加載不同的資源或配置】预吆,具體在createPreferenceHierarchy() 和 onResume中
以改鎖屏方式為主線龙填,點擊鎖屏項時,onPreferenceTreeClick #587
調用拐叉,
在key值為KEY_UNLOCK_SET_OR_CHANGE則跳轉到fragmen ----
`startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment", `
--------------------------------
偶然間發(fā)現(xiàn)岩遗,設置未知來源的開關
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操作發(fā)現(xiàn)是可以控制的
adb shell settings put secure install_non_market_apps 0 //設置不允許安裝未知來源
adb shell settings get secure install_non_market_apps //獲取狀態(tài)
--------------------------------
回到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中方式
選擇一種方式后返吻,執(zhí)行updateUnlockMethodAndFinish(方式,啟用鎖屏?)
繼續(xù)看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)方法處理舆乔。
這個方法的第一個參數(shù)表示解鎖方式的等級,在android.app.admin.DevicePolicyManager.java中定義了各個解鎖方式對應的等級值剂公,從小到大希俩,表示解鎖方式安全性的由弱到強:
無和滑動兩個對應的值都是PASSWORD_QUALITY_UNSPECIFIED = 0
注意這個方法的第二個參數(shù)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值就是上面?zhèn)鬟M來的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)中已經(jīng)設置為 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了眯牧。
最終我們看到的就是無這個選項了蹋岩。
另外,滑動解鎖與無的唯一區(qū)別就是,調用 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開始分析禁偎。
繼續(xù)看ChooseLockGenericFragment的 onPreferenceTreeClick()方法,
執(zhí)行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開始分析桦山。
執(zhí)行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()的值祝谚,來看一下這個方法的具體實現(xiàn):
一目了然,這個getLong獲取的值就是上面我們提到的標識圖案解鎖的值酣衷,所以必然走的是這個case:
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
resid = R.xml.security_settings_pattern;
break;
最終顯示出來的就是我們選擇了圖案解鎖交惯。
- --THE END.
- 感謝wisim.me提供的源碼解析啟發(fā)