Merge "[LockSettings] migrate password attempt throttling to hardware" into mnc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
5ccfe51d8b
24
core/java/android/service/gatekeeper/GateKeeperResponse.aidl
Normal file
24
core/java/android/service/gatekeeper/GateKeeperResponse.aidl
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.service.gatekeeper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response object for a GateKeeper verification request.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
parcelable GateKeeperResponse;
|
||||||
|
|
||||||
120
core/java/android/service/gatekeeper/GateKeeperResponse.java
Normal file
120
core/java/android/service/gatekeeper/GateKeeperResponse.java
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.service.gatekeeper;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response object for a GateKeeper verification request.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public final class GateKeeperResponse implements Parcelable {
|
||||||
|
|
||||||
|
public static final int RESPONSE_ERROR = -1;
|
||||||
|
public static final int RESPONSE_OK = 0;
|
||||||
|
public static final int RESPONSE_RETRY = 1;
|
||||||
|
|
||||||
|
private final int mResponseCode;
|
||||||
|
|
||||||
|
private int mTimeout;
|
||||||
|
private byte[] mPayload;
|
||||||
|
private boolean mShouldReEnroll;
|
||||||
|
|
||||||
|
private GateKeeperResponse(int responseCode) {
|
||||||
|
mResponseCode = responseCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GateKeeperResponse(int responseCode, int timeout) {
|
||||||
|
mResponseCode = responseCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Parcelable.Creator<GateKeeperResponse> CREATOR
|
||||||
|
= new Parcelable.Creator<GateKeeperResponse>() {
|
||||||
|
@Override
|
||||||
|
public GateKeeperResponse createFromParcel(Parcel source) {
|
||||||
|
int responseCode = source.readInt();
|
||||||
|
GateKeeperResponse response = new GateKeeperResponse(responseCode);
|
||||||
|
if (responseCode == RESPONSE_RETRY) {
|
||||||
|
response.setTimeout(source.readInt());
|
||||||
|
} else if (responseCode == RESPONSE_OK) {
|
||||||
|
response.setShouldReEnroll(source.readInt() == 1);
|
||||||
|
int size = source.readInt();
|
||||||
|
if (size > 0) {
|
||||||
|
byte[] payload = new byte[size];
|
||||||
|
source.readByteArray(payload);
|
||||||
|
response.setPayload(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GateKeeperResponse[] newArray(int size) {
|
||||||
|
return new GateKeeperResponse[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(mResponseCode);
|
||||||
|
if (mResponseCode == RESPONSE_RETRY) {
|
||||||
|
dest.writeInt(mTimeout);
|
||||||
|
} else if (mResponseCode == RESPONSE_OK) {
|
||||||
|
dest.writeInt(mShouldReEnroll ? 1 : 0);
|
||||||
|
if (mPayload != null) {
|
||||||
|
dest.writeInt(mPayload.length);
|
||||||
|
dest.writeByteArray(mPayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getPayload() {
|
||||||
|
return mPayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTimeout() {
|
||||||
|
return mTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getShouldReEnroll() {
|
||||||
|
return mShouldReEnroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getResponseCode() {
|
||||||
|
return mResponseCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTimeout(int timeout) {
|
||||||
|
mTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setShouldReEnroll(boolean shouldReEnroll) {
|
||||||
|
mShouldReEnroll = shouldReEnroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPayload(byte[] payload) {
|
||||||
|
mPayload = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package android.service.gatekeeper;
|
package android.service.gatekeeper;
|
||||||
|
|
||||||
|
import android.service.gatekeeper.GateKeeperResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for communication with GateKeeper, the
|
* Interface for communication with GateKeeper, the
|
||||||
* secure password storage daemon.
|
* secure password storage daemon.
|
||||||
@@ -34,9 +36,9 @@ interface IGateKeeperService {
|
|||||||
* If provided, must verify against the currentPasswordHandle.
|
* If provided, must verify against the currentPasswordHandle.
|
||||||
* @param desiredPassword The new desired password, for which a handle will be returned
|
* @param desiredPassword The new desired password, for which a handle will be returned
|
||||||
* upon success.
|
* upon success.
|
||||||
* @return the handle corresponding to desiredPassword, or null
|
* @return an EnrollResponse or null on failure
|
||||||
*/
|
*/
|
||||||
byte[] enroll(int uid, in byte[] currentPasswordHandle, in byte[] currentPassword,
|
GateKeeperResponse enroll(int uid, in byte[] currentPasswordHandle, in byte[] currentPassword,
|
||||||
in byte[] desiredPassword);
|
in byte[] desiredPassword);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,10 +47,10 @@ interface IGateKeeperService {
|
|||||||
* @param enrolledPasswordHandle The handle against which the provided password will be
|
* @param enrolledPasswordHandle The handle against which the provided password will be
|
||||||
* verified.
|
* verified.
|
||||||
* @param The plaintext blob to verify against enrolledPassword.
|
* @param The plaintext blob to verify against enrolledPassword.
|
||||||
* @return True if the authentication was successful
|
* @return a VerifyResponse, or null on failure.
|
||||||
*/
|
*/
|
||||||
boolean verify(int uid, in byte[] enrolledPasswordHandle,
|
GateKeeperResponse verify(int uid, in byte[] enrolledPasswordHandle, in byte[] providedPassword);
|
||||||
in byte[] providedPassword);
|
|
||||||
/**
|
/**
|
||||||
* Verifies an enrolled handle against a provided, plaintext blob.
|
* Verifies an enrolled handle against a provided, plaintext blob.
|
||||||
* @param uid The Android user ID associated to this enrollment
|
* @param uid The Android user ID associated to this enrollment
|
||||||
@@ -58,9 +60,9 @@ interface IGateKeeperService {
|
|||||||
* @param enrolledPasswordHandle The handle against which the provided password will be
|
* @param enrolledPasswordHandle The handle against which the provided password will be
|
||||||
* verified.
|
* verified.
|
||||||
* @param The plaintext blob to verify against enrolledPassword.
|
* @param The plaintext blob to verify against enrolledPassword.
|
||||||
* @return an opaque attestation of authentication on success, or null.
|
* @return a VerifyResponse with an attestation, or null on failure.
|
||||||
*/
|
*/
|
||||||
byte[] verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle,
|
GateKeeperResponse verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle,
|
||||||
in byte[] providedPassword);
|
in byte[] providedPassword);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.internal.widget;
|
package com.android.internal.widget;
|
||||||
|
|
||||||
|
import com.android.internal.widget.VerifyCredentialResponse;
|
||||||
|
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
interface ILockSettings {
|
interface ILockSettings {
|
||||||
void setBoolean(in String key, in boolean value, in int userId);
|
void setBoolean(in String key, in boolean value, in int userId);
|
||||||
@@ -25,11 +27,11 @@ interface ILockSettings {
|
|||||||
long getLong(in String key, in long defaultValue, in int userId);
|
long getLong(in String key, in long defaultValue, in int userId);
|
||||||
String getString(in String key, in String defaultValue, in int userId);
|
String getString(in String key, in String defaultValue, in int userId);
|
||||||
void setLockPattern(in String pattern, in String savedPattern, int userId);
|
void setLockPattern(in String pattern, in String savedPattern, int userId);
|
||||||
boolean checkPattern(in String pattern, int userId);
|
VerifyCredentialResponse checkPattern(in String pattern, int userId);
|
||||||
byte[] verifyPattern(in String pattern, long challenge, int userId);
|
VerifyCredentialResponse verifyPattern(in String pattern, long challenge, int userId);
|
||||||
void setLockPassword(in String password, in String savedPassword, int userId);
|
void setLockPassword(in String password, in String savedPassword, int userId);
|
||||||
boolean checkPassword(in String password, int userId);
|
VerifyCredentialResponse checkPassword(in String password, int userId);
|
||||||
byte[] verifyPassword(in String password, long challenge, int userId);
|
VerifyCredentialResponse verifyPassword(in String password, long challenge, int userId);
|
||||||
boolean checkVoldPassword(int userId);
|
boolean checkVoldPassword(int userId);
|
||||||
boolean havePattern(int userId);
|
boolean havePattern(int userId);
|
||||||
boolean havePassword(int userId);
|
boolean havePassword(int userId);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.android.internal.widget;
|
|||||||
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,8 +18,10 @@ public final class LockPatternChecker {
|
|||||||
* Invoked when a security check is finished.
|
* Invoked when a security check is finished.
|
||||||
*
|
*
|
||||||
* @param matched Whether the PIN/Password/Pattern matches the stored one.
|
* @param matched Whether the PIN/Password/Pattern matches the stored one.
|
||||||
|
* @param throttleTimeoutMs The amount of time in ms to wait before reattempting
|
||||||
|
* the call. Only non-0 if matched is false.
|
||||||
*/
|
*/
|
||||||
void onChecked(boolean matched);
|
void onChecked(boolean matched, int throttleTimeoutMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,8 +32,10 @@ public final class LockPatternChecker {
|
|||||||
* Invoked when a security verification is finished.
|
* Invoked when a security verification is finished.
|
||||||
*
|
*
|
||||||
* @param attestation The attestation that the challenge was verified, or null.
|
* @param attestation The attestation that the challenge was verified, or null.
|
||||||
|
* @param throttleTimeoutMs The amount of time in ms to wait before reattempting
|
||||||
|
* the call. Only non-0 if attestation is null.
|
||||||
*/
|
*/
|
||||||
void onVerified(byte[] attestation);
|
void onVerified(byte[] attestation, int throttleTimeoutMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,14 +53,21 @@ public final class LockPatternChecker {
|
|||||||
final int userId,
|
final int userId,
|
||||||
final OnVerifyCallback callback) {
|
final OnVerifyCallback callback) {
|
||||||
AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
|
AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
|
||||||
|
private int mThrottleTimeout;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected byte[] doInBackground(Void... args) {
|
protected byte[] doInBackground(Void... args) {
|
||||||
return utils.verifyPattern(pattern, challenge, userId);
|
try {
|
||||||
|
return utils.verifyPattern(pattern, challenge, userId);
|
||||||
|
} catch (RequestThrottledException ex) {
|
||||||
|
mThrottleTimeout = ex.getTimeoutMs();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(byte[] result) {
|
protected void onPostExecute(byte[] result) {
|
||||||
callback.onVerified(result);
|
callback.onVerified(result, mThrottleTimeout);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
task.execute();
|
task.execute();
|
||||||
@@ -74,14 +87,21 @@ public final class LockPatternChecker {
|
|||||||
final int userId,
|
final int userId,
|
||||||
final OnCheckCallback callback) {
|
final OnCheckCallback callback) {
|
||||||
AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
|
AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
|
||||||
|
private int mThrottleTimeout;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Void... args) {
|
protected Boolean doInBackground(Void... args) {
|
||||||
return utils.checkPattern(pattern, userId);
|
try {
|
||||||
|
return utils.checkPattern(pattern, userId);
|
||||||
|
} catch (RequestThrottledException ex) {
|
||||||
|
mThrottleTimeout = ex.getTimeoutMs();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean result) {
|
protected void onPostExecute(Boolean result) {
|
||||||
callback.onChecked(result);
|
callback.onChecked(result, mThrottleTimeout);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
task.execute();
|
task.execute();
|
||||||
@@ -103,14 +123,21 @@ public final class LockPatternChecker {
|
|||||||
final int userId,
|
final int userId,
|
||||||
final OnVerifyCallback callback) {
|
final OnVerifyCallback callback) {
|
||||||
AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
|
AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
|
||||||
|
private int mThrottleTimeout;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected byte[] doInBackground(Void... args) {
|
protected byte[] doInBackground(Void... args) {
|
||||||
return utils.verifyPassword(password, challenge, userId);
|
try {
|
||||||
|
return utils.verifyPassword(password, challenge, userId);
|
||||||
|
} catch (RequestThrottledException ex) {
|
||||||
|
mThrottleTimeout = ex.getTimeoutMs();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(byte[] result) {
|
protected void onPostExecute(byte[] result) {
|
||||||
callback.onVerified(result);
|
callback.onVerified(result, mThrottleTimeout);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
task.execute();
|
task.execute();
|
||||||
@@ -130,14 +157,21 @@ public final class LockPatternChecker {
|
|||||||
final int userId,
|
final int userId,
|
||||||
final OnCheckCallback callback) {
|
final OnCheckCallback callback) {
|
||||||
AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
|
AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
|
||||||
|
private int mThrottleTimeout;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Void... args) {
|
protected Boolean doInBackground(Void... args) {
|
||||||
return utils.checkPassword(password, userId);
|
try {
|
||||||
|
return utils.checkPassword(password, userId);
|
||||||
|
} catch (RequestThrottledException ex) {
|
||||||
|
mThrottleTimeout = ex.getTimeoutMs();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean result) {
|
protected void onPostExecute(Boolean result) {
|
||||||
callback.onChecked(result);
|
callback.onChecked(result, mThrottleTimeout);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
task.execute();
|
task.execute();
|
||||||
|
|||||||
@@ -59,24 +59,12 @@ public class LockPatternUtils {
|
|||||||
private static final String TAG = "LockPatternUtils";
|
private static final String TAG = "LockPatternUtils";
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum number of incorrect attempts before the user is prevented
|
|
||||||
* from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}.
|
|
||||||
*/
|
|
||||||
public static final int FAILED_ATTEMPTS_BEFORE_TIMEOUT = 5;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of incorrect attempts before which we fall back on an alternative
|
* The number of incorrect attempts before which we fall back on an alternative
|
||||||
* method of verifying the user, and resetting their lock pattern.
|
* method of verifying the user, and resetting their lock pattern.
|
||||||
*/
|
*/
|
||||||
public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20;
|
public static final int FAILED_ATTEMPTS_BEFORE_RESET = 20;
|
||||||
|
|
||||||
/**
|
|
||||||
* How long the user is prevented from trying again after entering the
|
|
||||||
* wrong pattern too many times.
|
|
||||||
*/
|
|
||||||
public static final long FAILED_ATTEMPT_TIMEOUT_MS = 30000L;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interval of the countdown for showing progress of the lockout.
|
* The interval of the countdown for showing progress of the lockout.
|
||||||
*/
|
*/
|
||||||
@@ -109,6 +97,7 @@ public class LockPatternUtils {
|
|||||||
@Deprecated
|
@Deprecated
|
||||||
public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
|
public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
|
||||||
public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
|
public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
|
||||||
|
public final static String LOCKOUT_ATTEMPT_TIMEOUT_MS = "lockscreen.lockoutattempttimeoutmss";
|
||||||
public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
|
public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
|
||||||
public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
|
public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@@ -144,6 +133,23 @@ public class LockPatternUtils {
|
|||||||
private DevicePolicyManager mDevicePolicyManager;
|
private DevicePolicyManager mDevicePolicyManager;
|
||||||
private ILockSettings mLockSettingsService;
|
private ILockSettings mLockSettingsService;
|
||||||
|
|
||||||
|
|
||||||
|
public static final class RequestThrottledException extends Exception {
|
||||||
|
private int mTimeoutMs;
|
||||||
|
public RequestThrottledException(int timeoutMs) {
|
||||||
|
mTimeoutMs = timeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The amount of time in ms before another request may
|
||||||
|
* be executed
|
||||||
|
*/
|
||||||
|
public int getTimeoutMs() {
|
||||||
|
return mTimeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public DevicePolicyManager getDevicePolicyManager() {
|
public DevicePolicyManager getDevicePolicyManager() {
|
||||||
if (mDevicePolicyManager == null) {
|
if (mDevicePolicyManager == null) {
|
||||||
mDevicePolicyManager =
|
mDevicePolicyManager =
|
||||||
@@ -239,9 +245,23 @@ public class LockPatternUtils {
|
|||||||
* @param challenge The challenge to verify against the pattern
|
* @param challenge The challenge to verify against the pattern
|
||||||
* @return the attestation that the challenge was verified, or null.
|
* @return the attestation that the challenge was verified, or null.
|
||||||
*/
|
*/
|
||||||
public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId) {
|
public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId)
|
||||||
|
throws RequestThrottledException {
|
||||||
try {
|
try {
|
||||||
return getLockSettings().verifyPattern(patternToString(pattern), challenge, userId);
|
VerifyCredentialResponse response =
|
||||||
|
getLockSettings().verifyPattern(patternToString(pattern), challenge, userId);
|
||||||
|
if (response == null) {
|
||||||
|
// Shouldn't happen
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||||
|
return response.getPayload();
|
||||||
|
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
|
||||||
|
throw new RequestThrottledException(response.getTimeout());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
} catch (RemoteException re) {
|
} catch (RemoteException re) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -253,9 +273,19 @@ public class LockPatternUtils {
|
|||||||
* @param pattern The pattern to check.
|
* @param pattern The pattern to check.
|
||||||
* @return Whether the pattern matches the stored one.
|
* @return Whether the pattern matches the stored one.
|
||||||
*/
|
*/
|
||||||
public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId) {
|
public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId)
|
||||||
|
throws RequestThrottledException {
|
||||||
try {
|
try {
|
||||||
return getLockSettings().checkPattern(patternToString(pattern), userId);
|
VerifyCredentialResponse response =
|
||||||
|
getLockSettings().checkPattern(patternToString(pattern), userId);
|
||||||
|
|
||||||
|
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||||
|
return true;
|
||||||
|
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
|
||||||
|
throw new RequestThrottledException(response.getTimeout());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} catch (RemoteException re) {
|
} catch (RemoteException re) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -270,9 +300,19 @@ public class LockPatternUtils {
|
|||||||
* @param challenge The challenge to verify against the password
|
* @param challenge The challenge to verify against the password
|
||||||
* @return the attestation that the challenge was verified, or null.
|
* @return the attestation that the challenge was verified, or null.
|
||||||
*/
|
*/
|
||||||
public byte[] verifyPassword(String password, long challenge, int userId) {
|
public byte[] verifyPassword(String password, long challenge, int userId)
|
||||||
|
throws RequestThrottledException {
|
||||||
try {
|
try {
|
||||||
return getLockSettings().verifyPassword(password, challenge, userId);
|
VerifyCredentialResponse response =
|
||||||
|
getLockSettings().verifyPassword(password, challenge, userId);
|
||||||
|
|
||||||
|
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||||
|
return response.getPayload();
|
||||||
|
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
|
||||||
|
throw new RequestThrottledException(response.getTimeout());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
} catch (RemoteException re) {
|
} catch (RemoteException re) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -284,9 +324,17 @@ public class LockPatternUtils {
|
|||||||
* @param password The password to check.
|
* @param password The password to check.
|
||||||
* @return Whether the password matches the stored one.
|
* @return Whether the password matches the stored one.
|
||||||
*/
|
*/
|
||||||
public boolean checkPassword(String password, int userId) {
|
public boolean checkPassword(String password, int userId) throws RequestThrottledException {
|
||||||
try {
|
try {
|
||||||
return getLockSettings().checkPassword(password, userId);
|
VerifyCredentialResponse response =
|
||||||
|
getLockSettings().checkPassword(password, userId);
|
||||||
|
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||||
|
return true;
|
||||||
|
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
|
||||||
|
throw new RequestThrottledException(response.getTimeout());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} catch (RemoteException re) {
|
} catch (RemoteException re) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -992,9 +1040,10 @@ public class LockPatternUtils {
|
|||||||
* pattern until the deadline has passed.
|
* pattern until the deadline has passed.
|
||||||
* @return the chosen deadline.
|
* @return the chosen deadline.
|
||||||
*/
|
*/
|
||||||
public long setLockoutAttemptDeadline(int userId) {
|
public long setLockoutAttemptDeadline(int userId, int timeoutMs) {
|
||||||
final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS;
|
final long deadline = SystemClock.elapsedRealtime() + timeoutMs;
|
||||||
setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId);
|
setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId);
|
||||||
|
setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, timeoutMs, userId);
|
||||||
return deadline;
|
return deadline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1005,8 +1054,9 @@ public class LockPatternUtils {
|
|||||||
*/
|
*/
|
||||||
public long getLockoutAttemptDeadline(int userId) {
|
public long getLockoutAttemptDeadline(int userId) {
|
||||||
final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId);
|
final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId);
|
||||||
|
final long timeoutMs = getLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0L, userId);
|
||||||
final long now = SystemClock.elapsedRealtime();
|
final long now = SystemClock.elapsedRealtime();
|
||||||
if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) {
|
if (deadline < now || deadline > (now + timeoutMs)) {
|
||||||
return 0L;
|
return 0L;
|
||||||
}
|
}
|
||||||
return deadline;
|
return deadline;
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.internal.widget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response object for an ILockSettings verification request.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
parcelable VerifyCredentialResponse;
|
||||||
|
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.internal.widget;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response object for a ILockSettings credential verification request.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public final class VerifyCredentialResponse implements Parcelable {
|
||||||
|
|
||||||
|
public static final int RESPONSE_ERROR = -1;
|
||||||
|
public static final int RESPONSE_OK = 0;
|
||||||
|
public static final int RESPONSE_RETRY = 1;
|
||||||
|
|
||||||
|
public static final VerifyCredentialResponse OK = new VerifyCredentialResponse();
|
||||||
|
public static final VerifyCredentialResponse ERROR
|
||||||
|
= new VerifyCredentialResponse(RESPONSE_ERROR, 0, null);
|
||||||
|
|
||||||
|
private int mResponseCode;
|
||||||
|
private byte[] mPayload;
|
||||||
|
private int mTimeout;
|
||||||
|
|
||||||
|
public static final Parcelable.Creator<VerifyCredentialResponse> CREATOR
|
||||||
|
= new Parcelable.Creator<VerifyCredentialResponse>() {
|
||||||
|
@Override
|
||||||
|
public VerifyCredentialResponse createFromParcel(Parcel source) {
|
||||||
|
int responseCode = source.readInt();
|
||||||
|
VerifyCredentialResponse response = new VerifyCredentialResponse(responseCode, 0, null);
|
||||||
|
if (responseCode == RESPONSE_RETRY) {
|
||||||
|
response.setTimeout(source.readInt());
|
||||||
|
} else if (responseCode == RESPONSE_OK) {
|
||||||
|
int size = source.readInt();
|
||||||
|
if (size > 0) {
|
||||||
|
byte[] payload = new byte[size];
|
||||||
|
source.readByteArray(payload);
|
||||||
|
response.setPayload(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VerifyCredentialResponse[] newArray(int size) {
|
||||||
|
return new VerifyCredentialResponse[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
public VerifyCredentialResponse() {
|
||||||
|
mResponseCode = RESPONSE_OK;
|
||||||
|
mPayload = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public VerifyCredentialResponse(byte[] payload) {
|
||||||
|
mPayload = payload;
|
||||||
|
mResponseCode = RESPONSE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VerifyCredentialResponse(int timeout) {
|
||||||
|
mTimeout = timeout;
|
||||||
|
mResponseCode = RESPONSE_RETRY;
|
||||||
|
mPayload = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private VerifyCredentialResponse(int responseCode, int timeout, byte[] payload) {
|
||||||
|
mResponseCode = responseCode;
|
||||||
|
mTimeout = timeout;
|
||||||
|
mPayload = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(mResponseCode);
|
||||||
|
if (mResponseCode == RESPONSE_RETRY) {
|
||||||
|
dest.writeInt(mTimeout);
|
||||||
|
} else if (mResponseCode == RESPONSE_OK) {
|
||||||
|
if (mPayload != null) {
|
||||||
|
dest.writeInt(mPayload.length);
|
||||||
|
dest.writeByteArray(mPayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getPayload() {
|
||||||
|
return mPayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTimeout() {
|
||||||
|
return mTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getResponseCode() {
|
||||||
|
return mResponseCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTimeout(int timeout) {
|
||||||
|
mTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPayload(byte[] payload) {
|
||||||
|
mPayload = payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -114,35 +114,40 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
|
|||||||
if (mPendingLockCheck != null) {
|
if (mPendingLockCheck != null) {
|
||||||
mPendingLockCheck.cancel(false);
|
mPendingLockCheck.cancel(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entry.length() < MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
|
||||||
|
// to avoid accidental lockout, only count attempts that are long enough to be a
|
||||||
|
// real password. This may require some tweaking.
|
||||||
|
setPasswordEntryInputEnabled(true);
|
||||||
|
onPasswordChecked(entry, false, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mPendingLockCheck = LockPatternChecker.checkPassword(
|
mPendingLockCheck = LockPatternChecker.checkPassword(
|
||||||
mLockPatternUtils,
|
mLockPatternUtils,
|
||||||
entry,
|
entry,
|
||||||
KeyguardUpdateMonitor.getCurrentUser(),
|
KeyguardUpdateMonitor.getCurrentUser(),
|
||||||
new LockPatternChecker.OnCheckCallback() {
|
new LockPatternChecker.OnCheckCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onChecked(boolean matched) {
|
public void onChecked(boolean matched, int timeoutMs) {
|
||||||
setPasswordEntryInputEnabled(true);
|
setPasswordEntryInputEnabled(true);
|
||||||
mPendingLockCheck = null;
|
mPendingLockCheck = null;
|
||||||
onPasswordChecked(entry, matched);
|
onPasswordChecked(entry, matched, timeoutMs);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPasswordChecked(String entry, boolean matched) {
|
private void onPasswordChecked(String entry, boolean matched, int timeoutMs) {
|
||||||
if (matched) {
|
if (matched) {
|
||||||
mCallback.reportUnlockAttempt(true);
|
mCallback.reportUnlockAttempt(true, 0);
|
||||||
mCallback.dismiss(true);
|
mCallback.dismiss(true);
|
||||||
} else {
|
} else {
|
||||||
if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
|
mCallback.reportUnlockAttempt(false, timeoutMs);
|
||||||
// to avoid accidental lockout, only count attempts that are long enough to be a
|
int attempts = KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
|
||||||
// real password. This may require some tweaking.
|
if (timeoutMs > 0) {
|
||||||
mCallback.reportUnlockAttempt(false);
|
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
|
||||||
int attempts = KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
|
KeyguardUpdateMonitor.getCurrentUser(), timeoutMs);
|
||||||
if (0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
|
handleAttemptLockout(deadline);
|
||||||
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
|
|
||||||
KeyguardUpdateMonitor.getCurrentUser());
|
|
||||||
handleAttemptLockout(deadline);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true);
|
mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,23 +224,30 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
|
|||||||
mPendingLockCheck.cancel(false);
|
mPendingLockCheck.cancel(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
|
||||||
|
mLockPatternView.enableInput();
|
||||||
|
onPatternChecked(pattern, false, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mPendingLockCheck = LockPatternChecker.checkPattern(
|
mPendingLockCheck = LockPatternChecker.checkPattern(
|
||||||
mLockPatternUtils,
|
mLockPatternUtils,
|
||||||
pattern,
|
pattern,
|
||||||
KeyguardUpdateMonitor.getCurrentUser(),
|
KeyguardUpdateMonitor.getCurrentUser(),
|
||||||
new LockPatternChecker.OnCheckCallback() {
|
new LockPatternChecker.OnCheckCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onChecked(boolean matched) {
|
public void onChecked(boolean matched, int timeoutMs) {
|
||||||
mLockPatternView.enableInput();
|
mLockPatternView.enableInput();
|
||||||
mPendingLockCheck = null;
|
mPendingLockCheck = null;
|
||||||
onPatternChecked(pattern, matched);
|
onPatternChecked(pattern, matched, timeoutMs);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPatternChecked(List<LockPatternView.Cell> pattern, boolean matched) {
|
private void onPatternChecked(List<LockPatternView.Cell> pattern, boolean matched,
|
||||||
|
int timeoutMs) {
|
||||||
if (matched) {
|
if (matched) {
|
||||||
mCallback.reportUnlockAttempt(true);
|
mCallback.reportUnlockAttempt(true, 0);
|
||||||
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
|
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
|
||||||
mCallback.dismiss(true);
|
mCallback.dismiss(true);
|
||||||
} else {
|
} else {
|
||||||
@@ -248,16 +255,11 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
|
|||||||
mCallback.userActivity();
|
mCallback.userActivity();
|
||||||
}
|
}
|
||||||
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
|
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
|
||||||
boolean registeredAttempt =
|
mCallback.reportUnlockAttempt(false, timeoutMs);
|
||||||
pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL;
|
|
||||||
if (registeredAttempt) {
|
|
||||||
mCallback.reportUnlockAttempt(false);
|
|
||||||
}
|
|
||||||
int attempts = mKeyguardUpdateMonitor.getFailedUnlockAttempts();
|
int attempts = mKeyguardUpdateMonitor.getFailedUnlockAttempts();
|
||||||
if (registeredAttempt &&
|
if (timeoutMs > 0) {
|
||||||
0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
|
|
||||||
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
|
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
|
||||||
KeyguardUpdateMonitor.getCurrentUser());
|
KeyguardUpdateMonitor.getCurrentUser(), timeoutMs);
|
||||||
handleAttemptLockout(deadline);
|
handleAttemptLockout(deadline);
|
||||||
} else {
|
} else {
|
||||||
mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern, true);
|
mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern, true);
|
||||||
|
|||||||
@@ -37,8 +37,10 @@ public interface KeyguardSecurityCallback {
|
|||||||
/**
|
/**
|
||||||
* Call to report an unlock attempt.
|
* Call to report an unlock attempt.
|
||||||
* @param success set to 'true' if user correctly entered security credentials.
|
* @param success set to 'true' if user correctly entered security credentials.
|
||||||
|
* @param timeoutMs timeout in milliseconds to wait before reattempting an unlock.
|
||||||
|
* Only nonzero if 'success' is false
|
||||||
*/
|
*/
|
||||||
void reportUnlockAttempt(boolean success);
|
void reportUnlockAttempt(boolean success, int timeoutMs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the keyguard view.
|
* Resets the keyguard view.
|
||||||
|
|||||||
@@ -176,8 +176,8 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showTimeoutDialog() {
|
private void showTimeoutDialog(int timeoutMs) {
|
||||||
int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
|
int timeoutInSeconds = (int) timeoutMs / 1000;
|
||||||
int messageId = 0;
|
int messageId = 0;
|
||||||
|
|
||||||
switch (mSecurityModel.getSecurityMode()) {
|
switch (mSecurityModel.getSecurityMode()) {
|
||||||
@@ -244,16 +244,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
|
|||||||
showDialog(null, message);
|
showDialog(null, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showAlmostAtAccountLoginDialog() {
|
private void reportFailedUnlockAttempt(int timeoutMs) {
|
||||||
final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
|
|
||||||
final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
|
|
||||||
- LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
|
|
||||||
String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
|
|
||||||
count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
|
|
||||||
showDialog(null, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reportFailedUnlockAttempt() {
|
|
||||||
final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
|
final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
|
||||||
final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
|
final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
|
||||||
|
|
||||||
@@ -290,14 +281,11 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
|
|||||||
Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
|
Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
|
||||||
showWipeDialog(failedAttempts, userType);
|
showWipeDialog(failedAttempts, userType);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
showTimeout =
|
|
||||||
(failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
|
|
||||||
}
|
}
|
||||||
monitor.reportFailedUnlockAttempt();
|
monitor.reportFailedUnlockAttempt();
|
||||||
mLockPatternUtils.reportFailedPasswordAttempt(KeyguardUpdateMonitor.getCurrentUser());
|
mLockPatternUtils.reportFailedPasswordAttempt(KeyguardUpdateMonitor.getCurrentUser());
|
||||||
if (showTimeout) {
|
if (timeoutMs > 0) {
|
||||||
showTimeoutDialog();
|
showTimeoutDialog(timeoutMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,14 +413,14 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
|
|||||||
return mIsVerifyUnlockOnly;
|
return mIsVerifyUnlockOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reportUnlockAttempt(boolean success) {
|
public void reportUnlockAttempt(boolean success, int timeoutMs) {
|
||||||
KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
|
KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
|
||||||
if (success) {
|
if (success) {
|
||||||
monitor.clearFailedUnlockAttempts();
|
monitor.clearFailedUnlockAttempts();
|
||||||
mLockPatternUtils.reportSuccessfulPasswordAttempt(
|
mLockPatternUtils.reportSuccessfulPasswordAttempt(
|
||||||
KeyguardUpdateMonitor.getCurrentUser());
|
KeyguardUpdateMonitor.getCurrentUser());
|
||||||
} else {
|
} else {
|
||||||
KeyguardSecurityContainer.this.reportFailedUnlockAttempt();
|
KeyguardSecurityContainer.this.reportFailedUnlockAttempt(timeoutMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,7 +436,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
|
|||||||
@Override
|
@Override
|
||||||
public void userActivity() { }
|
public void userActivity() { }
|
||||||
@Override
|
@Override
|
||||||
public void reportUnlockAttempt(boolean success) { }
|
public void reportUnlockAttempt(boolean success, int timeoutMs) { }
|
||||||
@Override
|
@Override
|
||||||
public boolean isVerifyUnlockOnly() { return false; }
|
public boolean isVerifyUnlockOnly() { return false; }
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -25,11 +25,9 @@ import android.content.Intent;
|
|||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
|
|
||||||
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
|
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
|
||||||
import static android.content.Context.USER_SERVICE;
|
import static android.content.Context.USER_SERVICE;
|
||||||
import static android.Manifest.permission.READ_PROFILE;
|
import static android.Manifest.permission.READ_PROFILE;
|
||||||
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@@ -44,6 +42,7 @@ import android.provider.Settings;
|
|||||||
import android.provider.Settings.Secure;
|
import android.provider.Settings.Secure;
|
||||||
import android.provider.Settings.SettingNotFoundException;
|
import android.provider.Settings.SettingNotFoundException;
|
||||||
import android.security.KeyStore;
|
import android.security.KeyStore;
|
||||||
|
import android.service.gatekeeper.GateKeeperResponse;
|
||||||
import android.service.gatekeeper.IGateKeeperService;
|
import android.service.gatekeeper.IGateKeeperService;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
@@ -51,6 +50,7 @@ import android.util.Slog;
|
|||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.internal.widget.ILockSettings;
|
import com.android.internal.widget.ILockSettings;
|
||||||
import com.android.internal.widget.LockPatternUtils;
|
import com.android.internal.widget.LockPatternUtils;
|
||||||
|
import com.android.internal.widget.VerifyCredentialResponse;
|
||||||
import com.android.server.LockSettingsStorage.CredentialHash;
|
import com.android.server.LockSettingsStorage.CredentialHash;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -76,6 +76,12 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
private boolean mFirstCallToVold;
|
private boolean mFirstCallToVold;
|
||||||
private IGateKeeperService mGateKeeperService;
|
private IGateKeeperService mGateKeeperService;
|
||||||
|
|
||||||
|
private interface CredentialUtil {
|
||||||
|
void setCredential(String credential, String savedCredential, int userId)
|
||||||
|
throws RemoteException;
|
||||||
|
byte[] toHash(String credential, int userId);
|
||||||
|
}
|
||||||
|
|
||||||
public LockSettingsService(Context context) {
|
public LockSettingsService(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
// Open the database
|
// Open the database
|
||||||
@@ -468,155 +474,163 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
byte[] toEnrollBytes = toEnroll == null
|
byte[] toEnrollBytes = toEnroll == null
|
||||||
? null
|
? null
|
||||||
: toEnroll.getBytes();
|
: toEnroll.getBytes();
|
||||||
byte[] hash = getGateKeeperService().enroll(userId, enrolledHandle, enrolledCredentialBytes,
|
GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle,
|
||||||
toEnrollBytes);
|
enrolledCredentialBytes, toEnrollBytes);
|
||||||
|
|
||||||
if (hash != null) {
|
if (response == null) {
|
||||||
setKeystorePassword(toEnroll, userId);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] hash = response.getPayload();
|
||||||
|
if (hash != null) {
|
||||||
|
setKeystorePassword(toEnroll, userId);
|
||||||
|
} else {
|
||||||
|
// Should not happen
|
||||||
|
Slog.e(TAG, "Throttled while enrolling a password");
|
||||||
|
}
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkPattern(String pattern, int userId) throws RemoteException {
|
public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
|
||||||
try {
|
return doVerifyPattern(pattern, false, 0, userId);
|
||||||
doVerifyPattern(pattern, false, 0, userId);
|
|
||||||
} catch (VerificationFailedException ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] verifyPattern(String pattern, long challenge, int userId)
|
public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
try {
|
return doVerifyPattern(pattern, true, challenge, userId);
|
||||||
return doVerifyPattern(pattern, true, challenge, userId);
|
|
||||||
} catch (VerificationFailedException ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] doVerifyPattern(String pattern, boolean hasChallenge, long challenge,
|
private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge, long challenge,
|
||||||
int userId) throws VerificationFailedException, RemoteException {
|
int userId) throws RemoteException {
|
||||||
checkPasswordReadPermission(userId);
|
checkPasswordReadPermission(userId);
|
||||||
|
|
||||||
CredentialHash storedHash = mStorage.readPatternHash(userId);
|
CredentialHash storedHash = mStorage.readPatternHash(userId);
|
||||||
|
return verifyCredential(userId, storedHash, pattern, hasChallenge,
|
||||||
|
challenge,
|
||||||
|
new CredentialUtil() {
|
||||||
|
@Override
|
||||||
|
public void setCredential(String pattern, String oldPattern, int userId)
|
||||||
|
throws RemoteException {
|
||||||
|
setLockPattern(pattern, oldPattern, userId);
|
||||||
|
}
|
||||||
|
|
||||||
if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(pattern)) {
|
@Override
|
||||||
// don't need to pass empty passwords to GateKeeper
|
public byte[] toHash(String pattern, int userId) {
|
||||||
return null;
|
return mLockPatternUtils.patternToHash(
|
||||||
}
|
mLockPatternUtils.stringToPattern(pattern));
|
||||||
|
}
|
||||||
if (TextUtils.isEmpty(pattern)) {
|
}
|
||||||
throw new VerificationFailedException();
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (storedHash.version == CredentialHash.VERSION_LEGACY) {
|
|
||||||
byte[] hash = mLockPatternUtils.patternToHash(
|
|
||||||
mLockPatternUtils.stringToPattern(pattern));
|
|
||||||
if (Arrays.equals(hash, storedHash.hash)) {
|
|
||||||
unlockKeystore(pattern, userId);
|
|
||||||
// migrate password to GateKeeper
|
|
||||||
setLockPattern(pattern, null, userId);
|
|
||||||
if (!hasChallenge) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Fall through to get the auth token. Technically this should never happen,
|
|
||||||
// as a user that had a legacy pattern would have to unlock their device
|
|
||||||
// before getting to a flow with a challenge, but supporting for consistency.
|
|
||||||
} else {
|
|
||||||
throw new VerificationFailedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] token = null;
|
|
||||||
if (hasChallenge) {
|
|
||||||
token = getGateKeeperService()
|
|
||||||
.verifyChallenge(userId, challenge, storedHash.hash, pattern.getBytes());
|
|
||||||
if (token == null) {
|
|
||||||
throw new VerificationFailedException();
|
|
||||||
}
|
|
||||||
} else if (!getGateKeeperService().verify(userId, storedHash.hash, pattern.getBytes())) {
|
|
||||||
throw new VerificationFailedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// pattern has matched
|
|
||||||
unlockKeystore(pattern, userId);
|
|
||||||
return token;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkPassword(String password, int userId) throws RemoteException {
|
public VerifyCredentialResponse checkPassword(String password, int userId)
|
||||||
try {
|
|
||||||
doVerifyPassword(password, false, 0, userId);
|
|
||||||
} catch (VerificationFailedException ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] verifyPassword(String password, long challenge, int userId)
|
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
try {
|
return doVerifyPassword(password, false, 0, userId);
|
||||||
return doVerifyPassword(password, true, challenge, userId);
|
|
||||||
} catch (VerificationFailedException ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] doVerifyPassword(String password, boolean hasChallenge, long challenge,
|
@Override
|
||||||
int userId) throws VerificationFailedException, RemoteException {
|
public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId)
|
||||||
|
throws RemoteException {
|
||||||
|
return doVerifyPassword(password, true, challenge, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
|
||||||
|
long challenge, int userId) throws RemoteException {
|
||||||
checkPasswordReadPermission(userId);
|
checkPasswordReadPermission(userId);
|
||||||
|
|
||||||
CredentialHash storedHash = mStorage.readPasswordHash(userId);
|
CredentialHash storedHash = mStorage.readPasswordHash(userId);
|
||||||
|
return verifyCredential(userId, storedHash, password, hasChallenge, challenge,
|
||||||
|
new CredentialUtil() {
|
||||||
|
@Override
|
||||||
|
public void setCredential(String password, String oldPassword, int userId)
|
||||||
|
throws RemoteException {
|
||||||
|
setLockPassword(password, oldPassword, userId);
|
||||||
|
}
|
||||||
|
|
||||||
if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(password)) {
|
@Override
|
||||||
// don't need to pass empty passwords to GateKeeper
|
public byte[] toHash(String password, int userId) {
|
||||||
return null;
|
return mLockPatternUtils.passwordToHash(password, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
|
||||||
|
String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil)
|
||||||
|
throws RemoteException {
|
||||||
|
if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) {
|
||||||
|
// don't need to pass empty credentials to GateKeeper
|
||||||
|
return VerifyCredentialResponse.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TextUtils.isEmpty(password)) {
|
if (TextUtils.isEmpty(credential)) {
|
||||||
throw new VerificationFailedException();
|
return VerifyCredentialResponse.ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storedHash.version == CredentialHash.VERSION_LEGACY) {
|
if (storedHash.version == CredentialHash.VERSION_LEGACY) {
|
||||||
byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
|
byte[] hash = credentialUtil.toHash(credential, userId);
|
||||||
if (Arrays.equals(hash, storedHash.hash)) {
|
if (Arrays.equals(hash, storedHash.hash)) {
|
||||||
unlockKeystore(password, userId);
|
unlockKeystore(credential, userId);
|
||||||
// migrate password to GateKeeper
|
// migrate credential to GateKeeper
|
||||||
setLockPassword(password, null, userId);
|
credentialUtil.setCredential(credential, null, userId);
|
||||||
if (!hasChallenge) {
|
if (!hasChallenge) {
|
||||||
return null;
|
return VerifyCredentialResponse.OK;
|
||||||
}
|
}
|
||||||
// Fall through to get the auth token. Technically this should never happen,
|
// Fall through to get the auth token. Technically this should never happen,
|
||||||
// as a user that had a legacy password would have to unlock their device
|
// as a user that had a legacy credential would have to unlock their device
|
||||||
// before getting to a flow with a challenge, but supporting for consistency.
|
// before getting to a flow with a challenge, but supporting for consistency.
|
||||||
} else {
|
} else {
|
||||||
throw new VerificationFailedException();
|
return VerifyCredentialResponse.ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] token = null;
|
VerifyCredentialResponse response;
|
||||||
|
boolean shouldReEnroll = false;;
|
||||||
if (hasChallenge) {
|
if (hasChallenge) {
|
||||||
token = getGateKeeperService()
|
byte[] token = null;
|
||||||
.verifyChallenge(userId, challenge, storedHash.hash, password.getBytes());
|
GateKeeperResponse gateKeeperResponse = getGateKeeperService()
|
||||||
if (token == null) {
|
.verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes());
|
||||||
throw new VerificationFailedException();
|
int responseCode = gateKeeperResponse.getResponseCode();
|
||||||
|
if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
|
||||||
|
response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
|
||||||
|
} else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
|
||||||
|
token = gateKeeperResponse.getPayload();
|
||||||
|
if (token == null) {
|
||||||
|
// something's wrong if there's no payload with a challenge
|
||||||
|
Slog.e(TAG, "verifyChallenge response had no associated payload");
|
||||||
|
response = VerifyCredentialResponse.ERROR;
|
||||||
|
} else {
|
||||||
|
shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
|
||||||
|
response = new VerifyCredentialResponse(token);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response = VerifyCredentialResponse.ERROR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GateKeeperResponse gateKeeperResponse = getGateKeeperService().verify(
|
||||||
|
userId, storedHash.hash, credential.getBytes());
|
||||||
|
int responseCode = gateKeeperResponse.getResponseCode();
|
||||||
|
if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
|
||||||
|
response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
|
||||||
|
} else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
|
||||||
|
shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
|
||||||
|
response = VerifyCredentialResponse.OK;
|
||||||
|
} else {
|
||||||
|
response = VerifyCredentialResponse.ERROR;
|
||||||
}
|
}
|
||||||
} else if (!getGateKeeperService().verify(userId, storedHash.hash, password.getBytes())) {
|
|
||||||
throw new VerificationFailedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// password has matched
|
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||||
unlockKeystore(password, userId);
|
// credential has matched
|
||||||
return token;
|
unlockKeystore(credential, userId);
|
||||||
}
|
if (shouldReEnroll) {
|
||||||
|
credentialUtil.setCredential(credential, credential, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkVoldPassword(int userId) throws RemoteException {
|
public boolean checkVoldPassword(int userId) throws RemoteException {
|
||||||
@@ -644,7 +658,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (mLockPatternUtils.isLockPatternEnabled(userId)) {
|
if (mLockPatternUtils.isLockPatternEnabled(userId)) {
|
||||||
if (checkPattern(password, userId)) {
|
if (checkPattern(password, userId).getResponseCode()
|
||||||
|
== GateKeeperResponse.RESPONSE_OK) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -653,7 +668,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
|
if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
|
||||||
if (checkPassword(password, userId)) {
|
if (checkPassword(password, userId).getResponseCode()
|
||||||
|
== GateKeeperResponse.RESPONSE_OK) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -741,6 +757,4 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class VerificationFailedException extends Exception {}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user