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
This commit is contained in:
Derek Jedral
2023-04-26 18:28:02 -07:00
committed by Beverly
parent c3b80dc70b
commit fd27baff34
12 changed files with 305 additions and 12 deletions

View File

@@ -29,4 +29,5 @@ oneway interface ITrustListener {
in List<String> trustGrantedMessages);
void onTrustManagedChanged(boolean managed, int userId);
void onTrustError(in CharSequence message);
void onIsActiveUnlockRunningChanged(boolean isRunning, int userId);
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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.

View File

@@ -95,6 +95,11 @@ constructor(
"onTrustManagedChanged"
)
}
override fun onIsActiveUnlockRunningChanged(
isRunning: Boolean,
userId: Int
) = Unit
}
trustManager.registerTrustListener(callback)
logger.trustListenerRegistered()

View File

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

View File

@@ -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;

View File

@@ -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<AgentInfo> mActiveAgents = new ArraySet<>();
private final SparseBooleanArray mLastActiveUnlockRunningState = new SparseBooleanArray();
private final ArrayList<ITrustListener> 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) {

View File

@@ -78,6 +78,16 @@
<action android:name="android.service.trust.TrustAgentService" />
</intent-filter>
</service>
<service
android:name=".IsActiveUnlockRunningTrustAgent"
android:exported="true"
android:label="Test Agent"
android:permission="android.permission.BIND_TRUST_AGENT">
<intent-filter>
<action android:name="android.service.trust.TrustAgentService" />
</intent-filter>
</service>
</application>
<!-- self-instrumenting test package. -->

View File

@@ -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<IsActiveUnlockRunningTrustAgent>()
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()

View File

@@ -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(

View File

@@ -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<String>
) {
}
override fun onTrustManagedChanged(enabled: Boolean, userId: Int) {
}
override fun onTrustError(message: CharSequence) {
}
override fun onEnabledTrustAgentsChanged(userId: Int) {
}
override fun onIsActiveUnlockRunningChanged(isRunning: Boolean, userId: Int) {
}
}