1 /* 2 * Copyright 2014, 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.managedprovisioning.common; 18 19 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; 20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE; 21 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; 22 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE; 23 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER; 24 import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC; 25 import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED; 26 import static java.nio.charset.StandardCharsets.UTF_8; 27 28 import android.accounts.Account; 29 import android.accounts.AccountManager; 30 import android.accounts.AccountManagerFuture; 31 import android.accounts.AuthenticatorException; 32 import android.accounts.OperationCanceledException; 33 import android.app.admin.DevicePolicyManager; 34 import android.content.ComponentName; 35 import android.content.Context; 36 import android.content.Intent; 37 import android.content.pm.ActivityInfo; 38 import android.content.pm.ApplicationInfo; 39 import android.content.pm.IPackageManager; 40 import android.content.pm.PackageInfo; 41 import android.content.pm.PackageManager; 42 import android.content.pm.PackageManager.NameNotFoundException; 43 import android.content.pm.ResolveInfo; 44 import android.content.pm.UserInfo; 45 import android.graphics.Color; 46 import android.net.ConnectivityManager; 47 import android.net.NetworkInfo; 48 import android.net.wifi.WifiManager; 49 import android.nfc.NdefMessage; 50 import android.nfc.NdefRecord; 51 import android.nfc.NfcAdapter; 52 import android.os.Build; 53 import android.os.Bundle; 54 import android.os.Parcelable; 55 import android.os.Process; 56 import android.os.RemoteException; 57 import android.os.ServiceManager; 58 import android.os.SystemProperties; 59 import android.os.UserHandle; 60 import android.os.UserManager; 61 import android.os.storage.StorageManager; 62 import android.provider.Settings.Global; 63 import android.provider.Settings.Secure; 64 import android.text.TextUtils; 65 import android.util.Base64; 66 67 import java.io.IOException; 68 import java.lang.Integer; 69 import java.lang.Math; 70 import java.lang.String; 71 import java.nio.charset.StandardCharsets; 72 import java.util.HashSet; 73 import java.util.List; 74 import java.util.Set; 75 import java.util.concurrent.TimeUnit; 76 77 import com.android.internal.annotations.VisibleForTesting; 78 import com.android.managedprovisioning.FinalizationActivity; 79 import com.android.managedprovisioning.ProvisionLogger; 80 import com.android.managedprovisioning.TrampolineActivity; 81 import com.android.managedprovisioning.model.ProvisioningParams; 82 import com.android.managedprovisioning.model.PackageDownloadInfo; 83 84 /** 85 * Class containing various auxiliary methods. 86 */ 87 public class Utils { 88 private static final int ACCOUNT_COPY_TIMEOUT_SECONDS = 60 * 3; // 3 minutes 89 90 private static final int THRESHOLD_BRIGHT_COLOR = 160; // A color needs a brightness of at least 91 // this value to be considered bright. (brightness being between 0 and 255). Utils()92 public Utils() {} 93 94 /** 95 * Returns the currently installed system apps on a given user. 96 * 97 * <p>Calls into the {@link IPackageManager} to retrieve all installed packages on the given 98 * user and returns the package names of all system apps. 99 * 100 * @param ipm an {@link IPackageManager} object 101 * @param userId the id of the user we are interested in 102 */ getCurrentSystemApps(IPackageManager ipm, int userId)103 public Set<String> getCurrentSystemApps(IPackageManager ipm, int userId) { 104 Set<String> apps = new HashSet<String>(); 105 List<ApplicationInfo> aInfos = null; 106 try { 107 aInfos = ipm.getInstalledApplications( 108 PackageManager.GET_UNINSTALLED_PACKAGES, userId).getList(); 109 } catch (RemoteException neverThrown) { 110 ProvisionLogger.loge("This should not happen.", neverThrown); 111 } 112 for (ApplicationInfo aInfo : aInfos) { 113 if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 114 apps.add(aInfo.packageName); 115 } 116 } 117 return apps; 118 } 119 120 /** 121 * Disables a given component in a given user. 122 * 123 * @param toDisable the component that should be disabled 124 * @param userId the id of the user where the component should be disabled. 125 */ disableComponent(ComponentName toDisable, int userId)126 public void disableComponent(ComponentName toDisable, int userId) { 127 setComponentEnabledSetting( 128 IPackageManager.Stub.asInterface(ServiceManager.getService("package")), 129 toDisable, 130 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 131 userId); 132 } 133 134 /** 135 * Enables a given component in a given user. 136 * 137 * @param toEnable the component that should be enabled 138 * @param userId the id of the user where the component should be disabled. 139 */ enableComponent(ComponentName toEnable, int userId)140 public void enableComponent(ComponentName toEnable, int userId) { 141 setComponentEnabledSetting( 142 IPackageManager.Stub.asInterface(ServiceManager.getService("package")), 143 toEnable, 144 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 145 userId); 146 } 147 148 /** 149 * Disables a given component in a given user. 150 * 151 * @param ipm an {@link IPackageManager} object 152 * @param toDisable the component that should be disabled 153 * @param userId the id of the user where the component should be disabled. 154 */ 155 @VisibleForTesting setComponentEnabledSetting(IPackageManager ipm, ComponentName toDisable, int enabledSetting, int userId)156 void setComponentEnabledSetting(IPackageManager ipm, ComponentName toDisable, 157 int enabledSetting, int userId) { 158 try { 159 ipm.setComponentEnabledSetting(toDisable, 160 enabledSetting, PackageManager.DONT_KILL_APP, 161 userId); 162 } catch (RemoteException neverThrown) { 163 ProvisionLogger.loge("This should not happen.", neverThrown); 164 } catch (Exception e) { 165 ProvisionLogger.logw("Component not found, not changing enabled setting: " 166 + toDisable.toShortString()); 167 } 168 } 169 170 /** 171 * Check the validity of the admin component name supplied, or try to infer this componentName 172 * from the package. 173 * 174 * We are supporting lookup by package name for legacy reasons. 175 * 176 * If mdmComponentName is supplied (not null): 177 * mdmPackageName is ignored. 178 * Check that the package of mdmComponentName is installed, that mdmComponentName is a 179 * receiver in this package, and return it. The receiver can be in disabled state. 180 * 181 * Otherwise: 182 * mdmPackageName must be supplied (not null). 183 * Check that this package is installed, try to infer a potential device admin in this package, 184 * and return it. 185 */ 186 // TODO: Add unit tests findDeviceAdmin(String mdmPackageName, ComponentName mdmComponentName, Context c)187 public ComponentName findDeviceAdmin(String mdmPackageName, 188 ComponentName mdmComponentName, Context c) throws IllegalProvisioningArgumentException { 189 if (mdmComponentName != null) { 190 mdmPackageName = mdmComponentName.getPackageName(); 191 } 192 if (mdmPackageName == null) { 193 throw new IllegalProvisioningArgumentException("Neither the package name nor the" 194 + " component name of the admin are supplied"); 195 } 196 PackageInfo pi; 197 try { 198 pi = c.getPackageManager().getPackageInfo(mdmPackageName, 199 PackageManager.GET_RECEIVERS | PackageManager.MATCH_DISABLED_COMPONENTS); 200 } catch (NameNotFoundException e) { 201 throw new IllegalProvisioningArgumentException("Mdm "+ mdmPackageName 202 + " is not installed. ", e); 203 } 204 if (mdmComponentName != null) { 205 // If the component was specified in the intent: check that it is in the manifest. 206 checkAdminComponent(mdmComponentName, pi); 207 return mdmComponentName; 208 } else { 209 // Otherwise: try to find a potential device admin in the manifest. 210 return findDeviceAdminInPackage(mdmPackageName, pi); 211 } 212 } 213 214 /** 215 * Verifies that an admin component is part of a given package. 216 * 217 * @param mdmComponentName the admin component to be checked 218 * @param pi the {@link PackageInfo} of the package to be checked. 219 * 220 * @throws IllegalProvisioningArgumentException if the given component is not part of the 221 * package 222 */ checkAdminComponent(ComponentName mdmComponentName, PackageInfo pi)223 private void checkAdminComponent(ComponentName mdmComponentName, PackageInfo pi) 224 throws IllegalProvisioningArgumentException{ 225 for (ActivityInfo ai : pi.receivers) { 226 if (mdmComponentName.getClassName().equals(ai.name)) { 227 return; 228 } 229 } 230 throw new IllegalProvisioningArgumentException("The component " + mdmComponentName 231 + " cannot be found"); 232 } 233 findDeviceAdminInPackage(String mdmPackageName, PackageInfo pi)234 private ComponentName findDeviceAdminInPackage(String mdmPackageName, PackageInfo pi) 235 throws IllegalProvisioningArgumentException { 236 ComponentName mdmComponentName = null; 237 for (ActivityInfo ai : pi.receivers) { 238 if (!TextUtils.isEmpty(ai.permission) && 239 ai.permission.equals(android.Manifest.permission.BIND_DEVICE_ADMIN)) { 240 if (mdmComponentName != null) { 241 throw new IllegalProvisioningArgumentException("There are several " 242 + "device admins in " + mdmPackageName + " but no one in specified"); 243 } else { 244 mdmComponentName = new ComponentName(mdmPackageName, ai.name); 245 } 246 } 247 } 248 if (mdmComponentName == null) { 249 throw new IllegalProvisioningArgumentException("There are no device admins in" 250 + mdmPackageName); 251 } 252 return mdmComponentName; 253 } 254 255 /** 256 * Returns whether the current user is the system user. 257 */ isCurrentUserSystem()258 public boolean isCurrentUserSystem() { 259 return UserHandle.myUserId() == UserHandle.USER_SYSTEM; 260 } 261 262 /** 263 * Returns whether the device is currently managed. 264 */ isDeviceManaged(Context context)265 public boolean isDeviceManaged(Context context) { 266 DevicePolicyManager dpm = 267 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); 268 return dpm.isDeviceManaged(); 269 } 270 271 /** 272 * Returns whether the calling user is a managed profile. 273 */ isManagedProfile(Context context)274 public boolean isManagedProfile(Context context) { 275 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 276 UserInfo user = um.getUserInfo(UserHandle.myUserId()); 277 return user != null ? user.isManagedProfile() : false; 278 } 279 280 /** 281 * Returns true if the given package requires an update. 282 * 283 * <p>There are two cases where an update is required: 284 * 1. The package is not currently present on the device. 285 * 2. The package is present, but the version is below the minimum supported version. 286 * 287 * @param packageName the package to be checked for updates 288 * @param minSupportedVersion the minimum supported version 289 * @param context a {@link Context} object 290 */ packageRequiresUpdate(String packageName, int minSupportedVersion, Context context)291 public boolean packageRequiresUpdate(String packageName, int minSupportedVersion, 292 Context context) { 293 try { 294 PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0); 295 // Always download packages if no minimum version given. 296 if (minSupportedVersion != PackageDownloadInfo.DEFAULT_MINIMUM_VERSION 297 && packageInfo.versionCode >= minSupportedVersion) { 298 return false; 299 } 300 } catch (NameNotFoundException e) { 301 // Package not on device. 302 } 303 304 return true; 305 } 306 307 /** 308 * Transforms a string into a byte array. 309 * 310 * @param s the string to be transformed 311 */ stringToByteArray(String s)312 public byte[] stringToByteArray(String s) 313 throws NumberFormatException { 314 try { 315 return Base64.decode(s, Base64.URL_SAFE); 316 } catch (IllegalArgumentException e) { 317 throw new NumberFormatException("Incorrect format. Should be Url-safe Base64 encoded."); 318 } 319 } 320 321 /** 322 * Transforms a byte array into a string. 323 * 324 * @param bytes the byte array to be transformed 325 */ byteArrayToString(byte[] bytes)326 public String byteArrayToString(byte[] bytes) { 327 return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP); 328 } 329 330 /** 331 * Sets user setup complete on a given user. 332 * 333 * <p>This will set USER_SETUP_COMPLETE to 1 on the given user. 334 */ markUserSetupComplete(Context context, int userId)335 public void markUserSetupComplete(Context context, int userId) { 336 ProvisionLogger.logd("Setting USER_SETUP_COMPLETE to 1 for user " + userId); 337 Secure.putIntForUser(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, userId); 338 } 339 340 /** 341 * Returns whether USER_SETUP_COMPLETE is set on the calling user. 342 */ isUserSetupCompleted(Context context)343 public boolean isUserSetupCompleted(Context context) { 344 return Secure.getInt(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 0) != 0; 345 } 346 347 /** 348 * Returns whether DEVICE_PROVISIONED is set. 349 */ isDeviceProvisioned(Context context)350 public boolean isDeviceProvisioned(Context context) { 351 return Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0; 352 } 353 354 /** 355 * Set the current users userProvisioningState depending on the following factors: 356 * <ul> 357 * <li>We're setting up a managed-profile - need to set state on two users.</li> 358 * <li>User-setup has previously been completed or not - skip states relating to 359 * communicating with setup-wizard</li> 360 * <li>DPC requested we skip the rest of setup-wizard.</li> 361 * </ul> 362 * 363 * @param context a {@link Context} object 364 * @param params configuration for current provisioning attempt 365 */ 366 // TODO: Add unit tests markUserProvisioningStateInitiallyDone(Context context, ProvisioningParams params)367 public void markUserProvisioningStateInitiallyDone(Context context, 368 ProvisioningParams params) { 369 int currentUserId = UserHandle.myUserId(); 370 int managedProfileUserId = UserHandle.USER_NULL; 371 DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); 372 373 // new provisioning state for current user, if non-null 374 Integer newState = null; 375 // New provisioning state for managed-profile of current user, if non-null. 376 Integer newProfileState = null; 377 378 boolean userSetupCompleted = isUserSetupCompleted(context); 379 if (params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) { 380 // Managed profiles are a special case as two users are involved. 381 managedProfileUserId = getManagedProfile(context).getIdentifier(); 382 if (userSetupCompleted) { 383 // SUW on current user is complete, so nothing much to do beyond indicating we're 384 // all done. 385 newProfileState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED; 386 } else { 387 // We're still in SUW, so indicate that a managed-profile was setup on current user, 388 // and that we're awaiting finalization on both. 389 newState = DevicePolicyManager.STATE_USER_PROFILE_COMPLETE; 390 newProfileState = DevicePolicyManager.STATE_USER_SETUP_COMPLETE; 391 } 392 } else if (userSetupCompleted) { 393 // User setup was previously completed this is an unexpected case. 394 ProvisionLogger.logw("user_setup_complete set, but provisioning was started"); 395 } else if (params.skipUserSetup) { 396 // DPC requested setup-wizard is skipped, indicate this to SUW. 397 newState = DevicePolicyManager.STATE_USER_SETUP_COMPLETE; 398 } else { 399 // DPC requested setup-wizard is not skipped, indicate this to SUW. 400 newState = DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE; 401 } 402 403 if (newState != null) { 404 setUserProvisioningState(dpm, newState, currentUserId); 405 } 406 if (newProfileState != null) { 407 setUserProvisioningState(dpm, newProfileState, managedProfileUserId); 408 } 409 if (!userSetupCompleted) { 410 // We expect a PROVISIONING_FINALIZATION intent to finish setup if we're still in 411 // user-setup. 412 FinalizationActivity.storeProvisioningParams(context, params); 413 } 414 } 415 416 /** 417 * Finalize the current users userProvisioningState depending on the following factors: 418 * <ul> 419 * <li>We're setting up a managed-profile - need to set state on two users.</li> 420 * </ul> 421 * 422 * @param context a {@link Context} object 423 * @param params configuration for current provisioning attempt - if null (because 424 * ManagedProvisioning wasn't used for first phase of provisioning) aassumes we 425 * can just mark current user as being in finalized provisioning state 426 */ 427 // TODO: Add unit tests markUserProvisioningStateFinalized(Context context, ProvisioningParams params)428 public void markUserProvisioningStateFinalized(Context context, 429 ProvisioningParams params) { 430 int currentUserId = UserHandle.myUserId(); 431 int managedProfileUserId = -1; 432 DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); 433 Integer newState = null; 434 Integer newProfileState = null; 435 436 if (params != null && params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) { 437 // Managed profiles are a special case as two users are involved. 438 managedProfileUserId = getManagedProfile(context).getIdentifier(); 439 440 newState = DevicePolicyManager.STATE_USER_UNMANAGED; 441 newProfileState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED; 442 } else { 443 newState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED; 444 } 445 446 if (newState != null) { 447 setUserProvisioningState(dpm, newState, currentUserId); 448 } 449 if (newProfileState != null) { 450 setUserProvisioningState(dpm, newProfileState, managedProfileUserId); 451 } 452 } 453 setUserProvisioningState(DevicePolicyManager dpm, int state, int userId)454 private void setUserProvisioningState(DevicePolicyManager dpm, int state, int userId) { 455 ProvisionLogger.logi("Setting userProvisioningState for user " + userId + " to: " + state); 456 dpm.setUserProvisioningState(state, userId); 457 } 458 459 /** 460 * Returns the first existing managed profile if any present, null otherwise. 461 * 462 * <p>Note that we currently only support one managed profile per device. 463 */ 464 // TODO: Add unit tests getManagedProfile(Context context)465 public UserHandle getManagedProfile(Context context) { 466 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 467 int currentUserId = userManager.getUserHandle(); 468 List<UserInfo> userProfiles = userManager.getProfiles(currentUserId); 469 for (UserInfo profile : userProfiles) { 470 if (profile.isManagedProfile()) { 471 return new UserHandle(profile.id); 472 } 473 } 474 return null; 475 } 476 477 /** 478 * Returns the user id of an already existing managed profile or -1 if none exists. 479 */ 480 // TODO: Add unit tests alreadyHasManagedProfile(Context context)481 public int alreadyHasManagedProfile(Context context) { 482 UserHandle managedUser = getManagedProfile(context); 483 if (managedUser != null) { 484 return managedUser.getIdentifier(); 485 } else { 486 return -1; 487 } 488 } 489 490 /** 491 * Removes an account. 492 * 493 * <p>This removes the given account from the calling user's list of accounts. 494 * 495 * @param context a {@link Context} object 496 * @param account the account to be removed 497 */ 498 // TODO: Add unit tests removeAccount(Context context, Account account)499 public void removeAccount(Context context, Account account) { 500 try { 501 AccountManager accountManager = 502 (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); 503 AccountManagerFuture<Bundle> bundle = accountManager.removeAccount(account, 504 null, null /* callback */, null /* handler */); 505 // Block to get the result of the removeAccount operation 506 if (bundle.getResult().getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 507 ProvisionLogger.logw("Account removed from the primary user."); 508 } else { 509 Intent removeIntent = (Intent) bundle.getResult().getParcelable( 510 AccountManager.KEY_INTENT); 511 if (removeIntent != null) { 512 ProvisionLogger.logi("Starting activity to remove account"); 513 TrampolineActivity.startActivity(context, removeIntent); 514 } else { 515 ProvisionLogger.logw("Could not remove account from the primary user."); 516 } 517 } 518 } catch (OperationCanceledException | AuthenticatorException | IOException e) { 519 ProvisionLogger.logw("Exception removing account from the primary user.", e); 520 } 521 } 522 523 /** 524 * Copies an account to a given user. 525 * 526 * <p>Copies a given account form {@code sourceUser} to {@code targetUser}. This call is 527 * blocking until the operation has succeeded. If within a timeout the account hasn't been 528 * successfully copied to the new user, we give up. 529 * 530 * @param context a {@link Context} object 531 * @param accountToMigrate the account to be migrated 532 * @param sourceUser the {@link UserHandle} of the user to copy from 533 * @param targetUser the {@link UserHandle} of the user to copy to 534 * @return whether account migration successfully completed 535 */ maybeCopyAccount(Context context, Account accountToMigrate, UserHandle sourceUser, UserHandle targetUser)536 public boolean maybeCopyAccount(Context context, Account accountToMigrate, 537 UserHandle sourceUser, UserHandle targetUser) { 538 if (accountToMigrate == null) { 539 ProvisionLogger.logd("No account to migrate."); 540 return false; 541 } 542 if (sourceUser.equals(targetUser)) { 543 ProvisionLogger.loge("sourceUser and targetUser are the same, won't migrate account."); 544 return false; 545 } 546 ProvisionLogger.logd("Attempting to copy account from " + sourceUser + " to " + targetUser); 547 try { 548 AccountManager accountManager = 549 (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); 550 boolean copySucceeded = accountManager.copyAccountToUser( 551 accountToMigrate, 552 sourceUser, 553 targetUser, 554 /* callback= */ null, /* handler= */ null) 555 .getResult(ACCOUNT_COPY_TIMEOUT_SECONDS, TimeUnit.SECONDS); 556 if (copySucceeded) { 557 ProvisionLogger.logi("Copied account to " + targetUser); 558 return true; 559 } else { 560 ProvisionLogger.loge("Could not copy account to " + targetUser); 561 } 562 } catch (OperationCanceledException | AuthenticatorException | IOException e) { 563 ProvisionLogger.loge("Exception copying account to " + targetUser, e); 564 } 565 return false; 566 } 567 568 /** 569 * Returns whether FRP is supported on the device. 570 */ isFrpSupported(Context context)571 public boolean isFrpSupported(Context context) { 572 Object pdbManager = context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); 573 return pdbManager != null; 574 } 575 576 /** 577 * Translates a given managed provisioning intent to its corresponding provisioning flow, using 578 * the action from the intent. 579 * 580 * <p/>This is necessary because, unlike other provisioning actions which has 1:1 mapping, there 581 * are multiple actions that can trigger the device owner provisioning flow. This includes 582 * {@link ACTION_PROVISION_MANAGED_DEVICE}, {@link ACTION_NDEF_DISCOVERED} and 583 * {@link ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}. These 3 actions are equivalent 584 * excepts they are sent from a different source. 585 * 586 * @return the appropriate DevicePolicyManager declared action for the given incoming intent. 587 * @throws IllegalProvisioningArgumentException if intent is malformed 588 */ 589 // TODO: Add unit tests mapIntentToDpmAction(Intent intent)590 public String mapIntentToDpmAction(Intent intent) 591 throws IllegalProvisioningArgumentException { 592 if (intent == null || intent.getAction() == null) { 593 throw new IllegalProvisioningArgumentException("Null intent action."); 594 } 595 596 // Map the incoming intent to a DevicePolicyManager.ACTION_*, as there is a N:1 mapping in 597 // some cases. 598 String dpmProvisioningAction; 599 switch (intent.getAction()) { 600 // Trivial cases. 601 case ACTION_PROVISION_MANAGED_DEVICE: 602 case ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE: 603 case ACTION_PROVISION_MANAGED_USER: 604 case ACTION_PROVISION_MANAGED_PROFILE: 605 dpmProvisioningAction = intent.getAction(); 606 break; 607 608 // NFC cases which need to take mime-type into account. 609 case ACTION_NDEF_DISCOVERED: 610 String mimeType = intent.getType(); 611 switch (mimeType) { 612 case MIME_TYPE_PROVISIONING_NFC: 613 dpmProvisioningAction = ACTION_PROVISION_MANAGED_DEVICE; 614 break; 615 616 default: 617 throw new IllegalProvisioningArgumentException( 618 "Unknown NFC bump mime-type: " + mimeType); 619 } 620 break; 621 622 // Device owner provisioning from a trusted app. 623 // TODO (b/27217042): review for new management modes in split system-user model 624 case ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE: 625 dpmProvisioningAction = ACTION_PROVISION_MANAGED_DEVICE; 626 break; 627 628 default: 629 throw new IllegalProvisioningArgumentException("Unknown intent action " 630 + intent.getAction()); 631 } 632 return dpmProvisioningAction; 633 } 634 635 /** 636 * Sends an intent to trigger a factory reset. 637 */ 638 // TODO: Move the FR intent into a Globals class. sendFactoryResetBroadcast(Context context, String reason)639 public void sendFactoryResetBroadcast(Context context, String reason) { 640 Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR); 641 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 642 intent.putExtra(Intent.EXTRA_REASON, reason); 643 context.sendBroadcast(intent); 644 } 645 646 /** 647 * Returns whether the given provisioning action is a profile owner action. 648 */ 649 // TODO: Move the list of device owner actions into a Globals class. isProfileOwnerAction(String action)650 public final boolean isProfileOwnerAction(String action) { 651 return action.equals(ACTION_PROVISION_MANAGED_PROFILE) 652 || action.equals(ACTION_PROVISION_MANAGED_USER); 653 } 654 655 /** 656 * Returns whether the given provisioning action is a device owner action. 657 */ 658 // TODO: Move the list of device owner actions into a Globals class. isDeviceOwnerAction(String action)659 public final boolean isDeviceOwnerAction(String action) { 660 return action.equals(ACTION_PROVISION_MANAGED_DEVICE) 661 || action.equals(ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE); 662 } 663 664 /** 665 * Returns whether the device currently has connectivity. 666 */ isConnectedToNetwork(Context context)667 public boolean isConnectedToNetwork(Context context) { 668 NetworkInfo info = getActiveNetworkInfo(context); 669 return info != null && info.isConnected(); 670 } 671 672 /** 673 * Returns whether the device is currently connected to a wifi. 674 */ isConnectedToWifi(Context context)675 public boolean isConnectedToWifi(Context context) { 676 NetworkInfo info = getActiveNetworkInfo(context); 677 return info != null 678 && info.isConnected() 679 && info.getType() == ConnectivityManager.TYPE_WIFI; 680 } 681 getActiveNetworkInfo(Context context)682 private NetworkInfo getActiveNetworkInfo(Context context) { 683 ConnectivityManager cm = 684 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 685 if (cm != null) { 686 return cm.getActiveNetworkInfo(); 687 } 688 return null; 689 } 690 691 /** 692 * Returns whether encryption is required on this device. 693 * 694 * <p>Encryption is required if the device is not currently encrypted and the persistent 695 * system flag {@code persist.sys.no_req_encrypt} is not set. 696 */ isEncryptionRequired()697 public boolean isEncryptionRequired() { 698 return !isPhysicalDeviceEncrypted() 699 && !SystemProperties.getBoolean("persist.sys.no_req_encrypt", false); 700 } 701 702 /** 703 * Returns whether the device is currently encrypted. 704 */ isPhysicalDeviceEncrypted()705 public boolean isPhysicalDeviceEncrypted() { 706 return StorageManager.isEncrypted(); 707 } 708 709 /** 710 * Returns the wifi pick intent. 711 */ 712 // TODO: Move this intent into a Globals class. getWifiPickIntent()713 public Intent getWifiPickIntent() { 714 Intent wifiIntent = new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK); 715 wifiIntent.putExtra("extra_prefs_show_button_bar", true); 716 wifiIntent.putExtra("wifi_enable_next_on_connect", true); 717 return wifiIntent; 718 } 719 720 /** 721 * Returns whether the device has a split system user. 722 * 723 * <p>Split system user means that user 0 is system only and all meat users are separate from 724 * the system user. 725 */ isSplitSystemUser()726 public boolean isSplitSystemUser() { 727 return UserManager.isSplitSystemUser(); 728 } 729 730 /** 731 * Returns whether the currently chosen launcher supports managed profiles. 732 * 733 * <p>A launcher is deemed to support managed profiles when its target API version is at least 734 * {@link Build.VERSION_CODES#LOLLIPOP}. 735 */ currentLauncherSupportsManagedProfiles(Context context)736 public boolean currentLauncherSupportsManagedProfiles(Context context) { 737 Intent intent = new Intent(Intent.ACTION_MAIN); 738 intent.addCategory(Intent.CATEGORY_HOME); 739 740 PackageManager pm = context.getPackageManager(); 741 ResolveInfo launcherResolveInfo = pm.resolveActivity(intent, 742 PackageManager.MATCH_DEFAULT_ONLY); 743 if (launcherResolveInfo == null) { 744 return false; 745 } 746 try { 747 // If the user has not chosen a default launcher, then launcherResolveInfo will be 748 // referring to the resolver activity. It is fine to create a managed profile in 749 // this case since there will always be at least one launcher on the device that 750 // supports managed profile feature. 751 ApplicationInfo launcherAppInfo = pm.getApplicationInfo( 752 launcherResolveInfo.activityInfo.packageName, 0 /* default flags */); 753 return versionNumberAtLeastL(launcherAppInfo.targetSdkVersion); 754 } catch (PackageManager.NameNotFoundException e) { 755 return false; 756 } 757 } 758 759 /** 760 * Returns whether the given version number is at least lollipop. 761 * 762 * @param versionNumber the version number to be verified. 763 */ versionNumberAtLeastL(int versionNumber)764 private boolean versionNumberAtLeastL(int versionNumber) { 765 return versionNumber >= Build.VERSION_CODES.LOLLIPOP; 766 } 767 isBrightColor(int color)768 public boolean isBrightColor(int color) { 769 // we're using the brightness formula: (r * 299 + g * 587 + b * 144) / 1000 770 return Color.red(color) * 299 + Color.green(color) * 587 + Color.blue(color) * 114 771 >= 1000 * THRESHOLD_BRIGHT_COLOR; 772 } 773 } 774