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.cts.crossprofileappstest;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.fail;
22 
23 import android.app.AppOpsManager;
24 import android.app.admin.DevicePolicyManager;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.CrossProfileApps;
29 import android.content.pm.PackageManager;
30 import android.os.Binder;
31 import android.os.Bundle;
32 import android.os.UserHandle;
33 import android.os.UserManager;
34 
35 import androidx.test.InstrumentationRegistry;
36 import androidx.test.runner.AndroidJUnit4;
37 
38 import com.android.bedstead.nene.TestApis;
39 import com.android.bedstead.nene.permissions.PermissionContext;
40 
41 import org.junit.After;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 
45 import java.util.Collections;
46 
47 @RunWith(AndroidJUnit4.class)
48 public class CrossProfileAppsPermissionToInteractTest {
49     public static final String MANAGE_APP_OPS_MODES_PERMISSION =
50             "android.permission.MANAGE_APP_OPS_MODES";
51     public static final String INTERACT_ACROSS_PROFILES_PERMISSION =
52             "android.permission.INTERACT_ACROSS_PROFILES";
53     public static final String INTERACT_ACROSS_USERS_PERMISSION =
54             "android.permission.INTERACT_ACROSS_USERS";
55     public static final String INTERACT_ACROSS_USERS_FULL_PERMISSION =
56             "android.permission.INTERACT_ACROSS_USERS_FULL";
57     public static final String ACTION_MANAGE_CROSS_PROFILE_ACCESS =
58             "android.settings.MANAGE_CROSS_PROFILE_ACCESS";
59 
60     private static final ComponentName ADMIN_RECEIVER_COMPONENT =
61             new ComponentName(
62                     AdminReceiver.class.getPackage().getName(), AdminReceiver.class.getName());
63     private static final String PARAM_CROSS_PROFILE_PACKAGE = "crossProfilePackage";
64     private static final TestApis sTestApis = new TestApis();
65 
66     private final Context mContext = InstrumentationRegistry.getContext();
67     private final CrossProfileApps mCrossProfileApps =
68             mContext.getSystemService(CrossProfileApps.class);
69     private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
70 
71     @After
tearDown()72     public void tearDown() {
73         InstrumentationRegistry.getInstrumentation().getUiAutomation()
74                 .dropShellPermissionIdentity();
75     }
76 
77     @Test
testCanRequestInteractAcrossProfiles_returnsFalse()78     public void testCanRequestInteractAcrossProfiles_returnsFalse() {
79         assertThat(mCrossProfileApps.canRequestInteractAcrossProfiles()).isFalse();
80     }
81 
82     @Test
testCanRequestInteractAcrossProfiles_returnsTrue()83     public void testCanRequestInteractAcrossProfiles_returnsTrue() {
84         assertThat(mCrossProfileApps.canRequestInteractAcrossProfiles()).isTrue();
85     }
86 
87     @Test
testCanInteractAcrossProfiles_withAppOpEnabled_returnsTrue()88     public void testCanInteractAcrossProfiles_withAppOpEnabled_returnsTrue() {
89         setAppOpOnAllProfiles(AppOpsManager.MODE_ALLOWED);
90 
91         assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isTrue();
92     }
93 
94     @Test
testCanInteractAcrossProfiles_withCrossProfilesPermission_returnsTrue()95     public void testCanInteractAcrossProfiles_withCrossProfilesPermission_returnsTrue() {
96         // Ideally we want to grant the permission in the other profile instead of allowing the
97         // appop, however UiAutomation#adoptShellPermission can't be used for multiple UIDs.
98         setAppOpOnAllProfiles(AppOpsManager.MODE_ALLOWED, /* includeCallingProfile= */ false);
99         setAppOpOnCurrentProfile(AppOpsManager.MODE_IGNORED);
100 
101         try (PermissionContext p = sTestApis.permissions().withPermission(
102                 INTERACT_ACROSS_PROFILES_PERMISSION)) {
103             assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isTrue();
104         }
105     }
106 
107     @Test
testCanInteractAcrossProfiles_withCrossUsersPermission_returnsTrue()108     public void testCanInteractAcrossProfiles_withCrossUsersPermission_returnsTrue() {
109         // Ideally we want to grant the permission in the other profile instead of allowing the
110         // appop, however UiAutomation#adoptShellPermission can't be used for multiple UIDs.
111         setAppOpOnAllProfiles(AppOpsManager.MODE_ALLOWED, /* includeCallingProfile= */ false);
112         setAppOpOnCurrentProfile(AppOpsManager.MODE_IGNORED);
113 
114         try (PermissionContext p = sTestApis.permissions().withPermission(
115                 INTERACT_ACROSS_USERS_PERMISSION)) {
116             assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isTrue();
117         }
118     }
119 
120     @Test
testCanInteractAcrossProfiles_withCrossUsersFullPermission_returnsTrue()121     public void testCanInteractAcrossProfiles_withCrossUsersFullPermission_returnsTrue() {
122         // Ideally we want to grant the permission in the other profile instead of allowing the
123         // appop, however UiAutomation#adoptShellPermission can't be used for multiple UIDs.
124         setAppOpOnAllProfiles(AppOpsManager.MODE_ALLOWED, /* includeCallingProfile= */ false);
125         setAppOpOnCurrentProfile(AppOpsManager.MODE_IGNORED);
126 
127         try (PermissionContext p = sTestApis.permissions().withPermission(
128                 INTERACT_ACROSS_USERS_FULL_PERMISSION)) {
129             assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isTrue();
130         }
131     }
132 
133     @Test
testCanInteractAcrossProfiles_withAppOpDisabledOnCallingProfile_returnsFalse()134     public void testCanInteractAcrossProfiles_withAppOpDisabledOnCallingProfile_returnsFalse() {
135         setAppOpOnAllProfiles(AppOpsManager.MODE_ALLOWED, /* includeCallingProfile= */ false);
136         setAppOpOnCurrentProfile(AppOpsManager.MODE_IGNORED);
137 
138         assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isFalse();
139     }
140 
141     @Test
testCanInteractAcrossProfiles_withAppOpDisabledOnOtherProfiles_returnsFalse()142     public void testCanInteractAcrossProfiles_withAppOpDisabledOnOtherProfiles_returnsFalse() {
143         setAppOpOnAllProfiles(AppOpsManager.MODE_IGNORED, /* includeCallingProfile= */ false);
144         setAppOpOnCurrentProfile(AppOpsManager.MODE_ALLOWED);
145 
146         assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isFalse();
147     }
148 
149     @Test
testCanInteractAcrossProfiles_withNoOtherProfile_returnsFalse()150     public void testCanInteractAcrossProfiles_withNoOtherProfile_returnsFalse() {
151         setAppOpOnCurrentProfile(AppOpsManager.MODE_ALLOWED);
152 
153         assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isFalse();
154     }
155 
156     @Test
testCreateRequestInteractAcrossProfilesIntent_canRequestInteraction_returnsIntent()157     public void testCreateRequestInteractAcrossProfilesIntent_canRequestInteraction_returnsIntent() {
158         Intent intent = mCrossProfileApps.createRequestInteractAcrossProfilesIntent();
159 
160         assertThat(intent).isNotNull();
161         assertThat(intent.getAction()).isEqualTo(ACTION_MANAGE_CROSS_PROFILE_ACCESS);
162         assertThat(intent.getData()).isNotNull();
163         assertThat(intent.getData().getSchemeSpecificPart()).isEqualTo(mContext.getPackageName());
164     }
165 
166     @Test
testCreateRequestInteractAcrossProfilesIntent_canNotRequestInteraction_throwsSecurityException()167     public void testCreateRequestInteractAcrossProfilesIntent_canNotRequestInteraction_throwsSecurityException() {
168         try {
169             mCrossProfileApps.createRequestInteractAcrossProfilesIntent();
170         } catch (SecurityException e) {
171             return;
172         }
173         fail("Should throw a Security Exception");
174     }
175 
176     /**
177      * Calls {@link CrossProfileApps#createRequestInteractAcrossProfilesIntent()}. This can then be
178      * used by host-side tests.
179      */
180     @Test
testCreateRequestInteractAcrossProfilesIntent_noAsserts()181     public void testCreateRequestInteractAcrossProfilesIntent_noAsserts() {
182         mCrossProfileApps.createRequestInteractAcrossProfilesIntent();
183     }
184 
185     @Test
testSetCrossProfilePackages_noAsserts()186     public void testSetCrossProfilePackages_noAsserts() {
187         final DevicePolicyManager devicePolicyManager =
188                 (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
189         devicePolicyManager.setCrossProfilePackages(
190                 ADMIN_RECEIVER_COMPONENT, Collections.singleton(getCrossProfilePackage()));
191     }
192 
getCrossProfilePackage()193     private String getCrossProfilePackage() {
194         final Bundle testArguments = InstrumentationRegistry.getArguments();
195         if (testArguments.containsKey(PARAM_CROSS_PROFILE_PACKAGE)) {
196             try {
197                 return testArguments.getString(PARAM_CROSS_PROFILE_PACKAGE);
198             } catch (NumberFormatException ignore) {
199             }
200         }
201         fail("cross profile package param not found.");
202         return null;
203     }
204 
setAppOpOnCurrentProfile(int mode)205     private void setAppOpOnCurrentProfile(int mode) {
206         try (PermissionContext p = sTestApis.permissions().withPermission(
207                 MANAGE_APP_OPS_MODES_PERMISSION)) {
208             mAppOpsManager.setMode(AppOpsManager.OPSTR_INTERACT_ACROSS_PROFILES,
209                     Binder.getCallingUid(), mContext.getPackageName(), mode);
210         }
211     }
212 
setAppOpOnAllProfiles(int mode)213     private void setAppOpOnAllProfiles(int mode) {
214         setAppOpOnAllProfiles(mode, /* includeCallingProfile= */ true);
215     }
216 
setAppOpOnAllProfiles(int mode, boolean includeCallingProfile)217     private void setAppOpOnAllProfiles(int mode, boolean includeCallingProfile) {
218         try (PermissionContext p = sTestApis.permissions().withPermission(
219                 MANAGE_APP_OPS_MODES_PERMISSION, INTERACT_ACROSS_USERS_PERMISSION)) {
220             for (UserHandle profile : mContext.getSystemService(
221                     UserManager.class).getAllProfiles()) {
222                 if (!includeCallingProfile && profile.getIdentifier() == mContext.getUserId()) {
223                     continue;
224                 }
225                 try {
226                     final int uid = mContext.createContextAsUser(profile, /* flags= */ 0)
227                             .getPackageManager().getPackageUid(
228                                     mContext.getPackageName(), /* flags= */ 0);
229                     mAppOpsManager.setMode(AppOpsManager.OPSTR_INTERACT_ACROSS_PROFILES,
230                             uid, mContext.getPackageName(), mode);
231                 } catch (PackageManager.NameNotFoundException e) {
232                     // Do nothing
233                 }
234             }
235         }
236     }
237 }
238