需求说明

客户设置密码后容易遗忘,反复刷机不符合需求,需要预留一个adb命令去直接清除掉锁屏密码。

为什么不直接删除对应文件

安卓11找不到对应的.key文件了,根据老的版本,大概8.0之前是有.key文件去管理锁屏密码的,但是现在没有了

修改

原理

制造假数据(空的数据)去覆盖掉,然后在通过方法去清除掉空数据

设置假数据去覆盖掉旧的密码

路径:
frameworks\base\core\java\com\android\internal\widget\LockPatternUtils.java
代码:
/**
     * Save a new lockscreen credential.
     *
     * <p> This method will fail (returning {@code false}) if the previously saved credential
     * provided is incorrect, or if the lockscreen verification is still being throttled.
     *
     * @param newCredential The new credential to save
     * @param savedCredential The current credential
     * @param userHandle the user whose lockscreen credential is to be changed
     *
     * @return whether this method saved the new password successfully or not. This flow will fail
     * and return false if the given credential is wrong.
     * @throws RuntimeException if password change encountered an unrecoverable error.
     * @throws UnsupportedOperationException secure lockscreen is not supported on this device.
     * @throws IllegalArgumentException if new credential is too short.
     */
 public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
            @NonNull LockscreenCredential savedCredential, int userHandle) {
        if (!hasSecureLockScreen()) {
            throw new UnsupportedOperationException(
                    "This operation requires the lock screen feature.");
        }
        newCredential.checkLength();

        try {
            if (!getLockSettings().setLockCredential(newCredential, savedCredential, userHandle)) {
                return false;
            }
        } catch (RemoteException e) {
            throw new RuntimeException("Unable to save lock password", e);
        }

        onPostPasswordChanged(newCredential, userHandle);
        return true;
    }

这个类怎么用呐?
直接百度翻译注释:
这个方法是用来保存新的锁屏凭证
这个方法有三个参数,分别代表:
新的锁屏凭证,旧的锁屏凭证,要修改凭证的用户
这里提到了LockscreenCredential这个类,前面两个参数是基于这个修改的

路径:

frameworks\base\core\java\com\android\internal\widget\LockscreenCredential.java
新的锁屏凭证:

这里我们需要的是一个空的密码凭证,对应方法为:

/**
     * Creates a LockscreenCredential object representing empty password.
     */
    public static LockscreenCredential createNone() {
        return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0]);
    }

很好,这里调用了自身的构造方法,返回一个基于自己的对象,这个构造方法怎么说
百度翻译注释结果:
创建表示给定模式的LockscreenCredential对象
第一个参数是一个类似标识符的东西,在这个类中通过对应标识去做不同的事情

 private LockscreenCredential(int type, byte[] credential) {
        Objects.requireNonNull(credential);
        if (type == CREDENTIAL_TYPE_NONE) {
            Preconditions.checkArgument(credential.length == 0);
        } else {
            // Do not allow constructing a CREDENTIAL_TYPE_PASSWORD_OR_PIN object.
            Preconditions.checkArgument(type == CREDENTIAL_TYPE_PIN
                    || type == CREDENTIAL_TYPE_PASSWORD
                    || type == CREDENTIAL_TYPE_PATTERN);
            Preconditions.checkArgument(credential.length > 0);
        }
        mType = type;
        mCredential = credential;
    }

看代码得知后面的参数是指这个的存放的长度,就相当于预留多少内存空间去存放,这里createNone给的是new byte[0],走if上面,不细究,喜欢看的自己去细看源码。
这样我们就大概可以知道直接LockscreenCredential.createNone()就可以拿到一个新的空的锁屏凭证了,(这里注一个小知识:被static修饰的方法可以直接调用,不需要new对象,且只能被继承,不能被重写)。

旧的锁屏凭证

旧的锁屏凭证这个就比较麻烦了,主要是有三种(除了无和滑动),这三种分别代表图案,PIN码和密码。
回到LockscreenCredential这个类,先说密码的锁屏凭证

/**
     * Creates a LockscreenCredential object representing the given alphabetic password.
     * If the supplied password is empty, create an empty credential object.
     */
    public static LockscreenCredential createPasswordOrNone(@Nullable CharSequence password) {
        if (TextUtils.isEmpty(password)) {
            return createNone();
        } else {
            return createPassword(password);
        }
    }

首先是空的凭证和非空凭证的判断,接下来找非空的判断走法

/**
     * Creates a LockscreenCredential object representing the given alphabetic password.
     */
    public static LockscreenCredential createPassword(@NonNull CharSequence password) {
        return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
                charSequenceToByteArray(password));
    }

传个密码就好了。嗯,当然,这就要知道安卓系统一般保存这种数据都是通过数据库的,既然可以写入,自然就可以拿到了,当然,如果没有怎么办,我们就自己做一个。
先拿密码和PIN码为例
路径:

vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\password\ChooseLockPassword.java

没有这个路径怎么办,老办法找对应类就好了

Settings.System.putString(getContext().getContentResolver(), "lock_password", passwordText.toString());

这句代码加在handleNext();方法里面,
为什么??
看调用:

 protected void onNextButtonClick(View view) {
            handleNext();
        }

        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            // Check if this was the result of hitting the enter or "done" key
            if (actionId == EditorInfo.IME_NULL
                    || actionId == EditorInfo.IME_ACTION_DONE
                    || actionId == EditorInfo.IME_ACTION_NEXT) {
                handleNext();
                return true;
            }
            return false;
        }

这里干了啥,点击按键的监听,什么按键?
在这里插入图片描述
很显然,FooterButton,这里是通过导包拿的,应该是自定义的一个控件,主要说明,为什么在这里面调用,这个就是输入密码的时候的下一步按钮的事件监听;
好了,这里我们还需要导包

import android.provider.Settings;

这里我们已经加上了在输入密码和PIN码的,接下来就是图案密码了

byte[] pBytes = LockPatternUtils.patternToByteArray(pattern);
                    String res = new String(pBytes);
                    Settings.System.putString(getContext().getContentResolver(), "lock_password", res.toString());

加在onPatternDetected(List<LockPatternView.Cell> pattern);方法里面,这里也需要导包
和上面的密码和PIN码导的包一样
由于这两个类都是Activity,这样我们需要考虑到这个值是否会受到生命周期的影响,比如说在切换横竖屏的时候,由于这个值不是固定的xml文件中的存在,且没有对应id去控制,所以,我们需要在onSaveInstanceState()中去保存

 if (mCurrentCredential != null) {
                LockscreenCredential currentCredential_dup = mCurrentCredential.duplicate();
                outState.putParcelable(KEY_CURRENT_CREDENTIAL, currentCredential_dup);
            }

这样我们就创建好了一个旧的密码的存储方式,且保存了旧的凭证的临时数据。
我们可以通过下面的方式拿到密码,且知道图案密码的保存值也是被转化的password

String password = Settings.System.getString(mContext.getContentResolver(), "lock_password");

这样就可以拿到旧的锁屏凭证之密码凭证了。
PIN码的拿法和这个一致,只是方法里面的参数换成了CharSequence pin
我们拿到密码是针对锁屏所有的,所以传入的参数还是password,只是方法换成了

 public static LockscreenCredential createPinOrNone(@Nullable CharSequence pin) {
        if (TextUtils.isEmpty(pin)) {
            return createNone();
        } else {
            return createPin(pin);
        }
    }

接下来就是图案密码了

 /**
     * Creates a LockscreenCredential object representing the given pattern.
     */
    public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) {
        return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN,
                LockPatternUtils.patternToByteArray(pattern));
    }

这里用同样的方法去将password转化成需要的参数。
这里需要借助LockPatternUtils的byteArrayToPattern()方法,先将password转化成byte数组,以byte数组转化成pattern,最后通过createPattern()方法拿到旧的锁屏凭证之图案。

要修改凭证的用户
int userId = UserHandle.USER_SYSTEM;

这里直接拿系统用户就可以了
这个是UserHandle的类,由于文章篇幅过长,不做阐述,自己去看详细代码

http://t.csdn.cn/2KVw0

现在就已经完成了新的空密码替换旧的密码了,我们还需要让锁屏界面不显示,也就是切换成无的状态
这里用的是LockPatternUtils的setLockScreenDisabled(boolean disable, int userId);方法

/**
     * Disable showing lock screen at all for a given user.
     * This is only meaningful if pattern, pin or password are not set.
     *
     * @param disable Disables lock screen when true
     * @param userId User ID of the user this has effect on
     */
    public void setLockScreenDisabled(boolean disable, int userId) {
        setBoolean(DISABLE_LOCKSCREEN_KEY, disable, userId);
    }

以下就是清除锁屏密码的全部代码

//清除锁屏密码
       public void clearLockscreen(){
		   
	   String password = Settings.System.getString(mContext.getContentResolver(), "lock_password");
          
            int userId = UserHandle.USER_SYSTEM;
            int type = mLockPatternUtils.getCredentialTypeForUser(userId);
           
            if(type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD){
                
                mLockPatternUtils.setLockCredential(LockscreenCredential.createNone(),
                LockscreenCredential.createPasswordOrNone(password), userId);
                mLockPatternUtils.setLockScreenDisabled(true, userId);
                
                ShutdownThread.reboot(mContext,"userrequested",false);
            }else if(type == LockPatternUtils.CREDENTIAL_TYPE_PIN){
                
                mLockPatternUtils.setLockCredential(LockscreenCredential.createNone(),
                LockscreenCredential.createPinOrNone(password), userId);
                mLockPatternUtils.setLockScreenDisabled(true, userId);
                
                ShutdownThread.reboot(mContext,"userrequested",false);
            }else if(type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN){
                byte[] pBytes = password.getBytes();
                mLockPatternUtils.setLockCredential(LockscreenCredential.createNone(),
                LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(pBytes)), userId);
                mLockPatternUtils.setLockScreenDisabled(true, userId);
               
                ShutdownThread.reboot(mContext,"userrequested",false);
            }

        }

这里用了ShutdownThread的reboot方法,这个方法是为了请求干净关机,等待子系统清理其

*状态等,必须从其UI所在的循环器线程调用。如果不使用这个方法会无效。
而type是为了筛选3种不同的密码实现方式。

方法已经给了,则adb命令可以通过广播去实现,这里不做具体阐述,注意在导包的时候可能由于不处在一个项目里面而失败,建议在PhoneWindowManager.java中实现。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐