android 5.1 屏幕锁定开发相关

系统设置的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,这是应该注意的。

  1. 接着就是绘制两次图案了,在ChooseLockPattern中的saveChosenPatternAndFinish()方法中有这样一行代码:

utils.saveIphoneLockPattern(mChosenPattern, isFallback, isFallbackFor);
看一下这个方法的内部:

setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);其实就是用来标识图案解锁这种方式的,在下面的显示解锁方式时会用到。

  1. 最后还是来看一下SecuritySettings.java中的createPreferenceHierarchy()方法:

首先要判断mLockPatternUtils.getKeyguardStoredPasswordQuality()的值,来看一下这个方法的具体实现:

一目了然,这个getLong获取的值就是上面我们提到的标识图案解锁的值,所以必然走的是这个case:

case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
resid = R.xml.security_settings_pattern;
break;

最终显示出来的就是我们选择了图案解锁。

  • --THE END.
  • 感谢wisim.me提供的源码解析启发
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容