1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.pm;
18 
19 import static android.app.AppOpsManager.MODE_ALLOWED;
20 import static android.app.AppOpsManager.MODE_DEFAULT;
21 import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES;
22 import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
23 import static android.content.Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND;
24 import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
25 import static android.content.pm.CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED;
26 
27 import static com.google.common.truth.Truth.assertThat;
28 
29 import static org.junit.Assert.fail;
30 import static org.mockito.ArgumentMatchers.anyInt;
31 import static org.mockito.ArgumentMatchers.anyLong;
32 import static org.mockito.ArgumentMatchers.eq;
33 import static org.mockito.Mockito.when;
34 import static org.robolectric.Shadows.shadowOf;
35 
36 import android.Manifest;
37 import android.annotation.UserIdInt;
38 import android.app.ActivityManagerInternal;
39 import android.app.AppOpsManager;
40 import android.app.AppOpsManager.Mode;
41 import android.app.admin.DevicePolicyManagerInternal;
42 import android.content.ComponentName;
43 import android.content.ContextWrapper;
44 import android.content.Intent;
45 import android.content.pm.ActivityInfo;
46 import android.content.pm.ApplicationInfo;
47 import android.content.pm.IPackageManager;
48 import android.content.pm.PackageInfo;
49 import android.content.pm.PackageManager;
50 import android.content.pm.PackageManagerInternal;
51 import android.content.pm.PermissionInfo;
52 import android.content.pm.ResolveInfo;
53 import android.os.Process;
54 import android.os.UserHandle;
55 import android.os.UserManager;
56 import android.platform.test.annotations.Presubmit;
57 
58 import androidx.test.core.app.ApplicationProvider;
59 
60 import com.android.internal.pm.parsing.pkg.PackageImpl;
61 import com.android.internal.pm.parsing.pkg.ParsedPackage;
62 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
63 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
64 import com.android.server.LocalServices;
65 import com.android.server.pm.pkg.AndroidPackage;
66 import com.android.server.testing.shadows.ShadowApplicationPackageManager;
67 import com.android.server.testing.shadows.ShadowUserManager;
68 import com.android.server.wm.ActivityTaskManagerInternal;
69 
70 import com.google.android.collect.Lists;
71 
72 import org.junit.Before;
73 import org.junit.Test;
74 import org.junit.runner.RunWith;
75 import org.mockito.Mock;
76 import org.mockito.MockitoAnnotations;
77 import org.robolectric.RobolectricTestRunner;
78 import org.robolectric.annotation.Config;
79 import org.robolectric.shadow.api.Shadow;
80 
81 import java.util.ArrayList;
82 import java.util.HashMap;
83 import java.util.HashSet;
84 import java.util.List;
85 import java.util.Map;
86 import java.util.Set;
87 
88 /** Unit tests for {@link CrossProfileAppsServiceImpl}. */
89 @RunWith(RobolectricTestRunner.class)
90 @Presubmit
91 @Config(shadows = {ShadowUserManager.class, ShadowApplicationPackageManager.class})
92 public class CrossProfileAppsServiceImplRoboTest {
93     private static final int CALLING_UID = 1111;
94     private static final int CALLING_PID = 1000;
95     private static final String CROSS_PROFILE_APP_PACKAGE_NAME =
96             "com.android.server.pm.crossprofileappsserviceimplrobotest.crossprofileapp";
97     @UserIdInt private static final int PERSONAL_PROFILE_USER_ID = 0;
98     private static final int PERSONAL_PROFILE_UID = 2222;
99     @UserIdInt private static final int WORK_PROFILE_USER_ID = 10;
100     private static final int WORK_PROFILE_UID = 3333;
101     private static final int OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID = 20;
102     @UserIdInt private static final int OTHER_PROFILE_GROUP_USER_ID = 30;
103     private static final int OTHER_PROFILE_GROUP_UID = 4444;
104     @UserIdInt private static final int OTHER_PROFILE_GROUP_2_USER_ID = 31;
105     private static final int OTHER_PROFILE_GROUP_2_UID = 5555;
106 
107     private final ContextWrapper mContext = ApplicationProvider.getApplicationContext();
108     private final UserManager mUserManager = mContext.getSystemService(UserManager.class);
109     private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
110     private final PackageManager mPackageManager = mContext.getPackageManager();
111     private final TestInjector mInjector = new TestInjector();
112     private final CrossProfileAppsServiceImpl mCrossProfileAppsServiceImpl =
113             new CrossProfileAppsServiceImpl(mContext, mInjector);
114     private final Map<UserHandle, Set<Intent>> mSentUserBroadcasts = new HashMap<>();
115     private final Map<Integer, List<ApplicationInfo>> installedApplications = new HashMap<>();
116     private final Set<Integer> mKilledUids = new HashSet<>();
117 
118     @Mock private PackageManagerInternal mPackageManagerInternal;
119     @Mock private IPackageManager mIPackageManager;
120     @Mock private DevicePolicyManagerInternal mDevicePolicyManagerInternal;
121 
122     @Before
initializeMocks()123     public void initializeMocks() throws Exception {
124         MockitoAnnotations.initMocks(this);
125         initializeInstalledApplicationsMock();
126         mockCrossProfileAppInstalledAndEnabledOnEachProfile();
127         mockCrossProfileAppRequestsInteractAcrossProfiles();
128         mockCrossProfileAppRegistersBroadcastReceiver();
129         mockCrossProfileAppWhitelisted();
130     }
131 
initializeInstalledApplicationsMock()132     private void initializeInstalledApplicationsMock() {
133         when(mPackageManagerInternal.getInstalledApplications(anyInt(), anyInt(), eq(CALLING_UID)))
134                 .thenAnswer(invocation -> installedApplications.get(invocation.getArgument(1)));
135     }
136 
mockCrossProfileAppInstalledAndEnabledOnEachProfile()137     private void mockCrossProfileAppInstalledAndEnabledOnEachProfile() {
138         // They are enabled by default, so we simply have to ensure that a package info with an
139         // application info is returned.
140         final PackageInfo packageInfo = buildTestPackageInfo();
141         mockCrossProfileAppInstalledOnProfile(
142                 packageInfo, PERSONAL_PROFILE_USER_ID, PERSONAL_PROFILE_UID);
143         mockCrossProfileAppInstalledOnProfile(packageInfo, WORK_PROFILE_USER_ID, WORK_PROFILE_UID);
144         mockCrossProfileAppInstalledOnProfile(
145                 packageInfo, OTHER_PROFILE_GROUP_USER_ID, OTHER_PROFILE_GROUP_UID);
146         mockCrossProfileAppInstalledOnProfile(
147                 packageInfo, OTHER_PROFILE_GROUP_2_USER_ID, OTHER_PROFILE_GROUP_2_UID);
148     }
149 
mockCrossProfileAppInstalledOnProfile( PackageInfo packageInfo, @UserIdInt int userId, int uid)150     private void mockCrossProfileAppInstalledOnProfile(
151             PackageInfo packageInfo, @UserIdInt int userId, int uid) {
152         when(mPackageManagerInternal.getPackageInfo(
153                         eq(CROSS_PROFILE_APP_PACKAGE_NAME),
154                         /* flags= */ anyLong(),
155                         /* filterCallingUid= */ anyInt(),
156                         eq(userId)))
157                 .thenReturn(packageInfo);
158         when(mPackageManagerInternal.getPackage(uid))
159                 .thenReturn(((ParsedPackage) PackageImpl.forTesting(CROSS_PROFILE_APP_PACKAGE_NAME)
160                         .hideAsParsed()).hideAsFinal());
161         installedApplications.putIfAbsent(userId, new ArrayList<>());
162         installedApplications.get(userId).add(packageInfo.applicationInfo);
163     }
164 
buildTestPackageInfo()165     private PackageInfo buildTestPackageInfo() {
166         PackageInfo packageInfo = new PackageInfo();
167         packageInfo.applicationInfo = new ApplicationInfo();
168         packageInfo.applicationInfo.packageName = CROSS_PROFILE_APP_PACKAGE_NAME;
169         return packageInfo;
170     }
171 
mockCrossProfileAppRequestsInteractAcrossProfiles()172     private void mockCrossProfileAppRequestsInteractAcrossProfiles() throws Exception {
173         final String permissionName = Manifest.permission.INTERACT_ACROSS_PROFILES;
174         when(mIPackageManager.getAppOpPermissionPackages(eq(permissionName), anyInt()))
175                 .thenReturn(new String[] {CROSS_PROFILE_APP_PACKAGE_NAME});
176     }
177 
mockCrossProfileAppRegistersBroadcastReceiver()178     private void mockCrossProfileAppRegistersBroadcastReceiver() {
179         final ShadowApplicationPackageManager shadowApplicationPackageManager =
180                 Shadow.extract(mPackageManager);
181         final Intent baseIntent =
182                 new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED)
183                         .setPackage(CROSS_PROFILE_APP_PACKAGE_NAME);
184         final Intent manifestIntent =
185                 new Intent(baseIntent)
186                         .setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
187                                 | Intent.FLAG_RECEIVER_FOREGROUND);
188         final Intent registeredIntent =
189                 new Intent(baseIntent).setFlags(FLAG_RECEIVER_REGISTERED_ONLY);
190         final List<ResolveInfo> resolveInfos = Lists.newArrayList(buildTestResolveInfo());
191         shadowApplicationPackageManager.setResolveInfosForIntent(manifestIntent, resolveInfos);
192         shadowApplicationPackageManager.setResolveInfosForIntent(registeredIntent, resolveInfos);
193     }
194 
buildTestResolveInfo()195     private ResolveInfo buildTestResolveInfo() {
196         final ResolveInfo resolveInfo = new ResolveInfo();
197         resolveInfo.activityInfo = new ActivityInfo();
198         resolveInfo.activityInfo.packageName = CROSS_PROFILE_APP_PACKAGE_NAME;
199         resolveInfo.activityInfo.name = CROSS_PROFILE_APP_PACKAGE_NAME + ".Receiver";
200         return resolveInfo;
201     }
202 
mockCrossProfileAppWhitelisted()203     private void mockCrossProfileAppWhitelisted() {
204         when(mDevicePolicyManagerInternal.getAllCrossProfilePackages(anyInt()))
205                 .thenReturn(Lists.newArrayList(CROSS_PROFILE_APP_PACKAGE_NAME));
206     }
207 
208     @Before
setUpCrossProfileAppUidsAndPackageNames()209     public void setUpCrossProfileAppUidsAndPackageNames() {
210         setUpCrossProfileAppUidAndPackageName(
211                 PERSONAL_PROFILE_UID, PERSONAL_PROFILE_USER_ID);
212         setUpCrossProfileAppUidAndPackageName(
213                 WORK_PROFILE_UID, WORK_PROFILE_USER_ID);
214         setUpCrossProfileAppUidAndPackageName(
215                 OTHER_PROFILE_GROUP_UID, OTHER_PROFILE_GROUP_USER_ID);
216         setUpCrossProfileAppUidAndPackageName(
217                 OTHER_PROFILE_GROUP_2_UID, OTHER_PROFILE_GROUP_2_USER_ID);
218     }
219 
setUpCrossProfileAppUidAndPackageName(int uid, @UserIdInt int userId)220     private void setUpCrossProfileAppUidAndPackageName(int uid, @UserIdInt int userId) {
221         ShadowApplicationPackageManager.setPackageUidAsUser(
222                 CROSS_PROFILE_APP_PACKAGE_NAME, uid, userId);
223         when(mPackageManagerInternal
224                 .getPackageUid(CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, userId))
225                 .thenReturn(uid);
226     }
227 
228     @Before
grantPermissions()229     public void grantPermissions() {
230         grantPermissions(
231                 Manifest.permission.MANAGE_APP_OPS_MODES,
232                 Manifest.permission.UPDATE_APP_OPS_STATS,
233                 Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
234                 Manifest.permission.INTERACT_ACROSS_USERS,
235                 Manifest.permission.INTERACT_ACROSS_USERS_FULL);
236     }
237 
238     @Before
setUpProfiles()239     public void setUpProfiles() {
240         final ShadowUserManager shadowUserManager = Shadow.extract(mUserManager);
241         shadowUserManager.addProfileIds(
242                 PERSONAL_PROFILE_USER_ID,
243                 WORK_PROFILE_USER_ID,
244                 OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID);
245         shadowUserManager.addProfileIds(
246                 OTHER_PROFILE_GROUP_USER_ID,
247                 OTHER_PROFILE_GROUP_2_USER_ID);
248     }
249 
250     @Before
setInteractAcrossProfilesAppOpDefault()251     public void setInteractAcrossProfilesAppOpDefault() {
252         // It seems to be necessary to provide the shadow with the default already specified in
253         // AppOpsManager.
254         final int defaultMode = AppOpsManager.opToDefaultMode(OP_INTERACT_ACROSS_PROFILES);
255         explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, defaultMode);
256         explicitlySetInteractAcrossProfilesAppOp(WORK_PROFILE_UID, defaultMode);
257         explicitlySetInteractAcrossProfilesAppOp(OTHER_PROFILE_GROUP_UID, defaultMode);
258         explicitlySetInteractAcrossProfilesAppOp(OTHER_PROFILE_GROUP_2_UID, defaultMode);
259     }
260 
261     @Test
setInteractAcrossProfilesAppOp_noPermissions_throwsSecurityException()262     public void setInteractAcrossProfilesAppOp_noPermissions_throwsSecurityException() {
263         denyPermissions(
264                 Manifest.permission.MANAGE_APP_OPS_MODES,
265                 Manifest.permission.UPDATE_APP_OPS_STATS,
266                 Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
267                 Manifest.permission.INTERACT_ACROSS_USERS,
268                 Manifest.permission.INTERACT_ACROSS_USERS_FULL);
269         try {
270             mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
271                     CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
272             fail();
273         } catch (SecurityException expected) {}
274     }
275 
276     @Test
setInteractAcrossProfilesAppOp_missingInteractAcrossUsersAndFull_throwsSecurityException()277     public void setInteractAcrossProfilesAppOp_missingInteractAcrossUsersAndFull_throwsSecurityException() {
278         denyPermissions(
279                 Manifest.permission.INTERACT_ACROSS_USERS,
280                 Manifest.permission.INTERACT_ACROSS_USERS_FULL);
281         grantPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES);
282         try {
283             mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
284                     CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
285             fail();
286         } catch (SecurityException expected) {}
287     }
288 
289     @Test
setInteractAcrossProfilesAppOp_setsAppOp()290     public void setInteractAcrossProfilesAppOp_setsAppOp() {
291         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
292                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
293         assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
294     }
295 
296     @Test
setInteractAcrossProfilesAppOp_configureInteractAcrossProfilesPermissionWithoutAppOpsPermissions_setsAppOp()297     public void setInteractAcrossProfilesAppOp_configureInteractAcrossProfilesPermissionWithoutAppOpsPermissions_setsAppOp() {
298         denyPermissions(
299                 Manifest.permission.MANAGE_APP_OPS_MODES,
300                 Manifest.permission.UPDATE_APP_OPS_STATS);
301         grantPermissions(
302                 Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
303                 Manifest.permission.INTERACT_ACROSS_USERS);
304 
305         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
306                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
307 
308         assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
309     }
310 
311     @Test
setInteractAcrossProfilesAppOp_appOpsPermissionsWithoutConfigureInteractAcrossProfilesPermission_setsAppOp()312     public void setInteractAcrossProfilesAppOp_appOpsPermissionsWithoutConfigureInteractAcrossProfilesPermission_setsAppOp() {
313         denyPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES);
314         grantPermissions(
315                 Manifest.permission.MANAGE_APP_OPS_MODES,
316                 Manifest.permission.UPDATE_APP_OPS_STATS,
317                 Manifest.permission.INTERACT_ACROSS_USERS);
318 
319         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
320                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
321 
322         assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
323     }
324 
325     @Test
setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull()326     public void setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull() {
327         denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
328         grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS);
329         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
330                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
331         assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
332     }
333 
334     @Test
setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers()335     public void setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers() {
336         denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS);
337         grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
338         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
339                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
340         assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
341     }
342 
343     @Test
setInteractAcrossProfilesAppOp_setsAppOpOnOtherProfile()344     public void setInteractAcrossProfilesAppOp_setsAppOpOnOtherProfile() {
345         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
346                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
347         assertThat(getCrossProfileAppOp(WORK_PROFILE_UID)).isEqualTo(MODE_ALLOWED);
348     }
349 
350     @Test
setInteractAcrossProfilesAppOp_sendsBroadcast()351     public void setInteractAcrossProfilesAppOp_sendsBroadcast() {
352         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
353                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
354         assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isTrue();
355     }
356 
357     @Test
setInteractAcrossProfilesAppOp_sendsBroadcastToOtherProfile()358     public void setInteractAcrossProfilesAppOp_sendsBroadcastToOtherProfile() {
359         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
360                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
361         assertThat(receivedCanInteractAcrossProfilesChangedBroadcast(WORK_PROFILE_USER_ID))
362                 .isTrue();
363     }
364 
365     @Test
setInteractAcrossProfilesAppOp_doesNotSendBroadcastToProfileWithoutPackage()366     public void setInteractAcrossProfilesAppOp_doesNotSendBroadcastToProfileWithoutPackage() {
367         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
368                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
369         assertThat(receivedCanInteractAcrossProfilesChangedBroadcast(
370                         OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID))
371                 .isFalse();
372     }
373 
374     @Test
setInteractAcrossProfilesAppOp_toSameAsCurrent_doesNotSendBroadcast()375     public void setInteractAcrossProfilesAppOp_toSameAsCurrent_doesNotSendBroadcast() {
376         explicitlySetInteractAcrossProfilesAppOp(MODE_ALLOWED);
377         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
378                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
379         assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isFalse();
380     }
381 
382     @Test
setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSet()383     public void setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSet() {
384         mockCrossProfileAppNotWhitelisted();
385         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
386                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
387         assertThat(getCrossProfileAppOp()).isNotEqualTo(MODE_ALLOWED);
388     }
389 
390     @Test
setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSendBroadcast()391     public void setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSendBroadcast() {
392         mockCrossProfileAppNotWhitelisted();
393         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
394                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
395         assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isFalse();
396     }
397 
398     @Test
setInteractAcrossProfilesAppOp_withoutCrossProfileAttribute_manifestReceiversDoNotGetBroadcast()399     public void setInteractAcrossProfilesAppOp_withoutCrossProfileAttribute_manifestReceiversDoNotGetBroadcast() {
400         declareCrossProfileAttributeOnCrossProfileApp(false);
401         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
402                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
403         assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isFalse();
404     }
405 
406     @Test
setInteractAcrossProfilesAppOp_withCrossProfileAttribute_manifestReceiversGetBroadcast()407     public void setInteractAcrossProfilesAppOp_withCrossProfileAttribute_manifestReceiversGetBroadcast() {
408         declareCrossProfileAttributeOnCrossProfileApp(true);
409         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
410                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
411         assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isTrue();
412     }
413 
414     @Test
setInteractAcrossProfilesAppOp_toAllowed_doesNotKillApp()415     public void setInteractAcrossProfilesAppOp_toAllowed_doesNotKillApp() {
416         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
417                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
418         assertThat(mKilledUids).isEmpty();
419     }
420 
421     @Test
setInteractAcrossProfilesAppOp_toDisallowed_killsAppsInBothProfiles()422     public void setInteractAcrossProfilesAppOp_toDisallowed_killsAppsInBothProfiles() {
423         shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo());
424         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
425                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
426 
427         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(/* userId= */ 0,
428                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_DEFAULT);
429 
430         assertThat(mKilledUids).contains(WORK_PROFILE_UID);
431         assertThat(mKilledUids).contains(PERSONAL_PROFILE_UID);
432     }
433 
createCrossProfilesPermissionInfo()434     private PermissionInfo createCrossProfilesPermissionInfo() {
435         PermissionInfo permissionInfo = new PermissionInfo();
436         permissionInfo.name = Manifest.permission.INTERACT_ACROSS_PROFILES;
437         permissionInfo.protectionLevel = PermissionInfo.PROTECTION_FLAG_APPOP;
438         return permissionInfo;
439     }
440 
441     @Test
setInteractAcrossProfilesAppOp_userToSetInDifferentProfileGroupToCaller_setsAppOp()442     public void setInteractAcrossProfilesAppOp_userToSetInDifferentProfileGroupToCaller_setsAppOp() {
443         mCrossProfileAppsServiceImpl.getLocalService().setInteractAcrossProfilesAppOp(
444                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED, OTHER_PROFILE_GROUP_USER_ID);
445         assertThat(getCrossProfileAppOp(OTHER_PROFILE_GROUP_UID)).isEqualTo(MODE_ALLOWED);
446     }
447 
448     @Test
setInteractAcrossProfilesAppOp_userToSetInDifferentProfileGroupToCaller_setsAppOpOnOtherProfile()449     public void setInteractAcrossProfilesAppOp_userToSetInDifferentProfileGroupToCaller_setsAppOpOnOtherProfile() {
450         mCrossProfileAppsServiceImpl.getLocalService().setInteractAcrossProfilesAppOp(
451                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED, OTHER_PROFILE_GROUP_USER_ID);
452         assertThat(getCrossProfileAppOp(OTHER_PROFILE_GROUP_2_UID)).isEqualTo(MODE_ALLOWED);
453     }
454 
455     @Test
setInteractAcrossProfilesAppOp_userToSetInDifferentProfileGroupToCaller_doesNotSetCallerAppOp()456     public void setInteractAcrossProfilesAppOp_userToSetInDifferentProfileGroupToCaller_doesNotSetCallerAppOp() {
457         mCrossProfileAppsServiceImpl.getLocalService().setInteractAcrossProfilesAppOp(
458                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED, OTHER_PROFILE_GROUP_USER_ID);
459         assertThat(getCrossProfileAppOp()).isEqualTo(MODE_DEFAULT);
460     }
461 
462     @Test
canConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsFalse()463     public void canConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsFalse() {
464         mockUninstallCrossProfileAppFromWorkProfile();
465         assertThat(mCrossProfileAppsServiceImpl
466                 .canConfigureInteractAcrossProfiles(
467                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
468                 .isFalse();
469     }
470 
mockUninstallCrossProfileAppFromWorkProfile()471     private void mockUninstallCrossProfileAppFromWorkProfile() {
472         when(mPackageManagerInternal.getPackageInfo(
473                         eq(CROSS_PROFILE_APP_PACKAGE_NAME),
474                         /* flags= */ anyLong(),
475                         /* filterCallingUid= */ anyInt(),
476                         eq(WORK_PROFILE_USER_ID)))
477                 .thenReturn(null);
478         when(mPackageManagerInternal.getPackage(WORK_PROFILE_UID)).thenReturn(null);
479     }
480 
481     @Test
canConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse()482     public void canConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse()
483             throws Exception {
484         mockCrossProfileAppDoesNotRequestInteractAcrossProfiles();
485         assertThat(mCrossProfileAppsServiceImpl
486                 .canConfigureInteractAcrossProfiles(
487                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
488                 .isFalse();
489     }
490 
mockCrossProfileAppDoesNotRequestInteractAcrossProfiles()491     private void mockCrossProfileAppDoesNotRequestInteractAcrossProfiles() throws Exception {
492         final String permissionName = Manifest.permission.INTERACT_ACROSS_PROFILES;
493         when(mIPackageManager.getAppOpPermissionPackages(eq(permissionName), anyInt()))
494                 .thenReturn(new String[] {});
495     }
496 
497     @Test
canConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsFalse()498     public void canConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsFalse() {
499         mockCrossProfileAppNotWhitelisted();
500         assertThat(mCrossProfileAppsServiceImpl
501                 .canConfigureInteractAcrossProfiles(
502                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
503                 .isFalse();
504     }
505 
506     @Test
canConfigureInteractAcrossProfiles_returnsTrue()507     public void canConfigureInteractAcrossProfiles_returnsTrue() {
508         assertThat(mCrossProfileAppsServiceImpl
509                 .canConfigureInteractAcrossProfiles(
510                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
511                 .isTrue();
512     }
513 
514     @Test
canUserAttemptToConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsTrue()515     public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsTrue() {
516         mockUninstallCrossProfileAppFromWorkProfile();
517         assertThat(mCrossProfileAppsServiceImpl
518                 .canUserAttemptToConfigureInteractAcrossProfiles(
519                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
520                 .isTrue();
521     }
522 
523     @Test
canUserAttemptToConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse()524     public void canUserAttemptToConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse()
525             throws Exception {
526         mockCrossProfileAppDoesNotRequestInteractAcrossProfiles();
527         assertThat(mCrossProfileAppsServiceImpl
528                 .canUserAttemptToConfigureInteractAcrossProfiles(
529                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
530                 .isFalse();
531     }
532 
533     @Test
canUserAttemptToConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsTrue()534     public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsTrue() {
535         mockCrossProfileAppNotWhitelisted();
536         assertThat(mCrossProfileAppsServiceImpl
537                 .canUserAttemptToConfigureInteractAcrossProfiles(
538                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
539                 .isTrue();
540     }
541 
542     @Test
canUserAttemptToConfigureInteractAcrossProfiles_platformSignedAppWithAutomaticPermission_returnsFalse()543     public void canUserAttemptToConfigureInteractAcrossProfiles_platformSignedAppWithAutomaticPermission_returnsFalse() {
544         mockCrossProfileAppNotWhitelistedByOem();
545         shadowOf(mContext).grantPermissions(
546                 Process.myPid(),
547                 PERSONAL_PROFILE_UID,
548                 Manifest.permission.INTERACT_ACROSS_PROFILES);
549 
550         assertThat(mCrossProfileAppsServiceImpl
551                 .canUserAttemptToConfigureInteractAcrossProfiles(
552                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
553                 .isFalse();
554     }
555 
556     @Test
canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerWorkProfile_returnsFalse()557     public void canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerWorkProfile_returnsFalse() {
558         when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(WORK_PROFILE_USER_ID))
559                 .thenReturn(buildCrossProfileComponentName());
560         assertThat(mCrossProfileAppsServiceImpl
561                 .canUserAttemptToConfigureInteractAcrossProfiles(
562                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
563                 .isFalse();
564     }
565 
566     @Test
canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerOtherProfile_returnsFalse()567     public void canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerOtherProfile_returnsFalse() {
568         // Normally, the DPC would not be a profile owner of the personal profile, but for the
569         // purposes of this test, it is just a profile owner of any profile within the profile
570         // group.
571         when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(PERSONAL_PROFILE_USER_ID))
572                 .thenReturn(buildCrossProfileComponentName());
573         assertThat(mCrossProfileAppsServiceImpl
574                 .canUserAttemptToConfigureInteractAcrossProfiles(
575                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
576                 .isFalse();
577     }
578 
579     @Test
canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerOutsideProfileGroup_returnsTrue()580     public void canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerOutsideProfileGroup_returnsTrue() {
581         when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(OTHER_PROFILE_GROUP_USER_ID))
582                 .thenReturn(buildCrossProfileComponentName());
583         assertThat(mCrossProfileAppsServiceImpl
584                 .canUserAttemptToConfigureInteractAcrossProfiles(
585                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
586                 .isTrue();
587     }
588 
589     @Test
canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue()590     public void canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue() {
591         assertThat(mCrossProfileAppsServiceImpl
592                 .canUserAttemptToConfigureInteractAcrossProfiles(
593                         /* userId= */ 0, CROSS_PROFILE_APP_PACKAGE_NAME))
594                 .isTrue();
595     }
596 
597     @Test
clearInteractAcrossProfilesAppOps()598     public void clearInteractAcrossProfilesAppOps() {
599         explicitlySetInteractAcrossProfilesAppOp(MODE_ALLOWED);
600         mCrossProfileAppsServiceImpl.clearInteractAcrossProfilesAppOps(/* userId= */ 0);
601         assertThat(getCrossProfileAppOp()).isEqualTo(MODE_DEFAULT);
602     }
603 
explicitlySetInteractAcrossProfilesAppOp(@ode int mode)604     private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) {
605         explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode);
606     }
607 
explicitlySetInteractAcrossProfilesAppOp(int uid, @Mode int mode)608     private void explicitlySetInteractAcrossProfilesAppOp(int uid, @Mode int mode) {
609         shadowOf(mAppOpsManager).setMode(
610                 OP_INTERACT_ACROSS_PROFILES, uid, CROSS_PROFILE_APP_PACKAGE_NAME, mode);
611     }
612 
grantPermissions(String... permissions)613     private void grantPermissions(String... permissions) {
614         shadowOf(mContext).grantPermissions(Process.myPid(), CALLING_UID, permissions);
615     }
616 
denyPermissions(String... permissions)617     private void denyPermissions(String... permissions) {
618         shadowOf(mContext).denyPermissions(Process.myPid(), CALLING_UID, permissions);
619     }
620 
getCrossProfileAppOp()621     private @Mode int getCrossProfileAppOp() {
622         return getCrossProfileAppOp(PERSONAL_PROFILE_UID);
623     }
624 
getCrossProfileAppOp(int uid)625     private @Mode int getCrossProfileAppOp(int uid) {
626         return mAppOpsManager.unsafeCheckOpNoThrow(
627                 AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES),
628                 uid,
629                 CROSS_PROFILE_APP_PACKAGE_NAME);
630     }
631 
receivedCanInteractAcrossProfilesChangedBroadcast()632     private boolean receivedCanInteractAcrossProfilesChangedBroadcast() {
633         return receivedCanInteractAcrossProfilesChangedBroadcast(PERSONAL_PROFILE_USER_ID);
634     }
635 
receivedCanInteractAcrossProfilesChangedBroadcast(@serIdInt int userId)636     private boolean receivedCanInteractAcrossProfilesChangedBroadcast(@UserIdInt int userId) {
637         final UserHandle userHandle = UserHandle.of(userId);
638         if (!mSentUserBroadcasts.containsKey(userHandle)) {
639             return false;
640         }
641         return mSentUserBroadcasts.get(userHandle)
642                 .stream()
643                 .anyMatch(this::isBroadcastCanInteractAcrossProfilesChanged);
644     }
645 
isBroadcastCanInteractAcrossProfilesChanged(Intent intent)646     private boolean isBroadcastCanInteractAcrossProfilesChanged(Intent intent) {
647         return intent.getAction().equals(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED)
648                 && CROSS_PROFILE_APP_PACKAGE_NAME.equals(intent.getPackage());
649     }
650 
mockCrossProfileAndroidPackage(AndroidPackage androidPackage)651     private void mockCrossProfileAndroidPackage(AndroidPackage androidPackage) {
652         when(mPackageManagerInternal.getPackage(CROSS_PROFILE_APP_PACKAGE_NAME))
653                 .thenReturn(androidPackage);
654         when(mPackageManagerInternal.getPackage(PERSONAL_PROFILE_UID))
655                 .thenReturn(androidPackage);
656         when(mPackageManagerInternal.getPackage(WORK_PROFILE_UID))
657                 .thenReturn(androidPackage);
658         when(mPackageManagerInternal.getPackage(OTHER_PROFILE_GROUP_UID))
659                 .thenReturn(androidPackage);
660         when(mPackageManagerInternal.getPackage(OTHER_PROFILE_GROUP_2_UID))
661                 .thenReturn(androidPackage);
662     }
663 
mockCrossProfileAppNotWhitelisted()664     private void mockCrossProfileAppNotWhitelisted() {
665         when(mDevicePolicyManagerInternal.getAllCrossProfilePackages(anyInt()))
666                 .thenReturn(new ArrayList<>());
667     }
668 
mockCrossProfileAppNotWhitelistedByOem()669     private void mockCrossProfileAppNotWhitelistedByOem() {
670         when(mDevicePolicyManagerInternal.getDefaultCrossProfilePackages())
671                 .thenReturn(new ArrayList<>());
672     }
673 
receivedManifestCanInteractAcrossProfilesChangedBroadcast()674     private boolean receivedManifestCanInteractAcrossProfilesChangedBroadcast() {
675         final UserHandle userHandle = UserHandle.of(PERSONAL_PROFILE_USER_ID);
676         if (!mSentUserBroadcasts.containsKey(userHandle)) {
677             return false;
678         }
679         return mSentUserBroadcasts.get(userHandle)
680                 .stream()
681                 .anyMatch(this::isBroadcastManifestCanInteractAcrossProfilesChanged);
682     }
683 
isBroadcastManifestCanInteractAcrossProfilesChanged(Intent intent)684     private boolean isBroadcastManifestCanInteractAcrossProfilesChanged(Intent intent) {
685         return isBroadcastCanInteractAcrossProfilesChanged(intent)
686                 && (intent.getFlags() & FLAG_RECEIVER_REGISTERED_ONLY) == 0
687                 && (intent.getFlags() & FLAG_RECEIVER_INCLUDE_BACKGROUND) != 0
688                 && (intent.getFlags() & FLAG_RECEIVER_FOREGROUND) != 0
689                 && intent.getComponent() != null
690                 && intent.getComponent().getPackageName().equals(CROSS_PROFILE_APP_PACKAGE_NAME);
691     }
692 
declareCrossProfileAttributeOnCrossProfileApp(boolean value)693     private void declareCrossProfileAttributeOnCrossProfileApp(boolean value) {
694         mockCrossProfileAndroidPackage(
695                 ((ParsedPackage) PackageImpl.forTesting(CROSS_PROFILE_APP_PACKAGE_NAME)
696                         .setCrossProfile(value)
697                         .hideAsParsed()).hideAsFinal());
698     }
699 
buildCrossProfileComponentName()700     private ComponentName buildCrossProfileComponentName() {
701         return new ComponentName(CROSS_PROFILE_APP_PACKAGE_NAME, "testClassName");
702     }
703 
704     private class TestInjector implements CrossProfileAppsServiceImpl.Injector {
705 
706         @Override
getCallingUid()707         public int getCallingUid() {
708             return CALLING_UID;
709         }
710 
711         @Override
getCallingPid()712         public int getCallingPid() {
713             return CALLING_PID;
714         }
715 
716         @Override
getCallingUserId()717         public @UserIdInt int getCallingUserId() {
718             return PERSONAL_PROFILE_USER_ID;
719         }
720 
721         @Override
getCallingUserHandle()722         public UserHandle getCallingUserHandle() {
723             return UserHandle.of(getCallingUserId());
724         }
725 
726         @Override
clearCallingIdentity()727         public long clearCallingIdentity() {
728             return 0;
729         }
730 
731         @Override
restoreCallingIdentity(long token)732         public void restoreCallingIdentity(long token) {}
733 
734         @Override
withCleanCallingIdentity(ThrowingRunnable action)735         public void withCleanCallingIdentity(ThrowingRunnable action) {
736             action.run();
737         }
738 
739         @Override
withCleanCallingIdentity(ThrowingSupplier<T> action)740         public <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) {
741             return action.get();
742         }
743 
744         @Override
getUserManager()745         public UserManager getUserManager() {
746             return mUserManager;
747         }
748 
749         @Override
getPackageManagerInternal()750         public PackageManagerInternal getPackageManagerInternal() {
751             return mPackageManagerInternal;
752         }
753 
754         @Override
getPackageManager()755         public PackageManager getPackageManager() {
756             return mPackageManager;
757         }
758 
759         @Override
getAppOpsManager()760         public AppOpsManager getAppOpsManager() {
761             return mAppOpsManager;
762         }
763 
764         @Override
getActivityManagerInternal()765         public ActivityManagerInternal getActivityManagerInternal() {
766             return LocalServices.getService(ActivityManagerInternal.class);
767         }
768 
769         @Override
getActivityTaskManagerInternal()770         public ActivityTaskManagerInternal getActivityTaskManagerInternal() {
771             return LocalServices.getService(ActivityTaskManagerInternal.class);
772         }
773 
774         @Override
getIPackageManager()775         public IPackageManager getIPackageManager() {
776             return mIPackageManager;
777         }
778 
779         @Override
getDevicePolicyManagerInternal()780         public DevicePolicyManagerInternal getDevicePolicyManagerInternal() {
781             return mDevicePolicyManagerInternal;
782         }
783 
784         @Override
sendBroadcastAsUser(Intent intent, UserHandle user)785         public void sendBroadcastAsUser(Intent intent, UserHandle user) {
786             // Robolectric's shadows do not currently support sendBroadcastAsUser.
787             final Set<Intent> broadcasts =
788                     mSentUserBroadcasts.containsKey(user)
789                             ? mSentUserBroadcasts.get(user)
790                             : new HashSet<>();
791             broadcasts.add(intent);
792             mSentUserBroadcasts.put(user, broadcasts);
793             mContext.sendBroadcastAsUser(intent, user);
794         }
795 
796         @Override
checkComponentPermission( String permission, int uid, int owningUid, boolean exported)797         public int checkComponentPermission(
798                 String permission, int uid, int owningUid, boolean exported) {
799             // ActivityManager#checkComponentPermission calls through to
800             // AppGlobals.getPackageManager()#checkUidPermission, which calls through to
801             // ShadowActivityThread with Robolectric. This method is currently not supported there.
802             return mContext.checkPermission(permission, Process.myPid(), uid);
803         }
804 
805         @Override
killUid(int uid)806         public void killUid(int uid) {
807             mKilledUids.add(uid);
808         }
809     }
810 }
811