1 /*
2  * Copyright (C) 2017 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.deviceandprofileowner;
18 
19 import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
20 import static android.app.admin.DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL;
21 import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
22 import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_SELECTION;
23 import static android.app.admin.DevicePolicyManager.DELEGATION_ENABLE_SYSTEM_APP;
24 import static android.app.admin.DevicePolicyManager.DELEGATION_NETWORK_LOGGING;
25 import static android.app.admin.DevicePolicyManager.DELEGATION_SECURITY_LOGGING;
26 import static android.app.admin.DevicePolicyManager.EXTRA_DELEGATION_SCOPES;
27 
28 import static com.google.common.truth.Truth.assertThat;
29 
30 import static org.junit.Assert.assertThrows;
31 
32 import android.app.admin.DevicePolicyManager;
33 import android.content.BroadcastReceiver;
34 import android.content.ComponentName;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.IntentFilter;
38 import android.os.Process;
39 import android.os.UserManager;
40 import android.test.MoreAsserts;
41 import android.util.Log;
42 
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Collections;
46 import java.util.List;
47 import java.util.concurrent.Semaphore;
48 import java.util.concurrent.TimeUnit;
49 
50 
51 /**
52  * Test that an app granted delegation scopes via {@link DevicePolicyManager#setDelegatedScopes} is
53  * notified of its new scopes by a broadcast.
54  */
55 public class DelegationTest extends BaseDeviceAdminTest {
56     private static final String TAG = "DelegationTest";
57 
58     private static final String DELEGATE_PKG = "com.android.cts.delegate";
59     private static final String DELEGATE_ACTIVITY_NAME =
60             DELEGATE_PKG + ".DelegatedScopesReceiverActivity";
61     private static final String DELEGATE_SERVICE_NAME =
62             DELEGATE_PKG + ".DelegatedScopesReceiverService";
63     private static final String TEST_PKG = "com.android.cts.apprestrictions.targetapp";
64 
65     // Broadcasts received from the delegate app.
66     private static final String ACTION_REPORT_SCOPES = "com.android.cts.delegate.report_scopes";
67     private static final String ACTION_RUNNING = "com.android.cts.delegate.running";
68 
69     // Semaphores to synchronize communication with delegate app.
70     private volatile String[] mReceivedScopes;
71     private Semaphore mReceivedScopeReportSemaphore;
72     private Semaphore mReceivedRunningSemaphore;
73 
74     // Receiver for incoming broadcasts from the delegate app.
75     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
76         @Override
77         public void onReceive(Context context, Intent intent) {
78             Log.v(TAG, "onReceive(): " + intent.getAction() + " on user " + Process.myUserHandle());
79             if (ACTION_REPORT_SCOPES.equals(intent.getAction())) {
80                 synchronized (DelegationTest.this) {
81                     mReceivedScopes = intent.getStringArrayExtra(EXTRA_DELEGATION_SCOPES);
82                     mReceivedScopeReportSemaphore.release();
83                 }
84             } else if (ACTION_RUNNING.equals(intent.getAction())) {
85                 synchronized (DelegationTest.this) {
86                     mReceivedRunningSemaphore.release();
87                 }
88             }
89         }
90     };
91 
92     @Override
setUp()93     public void setUp() throws Exception {
94         super.setUp();
95         mReceivedScopeReportSemaphore = new Semaphore(0);
96         mReceivedRunningSemaphore = new Semaphore(0);
97         mReceivedScopes = null;
98         IntentFilter filter = new IntentFilter();
99         filter.addAction(ACTION_REPORT_SCOPES);
100         filter.addAction(ACTION_RUNNING);
101         mContext.registerReceiver(mReceiver, filter);
102     }
103 
104     @Override
tearDown()105     public void tearDown() throws Exception {
106         mContext.unregisterReceiver(mReceiver);
107         mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT,
108                 TEST_PKG, Collections.emptyList());
109         mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT,
110                 DELEGATE_PKG, Collections.emptyList());
111         super.tearDown();
112     }
113 
testDelegateReceivesScopeChangedBroadcast()114     public void testDelegateReceivesScopeChangedBroadcast() throws InterruptedException {
115         if (UserManager.isHeadlessSystemUserMode()) {
116             // TODO(b/190627898): this test launched an activity to receive the broadcast from DPM,
117             // but headless system user cannot launch activity. To make things worse, the intent
118             // is only sent to registered receivers, so we cannot use the existing receivers from
119             // DpmWrapper, we would need to start a service on user 0 to receive the broadcast,
120             // which would require a lot of changes:
121             // - calling APIs / Shell commands to allow an app in the bg to start a service
122             // - add a "launchIntent()" method on DpmWrapper so the intent is launched by user 0
123             //
124             // It might not be worth to make these changes, but rather wait for the test refactoring
125             Log.i(TAG, "Skipping testDelegateReceivesScopeChangedBroadcast() on headless system "
126                     + "user mode");
127             return;
128         }
129 
130         // Prepare the scopes to be delegated.
131         final List<String> scopes = Arrays.asList(
132                 DELEGATION_CERT_INSTALL,
133                 DELEGATION_APP_RESTRICTIONS,
134                 DELEGATION_BLOCK_UNINSTALL,
135                 DELEGATION_ENABLE_SYSTEM_APP);
136 
137         // Start delegate so it can receive the scopes changed broadcast from DevicePolicyManager.
138         startAndWaitDelegateActivity();
139 
140         // Set the delegated scopes.
141         mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT, DELEGATE_PKG, scopes);
142 
143         // Wait until the delegate reports its new scopes.
144         String reportedScopes[] = waitReportedScopes();
145 
146         // Check that the reported scopes correspond to scopes we delegated.
147         assertNotNull("Received null scopes from delegate", reportedScopes);
148         MoreAsserts.assertContentsInAnyOrder("Delegated scopes do not match broadcasted scopes",
149                 scopes, reportedScopes);
150     }
151 
testCantDelegateToUninstalledPackage()152     public void testCantDelegateToUninstalledPackage() {
153         // Prepare the package name and scopes to be delegated.
154         final String NON_EXISTENT_PKG = "com.android.nonexistent.delegate";
155         final List<String> scopes = Arrays.asList(
156                 DELEGATION_CERT_INSTALL,
157                 DELEGATION_ENABLE_SYSTEM_APP);
158         try {
159             // Trying to delegate to non existent package should throw.
160             mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT,
161                     NON_EXISTENT_PKG, scopes);
162             fail("Should throw when delegating to non existent package");
163         } catch(IllegalArgumentException expected) {
164         }
165         // Assert no scopes were delegated.
166         assertTrue("Delegation scopes granted to non existent package", mDevicePolicyManager
167                 .getDelegatedScopes(ADMIN_RECEIVER_COMPONENT, NON_EXISTENT_PKG).isEmpty());
168     }
169 
testCanRetrieveDelegates()170     public void testCanRetrieveDelegates() {
171         final List<String> someScopes = Arrays.asList(
172                 DELEGATION_APP_RESTRICTIONS,
173                 DELEGATION_ENABLE_SYSTEM_APP);
174         final List<String> otherScopes = Arrays.asList(
175                 DELEGATION_BLOCK_UNINSTALL,
176                 DELEGATION_ENABLE_SYSTEM_APP);
177 
178         // In the beginning there are no delegates.
179         assertTrue("No delegates should be found", getDelegatePackages(DELEGATION_APP_RESTRICTIONS)
180                 .isEmpty());
181         assertTrue("No delegates should be found", getDelegatePackages(DELEGATION_BLOCK_UNINSTALL)
182                 .isEmpty());
183         assertTrue("No delegates should be found", getDelegatePackages(DELEGATION_ENABLE_SYSTEM_APP)
184                 .isEmpty());
185 
186         // After delegating scopes to two packages.
187         mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT,
188                 DELEGATE_PKG, someScopes);
189         mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT,
190                 TEST_PKG, otherScopes);
191 
192         // The expected delegates are returned.
193         assertTrue("Expected delegate not found", getDelegatePackages(DELEGATION_APP_RESTRICTIONS)
194                 .contains(DELEGATE_PKG));
195         assertTrue("Expected delegate not found", getDelegatePackages(DELEGATION_BLOCK_UNINSTALL)
196                 .contains(TEST_PKG));
197         assertTrue("Expected delegate not found", getDelegatePackages(DELEGATION_ENABLE_SYSTEM_APP)
198                 .contains(DELEGATE_PKG));
199         assertTrue("Expected delegate not found", getDelegatePackages(DELEGATION_ENABLE_SYSTEM_APP)
200                 .contains(TEST_PKG));
201 
202         // Packages are only returned in their recpective scopes.
203         assertFalse("Unexpected delegate package", getDelegatePackages(DELEGATION_APP_RESTRICTIONS)
204                 .contains(TEST_PKG));
205         assertFalse("Unexpected delegate package", getDelegatePackages(DELEGATION_BLOCK_UNINSTALL)
206                 .contains(DELEGATE_PKG));
207         assertFalse("Unexpected delegate package", getDelegatePackages(DELEGATION_CERT_INSTALL)
208                 .contains(DELEGATE_PKG));
209         assertFalse("Unexpected delegate package", getDelegatePackages(DELEGATION_CERT_INSTALL)
210                 .contains(TEST_PKG));
211     }
212 
testDeviceOwnerOrManagedPoOnlyDelegations()213     public void testDeviceOwnerOrManagedPoOnlyDelegations() throws Exception {
214         final String [] doOrManagedPoDelegations = { DELEGATION_NETWORK_LOGGING };
215         final boolean isDeviceOwner = mDevicePolicyManager.isDeviceOwnerApp(
216                 mContext.getPackageName());
217         final boolean isManagedProfileOwner = mDevicePolicyManager.getProfileOwner() != null
218                 && mDevicePolicyManager.isManagedProfile(ADMIN_RECEIVER_COMPONENT);
219         for (String scope : doOrManagedPoDelegations) {
220             if (isDeviceOwner || isManagedProfileOwner) {
221                 try {
222                     mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT, DELEGATE_PKG,
223                             Collections.singletonList(scope));
224                 } catch (SecurityException e) {
225                     fail("DO or managed PO fails to delegate " + scope + " exception: " + e);
226                     Log.e(TAG, "DO or managed PO fails to delegate " + scope, e);
227                 }
228             } else {
229                 assertThrows("PO not in a managed profile shouldn't be able to delegate " + scope,
230                         SecurityException.class,
231                         () -> mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT,
232                                 DELEGATE_PKG, Collections.singletonList(scope)));
233             }
234         }
235     }
236 
testDeviceOwnerOrOrgOwnedManagedPoOnlyDelegations()237     public void testDeviceOwnerOrOrgOwnedManagedPoOnlyDelegations() throws Exception {
238         final String [] doOrOrgOwnedManagedPoDelegations = { DELEGATION_SECURITY_LOGGING };
239         final boolean isDeviceOwner = mDevicePolicyManager.isDeviceOwnerApp(
240                 mContext.getPackageName());
241         final boolean isOrgOwnedManagedProfileOwner = mDevicePolicyManager.getProfileOwner() != null
242                 && mDevicePolicyManager.isManagedProfile(ADMIN_RECEIVER_COMPONENT)
243                 && mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile();
244         for (String scope : doOrOrgOwnedManagedPoDelegations) {
245             if (isDeviceOwner || isOrgOwnedManagedProfileOwner) {
246                 try {
247                     mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT, DELEGATE_PKG,
248                             Collections.singletonList(scope));
249                 } catch (SecurityException e) {
250                     fail("DO or organization-owned managed PO fails to delegate " + scope
251                             + " exception: " + e);
252                     Log.e(TAG, "DO or organization-owned managed PO fails to delegate " + scope, e);
253                 }
254             } else {
255                 assertThrows("PO not in an organization-owned managed profile shouldn't be able to "
256                         + "delegate " + scope,
257                         SecurityException.class,
258                         () -> mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT,
259                                 DELEGATE_PKG, Collections.singletonList(scope)));
260             }
261         }
262     }
263 
testExclusiveDelegations()264     public void testExclusiveDelegations() throws Exception {
265         final List<String> exclusiveDelegations = new ArrayList<>(Arrays.asList(
266                 DELEGATION_CERT_SELECTION));
267         if (mDevicePolicyManager.isDeviceOwnerApp(mContext.getPackageName())) {
268             exclusiveDelegations.add(DELEGATION_NETWORK_LOGGING);
269             exclusiveDelegations.add(DELEGATION_SECURITY_LOGGING);
270         }
271         for (String scope : exclusiveDelegations) {
272             testExclusiveDelegation(scope);
273         }
274     }
275 
testExclusiveDelegation(String scope)276     private void testExclusiveDelegation(String scope) throws Exception {
277 
278         mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT,
279                 DELEGATE_PKG, Collections.singletonList(scope));
280         // Set exclusive scope on TEST_PKG should lead to the scope being removed from the
281         // previous delegate DELEGATE_PKG
282         mDevicePolicyManager.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT,
283                 TEST_PKG, Collections.singletonList(scope));
284 
285 
286         assertThat(mDevicePolicyManager.getDelegatedScopes(ADMIN_RECEIVER_COMPONENT, TEST_PKG))
287                 .containsExactly(scope);
288         assertThat(mDevicePolicyManager.getDelegatedScopes(ADMIN_RECEIVER_COMPONENT, DELEGATE_PKG))
289                 .isEmpty();
290     }
291 
getDelegatePackages(String scope)292     private List<String> getDelegatePackages(String scope) {
293         List<String> packages = mDevicePolicyManager.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
294                 scope);
295         Log.d(TAG, "getDelegatePackages(" + scope + "): " + packages);
296         return packages;
297     }
298 
startAndWaitDelegateActivity()299     private void startAndWaitDelegateActivity() throws InterruptedException {
300         ComponentName componentName = new ComponentName(DELEGATE_PKG, DELEGATE_ACTIVITY_NAME);
301         Log.d(TAG, "Starting " + componentName + " on user " + Process.myUserHandle());
302         mContext.startActivity(new Intent()
303                 .setComponent(componentName)
304                 .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK));
305         assertTrue("DelegateApp did not start in time.",
306                 mReceivedRunningSemaphore.tryAcquire(10, TimeUnit.SECONDS));
307     }
308 
waitReportedScopes()309     private String[] waitReportedScopes() throws InterruptedException {
310         assertTrue("DelegateApp did not report scope in time.",
311                 mReceivedScopeReportSemaphore.tryAcquire(10, TimeUnit.SECONDS));
312         return mReceivedScopes;
313     }
314 }
315