Merge "Cleanup communal mode shared app logic."

This commit is contained in:
TreeHugger Robot
2022-01-06 17:36:26 +00:00
committed by Android (Google) Code Review
4 changed files with 1 additions and 538 deletions

View File

@@ -23,9 +23,6 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
@@ -46,31 +43,10 @@ public final class CommunalManager {
private final ICommunalManager mService;
private final ArrayMap<CommunalModeListener, ICommunalModeListener> mCommunalModeListeners;
/**
* This change id is used to annotate packages which can run in communal mode by default,
* without requiring user opt-in.
*
* @hide
*/
@ChangeId
@Overridable
@Disabled
public static final long ALLOW_COMMUNAL_MODE_BY_DEFAULT = 203673428L;
/**
* This change id is used to annotate packages which are allowed to run in communal mode.
*
* @hide
*/
@ChangeId
@Overridable
@Disabled
public static final long ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT = 200324021L;
/** @hide */
public CommunalManager(ICommunalManager service) {
mService = service;
mCommunalModeListeners = new ArrayMap<CommunalModeListener, ICommunalModeListener>();
mCommunalModeListeners = new ArrayMap<>();
}
/**

View File

@@ -10489,13 +10489,6 @@ public final class Settings {
*/
public static final String COMMUNAL_MODE_ENABLED = "communal_mode_enabled";
/**
* An array of all the packages which have been enabled for hub mode by the user.
*
* @hide
*/
public static final String COMMUNAL_MODE_PACKAGES = "communal_mode_packages";
/**
* An array of SSIDs of Wi-Fi networks that, when connected, are considered safe to enable
* the communal mode.

View File

@@ -16,117 +16,34 @@
package com.android.server.communal;
import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
import static android.app.communal.CommunalManager.ALLOW_COMMUNAL_MODE_BY_DEFAULT;
import static android.app.communal.CommunalManager.ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static com.android.server.wm.ActivityInterceptorCallback.COMMUNAL_MODE_ORDERED_ID;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.communal.ICommunalManager;
import android.app.communal.ICommunalModeListener;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LaunchAfterAuthenticationActivity;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityInterceptorCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* System service for handling Communal Mode state.
*/
public final class CommunalManagerService extends SystemService {
private static final String TAG = CommunalManagerService.class.getSimpleName();
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String DELIMITER = ",";
private final Context mContext;
private final ActivityTaskManagerInternal mAtmInternal;
private final KeyguardManager mKeyguardManager;
private final AtomicBoolean mCommunalViewIsShowing = new AtomicBoolean(false);
private final BinderService mBinderService;
private final PackageReceiver mPackageReceiver;
private final PackageManager mPackageManager;
private final DreamManagerInternal mDreamManagerInternal;
private final RemoteCallbackList<ICommunalModeListener> mListeners =
new RemoteCallbackList<>();
private final ActivityInterceptorCallback mActivityInterceptorCallback =
new ActivityInterceptorCallback() {
@Nullable
@Override
public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
if (!shouldIntercept(info.aInfo)) {
if (DEBUG) {
Slog.d(TAG, "Activity allowed, not intercepting: "
+ info.aInfo.getComponentName());
}
return null;
}
final IIntentSender target = mAtmInternal.getIntentSender(
INTENT_SENDER_ACTIVITY,
info.callingPackage,
info.callingFeatureId,
info.callingUid,
info.userId,
/* token= */null,
/* resultWho= */ null,
/* requestCode= */ 0,
new Intent[]{info.intent},
new String[]{info.resolvedType},
PendingIntent.FLAG_IMMUTABLE,
/* bOptions= */ null);
return new ActivityInterceptResult(
LaunchAfterAuthenticationActivity.createLaunchAfterAuthenticationIntent(
new IntentSender(target)),
info.checkedOptions);
}
};
public CommunalManagerService(Context context) {
super(context);
mContext = context;
mPackageManager = mContext.getPackageManager();
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
mBinderService = new BinderService();
mPackageReceiver = new PackageReceiver(mContext);
}
@VisibleForTesting
@@ -139,116 +56,6 @@ public final class CommunalManagerService extends SystemService {
publishBinderService(Context.COMMUNAL_SERVICE, mBinderService);
}
@Override
public void onBootPhase(int phase) {
if (phase != SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) return;
mAtmInternal.registerActivityStartInterceptor(
COMMUNAL_MODE_ORDERED_ID,
mActivityInterceptorCallback);
mPackageReceiver.register();
removeUninstalledPackagesFromSettings();
}
@Override
public void finalize() {
mPackageReceiver.unregister();
}
private Set<String> getUserEnabledApps() {
final String encodedApps = Settings.Secure.getStringForUser(
mContext.getContentResolver(),
Settings.Secure.COMMUNAL_MODE_PACKAGES,
UserHandle.USER_SYSTEM);
return TextUtils.isEmpty(encodedApps)
? Collections.emptySet()
: new HashSet<>(Arrays.asList(encodedApps.split(DELIMITER)));
}
private void removeUninstalledPackagesFromSettings() {
for (String packageName : getUserEnabledApps()) {
if (!isPackageInstalled(packageName, mPackageManager)) {
removePackageFromSettings(packageName);
}
}
}
private void removePackageFromSettings(String packageName) {
Set<String> enabledPackages = getUserEnabledApps();
if (enabledPackages.remove(packageName)) {
Settings.Secure.putStringForUser(
mContext.getContentResolver(),
Settings.Secure.COMMUNAL_MODE_PACKAGES,
String.join(DELIMITER, enabledPackages),
UserHandle.USER_SYSTEM);
}
}
@VisibleForTesting
static boolean isPackageInstalled(String packageName, PackageManager packageManager) {
if (packageManager == null) return false;
try {
return packageManager.getPackageInfo(packageName, 0) != null;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
private boolean isAppAllowed(ApplicationInfo appInfo) {
if (isActiveDream(appInfo) || isChangeEnabled(ALLOW_COMMUNAL_MODE_BY_DEFAULT, appInfo)) {
return true;
}
if (!isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, appInfo)) {
if (DEBUG) Slog.d(TAG, "App is not allowlisted: " + appInfo.packageName);
return false;
}
if (!getUserEnabledApps().contains(appInfo.packageName)) {
if (DEBUG) Slog.d(TAG, "App does not have user consent: " + appInfo.packageName);
return false;
}
return true;
}
private boolean isActiveDream(ApplicationInfo appInfo) {
final ComponentName activeDream = mDreamManagerInternal.getActiveDreamComponent(
/* doze= */ false);
final ComponentName activeDoze = mDreamManagerInternal.getActiveDreamComponent(
/* doze= */ true);
return isFromPackage(activeDream, appInfo) || isFromPackage(activeDoze, appInfo);
}
private static boolean isFromPackage(ComponentName componentName, ApplicationInfo appInfo) {
if (componentName == null) return false;
return TextUtils.equals(appInfo.packageName, componentName.getPackageName());
}
private static boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
return CompatChanges.isChangeEnabled(changeId, appInfo.packageName, UserHandle.SYSTEM);
}
private boolean shouldIntercept(ActivityInfo activityInfo) {
if (!mCommunalViewIsShowing.get() || !mKeyguardManager.isKeyguardLocked()) return false;
ApplicationInfo appInfo = activityInfo.applicationInfo;
// Dreams are allowed to show, and don't require the showWhenLocked attribute.
if (isActiveDream(appInfo)) return false;
// If the activity doesn't have showWhenLocked enabled, disallow the activity.
final boolean showWhenLocked =
(activityInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
if (!showWhenLocked) {
if (DEBUG) {
Slog.d(TAG, "Activity does not contain showWhenLocked attribute: "
+ activityInfo.getComponentName());
}
return true;
}
return !isAppAllowed(appInfo);
}
private void dispatchCommunalMode(boolean isShowing) {
synchronized (mListeners) {
int i = mListeners.beginBroadcast();
@@ -319,50 +126,4 @@ public final class CommunalManagerService extends SystemService {
}
}
}
/**
* A {@link BroadcastReceiver} that listens on package removed events and updates any stored
* package state in Settings.
*/
private final class PackageReceiver extends BroadcastReceiver {
private final Context mContext;
private final IntentFilter mIntentFilter;
private PackageReceiver(Context context) {
mContext = context;
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(ACTION_PACKAGE_REMOVED);
mIntentFilter.addDataScheme("package");
}
private void register() {
mContext.registerReceiverAsUser(
this,
UserHandle.SYSTEM,
mIntentFilter,
/* broadcastPermission= */null,
/* scheduler= */ null,
Context.RECEIVER_EXPORTED_UNAUDITED);
}
private void unregister() {
mContext.unregisterReceiver(this);
}
@Override
public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
final Uri data = intent.getData();
if (data == null) {
Slog.w(TAG, "Failed to get package name in package receiver");
return;
}
final String packageName = data.getSchemeSpecificPart();
final String action = intent.getAction();
if (ACTION_PACKAGE_REMOVED.equals(action)) {
removePackageFromSettings(packageName);
} else {
Slog.w(TAG, "Unsupported action in package receiver: " + action);
}
}
}
}

View File

@@ -16,58 +16,28 @@
package com.android.server.communal;
import static android.app.communal.CommunalManager.ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.ActivityInterceptorCallback.COMMUNAL_MODE_ORDERED_ID;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import android.Manifest;
import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.app.communal.ICommunalManager;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.net.Uri;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.test.mock.MockContentResolver;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityInterceptorCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.quality.Strictness;
@@ -82,25 +52,9 @@ import org.mockito.quality.Strictness;
@SmallTest
@Presubmit
public class CommunalManagerServiceTest {
private static final int TEST_USER_ID = 1;
private static final int TEST_REAL_CALLING_UID = 2;
private static final int TEST_REAL_CALLING_PID = 3;
private static final String TEST_CALLING_PACKAGE = "com.test.caller";
private static final String TEST_PACKAGE_NAME = "com.test.package";
private MockitoSession mMockingSession;
private CommunalManagerService mService;
@Mock
private ActivityTaskManagerInternal mAtmInternal;
@Mock
private KeyguardManager mKeyguardManager;
@Mock
private DreamManagerInternal mDreamManagerInternal;
private ActivityInterceptorCallback mActivityInterceptorCallback;
private BroadcastReceiver mPackageReceiver;
private ActivityInfo mAInfo;
private ICommunalManager mBinder;
private ContextWrapper mContextSpy;
@@ -108,19 +62,10 @@ public class CommunalManagerServiceTest {
public final void setUp() {
mMockingSession = mockitoSession()
.initMocks(this)
.spyStatic(CommunalManagerService.class)
.mockStatic(CompatChanges.class)
.strictness(Strictness.WARN)
.startMocking();
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
MockContentResolver cr = new MockContentResolver(mContextSpy);
cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContextSpy.getContentResolver()).thenReturn(cr);
when(mContextSpy.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
addLocalServiceMock(ActivityTaskManagerInternal.class, mAtmInternal);
addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternal);
doNothing().when(mContextSpy).enforceCallingPermission(
eq(Manifest.permission.WRITE_COMMUNAL_STATE), anyString());
@@ -128,83 +73,16 @@ public class CommunalManagerServiceTest {
eq(Manifest.permission.READ_COMMUNAL_STATE), anyString());
mService = new CommunalManagerService(mContextSpy);
mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
ArgumentCaptor<ActivityInterceptorCallback> activityInterceptorCaptor =
ArgumentCaptor.forClass(ActivityInterceptorCallback.class);
verify(mAtmInternal).registerActivityStartInterceptor(eq(COMMUNAL_MODE_ORDERED_ID),
activityInterceptorCaptor.capture());
mActivityInterceptorCallback = activityInterceptorCaptor.getValue();
ArgumentCaptor<BroadcastReceiver> packageReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mContextSpy).registerReceiverAsUser(packageReceiverCaptor.capture(),
eq(UserHandle.SYSTEM), any(), any(), any(), anyInt());
mPackageReceiver = packageReceiverCaptor.getValue();
mBinder = mService.getBinderServiceInstance();
mAInfo = new ActivityInfo();
mAInfo.applicationInfo = new ApplicationInfo();
mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
}
@After
public void tearDown() {
FakeSettingsProvider.clearSettingsProvider();
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
}
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
LocalServices.removeServiceForTest(clazz);
LocalServices.addService(clazz, mock);
}
private ActivityInterceptorCallback.ActivityInterceptorInfo buildActivityInfo(Intent intent) {
return new ActivityInterceptorCallback.ActivityInterceptorInfo(
TEST_REAL_CALLING_UID,
TEST_REAL_CALLING_PID,
TEST_USER_ID,
TEST_CALLING_PACKAGE,
"featureId",
intent,
null,
mAInfo,
"resolvedType",
TEST_REAL_CALLING_PID,
TEST_REAL_CALLING_UID,
null);
}
private void allowPackages(String packages) {
Settings.Secure.putStringForUser(mContextSpy.getContentResolver(),
Settings.Secure.COMMUNAL_MODE_PACKAGES, packages, UserHandle.USER_SYSTEM);
}
private String getAllowedPackages() {
return Settings.Secure.getStringForUser(mContextSpy.getContentResolver(),
Settings.Secure.COMMUNAL_MODE_PACKAGES, UserHandle.USER_SYSTEM);
}
private void assertDoesIntercept() {
final Intent intent = new Intent(Intent.ACTION_MAIN);
assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNotNull();
}
private void assertDoesNotIntercept() {
final Intent intent = new Intent(Intent.ACTION_MAIN);
assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
}
private Intent createPackageIntent(String packageName, @Nullable String action) {
return new Intent(action, Uri.parse("package:" + packageName));
}
@Test
public void testIsCommunalMode_isTrue() throws RemoteException {
mBinder.setCommunalViewShowing(true);
@@ -216,149 +94,4 @@ public class CommunalManagerServiceTest {
mBinder.setCommunalViewShowing(false);
assertThat(mBinder.isCommunalMode()).isFalse();
}
@Test
public void testIntercept_unlocked_communalOff_appNotEnabled_showWhenLockedOff() {
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
mAInfo.flags = 0;
assertDoesNotIntercept();
}
@Test
public void testIntercept_unlocked_communalOn_appNotEnabled_showWhenLockedOff()
throws RemoteException {
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
mAInfo.flags = 0;
assertDoesNotIntercept();
}
@Test
public void testIntercept_locked_communalOff_appNotEnabled_showWhenLockedOff() {
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
mAInfo.flags = 0;
assertDoesNotIntercept();
}
@Test
public void testIntercept_locked_communalOn_appNotEnabled_showWhenLockedOff_allowlistEnabled()
throws RemoteException {
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
UserHandle.SYSTEM)).thenReturn(true);
mAInfo.flags = 0;
assertDoesIntercept();
}
@Test
public void testIntercept_locked_communalOn_appNotEnabled_showWhenLockedOn_allowlistEnabled()
throws RemoteException {
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
UserHandle.SYSTEM)).thenReturn(true);
mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
allowPackages("package1,package2");
assertDoesIntercept();
}
@Test
public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOff_allowlistEnabled()
throws RemoteException {
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
UserHandle.SYSTEM)).thenReturn(true);
mAInfo.flags = 0;
allowPackages(TEST_PACKAGE_NAME);
assertDoesIntercept();
}
@Test
public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOn_allowlistEnabled()
throws RemoteException {
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
UserHandle.SYSTEM)).thenReturn(true);
mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
allowPackages(TEST_PACKAGE_NAME);
assertDoesNotIntercept();
}
@Test
public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOn_allowlistDisabled()
throws RemoteException {
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
UserHandle.SYSTEM)).thenReturn(false);
mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
allowPackages(TEST_PACKAGE_NAME);
assertDoesIntercept();
}
@Test
public void testIntercept_locked_communalOn_dream() throws RemoteException {
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
when(mDreamManagerInternal.getActiveDreamComponent(false)).thenReturn(
new ComponentName(TEST_PACKAGE_NAME, "SomeClass"));
allowPackages(TEST_PACKAGE_NAME);
assertDoesNotIntercept();
}
@Test
public void testUpdateSettings_packageUninstalled() {
allowPackages("package1,package2");
assertThat(getAllowedPackages()).isEqualTo("package1,package2");
mPackageReceiver.onReceive(mContextSpy,
createPackageIntent("package1", ACTION_PACKAGE_REMOVED));
assertThat(getAllowedPackages()).isEqualTo("package2");
}
@Test
public void testUpdateSettings_nullAction_doesNothing() {
allowPackages("package1,package2");
assertThat(getAllowedPackages()).isEqualTo("package1,package2");
mPackageReceiver.onReceive(mContextSpy,
createPackageIntent("package1", null));
assertThat(getAllowedPackages()).isEqualTo("package1,package2");
}
@Test
public void testUpdateSettings_invalidPackage_doesNothing() {
allowPackages("package1,package2");
assertThat(getAllowedPackages()).isEqualTo("package1,package2");
mPackageReceiver.onReceive(mContextSpy,
createPackageIntent("package3", ACTION_PACKAGE_REMOVED));
assertThat(getAllowedPackages()).isEqualTo("package1,package2");
}
@Test
public void testUpdateSettings_onBoot() {
allowPackages("package1,package2");
assertThat(getAllowedPackages()).isEqualTo("package1,package2");
when(CommunalManagerService.isPackageInstalled(eq("package1"), any())).thenReturn(true);
when(CommunalManagerService.isPackageInstalled(eq("package2"), any())).thenReturn(false);
mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
assertThat(getAllowedPackages()).isEqualTo("package1");
}
}