Changes to support reducing noise in active unlock notifications.

1. Add additional trigger onUserMayRequestUnlock
  - point existing trigger points from SystemUI to this
2. Argument added to onUserRequestedUnlock to indicate if user wants to
   dismiss keyguard.
3. Callback for result of grantTrust calls.
  - curently only calls back if device was unlocked as a result of the
    call

See design: go/au-noise-reduction

Note: changes made to SystemUI are manually tested and may need
refactoring which is being skipped now to meet the API Freeze deadline.

Bug: 225231929
Test: atest TrustTests
Test: Manual interaction with wake & fingerprint sensor
CTS-Coverage-Bug: 213944235
Change-Id: I8d08229f09c9a1f2295b7eb464d12cad0c6b8303
This commit is contained in:
Dave McCloskey 2022-03-17 16:09:26 -07:00
parent 34fba3edc7
commit 6c707a47da
17 changed files with 532 additions and 71 deletions

View File

@ -11830,11 +11830,23 @@ package android.service.translation {
package android.service.trust {
public final class GrantTrustResult implements android.os.Parcelable {
method public int describeContents();
method public int getStatus();
method @NonNull public static String statusToString(int);
method @NonNull public static android.service.trust.GrantTrustResult withStatus(int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.trust.GrantTrustResult> CREATOR;
field public static final int STATUS_UNKNOWN = 0; // 0x0
field public static final int STATUS_UNLOCKED_BY_GRANT = 1; // 0x1
}
public class TrustAgentService extends android.app.Service {
ctor public TrustAgentService();
method public final void addEscrowToken(byte[], android.os.UserHandle);
method @Deprecated public final void grantTrust(CharSequence, long, boolean);
method public final void grantTrust(CharSequence, long, int);
method @Deprecated public final void grantTrust(CharSequence, long, int);
method public final void grantTrust(@NonNull CharSequence, long, int, @Nullable java.util.function.Consumer<android.service.trust.GrantTrustResult>);
method public final void isEscrowTokenActive(long, android.os.UserHandle);
method public final void lockUser();
method public final android.os.IBinder onBind(android.content.Intent);
@ -11847,7 +11859,8 @@ package android.service.trust {
method public void onEscrowTokenStateReceived(long, int);
method public void onTrustTimeout();
method public void onUnlockAttempt(boolean);
method public void onUserRequestedUnlock();
method public void onUserMayRequestUnlock();
method public void onUserRequestedUnlock(boolean);
method public final void removeEscrowToken(long, android.os.UserHandle);
method public final void revokeTrust();
method public final void setManagingTrust(boolean);

View File

@ -26,7 +26,8 @@ import android.hardware.biometrics.BiometricSourceType;
*/
interface ITrustManager {
void reportUnlockAttempt(boolean successful, int userId);
void reportUserRequestedUnlock(int userId);
void reportUserRequestedUnlock(int userId, boolean dismissKeyguard);
void reportUserMayRequestUnlock(int userId);
void reportUnlockLockout(int timeoutMs, int userId);
void reportEnabledTrustAgentsChanged(int userId);
void registerTrustListener(in ITrustListener trustListener);

View File

@ -92,10 +92,25 @@ public class TrustManager {
* Reports that the user {@code userId} is likely interested in unlocking the device.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
*
* @param dismissKeyguard whether the user wants to dismiss keyguard
*/
public void reportUserRequestedUnlock(int userId) {
public void reportUserRequestedUnlock(int userId, boolean dismissKeyguard) {
try {
mService.reportUserRequestedUnlock(userId);
mService.reportUserRequestedUnlock(userId, dismissKeyguard);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Reports that the user {@code userId} may want to unlock the device soon.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
*/
public void reportUserMayRequestUnlock(int userId) {
try {
mService.reportUserMayRequestUnlock(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2022 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.trust;
parcelable GrantTrustResult;

View File

@ -0,0 +1,179 @@
/*
* Copyright (C) 2022 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.trust;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Result type for a callback in a call to
* {@link TrustAgentService#grantTrust(CharSequence, long, int)}.
*
* @hide
*/
@DataClass(genHiddenConstructor = true)
@SystemApi
public final class GrantTrustResult implements Parcelable {
/** Result status is unknown to this version of the SDK. */
public static final int STATUS_UNKNOWN = 0;
/** The device went from locked to unlocked as a result of the call. */
public static final int STATUS_UNLOCKED_BY_GRANT = 1;
@Status
private int mStatus;
/** Returns a new {@link GrantTrustResult} with the specified status. */
@NonNull
public static GrantTrustResult withStatus(@Status int status) {
return new GrantTrustResult(status);
}
// Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/trust/GrantTrustResult.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
//@formatter:off
/** @hide */
@IntDef(prefix = "STATUS_", value = {
STATUS_UNKNOWN,
STATUS_UNLOCKED_BY_GRANT
})
@Retention(RetentionPolicy.SOURCE)
@DataClass.Generated.Member
public @interface Status {}
@NonNull
@DataClass.Generated.Member
public static String statusToString(@Status int value) {
switch (value) {
case STATUS_UNKNOWN:
return "STATUS_UNKNOWN";
case STATUS_UNLOCKED_BY_GRANT:
return "STATUS_UNLOCKED_BY_GRANT";
default: return Integer.toHexString(value);
}
}
/**
* Creates a new GrantTrustResult.
*
* @hide
*/
@DataClass.Generated.Member
public GrantTrustResult(
@Status int status) {
this.mStatus = status;
if (!(mStatus == STATUS_UNKNOWN)
&& !(mStatus == STATUS_UNLOCKED_BY_GRANT)) {
throw new java.lang.IllegalArgumentException(
"status was " + mStatus + " but must be one of: "
+ "STATUS_UNKNOWN(" + STATUS_UNKNOWN + "), "
+ "STATUS_UNLOCKED_BY_GRANT(" + STATUS_UNLOCKED_BY_GRANT + ")");
}
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public @Status int getStatus() {
return mStatus;
}
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
dest.writeInt(mStatus);
}
@Override
@DataClass.Generated.Member
public int describeContents() { return 0; }
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
/* package-private */ GrantTrustResult(@NonNull android.os.Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
int status = in.readInt();
this.mStatus = status;
if (!(mStatus == STATUS_UNKNOWN)
&& !(mStatus == STATUS_UNLOCKED_BY_GRANT)) {
throw new java.lang.IllegalArgumentException(
"status was " + mStatus + " but must be one of: "
+ "STATUS_UNKNOWN(" + STATUS_UNKNOWN + "), "
+ "STATUS_UNLOCKED_BY_GRANT(" + STATUS_UNLOCKED_BY_GRANT + ")");
}
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<GrantTrustResult> CREATOR
= new Parcelable.Creator<GrantTrustResult>() {
@Override
public GrantTrustResult[] newArray(int size) {
return new GrantTrustResult[size];
}
@Override
public GrantTrustResult createFromParcel(@NonNull android.os.Parcel in) {
return new GrantTrustResult(in);
}
};
@DataClass.Generated(
time = 1647878197834L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/trust/GrantTrustResult.java",
inputSignatures = "public static final int STATUS_UNKNOWN\npublic static final int STATUS_UNLOCKED_BY_GRANT\nprivate @android.service.trust.GrantTrustResult.Status int mStatus\npublic static @android.annotation.NonNull android.service.trust.GrantTrustResult withStatus(int)\nclass GrantTrustResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
//@formatter:on
// End of generated code
}

View File

@ -25,7 +25,8 @@ import android.service.trust.ITrustAgentServiceCallback;
*/
interface ITrustAgentService {
oneway void onUnlockAttempt(boolean successful);
oneway void onUserRequestedUnlock();
oneway void onUserRequestedUnlock(boolean dismissKeyguard);
oneway void onUserMayRequestUnlock();
oneway void onUnlockLockout(int timeoutMs);
oneway void onTrustTimeout();
oneway void onDeviceLocked();

View File

@ -18,13 +18,15 @@ package android.service.trust;
import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
import com.android.internal.infra.AndroidFuture;
/**
* Communication channel from the TrustAgentService back to TrustManagerService.
* @hide
*/
oneway interface ITrustAgentServiceCallback {
void grantTrust(CharSequence message, long durationMs, int flags);
void grantTrust(
CharSequence message, long durationMs, int flags, in AndroidFuture resultCallback);
void revokeTrust();
void lockUser();
void setManagingTrust(boolean managingTrust);

View File

@ -19,6 +19,7 @@ package android.service.trust;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
@ -39,9 +40,12 @@ import android.os.UserManager;
import android.util.Log;
import android.util.Slog;
import com.android.internal.infra.AndroidFuture;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.function.Consumer;
/**
* A service that notifies the system about whether it believes the environment of the device
@ -183,6 +187,7 @@ public class TrustAgentService extends Service {
private static final int MSG_ESCROW_TOKEN_STATE_RECEIVED = 8;
private static final int MSG_ESCROW_TOKEN_REMOVED = 9;
private static final int MSG_USER_REQUESTED_UNLOCK = 10;
private static final int MSG_USER_MAY_REQUEST_UNLOCK = 11;
private static final String EXTRA_TOKEN = "token";
private static final String EXTRA_TOKEN_HANDLE = "token_handle";
@ -217,7 +222,10 @@ public class TrustAgentService extends Service {
onUnlockAttempt(msg.arg1 != 0);
break;
case MSG_USER_REQUESTED_UNLOCK:
onUserRequestedUnlock();
onUserRequestedUnlock(msg.arg1 != 0);
break;
case MSG_USER_MAY_REQUEST_UNLOCK:
onUserMayRequestUnlock();
break;
case MSG_UNLOCK_LOCKOUT:
onDeviceUnlockLockout(msg.arg1);
@ -296,17 +304,38 @@ public class TrustAgentService extends Service {
public void onUnlockAttempt(boolean successful) {
}
/**
* Called when the user has interacted with the locked device such that they are likely to want
* it to be unlocked soon. This approximates the timing when, for example, the platform would
* check for face authentication to unlock the device.
*
* This signal can be used for the agent to make preparations to quickly unlock the device
* with {@link #onUserRequestedUnlock}. Agents should not unlock the device based solely on this
* signal. There is no guarantee that this method will be called before
* {@link #onUserRequestedUnlock(boolean)}.
*/
public void onUserMayRequestUnlock() {
}
/**
* Called when the user has interacted with the locked device such that they likely want it
* to be unlocked. This approximates the timing when, for example, the platform would check for
* face authentication to unlock the device.
* to be unlocked.
*
* When this is called, there is a high probability that the user wants to unlock the device and
* that a biometric method is either not available or not the optimal method at this time. For
* example, this may be called after some kinds of biometric authentication failure.
*
* A call to this method may be preceded by a call to {@link #onUserMayRequestUnlock} which
* the agent can use as a signal to prepare for a subsequent call to this method.
*
* To attempt to unlock the device, the agent needs to call
* {@link #grantTrust(CharSequence, long, int)}.
*
* @param dismissKeyguard true when the user wants keyguard dismissed
*
* @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
*/
public void onUserRequestedUnlock() {
public void onUserRequestedUnlock(boolean dismissKeyguard) {
}
/**
@ -399,26 +428,10 @@ public class TrustAgentService extends Service {
}
/**
* Call to grant trust on the device.
* Attempts to grant trust on the device.
*
* @param message describes why the device is trusted, e.g. "Trusted by location".
* @param durationMs amount of time in milliseconds to keep the device in a trusted state.
* Trust for this agent will automatically be revoked when the timeout expires unless
* extended by a subsequent call to this function. The timeout is measured from the
* invocation of this function as dictated by {@link SystemClock#elapsedRealtime())}.
* For security reasons, the value should be no larger than necessary.
* The value may be adjusted by the system as necessary to comply with a policy controlled
* by the system or {@link DevicePolicyManager} restrictions. See {@link #onTrustTimeout()}
* for determining when trust expires.
* @param initiatedByUser this is a hint to the system that trust is being granted as the
* direct result of user action - such as solving a security challenge. The hint is used
* by the system to optimize the experience. Behavior may vary by device and release, so
* one should only set this parameter if it meets the above criteria rather than relying on
* the behavior of any particular device or release. Corresponds to
* {@link #FLAG_GRANT_TRUST_INITIATED_BY_USER}.
* @throws IllegalStateException if the agent is not currently managing trust.
*
* @deprecated use {@link #grantTrust(CharSequence, long, int)} instead.
* @param initiatedByUser see {@link #FLAG_GRANT_TRUST_INITIATED_BY_USER}
* @deprecated use {@link #grantTrust(CharSequence, long, int, Consumer)} instead.
*/
@Deprecated
public final void grantTrust(
@ -427,7 +440,17 @@ public class TrustAgentService extends Service {
}
/**
* Call to grant trust on the device.
* Attempts to grant trust on the device.
* @deprecated use {@link #grantTrust(CharSequence, long, int, Consumer)} instead.
*/
@Deprecated
public final void grantTrust(
final CharSequence message, final long durationMs, @GrantTrustFlags final int flags) {
grantTrust(message, durationMs, flags, null);
}
/**
* Attempts to grant trust on the device.
*
* @param message describes why the device is trusted, e.g. "Trusted by location".
* @param durationMs amount of time in milliseconds to keep the device in a trusted state.
@ -438,19 +461,36 @@ public class TrustAgentService extends Service {
* The value may be adjusted by the system as necessary to comply with a policy controlled
* by the system or {@link DevicePolicyManager} restrictions. See {@link #onTrustTimeout()}
* for determining when trust expires.
* @param flags TBDocumented
* @param flags flags to control call: see constants prefixed by {@code FLAG_GRANT_TRUST_}.
* @param resultCallback may be called with the results of the grant
* @throws IllegalStateException if the agent is not currently managing trust.
*
* See {@link GrantTrustResult} for the cases where {@code resultCallback} will be called.
*/
public final void grantTrust(
final CharSequence message, final long durationMs, @GrantTrustFlags final int flags) {
@NonNull final CharSequence message,
final long durationMs,
@GrantTrustFlags final int flags,
@Nullable final Consumer<GrantTrustResult> resultCallback) {
synchronized (mLock) {
if (!mManagingTrust) {
throw new IllegalStateException("Cannot grant trust if agent is not managing trust."
+ " Call setManagingTrust(true) first.");
}
// Prepare future for the IPC
AndroidFuture<GrantTrustResult> future = new AndroidFuture<>();
future.thenAccept(result -> {
if (resultCallback != null) {
// Instead of taking an explicit executor, we post this to mHandler to be
// consistent with the other event methods in this class.
mHandler.post(() -> resultCallback.accept(result));
}
});
if (mCallback != null) {
try {
mCallback.grantTrust(message.toString(), durationMs, flags);
mCallback.grantTrust(message.toString(), durationMs, flags, future);
} catch (RemoteException e) {
onError("calling enableTrust()");
}
@ -460,7 +500,7 @@ public class TrustAgentService extends Service {
mPendingGrantTrustTask = new Runnable() {
@Override
public void run() {
grantTrust(message, durationMs, flags);
grantTrust(message, durationMs, flags, resultCallback);
}
};
}
@ -666,8 +706,14 @@ public class TrustAgentService extends Service {
}
@Override
public void onUserRequestedUnlock() {
mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK).sendToTarget();
public void onUserRequestedUnlock(boolean dismissKeyguard) {
mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK, dismissKeyguard ? 1 : 0, 0)
.sendToTarget();
}
@Override
public void onUserMayRequestUnlock() {
mHandler.obtainMessage(MSG_USER_MAY_REQUEST_UNLOCK).sendToTarget();
}
@Override

View File

@ -1486,6 +1486,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void onAuthenticationFailed() {
handleFingerprintAuthFailed();
// TODO(b/225231929): Refactor as needed, add tests, etc.
mTrustManager.reportUserRequestedUnlock(
KeyguardUpdateMonitor.getCurrentUser(), true);
}
@Override
@ -2258,7 +2262,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
if (shouldTriggerActiveUnlock()) {
mTrustManager.reportUserRequestedUnlock(KeyguardUpdateMonitor.getCurrentUser());
// TODO(b/225231929): Refactor surrounding code to reflect calling of new method
mTrustManager.reportUserMayRequestUnlock(KeyguardUpdateMonitor.getCurrentUser());
}
}

View File

@ -40,12 +40,16 @@ import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.service.trust.GrantTrustResult;
import android.service.trust.ITrustAgentService;
import android.service.trust.ITrustAgentServiceCallback;
import android.service.trust.TrustAgentService;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.infra.AndroidFuture;
import java.util.Collections;
import java.util.List;
@ -156,7 +160,9 @@ public class TrustAgentWrapper {
}
mTrusted = true;
mTrustable = false;
mMessage = (CharSequence) msg.obj;
Pair<CharSequence, AndroidFuture<GrantTrustResult>> pair = (Pair) msg.obj;
mMessage = pair.first;
AndroidFuture<GrantTrustResult> resultCallback = pair.second;
int flags = msg.arg1;
mDisplayTrustGrantedMessage = (flags & FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0;
if ((flags & FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) {
@ -189,7 +195,7 @@ public class TrustAgentWrapper {
mTrustManagerService.mArchive.logGrantTrust(mUserId, mName,
(mMessage != null ? mMessage.toString() : null),
durationMs, flags);
mTrustManagerService.updateTrust(mUserId, flags);
mTrustManagerService.updateTrust(mUserId, flags, resultCallback);
break;
case MSG_TRUST_TIMEOUT:
if (DEBUG) Slog.d(TAG, "Trust timed out : " + mName.flattenToShortString());
@ -314,13 +320,18 @@ public class TrustAgentWrapper {
private ITrustAgentServiceCallback mCallback = new ITrustAgentServiceCallback.Stub() {
@Override
public void grantTrust(CharSequence message, long durationMs, int flags) {
public void grantTrust(
CharSequence message,
long durationMs,
int flags,
AndroidFuture resultCallback) {
if (DEBUG) {
Slog.d(TAG, "enableTrust(" + message + ", durationMs = " + durationMs
+ ", flags = " + flags + ")");
}
Message msg = mHandler.obtainMessage(MSG_GRANT_TRUST, flags, 0, message);
Message msg = mHandler.obtainMessage(
MSG_GRANT_TRUST, flags, 0, Pair.create(message, resultCallback));
msg.getData().putLong(DATA_DURATION, durationMs);
msg.sendToTarget();
}
@ -514,12 +525,23 @@ public class TrustAgentWrapper {
}
/**
* @see android.service.trust.TrustAgentService#onUserRequestedUnlock()
* @see android.service.trust.TrustAgentService#onUserRequestedUnlock(boolean)
*/
public void onUserRequestedUnlock() {
public void onUserRequestedUnlock(boolean dismissKeyguard) {
try {
if (mTrustAgentService != null) {
mTrustAgentService.onUserRequestedUnlock();
mTrustAgentService.onUserRequestedUnlock(dismissKeyguard);
}
} catch (RemoteException e) {
onError(e);
}
}
/** @see android.service.trust.TrustAgentService#onUserMayRequestUnlock() */
public void onUserMayRequestUnlock() {
try {
if (mTrustAgentService != null) {
mTrustAgentService.onUserMayRequestUnlock();
}
} catch (RemoteException e) {
onError(e);

View File

@ -61,6 +61,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.security.Authorization;
import android.service.trust.GrantTrustResult;
import android.service.trust.TrustAgentService;
import android.text.TextUtils;
import android.util.ArrayMap;
@ -77,6 +78,7 @@ import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
@ -131,6 +133,7 @@ public class TrustManagerService extends SystemService {
private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15;
private static final int MSG_USER_REQUESTED_UNLOCK = 16;
private static final int MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH = 17;
private static final int MSG_USER_MAY_REQUEST_UNLOCK = 18;
private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except";
@ -495,13 +498,28 @@ public class TrustManagerService extends SystemService {
}
}
public void updateTrust(int userId, int flags) {
updateTrust(userId, flags, false /* isFromUnlock */);
/** Triggers a trust update. */
public void updateTrust(
int userId,
int flags) {
updateTrust(userId, flags, null);
}
private void updateTrust(int userId, int flags, boolean isFromUnlock) {
/** Triggers a trust update. */
public void updateTrust(
int userId,
int flags,
@Nullable AndroidFuture<GrantTrustResult> resultCallback) {
updateTrust(userId, flags, false /* isFromUnlock */, resultCallback);
}
private void updateTrust(
int userId,
int flags,
boolean isFromUnlock,
@Nullable AndroidFuture<GrantTrustResult> resultCallback) {
if (ENABLE_ACTIVE_UNLOCK_FLAG) {
updateTrustWithRenewableUnlock(userId, flags, isFromUnlock);
updateTrustWithRenewableUnlock(userId, flags, isFromUnlock, resultCallback);
} else {
updateTrustWithNonrenewableTrust(userId, flags, isFromUnlock);
}
@ -553,7 +571,11 @@ public class TrustManagerService extends SystemService {
}
}
private void updateTrustWithRenewableUnlock(int userId, int flags, boolean isFromUnlock) {
private void updateTrustWithRenewableUnlock(
int userId,
int flags,
boolean isFromUnlock,
@Nullable AndroidFuture<GrantTrustResult> resultCallback) {
boolean managed = aggregateIsTrustManaged(userId);
dispatchOnTrustManagedChanged(managed, userId);
if (mStrongAuthTracker.isTrustAllowedForUser(userId)
@ -614,8 +636,18 @@ public class TrustManagerService extends SystemService {
isTrustableTimeout /* isTrustableTimeout */);
}
}
}
boolean wasLocked = !alreadyUnlocked;
boolean shouldSendCallback = wasLocked && pendingTrustState == TrustState.TRUSTED;
if (shouldSendCallback) {
if (resultCallback != null) {
if (DEBUG) Slog.d(TAG, "calling back with UNLOCKED_BY_GRANT");
resultCallback.complete(
GrantTrustResult.withStatus(
GrantTrustResult.STATUS_UNLOCKED_BY_GRANT));
}
}
}
private void updateTrustUsuallyManaged(int userId, boolean managed) {
synchronized (mTrustUsuallyManagedForUser) {
@ -1190,7 +1222,7 @@ public class TrustManagerService extends SystemService {
if (successful) {
mStrongAuthTracker.allowTrustFromUnlock(userId);
// Allow the presence of trust on a successful unlock attempt to extend unlock
updateTrust(userId, 0 /* flags */, true);
updateTrust(userId, 0 /* flags */, true, null);
mHandler.obtainMessage(MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH, userId).sendToTarget();
}
@ -1202,11 +1234,27 @@ public class TrustManagerService extends SystemService {
}
}
private void dispatchUserRequestedUnlock(int userId) {
private void dispatchUserRequestedUnlock(int userId, boolean dismissKeyguard) {
if (DEBUG) {
Slog.d(TAG, "dispatchUserRequestedUnlock(user=" + userId + ", dismissKeyguard="
+ dismissKeyguard + ")");
}
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
if (info.userId == userId) {
info.agent.onUserRequestedUnlock();
info.agent.onUserRequestedUnlock(dismissKeyguard);
}
}
}
private void dispatchUserMayRequestUnlock(int userId) {
if (DEBUG) {
Slog.d(TAG, "dispatchUserMayRequestUnlock(user=" + userId + ")");
}
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
if (info.userId == userId) {
info.agent.onUserMayRequestUnlock();
}
}
}
@ -1341,9 +1389,17 @@ public class TrustManagerService extends SystemService {
}
@Override
public void reportUserRequestedUnlock(int userId) throws RemoteException {
public void reportUserRequestedUnlock(int userId, boolean dismissKeyguard)
throws RemoteException {
enforceReportPermission();
mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK, userId).sendToTarget();
mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK, userId, dismissKeyguard ? 1 : 0)
.sendToTarget();
}
@Override
public void reportUserMayRequestUnlock(int userId) throws RemoteException {
enforceReportPermission();
mHandler.obtainMessage(MSG_USER_MAY_REQUEST_UNLOCK, userId).sendToTarget();
}
@Override
@ -1683,7 +1739,10 @@ public class TrustManagerService extends SystemService {
dispatchUnlockAttempt(msg.arg1 != 0, msg.arg2);
break;
case MSG_USER_REQUESTED_UNLOCK:
dispatchUserRequestedUnlock(msg.arg1);
dispatchUserRequestedUnlock(msg.arg1, msg.arg2 != 0);
break;
case MSG_USER_MAY_REQUEST_UNLOCK:
dispatchUserMayRequestUnlock(msg.arg1);
break;
case MSG_DISPATCH_UNLOCK_LOCKOUT:
dispatchUnlockLockout(msg.arg1, msg.arg2);
@ -1725,7 +1784,7 @@ public class TrustManagerService extends SystemService {
break;
case MSG_REFRESH_DEVICE_LOCKED_FOR_USER:
if (msg.arg2 == 1) {
updateTrust(msg.arg1, 0 /* flags */, true /* isFromUnlock */);
updateTrust(msg.arg1, 0 /* flags */, true /* isFromUnlock */, null);
}
final int unlockedUser = msg.getData().getInt(
REFRESH_DEVICE_LOCKED_EXCEPT_USER, UserHandle.USER_NULL);

View File

@ -25,6 +25,8 @@ android_test {
"androidx.test.rules",
"androidx.test.ext.junit",
"androidx.test.uiautomator",
"mockito-target-minus-junit4",
"servicestests-utils",
"truth-prebuilt",
],
libs: [

View File

@ -16,6 +16,7 @@
package android.trust.test
import android.service.trust.GrantTrustResult
import android.trust.BaseTrustAgentService
import android.trust.TrustTestActivity
import android.trust.test.lib.LockStateTrackingRule
@ -25,11 +26,13 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.UiDevice
import com.android.server.testutils.mock
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
import org.mockito.Mockito.verifyZeroInteractions
/**
* Test for testing revokeTrust & grantTrust for non-renewable trust.
@ -66,7 +69,7 @@ class GrantAndRevokeTrustTest {
@Test
fun grantKeepsDeviceUnlocked() {
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 10000, 0)
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 10000, 0) {}
uiDevice.sleep()
lockStateTrackingRule.assertUnlocked()
@ -74,7 +77,7 @@ class GrantAndRevokeTrustTest {
@Test
fun grantKeepsDeviceUnlocked_untilRevoked() {
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0)
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0) {}
await()
uiDevice.sleep()
trustAgentRule.agent.revokeTrust()
@ -82,6 +85,15 @@ class GrantAndRevokeTrustTest {
lockStateTrackingRule.assertLocked()
}
@Test
fun grantDoesNotCallBack() {
val callback = mock<(GrantTrustResult) -> Unit>()
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0, callback)
await()
verifyZeroInteractions(callback)
}
companion object {
private const val TAG = "GrantAndRevokeTrustTest"
private const val GRANT_MESSAGE = "granted by test"

View File

@ -16,16 +16,20 @@
package android.trust.test
import android.service.trust.GrantTrustResult
import android.service.trust.GrantTrustResult.STATUS_UNLOCKED_BY_GRANT
import android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
import android.trust.BaseTrustAgentService
import android.trust.TrustTestActivity
import android.trust.test.lib.LockStateTrackingRule
import android.trust.test.lib.ScreenLockRule
import android.trust.test.lib.TrustAgentRule
import android.util.Log
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.UiDevice
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -70,7 +74,8 @@ class TemporaryAndRenewableTrustTest {
uiDevice.sleep()
lockStateTrackingRule.assertLocked()
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
trustAgentRule.agent.grantTrust(
GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
uiDevice.wakeUp()
lockStateTrackingRule.assertLocked()
@ -78,7 +83,8 @@ class TemporaryAndRenewableTrustTest {
@Test
fun grantTrustUnlockedDevice_deviceLocksOnScreenOff() {
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
trustAgentRule.agent.grantTrust(
GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
uiDevice.sleep()
lockStateTrackingRule.assertLocked()
@ -86,20 +92,48 @@ class TemporaryAndRenewableTrustTest {
@Test
fun grantTrustLockedDevice_grantTrustOnLockedDeviceUnlocksDevice() {
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
trustAgentRule.agent.grantTrust(
GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
uiDevice.sleep()
lockStateTrackingRule.assertLocked()
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
trustAgentRule.agent.grantTrust(
GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
uiDevice.wakeUp()
lockStateTrackingRule.assertUnlocked()
}
@Test
fun grantTrustLockedDevice_callsBackWhenUnlocked() {
Log.i(TAG, "Granting renewable trust while unlocked")
trustAgentRule.agent.grantTrust(
GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
await(1000)
Log.i(TAG, "Locking device")
uiDevice.sleep()
lockStateTrackingRule.assertLocked()
Log.i(TAG, "Renewing trust and unlocking")
var result: GrantTrustResult? = null
trustAgentRule.agent.grantTrust(
GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {
Log.i(TAG, "Callback received; status=${it.status}")
result = it
}
uiDevice.wakeUp()
lockStateTrackingRule.assertUnlocked()
assertThat(result?.status).isEqualTo(STATUS_UNLOCKED_BY_GRANT)
}
@Test
fun grantTrustLockedDevice_revokeTrustPreventsSubsequentUnlock() {
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
trustAgentRule.agent.grantTrust(
GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
uiDevice.sleep()
lockStateTrackingRule.assertLocked()
@ -109,7 +143,8 @@ class TemporaryAndRenewableTrustTest {
uiDevice.wakeUp()
await(500)
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
trustAgentRule.agent.grantTrust(
GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
lockStateTrackingRule.assertLocked()
}

View File

@ -32,7 +32,7 @@ import org.junit.rules.RuleChain
import org.junit.runner.RunWith
/**
* Test for testing the user unlock trigger.
* Test for the user unlock triggers.
*
* atest TrustTests:UserUnlockRequestTest
*/
@ -53,13 +53,32 @@ class UserUnlockRequestTest {
@Test
fun reportUserRequestedUnlock_propagatesToAgent() {
val oldCount = trustAgentRule.agent.onUserRequestedUnlockCallCount
trustManager.reportUserRequestedUnlock(userId)
trustManager.reportUserRequestedUnlock(userId, false)
await()
assertThat(trustAgentRule.agent.onUserRequestedUnlockCallCount)
.isEqualTo(oldCount + 1)
}
@Test
fun reportUserRequestedUnlock_propagatesToAgentWithDismissKeyguard() {
trustManager.reportUserRequestedUnlock(userId, true)
await()
assertThat(trustAgentRule.agent.lastCallDismissKeyguard)
.isTrue()
}
@Test
fun reportUserMayRequestUnlock_propagatesToAgent() {
val oldCount = trustAgentRule.agent.onUserMayRequestUnlockCallCount
trustManager.reportUserMayRequestUnlock(userId)
await()
assertThat(trustAgentRule.agent.onUserMayRequestUnlockCallCount)
.isEqualTo(oldCount + 1)
}
companion object {
private const val TAG = "UserUnlockRequestTest"
private fun await() = Thread.sleep(250)
@ -69,10 +88,20 @@ class UserUnlockRequestTest {
class UserUnlockRequestTrustAgent : BaseTrustAgentService() {
var onUserRequestedUnlockCallCount: Long = 0
private set
var onUserMayRequestUnlockCallCount: Long = 0
private set
var lastCallDismissKeyguard: Boolean = false
private set
override fun onUserRequestedUnlock() {
Log.i(TAG, "onUserRequestedUnlock")
override fun onUserRequestedUnlock(dismissKeyguard: Boolean) {
Log.i(TAG, "onUserRequestedUnlock($dismissKeyguard)")
onUserRequestedUnlockCallCount++
lastCallDismissKeyguard = dismissKeyguard
}
override fun onUserMayRequestUnlock() {
Log.i(TAG, "onUserMayRequestUnlock")
onUserMayRequestUnlockCallCount++
}
companion object {

View File

@ -56,12 +56,22 @@ class LockStateTrackingRule : TestRule {
val maxWaits = 50
var waitCount = 0
// First verify we get the call in LockState via TrustListener
while ((lockState.locked == false) && waitCount < maxWaits) {
Log.i(TAG, "phone still locked, wait 50ms more ($waitCount)")
Log.i(TAG, "phone still unlocked (TrustListener), wait 50ms more ($waitCount)")
Thread.sleep(50)
waitCount++
}
assertThat(lockState.locked).isTrue()
// TODO(b/225231929): refactor checks into one loop and re-use for assertUnlocked
// Then verify we get the window manager locked
while (!windowManager.isKeyguardLocked && waitCount < maxWaits) {
Log.i(TAG, "phone still unlocked (WindowManager), wait 50ms more ($waitCount)")
Thread.sleep(50)
waitCount++
}
assertThat(windowManager.isKeyguardLocked).isTrue()
}
fun assertUnlocked() {

View File

@ -69,6 +69,17 @@ class ScreenLockRule : TestRule {
while (windowManager.isKeyguardLocked && waitCount < maxWaits) {
Log.i(TAG, "Keyguard still showing; attempting to dismiss and wait 50ms ($waitCount)")
windowManager.dismissKeyguard(null, null)
// Sometimes, bouncer gets shown due to a race, so we have to put display to sleep
// and wake it back up to get it to go away
if (waitCount >= 10 && waitCount % 5 == 0) {
Log.i(TAG, "Escalation: attempting screen off/on to get rid of bouncer (+500ms)")
uiDevice.sleep()
Thread.sleep(250)
uiDevice.wakeUp()
Thread.sleep(250)
}
Thread.sleep(50)
waitCount++
}