diff options
| author | 2022-09-21 21:37:29 +0000 | |
|---|---|---|
| committer | 2022-09-21 21:37:29 +0000 | |
| commit | 84435238c4a08c93b5f7aa7b752c6cb876b51df8 (patch) | |
| tree | 871cba0f888c3212a22a5830800f3eeb6292a721 | |
| parent | 5b0ad2818a00803b7dd77a03ae315fbd4c6e4022 (diff) | |
| parent | 73ce3e64f06f90c9e36e0bff405b05bc9ba8665a (diff) | |
Merge "Auto enable new system trust agents" into tm-qpr-dev am: 73ce3e64f0
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19824313
Change-Id: I79539f4e42c07d9eb694389febb0b32c15a2b265
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
7 files changed, 530 insertions, 27 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c4437c37c27c..34ca5d46cae1 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7427,6 +7427,13 @@ public final class Settings { "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. * @deprecated This identifier is poorly initialized and has diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index a94b30707604..1235b602cde9 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<ComponentName> 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<ComponentName> 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<ComponentName> 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<ComponentName> getKnownTrustAgents(int userId) { + return deserializeTrustAgents(getString(KNOWN_TRUST_AGENTS, userId)); + } + + private String serializeTrustAgents(Collection<ComponentName> 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<ComponentName> getEnabledTrustAgents(int userId) { - String serialized = getString(ENABLED_TRUST_AGENTS, userId); - if (TextUtils.isEmpty(serialized)) { - return new ArrayList<ComponentName>(); + private List<ComponentName> deserializeTrustAgents(String serializedTrustAgents) { + if (TextUtils.isEmpty(serializedTrustAgents)) { + return new ArrayList<>(); } - String[] split = serialized.split(","); - ArrayList<ComponentName> activeTrustAgents = new ArrayList<ComponentName>(split.length); + String[] split = serializedTrustAgents.split(","); + ArrayList<ComponentName> 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 940ca96fac4f..4679a9ea6f66 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<String> valueCaptor = ArgumentCaptor.forClass(String.class); + doNothing().when(ils).setString(anyString(), valueCaptor.capture(), anyInt()); + List<ComponentName> 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<ComponentName> 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<String> valueCaptor = ArgumentCaptor.forClass(String.class); + doNothing().when(ils).setString(anyString(), valueCaptor.capture(), anyInt()); + List<ComponentName> 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<ComponentName> 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 2209ff596cb5..8e82b8b5219a 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 3707c8e42e04..7170773edca4 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<ComponentName> 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<ComponentName> knownAgents = mLockPatternUtils.getKnownTrustAgents(userId); + List<ResolveInfo> agentInfoList = resolveAllowedTrustAgents(mContext.getPackageManager(), + userId); + ArraySet<ComponentName> newAgents = new ArraySet<>(agentInfoList.size()); + ArraySet<ComponentName> 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<ComponentName> 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<ComponentName> agents, int userId) { + if (agents.isEmpty()) { + return; + } + + ArraySet<ComponentName> agentsToEnable = new ArraySet<>(agents); + agentsToEnable.addAll(mLockPatternUtils.getEnabledTrustAgents(userId)); + mLockPatternUtils.setEnabledTrustAgents(agentsToEnable, userId); + } + + private void initializeKnownAgents(int userId) { + List<ResolveInfo> agentInfoList = resolveAllowedTrustAgents(mContext.getPackageManager(), + userId); + ArraySet<ComponentName> 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) { @@ -1826,7 +1906,13 @@ public class TrustManagerService extends SystemService { } @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 07b763dcd85b..33ac73560968 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -34,10 +34,15 @@ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> <uses-permission android:name="android.permission.MANAGE_GAME_ACTIVITY" /> <uses-permission android:name="android.permission.SET_ALWAYS_FINISH" /> + <uses-permission android:name="android.permission.MANAGE_USERS" /> + <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" /> <!-- needed by MasterClearReceiverTest to display a system dialog --> <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/> + <!-- needed by TrustManagerServiceTest to access LockSettings' secure storage --> + <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" /> + <application android:testOnly="true" android:debuggable="true"> <uses-library android:name="android.test.runner" /> 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 000000000000..33870f1d3b86 --- /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<ResolveInfo> 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<Intent> trustAgentIntentMatcher = new ArgumentMatcher<Intent>() { + @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<BroadcastReceiver> 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); + } + } + } +} |