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 17package com.android.managedprovisioning.common; 18 19import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; 20import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE; 21import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; 22import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE; 23import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER; 24import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC; 25import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED; 26 27import android.accounts.Account; 28import android.accounts.AccountManager; 29import android.accounts.AccountManagerFuture; 30import android.accounts.AuthenticatorException; 31import android.accounts.OperationCanceledException; 32import android.annotation.NonNull; 33import android.annotation.Nullable; 34import android.app.admin.DevicePolicyManager; 35import android.content.ComponentName; 36import android.content.Context; 37import android.content.Intent; 38import android.content.pm.ActivityInfo; 39import android.content.pm.ApplicationInfo; 40import android.content.pm.IPackageManager; 41import android.content.pm.PackageInfo; 42import android.content.pm.PackageManager; 43import android.content.pm.PackageManager.NameNotFoundException; 44import android.content.pm.ResolveInfo; 45import android.content.pm.UserInfo; 46import android.graphics.Color; 47import android.net.ConnectivityManager; 48import android.net.NetworkInfo; 49import android.net.wifi.WifiManager; 50import android.os.Build; 51import android.os.Bundle; 52import android.os.RemoteException; 53import android.os.ServiceManager; 54import android.os.SystemProperties; 55import android.os.UserHandle; 56import android.os.UserManager; 57import android.os.storage.StorageManager; 58import android.text.TextUtils; 59 60import com.android.internal.annotations.VisibleForTesting; 61import com.android.managedprovisioning.TrampolineActivity; 62import com.android.managedprovisioning.model.PackageDownloadInfo; 63 64import java.io.FileInputStream; 65import java.io.IOException; 66import java.io.InputStream; 67import java.security.MessageDigest; 68import java.security.NoSuchAlgorithmException; 69import java.util.HashSet; 70import java.util.List; 71import java.util.Set; 72 73/** 74 * Class containing various auxiliary methods. 75 */ 76public class Utils { 77 public static final String SHA256_TYPE = "SHA-256"; 78 public static final String SHA1_TYPE = "SHA-1"; 79 80 // value chosen to match UX designs; when updating check status bar icon colors 81 private static final int THRESHOLD_BRIGHT_COLOR = 190; 82 83 public Utils() {} 84 85 /** 86 * Returns the currently installed system apps on a given user. 87 * 88 * <p>Calls into the {@link IPackageManager} to retrieve all installed packages on the given 89 * user and returns the package names of all system apps. 90 * 91 * @param ipm an {@link IPackageManager} object 92 * @param userId the id of the user we are interested in 93 */ 94 public Set<String> getCurrentSystemApps(IPackageManager ipm, int userId) { 95 Set<String> apps = new HashSet<String>(); 96 List<ApplicationInfo> aInfos = null; 97 try { 98 aInfos = ipm.getInstalledApplications( 99 PackageManager.GET_UNINSTALLED_PACKAGES, userId).getList(); 100 } catch (RemoteException neverThrown) { 101 ProvisionLogger.loge("This should not happen.", neverThrown); 102 } 103 for (ApplicationInfo aInfo : aInfos) { 104 if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 105 apps.add(aInfo.packageName); 106 } 107 } 108 return apps; 109 } 110 111 /** 112 * Disables a given component in a given user. 113 * 114 * @param toDisable the component that should be disabled 115 * @param userId the id of the user where the component should be disabled. 116 */ 117 public void disableComponent(ComponentName toDisable, int userId) { 118 setComponentEnabledSetting( 119 IPackageManager.Stub.asInterface(ServiceManager.getService("package")), 120 toDisable, 121 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 122 userId); 123 } 124 125 /** 126 * Enables a given component in a given user. 127 * 128 * @param toEnable the component that should be enabled 129 * @param userId the id of the user where the component should be disabled. 130 */ 131 public void enableComponent(ComponentName toEnable, int userId) { 132 setComponentEnabledSetting( 133 IPackageManager.Stub.asInterface(ServiceManager.getService("package")), 134 toEnable, 135 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 136 userId); 137 } 138 139 /** 140 * Disables a given component in a given user. 141 * 142 * @param ipm an {@link IPackageManager} object 143 * @param toDisable the component that should be disabled 144 * @param userId the id of the user where the component should be disabled. 145 */ 146 @VisibleForTesting 147 void setComponentEnabledSetting(IPackageManager ipm, ComponentName toDisable, 148 int enabledSetting, int userId) { 149 try { 150 ipm.setComponentEnabledSetting(toDisable, 151 enabledSetting, PackageManager.DONT_KILL_APP, 152 userId); 153 } catch (RemoteException neverThrown) { 154 ProvisionLogger.loge("This should not happen.", neverThrown); 155 } catch (Exception e) { 156 ProvisionLogger.logw("Component not found, not changing enabled setting: " 157 + toDisable.toShortString()); 158 } 159 } 160 161 /** 162 * Check the validity of the admin component name supplied, or try to infer this componentName 163 * from the package. 164 * 165 * We are supporting lookup by package name for legacy reasons. 166 * 167 * If dpcComponentName is supplied (not null): dpcPackageName is ignored. 168 * Check that the package of dpcComponentName is installed, that dpcComponentName is a 169 * receiver in this package, and return it. The receiver can be in disabled state. 170 * 171 * Otherwise: dpcPackageName must be supplied (not null). 172 * Check that this package is installed, try to infer a potential device admin in this package, 173 * and return it. 174 */ 175 @NonNull 176 public ComponentName findDeviceAdmin(String dpcPackageName, ComponentName dpcComponentName, 177 Context context) throws IllegalProvisioningArgumentException { 178 if (dpcComponentName != null) { 179 dpcPackageName = dpcComponentName.getPackageName(); 180 } 181 if (dpcPackageName == null) { 182 throw new IllegalProvisioningArgumentException("Neither the package name nor the" 183 + " component name of the admin are supplied"); 184 } 185 PackageInfo pi; 186 try { 187 pi = context.getPackageManager().getPackageInfo(dpcPackageName, 188 PackageManager.GET_RECEIVERS | PackageManager.MATCH_DISABLED_COMPONENTS); 189 } catch (NameNotFoundException e) { 190 throw new IllegalProvisioningArgumentException("Dpc "+ dpcPackageName 191 + " is not installed. ", e); 192 } 193 194 final ComponentName componentName = findDeviceAdminInPackageInfo(dpcPackageName, 195 dpcComponentName, pi); 196 if (componentName == null) { 197 throw new IllegalProvisioningArgumentException("Cannot find any admin receiver in " 198 + "package " + dpcPackageName + " with component " + dpcComponentName); 199 } 200 return componentName; 201 } 202 203 /** 204 * If dpcComponentName is not null: dpcPackageName is ignored. 205 * Check that the package of dpcComponentName is installed, that dpcComponentName is a 206 * receiver in this package, and return it. The receiver can be in disabled state. 207 * 208 * Otherwise, try to infer a potential device admin component in this package info. 209 * 210 * @return infered device admin component in package info. Otherwise, null 211 */ 212 @Nullable 213 public ComponentName findDeviceAdminInPackageInfo(@NonNull String dpcPackageName, 214 @Nullable ComponentName dpcComponentName, @NonNull PackageInfo pi) { 215 if (dpcComponentName != null) { 216 if (!isComponentInPackageInfo(dpcComponentName, pi)) { 217 ProvisionLogger.logw("The component " + dpcComponentName + " isn't registered in " 218 + "the apk"); 219 return null; 220 } 221 return dpcComponentName; 222 } else { 223 return findDeviceAdminInPackage(dpcPackageName, pi); 224 } 225 } 226 227 /** 228 * Finds a device admin in a given {@link PackageInfo} object. 229 * 230 * <p>This function returns {@code null} if no or multiple admin receivers were found, and if 231 * the package name does not match dpcPackageName.</p> 232 * @param packageName packge name that should match the {@link PackageInfo} object. 233 * @param packageInfo package info to be examined. 234 * @return admin receiver or null in case of error. 235 */ 236 @Nullable 237 private ComponentName findDeviceAdminInPackage(String packageName, PackageInfo packageInfo) { 238 if (packageInfo == null || !TextUtils.equals(packageInfo.packageName, packageName)) { 239 return null; 240 } 241 242 ComponentName mdmComponentName = null; 243 for (ActivityInfo ai : packageInfo.receivers) { 244 if (TextUtils.equals(ai.permission, android.Manifest.permission.BIND_DEVICE_ADMIN)) { 245 if (mdmComponentName != null) { 246 ProvisionLogger.logw("more than 1 device admin component are found"); 247 return null; 248 } else { 249 mdmComponentName = new ComponentName(packageName, ai.name); 250 } 251 } 252 } 253 return mdmComponentName; 254 } 255 256 private boolean isComponentInPackageInfo(ComponentName dpcComponentName, 257 PackageInfo pi) { 258 for (ActivityInfo ai : pi.receivers) { 259 if (dpcComponentName.getClassName().equals(ai.name)) { 260 return true; 261 } 262 } 263 return false; 264 } 265 266 /** 267 * Return if a given package has testOnly="true", in which case we'll relax certain rules 268 * for CTS. 269 * 270 * The system allows this flag to be changed when an app is updated. But 271 * {@link DevicePolicyManager} uses the persisted version to do actual checks for relevant 272 * dpm command. 273 * 274 * @see DevicePolicyManagerService#isPackageTestOnly for more info 275 */ 276 public boolean isPackageTestOnly(PackageManager pm, String packageName, int userHandle) { 277 if (TextUtils.isEmpty(packageName)) { 278 return false; 279 } 280 281 try { 282 final ApplicationInfo ai = pm.getApplicationInfoAsUser(packageName, 283 PackageManager.MATCH_DIRECT_BOOT_AWARE 284 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle); 285 return ai != null && (ai.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0; 286 } catch (PackageManager.NameNotFoundException e) { 287 return false; 288 } 289 290 } 291 292 /** 293 * Returns whether the current user is the system user. 294 */ 295 public boolean isCurrentUserSystem() { 296 return UserHandle.myUserId() == UserHandle.USER_SYSTEM; 297 } 298 299 /** 300 * Returns whether the device is currently managed. 301 */ 302 public boolean isDeviceManaged(Context context) { 303 DevicePolicyManager dpm = 304 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); 305 return dpm.isDeviceManaged(); 306 } 307 308 /** 309 * Returns true if the given package requires an update. 310 * 311 * <p>There are two cases where an update is required: 312 * 1. The package is not currently present on the device. 313 * 2. The package is present, but the version is below the minimum supported version. 314 * 315 * @param packageName the package to be checked for updates 316 * @param minSupportedVersion the minimum supported version 317 * @param context a {@link Context} object 318 */ 319 public boolean packageRequiresUpdate(String packageName, int minSupportedVersion, 320 Context context) { 321 try { 322 PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0); 323 // Always download packages if no minimum version given. 324 if (minSupportedVersion != PackageDownloadInfo.DEFAULT_MINIMUM_VERSION 325 && packageInfo.versionCode >= minSupportedVersion) { 326 return false; 327 } 328 } catch (NameNotFoundException e) { 329 // Package not on device. 330 } 331 332 return true; 333 } 334 335 /** 336 * Returns the first existing managed profile if any present, null otherwise. 337 * 338 * <p>Note that we currently only support one managed profile per device. 339 */ 340 // TODO: Add unit tests 341 public UserHandle getManagedProfile(Context context) { 342 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 343 int currentUserId = userManager.getUserHandle(); 344 List<UserInfo> userProfiles = userManager.getProfiles(currentUserId); 345 for (UserInfo profile : userProfiles) { 346 if (profile.isManagedProfile()) { 347 return new UserHandle(profile.id); 348 } 349 } 350 return null; 351 } 352 353 /** 354 * Returns the user id of an already existing managed profile or -1 if none exists. 355 */ 356 // TODO: Add unit tests 357 public int alreadyHasManagedProfile(Context context) { 358 UserHandle managedUser = getManagedProfile(context); 359 if (managedUser != null) { 360 return managedUser.getIdentifier(); 361 } else { 362 return -1; 363 } 364 } 365 366 /** 367 * Removes an account. 368 * 369 * <p>This removes the given account from the calling user's list of accounts. 370 * 371 * @param context a {@link Context} object 372 * @param account the account to be removed 373 */ 374 // TODO: Add unit tests 375 public void removeAccount(Context context, Account account) { 376 try { 377 AccountManager accountManager = 378 (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); 379 AccountManagerFuture<Bundle> bundle = accountManager.removeAccount(account, 380 null, null /* callback */, null /* handler */); 381 // Block to get the result of the removeAccount operation 382 if (bundle.getResult().getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { 383 ProvisionLogger.logw("Account removed from the primary user."); 384 } else { 385 Intent removeIntent = (Intent) bundle.getResult().getParcelable( 386 AccountManager.KEY_INTENT); 387 if (removeIntent != null) { 388 ProvisionLogger.logi("Starting activity to remove account"); 389 TrampolineActivity.startActivity(context, removeIntent); 390 } else { 391 ProvisionLogger.logw("Could not remove account from the primary user."); 392 } 393 } 394 } catch (OperationCanceledException | AuthenticatorException | IOException e) { 395 ProvisionLogger.logw("Exception removing account from the primary user.", e); 396 } 397 } 398 399 /** 400 * Returns whether FRP is supported on the device. 401 */ 402 public boolean isFrpSupported(Context context) { 403 Object pdbManager = context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); 404 return pdbManager != null; 405 } 406 407 /** 408 * Translates a given managed provisioning intent to its corresponding provisioning flow, using 409 * the action from the intent. 410 * 411 * <p/>This is necessary because, unlike other provisioning actions which has 1:1 mapping, there 412 * are multiple actions that can trigger the device owner provisioning flow. This includes 413 * {@link ACTION_PROVISION_MANAGED_DEVICE}, {@link ACTION_NDEF_DISCOVERED} and 414 * {@link ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}. These 3 actions are equivalent 415 * excepts they are sent from a different source. 416 * 417 * @return the appropriate DevicePolicyManager declared action for the given incoming intent. 418 * @throws IllegalProvisioningArgumentException if intent is malformed 419 */ 420 // TODO: Add unit tests 421 public String mapIntentToDpmAction(Intent intent) 422 throws IllegalProvisioningArgumentException { 423 if (intent == null || intent.getAction() == null) { 424 throw new IllegalProvisioningArgumentException("Null intent action."); 425 } 426 427 // Map the incoming intent to a DevicePolicyManager.ACTION_*, as there is a N:1 mapping in 428 // some cases. 429 String dpmProvisioningAction; 430 switch (intent.getAction()) { 431 // Trivial cases. 432 case ACTION_PROVISION_MANAGED_DEVICE: 433 case ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE: 434 case ACTION_PROVISION_MANAGED_USER: 435 case ACTION_PROVISION_MANAGED_PROFILE: 436 dpmProvisioningAction = intent.getAction(); 437 break; 438 439 // NFC cases which need to take mime-type into account. 440 case ACTION_NDEF_DISCOVERED: 441 String mimeType = intent.getType(); 442 if (mimeType == null) { 443 throw new IllegalProvisioningArgumentException( 444 "Unknown NFC bump mime-type: " + mimeType); 445 } 446 switch (mimeType) { 447 case MIME_TYPE_PROVISIONING_NFC: 448 dpmProvisioningAction = ACTION_PROVISION_MANAGED_DEVICE; 449 break; 450 451 default: 452 throw new IllegalProvisioningArgumentException( 453 "Unknown NFC bump mime-type: " + mimeType); 454 } 455 break; 456 457 // Device owner provisioning from a trusted app. 458 // TODO (b/27217042): review for new management modes in split system-user model 459 case ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE: 460 dpmProvisioningAction = ACTION_PROVISION_MANAGED_DEVICE; 461 break; 462 463 default: 464 throw new IllegalProvisioningArgumentException("Unknown intent action " 465 + intent.getAction()); 466 } 467 return dpmProvisioningAction; 468 } 469 470 /** 471 * Sends an intent to trigger a factory reset. 472 */ 473 // TODO: Move the FR intent into a Globals class. 474 public void sendFactoryResetBroadcast(Context context, String reason) { 475 Intent intent = new Intent(Intent.ACTION_FACTORY_RESET); 476 // Send explicit broadcast due to Broadcast Limitations 477 intent.setPackage("android"); 478 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 479 intent.putExtra(Intent.EXTRA_REASON, reason); 480 context.sendBroadcast(intent); 481 } 482 483 /** 484 * Returns whether the given provisioning action is a profile owner action. 485 */ 486 // TODO: Move the list of device owner actions into a Globals class. 487 public final boolean isProfileOwnerAction(String action) { 488 return action.equals(ACTION_PROVISION_MANAGED_PROFILE) 489 || action.equals(ACTION_PROVISION_MANAGED_USER); 490 } 491 492 /** 493 * Returns whether the given provisioning action is a device owner action. 494 */ 495 // TODO: Move the list of device owner actions into a Globals class. 496 public final boolean isDeviceOwnerAction(String action) { 497 return action.equals(ACTION_PROVISION_MANAGED_DEVICE) 498 || action.equals(ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE); 499 } 500 501 /** 502 * Returns whether the device currently has connectivity. 503 */ 504 public boolean isConnectedToNetwork(Context context) { 505 NetworkInfo info = getActiveNetworkInfo(context); 506 return info != null && info.isConnected(); 507 } 508 509 /** 510 * Returns whether the device is currently connected to a wifi. 511 */ 512 public boolean isConnectedToWifi(Context context) { 513 NetworkInfo info = getActiveNetworkInfo(context); 514 return info != null 515 && info.isConnected() 516 && info.getType() == ConnectivityManager.TYPE_WIFI; 517 } 518 519 /** 520 * Returns the active network info of the device. 521 */ 522 public NetworkInfo getActiveNetworkInfo(Context context) { 523 ConnectivityManager cm = 524 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 525 return cm.getActiveNetworkInfo(); 526 } 527 528 /** 529 * Returns whether encryption is required on this device. 530 * 531 * <p>Encryption is required if the device is not currently encrypted and the persistent 532 * system flag {@code persist.sys.no_req_encrypt} is not set. 533 */ 534 public boolean isEncryptionRequired() { 535 return !isPhysicalDeviceEncrypted() 536 && !SystemProperties.getBoolean("persist.sys.no_req_encrypt", false); 537 } 538 539 /** 540 * Returns whether the device is currently encrypted. 541 */ 542 public boolean isPhysicalDeviceEncrypted() { 543 return StorageManager.isEncrypted(); 544 } 545 546 /** 547 * Returns the wifi pick intent. 548 */ 549 // TODO: Move this intent into a Globals class. 550 public Intent getWifiPickIntent() { 551 Intent wifiIntent = new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK); 552 wifiIntent.putExtra("extra_prefs_show_button_bar", true); 553 wifiIntent.putExtra("wifi_enable_next_on_connect", true); 554 return wifiIntent; 555 } 556 557 /** 558 * Returns whether the device has a split system user. 559 * 560 * <p>Split system user means that user 0 is system only and all meat users are separate from 561 * the system user. 562 */ 563 public boolean isSplitSystemUser() { 564 return UserManager.isSplitSystemUser(); 565 } 566 567 /** 568 * Returns whether the currently chosen launcher supports managed profiles. 569 * 570 * <p>A launcher is deemed to support managed profiles when its target API version is at least 571 * {@link Build.VERSION_CODES#LOLLIPOP}. 572 */ 573 public boolean currentLauncherSupportsManagedProfiles(Context context) { 574 Intent intent = new Intent(Intent.ACTION_MAIN); 575 intent.addCategory(Intent.CATEGORY_HOME); 576 577 PackageManager pm = context.getPackageManager(); 578 ResolveInfo launcherResolveInfo = pm.resolveActivity(intent, 579 PackageManager.MATCH_DEFAULT_ONLY); 580 if (launcherResolveInfo == null) { 581 return false; 582 } 583 try { 584 // If the user has not chosen a default launcher, then launcherResolveInfo will be 585 // referring to the resolver activity. It is fine to create a managed profile in 586 // this case since there will always be at least one launcher on the device that 587 // supports managed profile feature. 588 ApplicationInfo launcherAppInfo = pm.getApplicationInfo( 589 launcherResolveInfo.activityInfo.packageName, 0 /* default flags */); 590 return versionNumberAtLeastL(launcherAppInfo.targetSdkVersion); 591 } catch (PackageManager.NameNotFoundException e) { 592 return false; 593 } 594 } 595 596 /** 597 * Returns whether the given version number is at least lollipop. 598 * 599 * @param versionNumber the version number to be verified. 600 */ 601 private boolean versionNumberAtLeastL(int versionNumber) { 602 return versionNumber >= Build.VERSION_CODES.LOLLIPOP; 603 } 604 605 /** 606 * Computes the sha 256 hash of a byte array. 607 */ 608 @Nullable 609 public byte[] computeHashOfByteArray(byte[] bytes) { 610 try { 611 MessageDigest md = MessageDigest.getInstance(SHA256_TYPE); 612 md.update(bytes); 613 return md.digest(); 614 } catch (NoSuchAlgorithmException e) { 615 ProvisionLogger.loge("Hashing algorithm " + SHA256_TYPE + " not supported.", e); 616 return null; 617 } 618 } 619 620 /** 621 * Computes a hash of a file with a spcific hash algorithm. 622 */ 623 // TODO: Add unit tests 624 @Nullable 625 public byte[] computeHashOfFile(String fileLocation, String hashType) { 626 InputStream fis = null; 627 MessageDigest md; 628 byte hash[] = null; 629 try { 630 md = MessageDigest.getInstance(hashType); 631 } catch (NoSuchAlgorithmException e) { 632 ProvisionLogger.loge("Hashing algorithm " + hashType + " not supported.", e); 633 return null; 634 } 635 try { 636 fis = new FileInputStream(fileLocation); 637 638 byte[] buffer = new byte[256]; 639 int n = 0; 640 while (n != -1) { 641 n = fis.read(buffer); 642 if (n > 0) { 643 md.update(buffer, 0, n); 644 } 645 } 646 hash = md.digest(); 647 } catch (IOException e) { 648 ProvisionLogger.loge("IO error.", e); 649 } finally { 650 // Close input stream quietly. 651 try { 652 if (fis != null) { 653 fis.close(); 654 } 655 } catch (IOException e) { 656 // Ignore. 657 } 658 } 659 return hash; 660 } 661 662 public boolean isBrightColor(int color) { 663 // This comes from the YIQ transformation. We're using the formula: 664 // Y = .299 * R + .587 * G + .114 * B 665 return Color.red(color) * 299 + Color.green(color) * 587 + Color.blue(color) * 114 666 >= 1000 * THRESHOLD_BRIGHT_COLOR; 667 } 668 669 /** 670 * Returns whether given intent can be resolved for the user. 671 */ 672 public boolean canResolveIntentAsUser(Context context, Intent intent, int userId) { 673 return intent != null 674 && context.getPackageManager().resolveActivityAsUser(intent, 0, userId) != null; 675 } 676 677 public boolean isPackageDeviceOwner(DevicePolicyManager dpm, String packageName) { 678 final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnCallingUser(); 679 return deviceOwner != null && deviceOwner.getPackageName().equals(packageName); 680 } 681} 682