From fd27baff3430033aafbb31bbab6bc560afb8a219 Mon Sep 17 00:00:00 2001 From: Derek Jedral Date: Wed, 26 Apr 2023 18:28:02 -0700 Subject: [PATCH] Add isActiveUnlockRunning Add isActiveUnlockRunning and onIsActiveUnlockRunningChanged, in order to track when active unlock is available on the device or not. Listeners are notified if the value is true on subscription. Test: atest, local build, verify listeners notified Bug: b/267322286 Change-Id: Ibceac9d721190a4da3905e71d11c4424f7ee9676 Merged-In: Ibceac9d721190a4da3905e71d11c4424f7ee9676 --- .../android/app/trust/ITrustListener.aidl | 1 + .../java/android/app/trust/ITrustManager.aidl | 1 + core/java/android/app/trust/TrustManager.java | 29 ++++ .../keyguard/KeyguardUpdateMonitor.java | 4 + .../data/repository/TrustRepository.kt | 5 + .../hidl/Fingerprint21UdfpsMock.java | 5 + .../server/trust/TrustAgentWrapper.java | 4 + .../server/trust/TrustManagerService.java | 70 +++++++++- tests/TrustTests/AndroidManifest.xml | 10 ++ .../test/CanUnlockWithActiveUnlockTest.kt | 131 ++++++++++++++++++ .../trust/test/lib/LockStateTrackingRule.kt | 12 +- .../trust/test/lib/TestTrustListener.kt | 45 ++++++ 12 files changed, 305 insertions(+), 12 deletions(-) create mode 100644 tests/TrustTests/src/android/trust/test/CanUnlockWithActiveUnlockTest.kt create mode 100644 tests/TrustTests/src/android/trust/test/lib/TestTrustListener.kt diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl index 8d4478493b13a..665b24c568a6b 100644 --- a/core/java/android/app/trust/ITrustListener.aidl +++ b/core/java/android/app/trust/ITrustListener.aidl @@ -29,4 +29,5 @@ oneway interface ITrustListener { in List trustGrantedMessages); void onTrustManagedChanged(boolean managed, int userId); void onTrustError(in CharSequence message); + void onIsActiveUnlockRunningChanged(boolean isRunning, int userId); } diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl index 94a9656e2c6be..a5e5135f2cb00 100644 --- a/core/java/android/app/trust/ITrustManager.aidl +++ b/core/java/android/app/trust/ITrustManager.aidl @@ -40,4 +40,5 @@ interface ITrustManager { boolean isTrustUsuallyManaged(int userId); void unlockedByBiometricForUser(int userId, in BiometricSourceType source); void clearAllBiometricRecognized(in BiometricSourceType target, int unlockedUser); + boolean isActiveUnlockRunning(int userId); } diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java index 3552ce0e5889d..e5f2976417abb 100644 --- a/core/java/android/app/trust/TrustManager.java +++ b/core/java/android/app/trust/TrustManager.java @@ -44,6 +44,7 @@ public class TrustManager { 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 int MSG_IS_ACTIVE_UNLOCK_RUNNING = 5; private static final String TAG = "TrustManager"; private static final String DATA_FLAGS = "initiatedByUser"; @@ -165,6 +166,17 @@ public class TrustManager { } } + /** + * Returns whether active unlock can be used to unlock the device for user {@code userId}. + */ + public boolean isActiveUnlockRunning(int userId) { + try { + return mService.isActiveUnlockRunning(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Registers a listener for trust events. * @@ -206,6 +218,12 @@ public class TrustManager { m.getData().putCharSequence(DATA_MESSAGE, message); m.sendToTarget(); } + + @Override + public void onIsActiveUnlockRunningChanged(boolean isRunning, int userId) { + mHandler.obtainMessage(MSG_IS_ACTIVE_UNLOCK_RUNNING, + (isRunning ? 1 : 0), userId, trustListener).sendToTarget(); + } }; mService.registerTrustListener(iTrustListener); mTrustListeners.put(trustListener, iTrustListener); @@ -295,6 +313,10 @@ public class TrustManager { case MSG_ENABLED_TRUST_AGENTS_CHANGED: ((TrustListener) msg.obj).onEnabledTrustAgentsChanged(msg.arg1); break; + case MSG_IS_ACTIVE_UNLOCK_RUNNING: + ((TrustListener) msg.obj) + .onIsActiveUnlockRunningChanged(msg.arg1 != 0, msg.arg2); + break; } } }; @@ -333,5 +355,12 @@ public class TrustManager { * Reports that the enabled trust agents for the specified user has changed. */ void onEnabledTrustAgentsChanged(int userId); + + /** + * Reports changes on if the device can be unlocked with active unlock. + * @param isRunning If true, the device can be unlocked with active unlock. + * @param userId The user, for which the state changed. + */ + void onIsActiveUnlockRunningChanged(boolean isRunning, int userId); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index e1707cd08bb88..77e13ce28d965 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -570,6 +570,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + @Override + public void onIsActiveUnlockRunningChanged(boolean isRunning, int userId) { + } + /** * Whether the trust granted call with its passed flags should dismiss keyguard. * It's assumed that the trust was granted for the current user. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt index e4906696a5e34..f2f1c48476bce 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt @@ -95,6 +95,11 @@ constructor( "onTrustManagedChanged" ) } + + override fun onIsActiveUnlockRunningChanged( + isRunning: Boolean, + userId: Int + ) = Unit } trustManager.registerTrustListener(callback) logger.trustListenerRegistered() diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index 5e6ebf9d170cf..c1a9370b54232 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -442,6 +442,11 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage } + @Override + public void onIsActiveUnlockRunningChanged(boolean isRunning, int userId) { + + } + @Override @NonNull public List getSensorProperties() { diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java index e796275c53e3d..415d05e967993 100644 --- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java +++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java @@ -667,6 +667,10 @@ public class TrustAgentWrapper { return mTrustable && mManagingTrust && !mTrustDisabledByDpm; } + public boolean isTrustableOrWaitingForDowngrade() { + return mWaitingForTrustableDowngrade || isTrustable(); + } + /** Set the trustagent as not trustable */ public void setUntrustable() { mTrustable = false; diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 3c5ad2acc8a08..bab4886a6c6a9 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -149,6 +149,7 @@ public class TrustManagerService extends SystemService { private static final String PRIV_NAMESPACE = "http://schemas.android.com/apk/prv/res/android"; private final ArraySet mActiveAgents = new ArraySet<>(); + private final SparseBooleanArray mLastActiveUnlockRunningState = new SparseBooleanArray(); private final ArrayList mTrustListeners = new ArrayList<>(); private final Receiver mReceiver = new Receiver(); @@ -681,6 +682,7 @@ public class TrustManagerService extends SystemService { boolean isNowTrusted = pendingTrustState == TrustState.TRUSTED; boolean newlyUnlocked = !alreadyUnlocked && isNowTrusted; + maybeActiveUnlockRunningChanged(userId); dispatchOnTrustChanged( isNowTrusted, newlyUnlocked, userId, flags, getTrustGrantedMessages(userId)); if (isNowTrusted != wasTrusted) { @@ -921,6 +923,18 @@ public class TrustManagerService extends SystemService { } } + private void maybeActiveUnlockRunningChanged(int userId) { + boolean oldValue = mLastActiveUnlockRunningState.get(userId); + boolean newValue = aggregateIsActiveUnlockRunning(userId); + if (oldValue == newValue) { + return; + } + mLastActiveUnlockRunningState.put(userId, newValue); + for (int i = 0; i < mTrustListeners.size(); i++) { + notifyListenerIsActiveUnlockRunning(mTrustListeners.get(i), newValue, userId); + } + } + private void refreshDeviceLockedForUser(int userId) { refreshDeviceLockedForUser(userId, UserHandle.USER_NULL); } @@ -1325,6 +1339,27 @@ public class TrustManagerService extends SystemService { return false; } + private boolean aggregateIsActiveUnlockRunning(int userId) { + if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) { + return false; + } + synchronized (mUserTrustState) { + TrustState currentState = mUserTrustState.get(userId); + if (currentState != TrustState.TRUSTED && currentState != TrustState.TRUSTABLE) { + return false; + } + } + for (int i = 0; i < mActiveAgents.size(); i++) { + AgentInfo info = mActiveAgents.valueAt(i); + if (info.userId == userId) { + if (info.agent.isTrustableOrWaitingForDowngrade()) { + return true; + } + } + } + return false; + } + /** * We downgrade to trustable whenever keyguard changes its showing value. * - becomes showing: something has caused the device to show keyguard which happens due to @@ -1366,7 +1401,7 @@ public class TrustManagerService extends SystemService { for (int i = 0; i < mActiveAgents.size(); i++) { AgentInfo info = mActiveAgents.valueAt(i); if (info.userId == userId) { - if (info.agent.isManagingTrust()) { + if (info.agent.isTrustableOrWaitingForDowngrade()) { return true; } } @@ -1424,6 +1459,26 @@ public class TrustManagerService extends SystemService { } } + private void notifyListenerIsActiveUnlockRunningInitialState(ITrustListener listener) { + int numUsers = mLastActiveUnlockRunningState.size(); + for (int i = 0; i < numUsers; i++) { + int userId = mLastActiveUnlockRunningState.keyAt(i); + boolean isRunning = aggregateIsActiveUnlockRunning(userId); + notifyListenerIsActiveUnlockRunning(listener, isRunning, userId); + } + } + + private void notifyListenerIsActiveUnlockRunning( + ITrustListener listener, boolean isRunning, int userId) { + try { + listener.onIsActiveUnlockRunningChanged(isRunning, userId); + } catch (DeadObjectException e) { + Slog.d(TAG, "TrustListener dead while trying to notify Active Unlock running state"); + } catch (RemoteException e) { + Slog.e(TAG, "Exception while notifying TrustListener.", e); + } + } + // Listeners private void addListener(ITrustListener listener) { @@ -1433,6 +1488,7 @@ public class TrustManagerService extends SystemService { } } mTrustListeners.add(listener); + notifyListenerIsActiveUnlockRunningInitialState(listener); updateTrustAll(); } @@ -1747,6 +1803,8 @@ public class TrustManagerService extends SystemService { fout.print(": trusted=" + dumpBool(aggregateIsTrusted(user.id))); fout.print(", trustManaged=" + dumpBool(aggregateIsTrustManaged(user.id))); fout.print(", deviceLocked=" + dumpBool(isDeviceLockedInner(user.id))); + fout.print(", isActiveUnlockRunning=" + dumpBool( + aggregateIsActiveUnlockRunning(user.id))); fout.print(", strongAuthRequired=" + dumpHex( mStrongAuthTracker.getStrongAuthForUser(user.id))); fout.println(); @@ -1865,6 +1923,16 @@ public class TrustManagerService extends SystemService { } message.sendToTarget(); } + + @Override + public boolean isActiveUnlockRunning(int userId) throws RemoteException { + final long identity = Binder.clearCallingIdentity(); + try { + return aggregateIsActiveUnlockRunning(userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } }; private boolean isTrustUsuallyManagedInternal(int userId) { diff --git a/tests/TrustTests/AndroidManifest.xml b/tests/TrustTests/AndroidManifest.xml index 8b4cbfd0e44b3..30cf345db34d5 100644 --- a/tests/TrustTests/AndroidManifest.xml +++ b/tests/TrustTests/AndroidManifest.xml @@ -78,6 +78,16 @@ + + + + + + diff --git a/tests/TrustTests/src/android/trust/test/CanUnlockWithActiveUnlockTest.kt b/tests/TrustTests/src/android/trust/test/CanUnlockWithActiveUnlockTest.kt new file mode 100644 index 0000000000000..7b68a829e23bf --- /dev/null +++ b/tests/TrustTests/src/android/trust/test/CanUnlockWithActiveUnlockTest.kt @@ -0,0 +1,131 @@ +/* + * 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.trust.test + +import android.app.trust.TrustManager +import android.content.Context +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.TestTrustListener +import android.trust.test.lib.TrustAgentRule +import androidx.test.core.app.ApplicationProvider.getApplicationContext +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.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.runner.RunWith + +/** + * Test for testing isActiveUnlockRunning. + * + * atest TrustTests:IsActiveUnlockRunningTest + */ +@RunWith(AndroidJUnit4::class) +class IsActiveUnlockRunningTest { + private val uiDevice = UiDevice.getInstance(getInstrumentation()) + private val context: Context = getApplicationContext() + private val userId = context.userId + private val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager + private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java) + private val lockStateTrackingRule = LockStateTrackingRule() + private val trustAgentRule = TrustAgentRule() + + private val listener = object : TestTrustListener() { + var isRunning = false + private set + + override fun onIsActiveUnlockRunningChanged(isRunning: Boolean, userId: Int) { + this.isRunning = isRunning + } + } + + @get:Rule + val rule: RuleChain = RuleChain + .outerRule(activityScenarioRule) + .around(ScreenLockRule()) + .around(lockStateTrackingRule) + .around(trustAgentRule) + + @Before + fun manageTrust() { + trustAgentRule.agent.setManagingTrust(true) + trustManager.registerTrustListener(listener) + } + + @After + fun unregisterListener() { + trustManager.unregisterTrustListener(listener) + } + + @Test + fun defaultState_isActiveUnlockRunningIsFalse() { + assertThat(trustManager.isActiveUnlockRunning(userId)).isFalse() + assertThat(listener.isRunning).isFalse() + } + + @Test + fun grantTrustLockedDevice_isActiveUnlockRunningIsFalse() { + uiDevice.sleep() + lockStateTrackingRule.assertLocked() + + uiDevice.wakeUp() + trustAgentRule.agent.grantTrust( + GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {} + + assertThat(trustManager.isActiveUnlockRunning(userId)).isFalse() + assertThat(listener.isRunning).isFalse() + } + + @Test + fun grantTrustUnlockedDevice_isActiveUnlockRunningIsTrueWhileLocked() { + trustAgentRule.agent.grantTrust( + GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {} + uiDevice.sleep() + + lockStateTrackingRule.assertLocked() + + assertThat(trustManager.isActiveUnlockRunning(userId)).isTrue() + assertThat(listener.isRunning).isTrue() + } + + @Test + fun trustRevoked_isActiveUnlockRunningIsFalse() { + trustAgentRule.agent.grantTrust( + GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {} + + trustAgentRule.agent.revokeTrust() + + assertThat(trustManager.isActiveUnlockRunning(userId)).isFalse() + assertThat(listener.isRunning).isFalse() + } + + companion object { + private const val GRANT_MESSAGE = "granted by test" + private fun await(millis: Long) = Thread.sleep(millis) + } +} + +class IsActiveUnlockRunningTrustAgent : BaseTrustAgentService() diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt index a4ebb25d4b4cf..1400dde5781d8 100644 --- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt +++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt @@ -17,7 +17,6 @@ package android.trust.test.lib import android.app.trust.TrustManager -import android.app.trust.TrustManager.TrustListener import android.content.Context import android.util.Log import android.view.WindowManagerGlobal @@ -60,7 +59,7 @@ class LockStateTrackingRule : TestRule { wait("locked per TrustListener") { lockState.locked == false } } - inner class Listener : TrustListener { + inner class Listener : TestTrustListener() { override fun onTrustChanged( enabled: Boolean, newlyUnlocked: Boolean, @@ -71,15 +70,6 @@ class LockStateTrackingRule : TestRule { Log.d(TAG, "Device became trusted=$enabled") lockState = lockState.copy(locked = !enabled) } - - override fun onTrustManagedChanged(enabled: Boolean, userId: Int) { - } - - override fun onTrustError(message: CharSequence) { - } - - override fun onEnabledTrustAgentsChanged(userId: Int) { - } } data class LockState( diff --git a/tests/TrustTests/src/android/trust/test/lib/TestTrustListener.kt b/tests/TrustTests/src/android/trust/test/lib/TestTrustListener.kt new file mode 100644 index 0000000000000..880497e954f15 --- /dev/null +++ b/tests/TrustTests/src/android/trust/test/lib/TestTrustListener.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 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.trust.test.lib + +import android.app.trust.TrustManager.TrustListener + +/** + * A listener that has default empty implementations for all [TrustListener] methods. + */ +open class TestTrustListener : TrustListener { + override fun onTrustChanged( + enabled: Boolean, + newlyUnlocked: Boolean, + userId: Int, + flags: Int, + trustGrantedMessages: MutableList + ) { + } + + override fun onTrustManagedChanged(enabled: Boolean, userId: Int) { + } + + override fun onTrustError(message: CharSequence) { + } + + override fun onEnabledTrustAgentsChanged(userId: Int) { + } + + override fun onIsActiveUnlockRunningChanged(isRunning: Boolean, userId: Int) { + } +}