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) { + } +}