1 /* 2 * Copyright (C) 2021 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.bedstead.nene.devicepolicy; 18 19 import static android.Manifest.permission.CREATE_USERS; 20 import static android.Manifest.permission.INTERACT_ACROSS_USERS; 21 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; 22 import static android.os.Build.VERSION.SDK_INT; 23 24 import static com.android.bedstead.nene.permissions.CommonPermissions.FORCE_DEVICE_POLICY_MANAGER_LOGS; 25 import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_DEVICE_ADMINS; 26 import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_PROFILE_AND_DEVICE_OWNERS; 27 import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_ROLE_HOLDERS; 28 import static com.android.bedstead.nene.utils.Versions.T; 29 30 import static org.junit.Assert.fail; 31 32 import android.annotation.TargetApi; 33 import android.app.admin.DevicePolicyManager; 34 import android.app.role.RoleManager; 35 import android.content.ComponentName; 36 import android.content.Intent; 37 import android.content.pm.PackageManager; 38 import android.content.pm.ResolveInfo; 39 import android.os.Build; 40 import android.util.Log; 41 42 import androidx.annotation.Nullable; 43 44 import com.android.bedstead.nene.TestApis; 45 import com.android.bedstead.nene.annotations.Experimental; 46 import com.android.bedstead.nene.exceptions.AdbException; 47 import com.android.bedstead.nene.exceptions.AdbParseException; 48 import com.android.bedstead.nene.exceptions.NeneException; 49 import com.android.bedstead.nene.packages.Package; 50 import com.android.bedstead.nene.permissions.PermissionContext; 51 import com.android.bedstead.nene.users.UserReference; 52 import com.android.bedstead.nene.utils.Poll; 53 import com.android.bedstead.nene.utils.Retry; 54 import com.android.bedstead.nene.utils.ShellCommand; 55 import com.android.bedstead.nene.utils.ShellCommandUtils; 56 import com.android.bedstead.nene.utils.Versions; 57 import com.android.compatibility.common.util.BlockingCallback; 58 59 import java.time.Duration; 60 import java.util.Collection; 61 import java.util.List; 62 import java.util.Map; 63 import java.util.Set; 64 65 66 /** 67 * Test APIs related to device policy. 68 */ 69 public final class DevicePolicy { 70 71 public static final DevicePolicy sInstance = new DevicePolicy(); 72 73 private static final String LOG_TAG = "DevicePolicy"; 74 75 private final AdbDevicePolicyParser mParser; 76 77 private DeviceOwner mCachedDeviceOwner; 78 private Map<UserReference, ProfileOwner> mCachedProfileOwners; 79 DevicePolicy()80 private DevicePolicy() { 81 mParser = AdbDevicePolicyParser.get(SDK_INT); 82 } 83 84 /** 85 * Set the profile owner for a given {@link UserReference}. 86 */ setProfileOwner(UserReference user, ComponentName profileOwnerComponent)87 public ProfileOwner setProfileOwner(UserReference user, ComponentName profileOwnerComponent) { 88 if (user == null || profileOwnerComponent == null) { 89 throw new NullPointerException(); 90 } 91 92 ShellCommand.Builder command = 93 ShellCommand.builderForUser(user, "dpm set-profile-owner") 94 .addOperand(profileOwnerComponent.flattenToShortString()) 95 .validate(ShellCommandUtils::startsWithSuccess); 96 97 // TODO(b/187925230): If it fails, we check for terminal failure states - and if not 98 // we retry because if the profile owner was recently removed, it can take some time 99 // to be allowed to set it again 100 try { 101 Retry.logic(command::execute) 102 .terminalException((ex) -> { 103 if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) { 104 return false; // Just retry on old versions as we don't have stderr 105 } 106 if (ex instanceof AdbException) { 107 String error = ((AdbException) ex).error(); 108 if (error.contains("is being removed")) { 109 return false; 110 } 111 } 112 113 // Assume all other errors are terminal 114 return true; 115 }) 116 .timeout(Duration.ofMinutes(5)) 117 .run(); 118 } catch (Throwable e) { 119 throw new NeneException("Could not set profile owner for user " 120 + user + " component " + profileOwnerComponent, e); 121 } 122 123 Poll.forValue("Profile Owner", () -> TestApis.devicePolicy().getProfileOwner(user)) 124 .toNotBeNull() 125 .errorOnFail() 126 .await(); 127 128 return new ProfileOwner(user, 129 TestApis.packages().find( 130 profileOwnerComponent.getPackageName()), profileOwnerComponent); 131 } 132 133 /** 134 * Get the profile owner for the instrumented user. 135 */ getProfileOwner()136 public ProfileOwner getProfileOwner() { 137 return getProfileOwner(TestApis.users().instrumented()); 138 } 139 140 /** 141 * Get the profile owner for a given {@link UserReference}. 142 */ getProfileOwner(UserReference user)143 public ProfileOwner getProfileOwner(UserReference user) { 144 if (user == null) { 145 throw new NullPointerException(); 146 } 147 fillCache(); 148 return mCachedProfileOwners.get(user); 149 } 150 151 /** 152 * Set the device owner. 153 */ setDeviceOwner(ComponentName deviceOwnerComponent)154 public DeviceOwner setDeviceOwner(ComponentName deviceOwnerComponent) { 155 if (deviceOwnerComponent == null) { 156 throw new NullPointerException(); 157 } 158 159 if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) { 160 return setDeviceOwnerPreS(deviceOwnerComponent); 161 } 162 163 DevicePolicyManager devicePolicyManager = 164 TestApis.context().instrumentedContext() 165 .getSystemService(DevicePolicyManager.class); 166 UserReference user = TestApis.users().system(); 167 168 boolean dpmUserSetupComplete = user.getSetupComplete(); 169 Boolean currentUserSetupComplete = null; 170 171 try { 172 user.setSetupComplete(false); 173 174 try (PermissionContext p = 175 TestApis.permissions().withPermission( 176 MANAGE_PROFILE_AND_DEVICE_OWNERS, MANAGE_DEVICE_ADMINS, 177 INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, CREATE_USERS)) { 178 179 // TODO(b/187925230): If it fails, we check for terminal failure states - and if not 180 // we retry because if the DO/PO was recently removed, it can take some time 181 // to be allowed to set it again 182 Retry.logic(() -> { 183 devicePolicyManager.setActiveAdmin(deviceOwnerComponent, 184 /* refreshing= */ true, user.id()); 185 setDeviceOwnerOnly(devicePolicyManager, 186 deviceOwnerComponent, "Nene", user.id()); 187 }).terminalException((e) -> checkForTerminalDeviceOwnerFailures( 188 user, deviceOwnerComponent, /* allowAdditionalUsers= */ true)) 189 .timeout(Duration.ofMinutes(5)) 190 .run(); 191 } catch (Throwable e) { 192 throw new NeneException("Error setting device owner", e); 193 } 194 } finally { 195 user.setSetupComplete(dpmUserSetupComplete); 196 if (currentUserSetupComplete != null) { 197 TestApis.users().current().setSetupComplete(currentUserSetupComplete); 198 } 199 } 200 201 Package deviceOwnerPackage = TestApis.packages().find( 202 deviceOwnerComponent.getPackageName()); 203 204 Poll.forValue("Device Owner", () -> TestApis.devicePolicy().getDeviceOwner()) 205 .toNotBeNull() 206 .errorOnFail() 207 .await(); 208 209 return new DeviceOwner(user, deviceOwnerPackage, deviceOwnerComponent); 210 } 211 212 /** 213 * Set Device Owner without changing any other device state. 214 * 215 * <p>This is used instead of {@link DevicePolicyManager#setDeviceOwner(ComponentName)} directly 216 * because on S_V2 and above, that method can also set profile owners and install packages in 217 * some circumstances. 218 */ setDeviceOwnerOnly(DevicePolicyManager devicePolicyManager, ComponentName component, String name, int deviceOwnerUserId)219 private void setDeviceOwnerOnly(DevicePolicyManager devicePolicyManager, 220 ComponentName component, String name, int deviceOwnerUserId) { 221 if (Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S_V2)) { 222 devicePolicyManager.setDeviceOwnerOnly(component, name, deviceOwnerUserId); 223 } else { 224 devicePolicyManager.setDeviceOwner(component, name, deviceOwnerUserId); 225 } 226 } 227 228 /** 229 * Resets organization ID via @TestApi. 230 * @param user whose organization ID to clear 231 */ clearOrganizationId(UserReference user)232 public void clearOrganizationId(UserReference user) { 233 try (PermissionContext p = 234 TestApis.permissions().withPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)) { 235 DevicePolicyManager devicePolicyManager = 236 TestApis.context().instrumentedContextAsUser(user) 237 .getSystemService(DevicePolicyManager.class); 238 devicePolicyManager.clearOrganizationId(); 239 } 240 } 241 setDeviceOwnerPreS(ComponentName deviceOwnerComponent)242 private DeviceOwner setDeviceOwnerPreS(ComponentName deviceOwnerComponent) { 243 UserReference user = TestApis.users().system(); 244 245 ShellCommand.Builder command = ShellCommand.builderForUser( 246 user, "dpm set-device-owner") 247 .addOperand(deviceOwnerComponent.flattenToShortString()) 248 .validate(ShellCommandUtils::startsWithSuccess); 249 // TODO(b/187925230): If it fails, we check for terminal failure states - and if not 250 // we retry because if the device owner was recently removed, it can take some time 251 // to be allowed to set it again 252 253 try { 254 Retry.logic(command::execute) 255 .terminalException((e) -> checkForTerminalDeviceOwnerFailures( 256 user, deviceOwnerComponent, /* allowAdditionalUsers= */ false)) 257 .timeout(Duration.ofMinutes(5)) 258 .run(); 259 } catch (Throwable e) { 260 throw new NeneException("Error setting device owner", e); 261 } 262 263 return new DeviceOwner(user, 264 TestApis.packages().find( 265 deviceOwnerComponent.getPackageName()), deviceOwnerComponent); 266 } 267 checkForTerminalDeviceOwnerFailures( UserReference user, ComponentName deviceOwnerComponent, boolean allowAdditionalUsers)268 private boolean checkForTerminalDeviceOwnerFailures( 269 UserReference user, ComponentName deviceOwnerComponent, boolean allowAdditionalUsers) { 270 DeviceOwner deviceOwner = getDeviceOwner(); 271 if (deviceOwner != null) { 272 // TODO(scottjonathan): Should we actually fail here if the component name is the 273 // same? 274 275 throw new NeneException( 276 "Could not set device owner for user " + user 277 + " as a device owner is already set: " + deviceOwner); 278 } 279 280 Package pkg = TestApis.packages().find( 281 deviceOwnerComponent.getPackageName()); 282 if (!TestApis.packages().installedForUser(user).contains(pkg)) { 283 throw new NeneException( 284 "Could not set device owner for user " + user 285 + " as the package " + pkg + " is not installed"); 286 } 287 288 if (!componentCanBeSetAsDeviceAdmin(deviceOwnerComponent, user)) { 289 throw new NeneException("Could not set device owner for user " 290 + user + " as component " + deviceOwnerComponent + " is not valid"); 291 } 292 293 if (!allowAdditionalUsers) { 294 Collection<UserReference> users = TestApis.users().all(); 295 296 if (users.size() > 1) { 297 throw new NeneException("Could not set device owner for user " 298 + user + " as there are already additional users on the device: " + users); 299 } 300 301 } 302 // TODO(scottjonathan): Check accounts 303 304 return false; 305 } 306 componentCanBeSetAsDeviceAdmin(ComponentName component, UserReference user)307 private boolean componentCanBeSetAsDeviceAdmin(ComponentName component, UserReference user) { 308 PackageManager packageManager = 309 TestApis.context().instrumentedContext().getPackageManager(); 310 Intent intent = new Intent("android.app.action.DEVICE_ADMIN_ENABLED"); 311 intent.setComponent(component); 312 313 try (PermissionContext p = 314 TestApis.permissions().withPermission(INTERACT_ACROSS_USERS_FULL)) { 315 List<ResolveInfo> r = 316 packageManager.queryBroadcastReceiversAsUser( 317 intent, /* flags= */ 0, user.userHandle()); 318 return (!r.isEmpty()); 319 } 320 } 321 322 /** 323 * Get the device owner. 324 */ 325 @Nullable getDeviceOwner()326 public DeviceOwner getDeviceOwner() { 327 fillCache(); 328 return mCachedDeviceOwner; 329 } 330 fillCache()331 private void fillCache() { 332 int retries = 5; 333 while (true) { 334 try { 335 // TODO: Replace use of adb on supported versions of Android 336 String devicePolicyDumpsysOutput = 337 ShellCommand.builder("dumpsys device_policy").execute(); 338 AdbDevicePolicyParser.ParseResult result = mParser.parse(devicePolicyDumpsysOutput); 339 340 mCachedDeviceOwner = result.mDeviceOwner; 341 mCachedProfileOwners = result.mProfileOwners; 342 343 return; 344 } catch (AdbParseException e) { 345 if (e.adbOutput().contains("DUMP TIMEOUT") && retries-- > 0) { 346 // Sometimes this call times out - just retry 347 Log.e(LOG_TAG, "Dump timeout when filling cache, retrying", e); 348 } else { 349 throw new NeneException("Error filling cache", e); 350 } 351 } catch (AdbException e) { 352 throw new NeneException("Error filling cache", e); 353 } 354 } 355 } 356 357 /** See {@link android.app.admin.DevicePolicyManager#getPolicyExemptApps()}. */ 358 @Experimental getPolicyExemptApps()359 public Set<String> getPolicyExemptApps() { 360 try (PermissionContext p = TestApis.permissions().withPermission(MANAGE_DEVICE_ADMINS)) { 361 return TestApis.context() 362 .instrumentedContext() 363 .getSystemService(DevicePolicyManager.class) 364 .getPolicyExemptApps(); 365 } 366 } 367 368 @Experimental forceNetworkLogs()369 public void forceNetworkLogs() { 370 try (PermissionContext p = TestApis.permissions().withPermission(FORCE_DEVICE_POLICY_MANAGER_LOGS)) { 371 long throttle = TestApis.context() 372 .instrumentedContext() 373 .getSystemService(DevicePolicyManager.class) 374 .forceNetworkLogs(); 375 376 if (throttle == -1) { 377 throw new NeneException("Error forcing network logs: returned -1"); 378 } 379 if (throttle == 0) { 380 return; 381 } 382 try { 383 Thread.sleep(throttle); 384 } catch (InterruptedException e) { 385 throw new NeneException("Error waiting for network log throttle", e); 386 } 387 388 forceNetworkLogs(); 389 } 390 } 391 392 /** 393 * Sets the provided {@code packageName} as a device policy management role holder. 394 */ 395 @TargetApi(Build.VERSION_CODES.TIRAMISU) 396 @Experimental setDevicePolicyManagementRoleHolder(String packageName)397 public void setDevicePolicyManagementRoleHolder(String packageName) 398 throws InterruptedException { 399 if (!Versions.meetsMinimumSdkVersionRequirement(T)) { 400 return; 401 } 402 try (PermissionContext p = TestApis.permissions().withPermission( 403 MANAGE_ROLE_HOLDERS)) { 404 DefaultBlockingCallback blockingCallback = new DefaultBlockingCallback(); 405 RoleManager roleManager = TestApis.context().instrumentedContext() 406 .getSystemService(RoleManager.class); 407 TestApis.roles().setBypassingRoleQualification(true); 408 roleManager.addRoleHolderAsUser( 409 RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, 410 packageName, 411 /* flags= */ 0, 412 TestApis.context().instrumentationContext().getUser(), 413 TestApis.context().instrumentedContext().getMainExecutor(), 414 blockingCallback::triggerCallback); 415 416 boolean success = blockingCallback.await(); 417 if (!success) { 418 fail("Could not set role holder of " 419 + RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT + "."); 420 } 421 } 422 } 423 424 /** 425 * Unsets the provided {@code packageName} as a device policy management role holder. 426 */ 427 @TargetApi(Build.VERSION_CODES.TIRAMISU) 428 @Experimental unsetDevicePolicyManagementRoleHolder(String packageName)429 public void unsetDevicePolicyManagementRoleHolder(String packageName) 430 throws InterruptedException { 431 if (!Versions.meetsMinimumSdkVersionRequirement(T)) { 432 return; 433 } 434 try (PermissionContext p = TestApis.permissions().withPermission( 435 MANAGE_ROLE_HOLDERS)) { 436 DefaultBlockingCallback blockingCallback = new DefaultBlockingCallback(); 437 RoleManager roleManager = TestApis.context().instrumentedContext() 438 .getSystemService(RoleManager.class); 439 roleManager.removeRoleHolderAsUser( 440 RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, 441 packageName, 442 /* flags= */ 0, 443 TestApis.context().instrumentationContext().getUser(), 444 TestApis.context().instrumentedContext().getMainExecutor(), 445 blockingCallback::triggerCallback); 446 TestApis.roles().setBypassingRoleQualification(false); 447 448 boolean success = blockingCallback.await(); 449 if (!success) { 450 fail("Failed to clear the role holder of " 451 + RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT + "."); 452 } 453 } 454 } 455 456 private static class DefaultBlockingCallback extends BlockingCallback<Boolean> { triggerCallback(Boolean success)457 public void triggerCallback(Boolean success) { 458 callbackTriggered(success); 459 } 460 } 461 } 462