View Hierarchy

View Hierarchy不能启动的原因

To preserve security, Hierarchy Viewer can only connect to devices running a developer version of the Android system

即:出于安全考虑,Hierarchy Viewer只能连接Android开发版手机或是模拟器(准确地说,只有ro.secure参数等于0且ro.debuggable等于1的android系统)。

Hierarchy Viewer在连接手机时,手机上必须启动一个叫View Server的客户端与其进行socket通信。而在商业手机上,是无法开启View Server的,故Hierarchy Viewer是无法连接到普通的商业手机。

Android源码实现这一限制的地方在:

ANDROID源码根目录\frameworks\base\services\java\com\android\server\wm\WindowManageService.java

中的一段:

/**
 * Starts the view server on the specified port.
 *
 * @param port The port to listener to.
 *
 * @return True if the server was successfully started, false otherwise.
 *
 * @see com.android.server.wm.ViewServer
 * @see com.android.server.wm.ViewServer#VIEW_SERVER_DEFAULT_PORT
 */
@Override
public boolean startViewServer(int port) {
    if (isSystemSecure()) {
        return false;
    }

    if (!checkCallingPermission(Manifest.permission.DUMP, "startViewServer")) {
        return false;
    }

    if (port < 1024) {
        return false;
    }

    if (mViewServer != null) {
        if (!mViewServer.isRunning()) {

检验一台手机是否开启了View Server的办法为:

adb shell service call window 3

若返回值是:Result: Parcel(00000000 00000000 ‘……..’)” 说明View Server处于关闭状态

若返回值是:Result: Parcel(00000000 00000001 ‘……..’)” 说明View Server处于开启状态

若是一台可以打开View Server的手机(Android开发版手机 、模拟器or 按照本帖步骤给系统打补丁的手机),我们可以使用以下命令打开View Server:

adb shell service call window 1 i32 4939

使用以下命令关闭View Server:

adb shell service call window 2 i32 4939

布局优化必备 Hierarchy Viewer 工具使用

https://blog.csdn.net/u012792686/article/details/72921379

Android Framework------之Keyguard 简单分析

https://www.cnblogs.com/haiming/p/2989678.html

https://blog.csdn.net/qq_36946260/article/details/75232263

Android中锁屏密码算法解析以及破解方案

https://blog.csdn.net/po__oq/article/details/80985658

 // 设置中选不同的锁屏密码会到这里
830         private boolean setUnlockMethod(String unlockMethod) {
831             EventLog.writeEvent(EventLogTags.LOCK_SCREEN_TYPE, unlockMethod);
832 
833             ScreenLockType lock = ScreenLockType.fromKey(unlockMethod);
834             if (lock != null) {
835                 switch (lock) {
836                     case NONE:
837                     case SWIPE:
838                         updateUnlockMethodAndFinish(
839                                 lock.defaultQuality,
840                                 lock == ScreenLockType.NONE,
841                                 false /* chooseLockSkipped */);
842                         return true;
843                     case PATTERN:
844                     case PIN:
845                     case PASSWORD:
846                     case MANAGED:
847                         maybeEnableEncryption(lock.defaultQuality, false);
848                         return true;
849                 }
850             }
851             Log.e(TAG, "Encountered unknown unlock method to set: " + unlockMethod);
852             return false;
853         }
854 
 

frameworks\base\packages\SystemUI\src\com\android\keyguard\KeyguardSecurityModel.java

getSecurityMode=>

frameworks\base\core\java\com\android\internal\widget\LockPatternUtils.java

=>getActivePasswordQuality

这里是获取密码的质量,比如是简单的数字pin 码,复杂的数值和字符码等。

这些信息是保存在数据库中的,/data/system/locksettings.db 中,

getKeyguardStoredPasswordQuality 就是获取lockscreen.password_type    密码复杂类型

    /**
     * Used by device policy manager to validate the current password
     * information it has.
     */
    public int getActivePasswordQuality(int userId) {
        int quality = getKeyguardStoredPasswordQuality(userId);

        if (isLockPasswordEnabled(quality, userId)) {
            // Quality is a password and a password exists. Return the quality.
            return quality;
        }

        if (isLockPatternEnabled(quality, userId)) {
            // Quality is a pattern and a pattern exists. Return the quality.
            return quality;
        }

        return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
    }

    private boolean isLockPasswordEnabled(int mode, int userId) {
        final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
                || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
                || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
                || mode == DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
        return passwordEnabled && savedPasswordExists(userId);
    }

    private boolean savedPasswordExists(int userId) {
        try {
            return getLockSettings().havePassword(userId);
        } catch (RemoteException re) {
            return false;
        }
    }

frameworks\base\services\core\java\com\android\server\locksettings\LockSettingsService.java

    @Override
    public boolean havePassword(int userId) throws RemoteException {
        checkPasswordHavePermission(userId);
        synchronized (mSpManager) {
            if (isSyntheticPasswordBasedCredentialLocked(userId)) {
                long handle = getSyntheticPasswordHandleLocked(userId);
                return mSpManager.getCredentialType(handle, userId) ==
                        LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
            }
        }
        // Do we need a permissions check here?
        return mStorage.hasPassword(userId);
    }
 

这里从数据库中获取了密码的复杂类型,需要进一步从保存的sp-hadle.pwd 中获取是否存在passwd 与pattern 类型密码

frameworks\base\services\core\java\com\android\server\locksettings\SyntheticPasswordManager.java

havePassword这里判断对应用户是否存在基于凭据的合成password

1、从locksettings.db 中获取合成handle , 即字段数据库字段 "sp-handle"

2、根据sp-handle 获取对应用户的凭据mSpManager.getCredentialType(handle, userId),这里是读文件

/data/system_de/0/spblob/sp-hadle.pwd 

0 对应userId  ,sp-handle  对应合成hadle 

pattern 与passwd 同一个文件,一样通过获取SyntheticPasswordManager 中凭据结构体PasswordData的类型字段判读是passwd 还是pattern类型

    private boolean isLockPatternEnabled(int mode, int userId) {
        return mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
                && savedPatternExists(userId);
    }
    /**
     * Check to see if the user has stored a lock pattern.
     * @return Whether a saved pattern exists.
     */
    private boolean savedPatternExists(int userId) {
        try {
            return getLockSettings().havePattern(userId);
        } catch (RemoteException re) {
            return false;
        }
    }


    static class PasswordData {
        byte scryptN;
        byte scryptR;
        byte scryptP;
        public int passwordType;
        byte[] salt;
        // For GateKeeper-based credential, this is the password handle returned by GK,
        // for weaver-based credential, this is empty.
        public byte[] passwordHandle;

        public static PasswordData create(int passwordType) {
            PasswordData result = new PasswordData();
            result.scryptN = PASSWORD_SCRYPT_N;
            result.scryptR = PASSWORD_SCRYPT_R;
            result.scryptP = PASSWORD_SCRYPT_P;
            result.passwordType = passwordType;
            result.salt = secureRandom(PASSWORD_SALT_LENGTH);
            return result;
        }

        public static PasswordData fromBytes(byte[] data) {
            PasswordData result = new PasswordData();
            ByteB

如果数据库lockscreen.password_type与sp-hadle.pwd  两者一直获取的信息一致,就会根据根据类型信息下一个输入密码的view。

滑动显示pin 码界面
01-12 22:22:00.271  1114  1114 D KeyguardSecurityView: showNextSecurityScreenOrFinish(false)
01-12 22:22:00.271  1114  1114 D KeyguardSecurityView: showNext.. mCurrentSecuritySelection = PIN
01-12 22:22:00.271  1114  1114 D KeyguardSecurityView: showNextSecurityScreenOrFinish() - return finish = false
01-12 22:22:00.271  1114  1114 D KeyguardSecurityView: showNextSecurityScreenOrFinish(false)
01-12 22:22:00.271  1114  1114 D KeyguardSecurityView: showNext.. mCurrentSecuritySelection = PIN
01-12 22:22:00.272  1114  1114 D KeyguardSecurityView: showNextSecurityScreenOrFinish() - return finish = false
01-12 22:22:00.392  1114  1114 D KeyguardViewBase: screen on, instance 925d934
 

灭屏
01-12 22:22:10.085  1114  1114 D KeyguardViewBase: screen off, instance 925d934 at 40443586
01-12 22:22:10.090  1114  1114 V KeyguardSecurityView: showPrimarySecurityScreen(turningOff=true)
01-12 22:22:10.091  1114  1114 V KeyguardSecurityView: showPrimarySecurityScreen(securityMode=PIN)
01-12 22:22:10.091  1114  1114 D KeyguardSecurityView: showSecurityScreen(PIN)
01-12 22:22:10.198  1114  1114 D KeyguardViewBase: show()
01-12 22:22:10.201  1114  1114 V KeyguardSecurityView: showPrimarySecurityScreen(turningOff=false)
01-12 22:22:10.201  1114  1114 V KeyguardSecurityView: showPrimarySecurityScreen(securityMode=PIN)
01-12 22:22:10.201  1114  1114 D KeyguardSecurityView: showSecurityScreen(PIN)

在设置中录入pin、pattern点确定,或设置为滑动、或设置为 none 时,

frameworks\base\core\java\com\android\internal\widget\LockPatternUtils.java

设置为none 与滑动,或从pin/passwd/pattern 切换到none 与滑动

/**
 * Clear any lock pattern or password.
 */
public void clearLock(String savedCredential, int userHandle) {

。。。

}

//设置为手势码

/**
 * Save a lock pattern.
 * @param pattern The new pattern to save.
 * @param savedPattern The previously saved pattern, converted to String format
 * @param userId the user whose pattern is to be saved.
 */
public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern, int userId) {

。。。

}

//设置为pin/passwd

/**
 * Save a lock password.  Does not ensure that the password is as good
 * as the requested mode, but will adjust the mode to be as good as the
 * password.
 * @param password The password to save
 * @param savedPassword The previously saved lock password, or null if none
 * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
 * @param userHandle The userId of the user to change the password for
 */
public void saveLockPassword(String password, String savedPassword, int requestedQuality,
        int userHandle) {

。。。

}

// This method should be called by LockPatternUtil only, all internal methods in this class
// should call setLockCredentialInternal.
@Override
public void setLockCredential(String credential, int type, String savedCredential,
        int requestedQuality, int userId)
        throws RemoteException {
    checkWritePermission(userId);
    synchronized (mSeparateChallengeLock) {
        setLockCredentialInternal(credential, type, savedCredential, requestedQuality, userId);
        setSeparateProfileChallengeEnabled(userId, true, null);
        notifyPasswordChanged(userId);
    }
}

credential = pin 码或者手势码对应的数字 或者设置为滑动、none 则 null

type  类型其中一种

    public static final int CREDENTIAL_TYPE_NONE = -1;
    public static final int CREDENTIAL_TYPE_PATTERN = 1;
    public static final int CREDENTIAL_TYPE_PASSWORD = 2;

savedCredential上一次的credential 


    private void setLockCredentialInternal(String credential, int credentialType,
            String savedCredential, int requestedQuality, int userId) throws RemoteException {
        // Normalize savedCredential and credential such that empty string is always represented
        // as null.
        if (TextUtils.isEmpty(savedCredential)) {
            savedCredential = null;
        }
        if (TextUtils.isEmpty(credential)) {
            credential = null;
        }
        synchronized (mSpManager) {
            if (isSyntheticPasswordBasedCredentialLocked(userId)) {

                //locksettings.db 中对应用户数据库字段 "sp-handle" 是否存在,存在这里
                spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
                        requestedQuality, userId);
                return;
            }
        }

     //locksettings.db 中对应用户数据库字段 "sp-handle" 是否存在,不存在这里

        if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
            if (credential != null) {
                Slog.wtf(TAG, "CredentialType is none, but credential is non-null.");
            }
            clearUserKeyProtection(userId);
            getGateKeeperService().clearSecureUserId(userId);
            mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId);
            setKeystorePassword(null, userId);
            fixateNewestUserKeyAuth(userId);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
            notifyActivePasswordMetricsAvailable(null, userId);
            return;
        }
        if (credential == null) {
            throw new RemoteException("Null credential with mismatched credential type");
        }

        CredentialHash currentHandle = mStorage.readCredentialHash(userId);
        if (isManagedProfileWithUnifiedLock(userId)) {
            // get credential from keystore when managed profile has unified lock
            if (savedCredential == null) {
                try {
                    savedCredential = getDecryptedPasswordForTiedProfile(userId);
                } catch (FileNotFoundException e) {
                    Slog.i(TAG, "Child profile key not found");
                } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
                        | NoSuchAlgorithmException | NoSuchPaddingException
                        | InvalidAlgorithmParameterException | IllegalBlockSizeException
                        | BadPaddingException | CertificateException | IOException e) {
                    Slog.e(TAG, "Failed to decrypt child profile key", e);
                }
            }
        } else {
            if (currentHandle.hash == null) {
                if (savedCredential != null) {
                    Slog.w(TAG, "Saved credential provided, but none stored");
                }
                savedCredential = null;
            }
        }
        synchronized (mSpManager) {
            if (shouldMigrateToSyntheticPasswordLocked(userId)) {
                initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential,
                        currentHandle.type, requestedQuality, userId);
                spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
                        requestedQuality, userId);
                return;
            }
        }
        if (DEBUG) Slog.d(TAG, "setLockCredentialInternal: user=" + userId);
        byte[] enrolledHandle = enrollCredential(currentHandle.hash, savedCredential, credential,
                userId);
        if (enrolledHandle != null) {
            CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType);
            mStorage.writeCredentialHash(willStore, userId);
            // push new secret and auth token to vold
            GateKeeperResponse gkResponse = getGateKeeperService()
                    .verifyChallenge(userId, 0, willStore.hash, credential.getBytes());
            setUserKeyProtection(userId, credential, convertResponse(gkResponse));
            fixateNewestUserKeyAuth(userId);
            // Refresh the auth token
            doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
        } else {
            throw new RemoteException("Failed to enroll " +
                    (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? "password"
                            : "pattern"));
        }
    }
 

//locksettings.db 中对应用户数据库字段 "sp-handle" 是否存在,存在这里


    private void spBasedSetLockCredentialInternalLocked(String credential, int credentialType,
            String savedCredential, int requestedQuality, int userId){ AuthenticationResult authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
                getGateKeeperService(), handle, savedCredential, userId);


   long handle = getSyntheticPasswordHandleLocked(userId);//从数据库或者缓存获取sp-handle  

          AuthenticationResult authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
                getGateKeeperService(), handle, savedCredential, userId);
        VerifyCredentialResponse response = authResult.gkResponse;
        AuthenticationToken auth = authResult.authToken;   

// unwrapPasswordBasedSyntheticPassword  获取上一次的AT


        // If existing credential is provided, then it must match.
        if (savedCredential != null && auth == null) {
            throw new RemoteException("Failed to enroll " +
                    (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? "password"
                            : "pattern"));
        }

        if (auth != null) { //有设置过pin/pattern或当次是设置pin/pattern,就会到这之前通过initializeSyntheticPasswordLocked

//已经保存密码相关信息
            // We are performing a trusted credential change i.e. a correct existing credential
            // is provided
            setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality,
                    userId);//设置这一次的信息
            mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);//删除上一次的信息
        } else if (response != null
                && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR){
            // We are performing an untrusted credential change i.e. by DevicePolicyManager.
            // So provision a new SP and SID. This would invalidate existing escrow tokens.
            // Still support this for now but this flow will be removed in the next release.

            Slog.w(TAG, "Untrusted credential change invoked");
            initializeSyntheticPasswordLocked(null, credential, credentialType, requestedQuality,
                    userId);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
            mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);

            notifyActivePasswordMetricsAvailable(credential, userId);
        } else /* response == null || responseCode == VerifyCredentialResponse.RESPONSE_RETRY */ {
            Slog.w(TAG, "spBasedSetLockCredentialInternalLocked: " +
                    (response != null ? "rate limit exceeded" : "failed"));
            return;
        }  

}

 /**
     * Decrypt a synthetic password by supplying the user credential and corresponding password
     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
     * verification to referesh the SID & Auth token maintained by the system.
     * Note: the credential type is not validated here since there are call sites where the type is
     * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
     */
    public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
            long handle, String credential, int userId) throws RemoteException {
        if (credential == null) {
            credential = DEFAULT_PASSWORD;//滑动或者none ,为 "default-password"
        }
        AuthenticationResult result = new AuthenticationResult();
        PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));

           //从/data/system_de/0/spblob/sp-hadle.pwd 读并构造PasswordData对象。
        result.credentialType = pwd.passwordType;
        byte[] pwdToken = computePasswordToken(credential, pwd);

        //根据pin 码等并和PasswordData(有盐值字段) 一起加密得到pwdToken

        final byte[] applicationId;
        final long sid;
        int weaverSlot = loadWeaverSlot(handle, userId);// SyntheticPasswordManager: Device does not support weaver
        if (weaverSlot != INVALID_WEAVER_SLOT) {
            // Weaver based user password,不支持,跳过
            if (!isWeaverAvailable()) {
                Log.e(TAG, "No weaver service to unwrap password based SP");
                result.gkResponse = VerifyCredentialResponse.ERROR;
                return result;
            }
            result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
            if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
                return result;
            }
            sid = GateKeeper.INVALID_SECURE_USER_ID;
            applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
        } else {
            byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);//加密及hash 相关
            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
                    pwd.passwordHandle, gkPwdToken);//gatekeeper 验证
            int responseCode = response.getResponseCode();
            if (responseCode == GateKeeperResponse.RESPONSE_OK) {
                result.gkResponse = VerifyCredentialResponse.OK;
                if (response.getShouldReEnroll()) {
                    GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
                            pwd.passwordHandle, gkPwdToken, gkPwdToken);
                    if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
                        pwd.passwordHandle = reenrollResponse.getPayload();
                        saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
                        synchronizeFrpPassword(pwd,
                                pwd.passwordType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
                                ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
                                : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
                                /* TODO(roosa): keep the same password quality */,
                                userId);
                    } else {
                        Log.w(TAG, "Fail to re-enroll user password for user " + userId);
                        // continue the flow anyway
                    }
                }
            } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
                result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
                return result;
            } else  {
                result.gkResponse = VerifyCredentialResponse.ERROR;
                return result;
            }
            sid = sidFromPasswordHandle(pwd.passwordHandle);//从native 层获取sid
            applicationId = transformUnderSecdiscardable(pwdToken,
                    loadSecdiscardable(handle, userId));

//loadSecdiscardable从/data/system_de/0/spblob/sp-hadle.secdis 获取

//pwdToken 与secdis 根据加密及hash 得到applicationId

        }

        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
                applicationId, sid, userId);

//从/data/system_de/0/spblob/sp-hadle.spblob 文件读的到AuthenticationToken

        // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
        result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);//gatekeeper 验证
        return result;
    }

//locksettings.db 中对应用户数据库字段 "sp-handle" 是否存在,不存在这里,说明没有设置过pin/patter码

    private void setLockCredentialInternal(String credential, int credentialType,
            String savedCredential, int requestedQuality, int userId) throws RemoteException {
           。。。

        if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {//选中滑动或者NONE
            Slog.d(TAG,"locksetting credentialType=NONE");
            if (credential != null) {
                Slog.wtf(TAG, "CredentialType is none, but credential is non-null.");
            }
            clearUserKeyProtection(userId);
            getGateKeeperService().clearSecureUserId(userId);
            mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId);

//writeCredentialHash创建 /data/system/gatekeeper.password.key、/data/system/gatekeeper.pattern.key ,但内容为空
            setKeystorePassword(null, userId);
            fixateNewestUserKeyAuth(userId);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
            notifyActivePasswordMetricsAvailable(null, userId);
            return;

          }

        synchronized (mSpManager) {
            if (shouldMigrateToSyntheticPasswordLocked(userId)) {

          //SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT  默认1,支持混合passworld (sp-handle),这里

                initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential,
                        currentHandle.type, requestedQuality, userId);

                 //initializeSyntheticPasswordLocked  创建与保存相关信息,这里sp-handle 就存在数据库
                spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
                        requestedQuality, userId);

               //spBasedSetLockCredentialInternalLocked,这里跟上边分支locksettings.db 中对应用户数据库字段 "sp-handle" 存在是处理一样。
                return;
            }

         //SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT 为 0,不支持混合passworld(sp-handle)

          这里密码信息保存在 /data/system/gatekeeper.password.key、/data/system/gatekeeper.pattern.key 等文件中

        byte[] enrolledHandle = enrollCredential(currentHandle.hash, savedCredential, credential,
                userId);
        if (enrolledHandle != null) {
            CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType);
            mStorage.writeCredentialHash(willStore, userId);
            // push new secret and auth token to vold
            GateKeeperResponse gkResponse = getGateKeeperService()
                    .verifyChallenge(userId, 0, willStore.hash, credential.getBytes());
            setUserKeyProtection(userId, credential, convertResponse(gkResponse));
            fixateNewestUserKeyAuth(userId);
            // Refresh the auth token
            doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
        } else {
            throw new RemoteException("Failed to enroll " +
                    (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? "password"
                            : "pattern"));
        }

 }

猜你喜欢

转载自blog.csdn.net/lei7143/article/details/81185334