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
https://blog.csdn.net/u012792686/article/details/72921379
https://www.cnblogs.com/haiming/p/2989678.html
https://blog.csdn.net/qq_36946260/article/details/75232263
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 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"));
}
}
原因:
这个问题是由于从数据库中获取的锁屏码方式跟从保存的文件中读取的信息并解析的锁屏码方式不匹配导致的,
这个问题可能是某种原因导致数据库写入失败或错误。设置中与systemUI 中不一致是由于设置中是直接从数据
库获取锁屏方式,而systemUI从数据库与文件中分别获取并比较,不一致则认为没有设置锁屏方式。
措施:
现在的修改措施是将写入数据库的信息在写入数据库前保存到文件中,获取的时候先获取文件中的,再获取数据库
中的并比较,如果不一致则以文件中的为准。
frameworks\base\services\core\java\com\android\server\locksettings\LockSettingsStorage.java
public void writeKeyValue(SQLiteDatabase db, String key, String value, int userId) {
if(LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY.equals(key) || LockPatternUtils.PASSWORD_TYPE_KEY.equals(key)) {
byte[] stored = readFile(getFileNameForKey(key, userId));
if(stored != null && new String(stored).length() > value.length()) {
new File(getFileNameForKey(key, userId)).delete();
}
writeFile(getFileNameForKey(key, userId), value.getBytes());
}
public String readKeyValue(String key, String defaultValue, int userId) {
int version;
synchronized (mCache) {
if (mCache.hasKeyValue(key, userId)) {
return mCache.peekKeyValue(key, defaultValue, userId);
}
version = mCache.getVersion();
}
Cursor cursor;
Object result = DEFAULT;
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY,
COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?",
new String[] { Integer.toString(userId), key },
null, null, null)) != null) {
if (cursor.moveToFirst()) {
result = cursor.getString(0);
}
cursor.close();
}
if(LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY.equals(key) || LockPatternUtils.PASSWORD_TYPE_KEY.equals(key)) {
byte[] stored = readFile(getFileNameForKey(key, userId));
if(stored != null) {
if(result == DEFAULT || !new String(stored).equals((String) result)) {
result = new String(stored);
}
}
}
mCache.putKeyValueIfUnchanged(key, result, userId, version);
return result == DEFAULT ? defaultValue : (String) result;
}