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