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