Merge "Notify TrustListeners when enabled trust agents change." into udc-dev

This commit is contained in:
Bryce Lee
2023-05-03 21:35:45 +00:00
committed by Android (Google) Code Review
12 changed files with 275 additions and 88 deletions

View File

@@ -24,6 +24,7 @@ import java.util.List;
* {@hide}
*/
oneway interface ITrustListener {
void onEnabledTrustAgentsChanged(int userId);
void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags,
in List<String> trustGrantedMessages);
void onTrustManagedChanged(boolean managed, int userId);

View File

@@ -43,6 +43,7 @@ public class TrustManager {
private static final int MSG_TRUST_CHANGED = 1;
private static final int MSG_TRUST_MANAGED_CHANGED = 2;
private static final int MSG_TRUST_ERROR = 3;
private static final int MSG_ENABLED_TRUST_AGENTS_CHANGED = 4;
private static final String TAG = "TrustManager";
private static final String DATA_FLAGS = "initiatedByUser";
@@ -186,6 +187,13 @@ public class TrustManager {
m.sendToTarget();
}
@Override
public void onEnabledTrustAgentsChanged(int userId) {
final Message m = mHandler.obtainMessage(MSG_ENABLED_TRUST_AGENTS_CHANGED,
userId, 0, trustListener);
m.sendToTarget();
}
@Override
public void onTrustManagedChanged(boolean managed, int userId) {
mHandler.obtainMessage(MSG_TRUST_MANAGED_CHANGED, (managed ? 1 : 0), userId,
@@ -283,6 +291,10 @@ public class TrustManager {
case MSG_TRUST_ERROR:
final CharSequence message = msg.peekData().getCharSequence(DATA_MESSAGE);
((TrustListener) msg.obj).onTrustError(message);
break;
case MSG_ENABLED_TRUST_AGENTS_CHANGED:
((TrustListener) msg.obj).onEnabledTrustAgentsChanged(msg.arg1);
break;
}
}
};
@@ -316,5 +328,10 @@ public class TrustManager {
* @param message A message that should be displayed on the UI.
*/
void onTrustError(CharSequence message);
/**
* Reports that the enabled trust agents for the specified user has changed.
*/
void onEnabledTrustAgentsChanged(int userId);
}
}

View File

@@ -587,6 +587,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
dispatchErrorMessage(message);
}
@Override
public void onEnabledTrustAgentsChanged(int userId) {
Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onEnabledTrustAgentsChanged(userId);
}
}
}
private void handleSimSubscriptionInfoChanged() {
Assert.isMainThread();
mLogger.v("onSubscriptionInfoChanged()");

View File

@@ -322,4 +322,9 @@ public class KeyguardUpdateMonitorCallback {
* Called when keyguard is going away or not going away.
*/
public void onKeyguardGoingAway() { }
/**
* Called when the enabled trust agents associated with the specified user.
*/
public void onEnabledTrustAgentsChanged(int userId) { }
}

View File

@@ -80,6 +80,8 @@ constructor(
override fun onTrustError(message: CharSequence?) = Unit
override fun onTrustManagedChanged(enabled: Boolean, userId: Int) = Unit
override fun onEnabledTrustAgentsChanged(userId: Int) = Unit
}
trustManager.registerTrustListener(callback)
logger.trustListenerRegistered()

View File

@@ -452,5 +452,10 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
public void onBiometricsCleared() {
update(false /* alwaysUpdate */);
}
@Override
public void onEnabledTrustAgentsChanged(int userId) {
update(false /* updateAlways */);
}
}
}

View File

@@ -163,6 +163,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -834,6 +835,18 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
Assert.assertTrue(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()));
}
@Test
public void testOnEnabledTrustAgentsChangedCallback() {
final Random random = new Random();
final int userId = random.nextInt();
final KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
mKeyguardUpdateMonitor.registerCallback(callback);
mKeyguardUpdateMonitor.onEnabledTrustAgentsChanged(userId);
verify(callback).onEnabledTrustAgentsChanged(eq(userId));
}
@Test
public void trustAgentHasTrust_fingerprintLockout() {
// GIVEN user has trust

View File

@@ -31,18 +31,23 @@ import androidx.test.filters.SmallTest;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import dagger.Lazy;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import dagger.Lazy;
import java.util.Random;
@SmallTest
@TestableLooper.RunWithLooper
@@ -169,4 +174,19 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
verify(callback).onKeyguardDismissAmountChanged();
}
@Test
public void testOnEnabledTrustAgentsChangedCallback() {
final Random random = new Random();
final ArgumentCaptor<KeyguardUpdateMonitorCallback> updateCallbackCaptor =
ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
verify(mKeyguardUpdateMonitor).registerCallback(updateCallbackCaptor.capture());
final KeyguardStateController.Callback stateCallback =
mock(KeyguardStateController.Callback.class);
mKeyguardStateController.addCallback(stateCallback);
when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
updateCallbackCaptor.getValue().onEnabledTrustAgentsChanged(random.nextInt());
verify(stateCallback).onUnlockedChanged();
}
}

View File

@@ -437,6 +437,11 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
}
@Override
public void onEnabledTrustAgentsChanged(int userId) {
}
@Override
@NonNull
public List<FingerprintSensorPropertiesInternal> getSensorProperties() {

View File

@@ -53,6 +53,7 @@ import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.RemoteException;
@@ -151,6 +152,8 @@ public class TrustManagerService extends SystemService {
private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>();
private final Receiver mReceiver = new Receiver();
private final Handler mHandler;
/* package */ final TrustArchive mArchive = new TrustArchive();
private final Context mContext;
private final LockPatternUtils mLockPatternUtils;
@@ -232,13 +235,40 @@ public class TrustManagerService extends SystemService {
private boolean mTrustAgentsCanRun = false;
private int mCurrentUser = UserHandle.USER_SYSTEM;
/**
* A class for providing dependencies to {@link TrustManagerService} in both production and test
* cases.
*/
protected static class Injector {
private final LockPatternUtils mLockPatternUtils;
private final Looper mLooper;
public Injector(LockPatternUtils lockPatternUtils, Looper looper) {
mLockPatternUtils = lockPatternUtils;
mLooper = looper;
}
LockPatternUtils getLockPatternUtils() {
return mLockPatternUtils;
}
Looper getLooper() {
return mLooper;
}
}
public TrustManagerService(Context context) {
this(context, new Injector(new LockPatternUtils(context), Looper.myLooper()));
}
protected TrustManagerService(Context context, Injector injector) {
super(context);
mContext = context;
mHandler = createHandler(injector.getLooper());
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mLockPatternUtils = new LockPatternUtils(context);
mStrongAuthTracker = new StrongAuthTracker(context);
mLockPatternUtils = injector.getLockPatternUtils();
mStrongAuthTracker = new StrongAuthTracker(context, injector.getLooper());
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mSettingsObserver = new SettingsObserver(mHandler);
}
@@ -1436,6 +1466,23 @@ public class TrustManagerService extends SystemService {
}
}
private void dispatchOnEnabledTrustAgentsChanged(int userId) {
if (DEBUG) {
Log.i(TAG, "onEnabledTrustAgentsChanged(" + userId + ")");
}
for (int i = 0; i < mTrustListeners.size(); i++) {
try {
mTrustListeners.get(i).onEnabledTrustAgentsChanged(userId);
} catch (DeadObjectException e) {
Slog.d(TAG, "Removing dead TrustListener.");
mTrustListeners.remove(i);
i--;
} catch (RemoteException e) {
Slog.e(TAG, "Exception while notifying TrustListener.", e);
}
}
}
private void dispatchOnTrustManagedChanged(boolean managed, int userId) {
if (DEBUG) {
Log.i(TAG, "onTrustManagedChanged(" + managed + ", " + userId + ")");
@@ -1539,9 +1586,7 @@ public class TrustManagerService extends SystemService {
@Override
public void reportEnabledTrustAgentsChanged(int userId) throws RemoteException {
enforceReportPermission();
// coalesce refresh messages.
mHandler.removeMessages(MSG_ENABLED_AGENTS_CHANGED);
mHandler.sendEmptyMessage(MSG_ENABLED_AGENTS_CHANGED);
mHandler.obtainMessage(MSG_ENABLED_AGENTS_CHANGED, userId, 0).sendToTarget();
}
@Override
@@ -1857,88 +1902,91 @@ public class TrustManagerService extends SystemService {
}
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_LISTENER:
addListener((ITrustListener) msg.obj);
break;
case MSG_UNREGISTER_LISTENER:
removeListener((ITrustListener) msg.obj);
break;
case MSG_DISPATCH_UNLOCK_ATTEMPT:
dispatchUnlockAttempt(msg.arg1 != 0, msg.arg2);
break;
case MSG_USER_REQUESTED_UNLOCK:
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);
break;
case MSG_ENABLED_AGENTS_CHANGED:
refreshAgentList(UserHandle.USER_ALL);
// This is also called when the security mode of a user changes.
refreshDeviceLockedForUser(UserHandle.USER_ALL);
break;
case MSG_KEYGUARD_SHOWING_CHANGED:
dispatchTrustableDowngrade();
refreshDeviceLockedForUser(mCurrentUser);
break;
case MSG_START_USER:
case MSG_CLEANUP_USER:
case MSG_UNLOCK_USER:
refreshAgentList(msg.arg1);
break;
case MSG_SWITCH_USER:
mCurrentUser = msg.arg1;
mSettingsObserver.updateContentObserver();
refreshDeviceLockedForUser(UserHandle.USER_ALL);
break;
case MSG_STOP_USER:
setDeviceLockedForUser(msg.arg1, true);
break;
case MSG_FLUSH_TRUST_USUALLY_MANAGED:
SparseBooleanArray usuallyManaged;
synchronized (mTrustUsuallyManagedForUser) {
usuallyManaged = mTrustUsuallyManagedForUser.clone();
}
for (int i = 0; i < usuallyManaged.size(); i++) {
int userId = usuallyManaged.keyAt(i);
boolean value = usuallyManaged.valueAt(i);
if (value != mLockPatternUtils.isTrustUsuallyManaged(userId)) {
mLockPatternUtils.setTrustUsuallyManaged(value, userId);
private Handler createHandler(Looper looper) {
return new Handler(looper) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_LISTENER:
addListener((ITrustListener) msg.obj);
break;
case MSG_UNREGISTER_LISTENER:
removeListener((ITrustListener) msg.obj);
break;
case MSG_DISPATCH_UNLOCK_ATTEMPT:
dispatchUnlockAttempt(msg.arg1 != 0, msg.arg2);
break;
case MSG_USER_REQUESTED_UNLOCK:
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);
break;
case MSG_ENABLED_AGENTS_CHANGED:
refreshAgentList(UserHandle.USER_ALL);
// This is also called when the security mode of a user changes.
refreshDeviceLockedForUser(UserHandle.USER_ALL);
dispatchOnEnabledTrustAgentsChanged(msg.arg1);
break;
case MSG_KEYGUARD_SHOWING_CHANGED:
dispatchTrustableDowngrade();
refreshDeviceLockedForUser(mCurrentUser);
break;
case MSG_START_USER:
case MSG_CLEANUP_USER:
case MSG_UNLOCK_USER:
refreshAgentList(msg.arg1);
break;
case MSG_SWITCH_USER:
mCurrentUser = msg.arg1;
mSettingsObserver.updateContentObserver();
refreshDeviceLockedForUser(UserHandle.USER_ALL);
break;
case MSG_STOP_USER:
setDeviceLockedForUser(msg.arg1, true);
break;
case MSG_FLUSH_TRUST_USUALLY_MANAGED:
SparseBooleanArray usuallyManaged;
synchronized (mTrustUsuallyManagedForUser) {
usuallyManaged = mTrustUsuallyManagedForUser.clone();
}
}
break;
case MSG_REFRESH_DEVICE_LOCKED_FOR_USER:
if (msg.arg2 == 1) {
updateTrust(msg.arg1, 0 /* flags */, true /* isFromUnlock */, null);
}
final int unlockedUser = msg.getData().getInt(
REFRESH_DEVICE_LOCKED_EXCEPT_USER, UserHandle.USER_NULL);
refreshDeviceLockedForUser(msg.arg1, unlockedUser);
break;
case MSG_SCHEDULE_TRUST_TIMEOUT:
boolean shouldOverride = msg.arg1 == 1 ? true : false;
TimeoutType timeoutType =
msg.arg2 == 1 ? TimeoutType.TRUSTABLE : TimeoutType.TRUSTED;
handleScheduleTrustTimeout(shouldOverride, timeoutType);
break;
case MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH:
TrustableTimeoutAlarmListener trustableAlarm =
mTrustableTimeoutAlarmListenerForUser.get(msg.arg1);
if (trustableAlarm != null && trustableAlarm.isQueued()) {
refreshTrustableTimers(msg.arg1);
}
break;
for (int i = 0; i < usuallyManaged.size(); i++) {
int userId = usuallyManaged.keyAt(i);
boolean value = usuallyManaged.valueAt(i);
if (value != mLockPatternUtils.isTrustUsuallyManaged(userId)) {
mLockPatternUtils.setTrustUsuallyManaged(value, userId);
}
}
break;
case MSG_REFRESH_DEVICE_LOCKED_FOR_USER:
if (msg.arg2 == 1) {
updateTrust(msg.arg1, 0 /* flags */, true /* isFromUnlock */, null);
}
final int unlockedUser = msg.getData().getInt(
REFRESH_DEVICE_LOCKED_EXCEPT_USER, UserHandle.USER_NULL);
refreshDeviceLockedForUser(msg.arg1, unlockedUser);
break;
case MSG_SCHEDULE_TRUST_TIMEOUT:
boolean shouldOverride = msg.arg1 == 1 ? true : false;
TimeoutType timeoutType =
msg.arg2 == 1 ? TimeoutType.TRUSTABLE : TimeoutType.TRUSTED;
handleScheduleTrustTimeout(shouldOverride, timeoutType);
break;
case MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH:
TrustableTimeoutAlarmListener trustableAlarm =
mTrustableTimeoutAlarmListenerForUser.get(msg.arg1);
if (trustableAlarm != null && trustableAlarm.isQueued()) {
refreshTrustableTimers(msg.arg1);
}
break;
}
}
}
};
};
}
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
@@ -2039,8 +2087,8 @@ public class TrustManagerService extends SystemService {
SparseBooleanArray mStartFromSuccessfulUnlock = new SparseBooleanArray();
public StrongAuthTracker(Context context) {
super(context);
StrongAuthTracker(Context context, Looper looper) {
super(context, looper);
}
@Override

View File

@@ -16,15 +16,26 @@
package com.android.server.trust;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.argThat;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
import android.Manifest;
import android.annotation.Nullable;
import android.app.trust.ITrustListener;
import android.app.trust.ITrustManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -36,11 +47,16 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.provider.Settings;
import android.service.trust.TrustAgentService;
import android.testing.TestableContext;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import androidx.test.core.app.ApplicationProvider;
@@ -55,13 +71,16 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class TrustManagerServiceTest {
@@ -255,6 +274,43 @@ public class TrustManagerServiceTest {
systemTrustAgent1);
}
@Test
public void reportEnabledTrustAgentsChangedInformsListener() throws RemoteException {
final LockPatternUtils utils = mock(LockPatternUtils.class);
final TrustManagerService service = new TrustManagerService(mMockContext,
new TrustManagerService.Injector(utils, mLooper.getLooper()));
final ITrustListener trustListener = mock(ITrustListener.class);
final IWindowManager windowManager = mock(IWindowManager.class);
final int userId = new Random().nextInt();
mMockContext.getTestablePermissions().setPermission(Manifest.permission.TRUST_LISTENER,
PERMISSION_GRANTED);
when(utils.getKnownTrustAgents(anyInt())).thenReturn(new ArrayList<>());
MockitoSession mockSession = mockitoSession()
.initMocks(this)
.mockStatic(ServiceManager.class)
.mockStatic(WindowManagerGlobal.class)
.startMocking();
doReturn(windowManager).when(() -> {
WindowManagerGlobal.getWindowManagerService();
});
service.onStart();
ArgumentCaptor<IBinder> binderArgumentCaptor = ArgumentCaptor.forClass(IBinder.class);
verify(() -> ServiceManager.addService(eq(Context.TRUST_SERVICE),
binderArgumentCaptor.capture(), anyBoolean(), anyInt()));
ITrustManager manager = ITrustManager.Stub.asInterface(binderArgumentCaptor.getValue());
manager.registerTrustListener(trustListener);
mLooper.dispatchAll();
manager.reportEnabledTrustAgentsChanged(userId);
mLooper.dispatchAll();
verify(trustListener).onEnabledTrustAgentsChanged(eq(userId));
mockSession.finishMocking();
}
private void addTrustAgent(ComponentName agentComponentName, boolean isSystemApp) {
ApplicationInfo applicationInfo = new ApplicationInfo();
if (isSystemApp) {

View File

@@ -77,6 +77,9 @@ class LockStateTrackingRule : TestRule {
override fun onTrustError(message: CharSequence) {
}
override fun onEnabledTrustAgentsChanged(userId: Int) {
}
}
data class LockState(