From fb3f361d547d47e9ab3abb43dc317278817d0347 Mon Sep 17 00:00:00 2001 From: Jason Sun Date: Fri, 2 Sep 2022 05:44:43 +0000 Subject: [PATCH] Auto enable new system trust agents Previously we only auto enable factory system trust agents when the devices is booted for the first time. This change added logic to detect newly installed/enabled trust agents, and enable them if appropriate. Fixes: b/243705470 Test: atest FrameworksMockingServicesTests:TrustManagerServiceTest Change-Id: I3c345a44cfcfccc6f7e2507ba37e378b6b16bd98 --- core/java/android/provider/Settings.java | 7 + .../internal/widget/LockPatternUtils.java | 44 ++- .../internal/util/LockPatternUtilsTest.java | 64 +++- .../android/provider/SettingsBackupTest.java | 1 + .../server/trust/TrustManagerService.java | 102 +++++- .../mockingservicestests/AndroidManifest.xml | 5 + .../server/trust/TrustManagerServiceTest.java | 334 ++++++++++++++++++ 7 files changed, 530 insertions(+), 27 deletions(-) create mode 100644 services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1fcdd608c5a21..dc91cc27206fc 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7406,6 +7406,13 @@ public final class Settings { public static final String TRUST_AGENTS_INITIALIZED = "trust_agents_initialized"; + /** + * Set to 1 by the system after the list of known trust agents have been initialized. + * @hide + */ + public static final String KNOWN_TRUST_AGENTS_INITIALIZED = + "known_trust_agents_initialized"; + /** * The Logging ID (a unique 64-bit value) as a hex string. * Used as a pseudonymous identifier for logging. diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index a94b307076040..1235b602cde97 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -39,7 +39,6 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.Build; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; @@ -47,7 +46,6 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.provider.Settings; import android.text.TextUtils; @@ -175,6 +173,7 @@ public class LockPatternUtils { private static final String LOCK_SCREEN_DEVICE_OWNER_INFO = "lockscreen.device_owner_info"; private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents"; + private static final String KNOWN_TRUST_AGENTS = "lockscreen.knowntrustagents"; private static final String IS_TRUST_USUALLY_MANAGED = "lockscreen.istrustusuallymanaged"; public static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_"; @@ -1106,31 +1105,50 @@ public class LockPatternUtils { return getString(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, userId) != null; } + /** Updates the list of enabled trust agent in LockSettings storage for the given user. */ public void setEnabledTrustAgents(Collection activeTrustAgents, int userId) { + setString(ENABLED_TRUST_AGENTS, serializeTrustAgents(activeTrustAgents), userId); + getTrustManager().reportEnabledTrustAgentsChanged(userId); + } + + /** Returns the list of enabled trust agent in LockSettings storage for the given user. */ + public List getEnabledTrustAgents(int userId) { + return deserializeTrustAgents(getString(ENABLED_TRUST_AGENTS, userId)); + } + + /** Updates the list of known trust agent in LockSettings storage for the given user. */ + public void setKnownTrustAgents(Collection knownTrustAgents, int userId) { + setString(KNOWN_TRUST_AGENTS, serializeTrustAgents(knownTrustAgents), userId); + } + + /** Returns the list of known trust agent in LockSettings storage for the given user. */ + public List getKnownTrustAgents(int userId) { + return deserializeTrustAgents(getString(KNOWN_TRUST_AGENTS, userId)); + } + + private String serializeTrustAgents(Collection trustAgents) { StringBuilder sb = new StringBuilder(); - for (ComponentName cn : activeTrustAgents) { + for (ComponentName cn : trustAgents) { if (sb.length() > 0) { sb.append(','); } sb.append(cn.flattenToShortString()); } - setString(ENABLED_TRUST_AGENTS, sb.toString(), userId); - getTrustManager().reportEnabledTrustAgentsChanged(userId); + return sb.toString(); } - public List getEnabledTrustAgents(int userId) { - String serialized = getString(ENABLED_TRUST_AGENTS, userId); - if (TextUtils.isEmpty(serialized)) { - return new ArrayList(); + private List deserializeTrustAgents(String serializedTrustAgents) { + if (TextUtils.isEmpty(serializedTrustAgents)) { + return new ArrayList<>(); } - String[] split = serialized.split(","); - ArrayList activeTrustAgents = new ArrayList(split.length); + String[] split = serializedTrustAgents.split(","); + ArrayList trustAgents = new ArrayList<>(split.length); for (String s : split) { if (!TextUtils.isEmpty(s)) { - activeTrustAgents.add(ComponentName.unflattenFromString(s)); + trustAgents.add(ComponentName.unflattenFromString(s)); } } - return activeTrustAgents; + return trustAgents; } /** diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java index 940ca96fac4f2..4679a9ea6f667 100644 --- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java @@ -19,14 +19,15 @@ package com.android.internal.util; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; -import static org.junit.Assert.assertEquals; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -51,8 +52,11 @@ import com.android.internal.widget.IWeakEscrowTokenActivatedListener; import com.android.internal.widget.IWeakEscrowTokenRemovedListener; import com.android.internal.widget.LockPatternUtils; +import com.google.android.collect.Lists; + import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import java.nio.charset.StandardCharsets; @@ -172,13 +176,61 @@ public class LockPatternUtilsTest { } @Test - public void testGetEnabledTrustAgentsNotNull() throws RemoteException { + public void testSetEnabledTrustAgents() throws RemoteException { int testUserId = 10; ILockSettings ils = createTestLockSettings(); - when(ils.getString(anyString(), any(), anyInt())).thenReturn(""); + ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class); + doNothing().when(ils).setString(anyString(), valueCaptor.capture(), anyInt()); + List enabledTrustAgents = Lists.newArrayList( + ComponentName.unflattenFromString("com.android/.TrustAgent"), + ComponentName.unflattenFromString("com.test/.TestAgent")); + + mLockPatternUtils.setEnabledTrustAgents(enabledTrustAgents, testUserId); + + assertThat(valueCaptor.getValue()).isEqualTo("com.android/.TrustAgent,com.test/.TestAgent"); + } + + @Test + public void testGetEnabledTrustAgents() throws RemoteException { + int testUserId = 10; + ILockSettings ils = createTestLockSettings(); + when(ils.getString(anyString(), any(), anyInt())).thenReturn( + "com.android/.TrustAgent,com.test/.TestAgent"); + List trustAgents = mLockPatternUtils.getEnabledTrustAgents(testUserId); - assertNotNull(trustAgents); - assertEquals(0, trustAgents.size()); + + assertThat(trustAgents).containsExactly( + ComponentName.unflattenFromString("com.android/.TrustAgent"), + ComponentName.unflattenFromString("com.test/.TestAgent")); + } + + @Test + public void testSetKnownTrustAgents() throws RemoteException { + int testUserId = 10; + ILockSettings ils = createTestLockSettings(); + ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class); + doNothing().when(ils).setString(anyString(), valueCaptor.capture(), anyInt()); + List knownTrustAgents = Lists.newArrayList( + ComponentName.unflattenFromString("com.android/.TrustAgent"), + ComponentName.unflattenFromString("com.test/.TestAgent")); + + mLockPatternUtils.setKnownTrustAgents(knownTrustAgents, testUserId); + + assertThat(valueCaptor.getValue()).isEqualTo("com.android/.TrustAgent,com.test/.TestAgent"); + } + + @Test + public void testGetKnownTrustAgents() throws RemoteException { + int testUserId = 10; + ILockSettings ils = createTestLockSettings(); + when(ils.getString(anyString(), any(), anyInt())).thenReturn( + "com.android/.TrustAgent,com.test/.TestAgent"); + + List trustAgents = mLockPatternUtils.getKnownTrustAgents(testUserId); + + assertThat(trustAgents).containsExactly( + ComponentName.unflattenFromString("com.android/.TrustAgent"), + ComponentName.unflattenFromString("com.test/.TestAgent")); } private ILockSettings createTestLockSettings() { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 2209ff596cb53..8e82b8b5219a8 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -780,6 +780,7 @@ public class SettingsBackupTest { Settings.Secure.SMS_DEFAULT_APPLICATION, Settings.Secure.SPELL_CHECKER_ENABLED, // Intentionally removed in Q Settings.Secure.TRUST_AGENTS_INITIALIZED, + Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS, Settings.Secure.TV_INPUT_CUSTOM_LABELS, Settings.Secure.TV_INPUT_HIDDEN_INPUTS, diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 80ce70de2138c..131b623f290aa 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -93,6 +93,7 @@ import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collection; import java.util.List; @@ -254,6 +255,7 @@ public class TrustManagerService extends SystemService { return; } if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + checkNewAgents(); mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true); mReceiver.register(mContext); mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); @@ -262,7 +264,7 @@ public class TrustManagerService extends SystemService { refreshAgentList(UserHandle.USER_ALL); refreshDeviceLockedForUser(UserHandle.USER_ALL); } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { - maybeEnableFactoryTrustAgents(mLockPatternUtils, UserHandle.USER_SYSTEM); + maybeEnableFactoryTrustAgents(UserHandle.USER_SYSTEM); } } @@ -1083,7 +1085,7 @@ public class TrustManagerService extends SystemService { return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name); } - private void maybeEnableFactoryTrustAgents(LockPatternUtils utils, int userId) { + private void maybeEnableFactoryTrustAgents(int userId) { if (0 != Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.TRUST_AGENTS_INITIALIZED, 0, userId)) { return; @@ -1100,8 +1102,7 @@ public class TrustManagerService extends SystemService { } else { // A default agent is not set; perform regular trust agent discovery for (ResolveInfo resolveInfo : resolveInfos) { ComponentName componentName = getComponentName(resolveInfo); - int applicationInfoFlags = resolveInfo.serviceInfo.applicationInfo.flags; - if ((applicationInfoFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { + if (!isSystemTrustAgent(resolveInfo)) { Log.i(TAG, "Leaving agent " + componentName + " disabled because package " + "is not a system package."); continue; @@ -1110,13 +1111,88 @@ public class TrustManagerService extends SystemService { } } - List previouslyEnabledAgents = utils.getEnabledTrustAgents(userId); - discoveredAgents.addAll(previouslyEnabledAgents); - utils.setEnabledTrustAgents(discoveredAgents, userId); + enableNewAgents(discoveredAgents, userId); Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, userId); } + private void checkNewAgents() { + for (UserInfo userInfo : mUserManager.getAliveUsers()) { + checkNewAgentsForUser(userInfo.id); + } + } + + /** + * Checks for any new trust agents that become available after the first boot, add them to the + * list of known agents, and enable them if they should be enabled by default. + */ + private void checkNewAgentsForUser(int userId) { + // When KNOWN_TRUST_AGENTS_INITIALIZED is not set, only update the known agent list but do + // not enable any agent. + // These agents will be enabled by #maybeEnableFactoryTrustAgents if this is the first time + // that this device boots and TRUST_AGENTS_INITIALIZED is not already set. + // Otherwise, these agents may have been manually disabled by the user, and we should not + // re-enable them. + if (0 == Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, 0, userId)) { + initializeKnownAgents(userId); + return; + } + + List knownAgents = mLockPatternUtils.getKnownTrustAgents(userId); + List agentInfoList = resolveAllowedTrustAgents(mContext.getPackageManager(), + userId); + ArraySet newAgents = new ArraySet<>(agentInfoList.size()); + ArraySet newSystemAgents = new ArraySet<>(agentInfoList.size()); + + for (ResolveInfo agentInfo : agentInfoList) { + ComponentName agentComponentName = getComponentName(agentInfo); + if (knownAgents.contains(agentComponentName)) { + continue; + } + newAgents.add(agentComponentName); + if (isSystemTrustAgent(agentInfo)) { + newSystemAgents.add(agentComponentName); + } + } + + if (newAgents.isEmpty()) { + return; + } + + ArraySet updatedKnowAgents = new ArraySet<>(knownAgents); + updatedKnowAgents.addAll(newAgents); + mLockPatternUtils.setKnownTrustAgents(updatedKnowAgents, userId); + + // Do not auto enable new trust agents when the default agent is set + boolean hasDefaultAgent = getDefaultFactoryTrustAgent(mContext) != null; + if (!hasDefaultAgent) { + enableNewAgents(newSystemAgents, userId); + } + } + + private void enableNewAgents(Collection agents, int userId) { + if (agents.isEmpty()) { + return; + } + + ArraySet agentsToEnable = new ArraySet<>(agents); + agentsToEnable.addAll(mLockPatternUtils.getEnabledTrustAgents(userId)); + mLockPatternUtils.setEnabledTrustAgents(agentsToEnable, userId); + } + + private void initializeKnownAgents(int userId) { + List agentInfoList = resolveAllowedTrustAgents(mContext.getPackageManager(), + userId); + ArraySet agentComponentNames = new ArraySet<>(agentInfoList.size()); + for (ResolveInfo agentInfo : agentInfoList) { + agentComponentNames.add(getComponentName(agentInfo)); + } + mLockPatternUtils.setKnownTrustAgents(agentComponentNames, userId); + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, 1, userId); + } + /** * Returns the {@link ComponentName} for the default trust agent, or {@code null} if there * is no trust agent set. @@ -1152,6 +1228,10 @@ public class TrustManagerService extends SystemService { return allowedAgents; } + private static boolean isSystemTrustAgent(ResolveInfo agentInfo) { + return (agentInfo.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + // Agent dispatch and aggregation private boolean aggregateIsTrusted(int userId) { @@ -1825,8 +1905,14 @@ public class TrustManagerService extends SystemService { refreshAgentList(UserHandle.USER_ALL); } + @Override + public void onPackageAdded(String packageName, int uid) { + checkNewAgentsForUser(UserHandle.getUserId(uid)); + } + @Override public boolean onPackageChanged(String packageName, int uid, String[] components) { + checkNewAgentsForUser(UserHandle.getUserId(uid)); // We're interested in all changes, even if just some components get enabled / disabled. return true; } @@ -1861,7 +1947,7 @@ public class TrustManagerService extends SystemService { action)) { int userId = getUserId(intent); if (userId > 0) { - maybeEnableFactoryTrustAgents(mLockPatternUtils, userId); + maybeEnableFactoryTrustAgents(userId); } } else if (Intent.ACTION_USER_REMOVED.equals(action)) { int userId = getUserId(intent); diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index 07b763dcd85b4..33ac735609686 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -34,10 +34,15 @@ + + + + + diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java new file mode 100644 index 0000000000000..33870f1d3b865 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java @@ -0,0 +1,334 @@ +/* + * 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 com.android.server.trust; + +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.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static com.google.common.truth.Truth.assertThat; + +import android.annotation.Nullable; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.net.Uri; +import android.os.Handler; +import android.os.UserHandle; +import android.os.test.TestLooper; +import android.provider.Settings; +import android.service.trust.TrustAgentService; +import android.testing.TestableContext; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.internal.widget.LockPatternUtils; +import com.android.server.LocalServices; +import com.android.server.SystemService; +import com.android.server.SystemServiceManager; + +import com.google.android.collect.Lists; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Collections; + +public class TrustManagerServiceTest { + + @Rule + public MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule + public final MockContext mMockContext = new MockContext( + ApplicationProvider.getApplicationContext()); + + private static final String URI_SCHEME_PACKAGE = "package"; + private static final int TEST_USER_ID = UserHandle.USER_SYSTEM; + + private final TestLooper mLooper = new TestLooper(); + private final ArrayList mTrustAgentResolveInfoList = new ArrayList<>(); + private final LockPatternUtils mLockPatternUtils = new LockPatternUtils(mMockContext); + private final TrustManagerService mService = new TrustManagerService(mMockContext); + + @Mock + private PackageManager mPackageManagerMock; + + @Before + public void setUp() { + resetTrustAgentLockSettings(); + LocalServices.addService(SystemServiceManager.class, mock(SystemServiceManager.class)); + + ArgumentMatcher trustAgentIntentMatcher = new ArgumentMatcher() { + @Override + public boolean matches(Intent argument) { + return TrustAgentService.SERVICE_INTERFACE.equals(argument.getAction()); + } + }; + when(mPackageManagerMock.queryIntentServicesAsUser(argThat(trustAgentIntentMatcher), + anyInt(), anyInt())).thenReturn(mTrustAgentResolveInfoList); + when(mPackageManagerMock.checkPermission(any(), any())).thenReturn( + PackageManager.PERMISSION_GRANTED); + mMockContext.setMockPackageManager(mPackageManagerMock); + } + + @After + public void tearDown() { + resetTrustAgentLockSettings(); + LocalServices.removeServiceForTest(SystemServiceManager.class); + } + + @Test + public void firstBootCompleted_systemTrustAgentsEnabled() { + ComponentName systemTrustAgent1 = ComponentName.unflattenFromString( + "com.android/.SystemTrustAgent"); + ComponentName systemTrustAgent2 = ComponentName.unflattenFromString( + "com.android/.AnotherSystemTrustAgent"); + ComponentName userTrustAgent1 = ComponentName.unflattenFromString( + "com.user/.UserTrustAgent"); + ComponentName userTrustAgent2 = ComponentName.unflattenFromString( + "com.user/.AnotherUserTrustAgent"); + addTrustAgent(systemTrustAgent1, /* isSystemApp= */ true); + addTrustAgent(systemTrustAgent2, /* isSystemApp= */ true); + addTrustAgent(userTrustAgent1, /* isSystemApp= */ false); + addTrustAgent(userTrustAgent2, /* isSystemApp= */ false); + + bootService(); + + assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly( + systemTrustAgent1, systemTrustAgent2); + assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly( + systemTrustAgent1, systemTrustAgent2, userTrustAgent1, userTrustAgent2); + } + + @Test + public void firstBootCompleted_defaultTrustAgentEnabled() { + ComponentName systemTrustAgent = ComponentName.unflattenFromString( + "com.android/.SystemTrustAgent"); + ComponentName defaultTrustAgent = ComponentName.unflattenFromString( + "com.user/.DefaultTrustAgent"); + addTrustAgent(systemTrustAgent, /* isSystemApp= */ true); + addTrustAgent(defaultTrustAgent, /* isSystemApp= */ false); + mMockContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.string.config_defaultTrustAgent, + defaultTrustAgent.flattenToString()); + + bootService(); + + assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly( + defaultTrustAgent); + assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly( + systemTrustAgent, defaultTrustAgent); + } + + @Test + public void serviceBooted_knownAgentsNotSet_enabledAgentsNotUpdated() { + ComponentName trustAgent1 = ComponentName.unflattenFromString( + "com.android/.SystemTrustAgent"); + ComponentName trustAgent2 = ComponentName.unflattenFromString( + "com.android/.AnotherSystemTrustAgent"); + initializeEnabledAgents(trustAgent1); + addTrustAgent(trustAgent1, /* isSystemApp= */ true); + addTrustAgent(trustAgent2, /* isSystemApp= */ true); + + bootService(); + + assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly( + trustAgent1); + assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly( + trustAgent1, trustAgent2); + } + + @Test + public void serviceBooted_knownAgentsSet_enabledAgentsUpdated() { + ComponentName trustAgent1 = ComponentName.unflattenFromString( + "com.android/.SystemTrustAgent"); + ComponentName trustAgent2 = ComponentName.unflattenFromString( + "com.android/.AnotherSystemTrustAgent"); + initializeEnabledAgents(trustAgent1); + initializeKnownAgents(trustAgent1); + addTrustAgent(trustAgent1, /* isSystemApp= */ true); + addTrustAgent(trustAgent2, /* isSystemApp= */ true); + + bootService(); + + assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly( + trustAgent1, trustAgent2); + assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly( + trustAgent1, trustAgent2); + } + + @Test + public void newSystemTrustAgent_setToEnabledAndKnown() { + bootService(); + ComponentName newAgentComponentName = ComponentName.unflattenFromString( + "com.android/.SystemTrustAgent"); + addTrustAgent(newAgentComponentName, /* isSystemApp= */ true); + + mMockContext.sendPackageChangedBroadcast(newAgentComponentName); + + assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly( + newAgentComponentName); + assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly( + newAgentComponentName); + } + + @Test + public void newSystemTrustAgent_notEnabledWhenDefaultAgentIsSet() { + ComponentName defaultTrustAgent = ComponentName.unflattenFromString( + "com.user/.DefaultTrustAgent"); + addTrustAgent(defaultTrustAgent, /* isSystemApp= */ false); + mMockContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.string.config_defaultTrustAgent, + defaultTrustAgent.flattenToString()); + bootService(); + ComponentName newAgentComponentName = ComponentName.unflattenFromString( + "com.android/.SystemTrustAgent"); + addTrustAgent(newAgentComponentName, /* isSystemApp= */ true); + + mMockContext.sendPackageChangedBroadcast(newAgentComponentName); + + assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly( + defaultTrustAgent); + assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly( + defaultTrustAgent, newAgentComponentName); + } + + @Test + public void newNonSystemTrustAgent_notEnabledButMarkedAsKnown() { + bootService(); + ComponentName newAgentComponentName = ComponentName.unflattenFromString( + "com.user/.UserTrustAgent"); + addTrustAgent(newAgentComponentName, /* isSystemApp= */ false); + + mMockContext.sendPackageChangedBroadcast(newAgentComponentName); + + assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).isEmpty(); + assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly( + newAgentComponentName); + } + + @Test + public void existingTrustAgentChanged_notEnabled() { + ComponentName systemTrustAgent1 = ComponentName.unflattenFromString( + "com.android/.SystemTrustAgent"); + ComponentName systemTrustAgent2 = ComponentName.unflattenFromString( + "com.android/.AnotherSystemTrustAgent"); + addTrustAgent(systemTrustAgent1, /* isSystemApp= */ true); + addTrustAgent(systemTrustAgent2, /* isSystemApp= */ true); + bootService(); + // Simulate user turning off systemTrustAgent2 + mLockPatternUtils.setEnabledTrustAgents(Collections.singletonList(systemTrustAgent1), + TEST_USER_ID); + + mMockContext.sendPackageChangedBroadcast(systemTrustAgent2); + + assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly( + systemTrustAgent1); + } + + private void addTrustAgent(ComponentName agentComponentName, boolean isSystemApp) { + ApplicationInfo applicationInfo = new ApplicationInfo(); + if (isSystemApp) { + applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; + } + + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.packageName = agentComponentName.getPackageName(); + serviceInfo.name = agentComponentName.getClassName(); + serviceInfo.applicationInfo = applicationInfo; + + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.serviceInfo = serviceInfo; + mTrustAgentResolveInfoList.add(resolveInfo); + } + + private void initializeEnabledAgents(ComponentName... enabledAgents) { + mLockPatternUtils.setEnabledTrustAgents(Lists.newArrayList(enabledAgents), TEST_USER_ID); + Settings.Secure.putIntForUser(mMockContext.getContentResolver(), + Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID); + } + + private void initializeKnownAgents(ComponentName... knownAgents) { + mLockPatternUtils.setKnownTrustAgents(Lists.newArrayList(knownAgents), TEST_USER_ID); + Settings.Secure.putIntForUser(mMockContext.getContentResolver(), + Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID); + } + + private void bootService() { + mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START); + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + } + + private void resetTrustAgentLockSettings() { + mLockPatternUtils.setEnabledTrustAgents(Collections.emptyList(), TEST_USER_ID); + mLockPatternUtils.setKnownTrustAgents(Collections.emptyList(), TEST_USER_ID); + } + + /** A mock Context that allows the test process to send protected broadcasts. */ + private static final class MockContext extends TestableContext { + + private final ArrayList mPackageChangedBroadcastReceivers = + new ArrayList<>(); + + MockContext(Context base) { + super(base); + } + + @Override + @Nullable + public Intent registerReceiverAsUser(BroadcastReceiver receiver, + UserHandle user, IntentFilter filter, @Nullable String broadcastPermission, + @Nullable Handler scheduler) { + + if (filter.hasAction(Intent.ACTION_PACKAGE_CHANGED)) { + mPackageChangedBroadcastReceivers.add(receiver); + } + return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission, + scheduler); + } + + void sendPackageChangedBroadcast(ComponentName changedComponent) { + Intent intent = new Intent( + Intent.ACTION_PACKAGE_CHANGED, + Uri.fromParts(URI_SCHEME_PACKAGE, + changedComponent.getPackageName(), /* fragment= */ null)) + .putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, + new String[]{changedComponent.getClassName()}) + .putExtra(Intent.EXTRA_USER_HANDLE, TEST_USER_ID) + .putExtra(Intent.EXTRA_UID, UserHandle.of(TEST_USER_ID).getUid(1234)); + for (BroadcastReceiver receiver : mPackageChangedBroadcastReceivers) { + receiver.onReceive(this, intent); + } + } + } +}