Home | History | Annotate | Download | only in common
      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 android.accounts.Account;
     28 import android.accounts.AccountManager;
     29 import android.accounts.AccountManagerFuture;
     30 import android.accounts.AuthenticatorException;
     31 import android.accounts.OperationCanceledException;
     32 import android.annotation.NonNull;
     33 import android.annotation.Nullable;
     34 import android.app.admin.DevicePolicyManager;
     35 import android.content.ComponentName;
     36 import android.content.Context;
     37 import android.content.Intent;
     38 import android.content.pm.ActivityInfo;
     39 import android.content.pm.ApplicationInfo;
     40 import android.content.pm.IPackageManager;
     41 import android.content.pm.PackageInfo;
     42 import android.content.pm.PackageManager;
     43 import android.content.pm.PackageManager.NameNotFoundException;
     44 import android.content.pm.ResolveInfo;
     45 import android.content.pm.UserInfo;
     46 import android.graphics.Color;
     47 import android.net.ConnectivityManager;
     48 import android.net.NetworkInfo;
     49 import android.net.wifi.WifiManager;
     50 import android.os.Build;
     51 import android.os.Bundle;
     52 import android.os.RemoteException;
     53 import android.os.ServiceManager;
     54 import android.os.SystemProperties;
     55 import android.os.UserHandle;
     56 import android.os.UserManager;
     57 import android.os.storage.StorageManager;
     58 import android.text.TextUtils;
     59 
     60 import com.android.internal.annotations.VisibleForTesting;
     61 import com.android.managedprovisioning.TrampolineActivity;
     62 import com.android.managedprovisioning.model.PackageDownloadInfo;
     63 
     64 import java.io.FileInputStream;
     65 import java.io.IOException;
     66 import java.io.InputStream;
     67 import java.security.MessageDigest;
     68 import java.security.NoSuchAlgorithmException;
     69 import java.util.HashSet;
     70 import java.util.List;
     71 import java.util.Set;
     72 
     73 /**
     74  * Class containing various auxiliary methods.
     75  */
     76 public 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 system apps currently available to a given user.
     87      *
     88      * <p>Calls the {@link IPackageManager} to retrieve all system apps available to a user and
     89      * returns their package names.
     90      *
     91      * @param ipm an {@link IPackageManager} object
     92      * @param userId the id of the user to check the apps for
     93      */
     94     public Set<String> getCurrentSystemApps(IPackageManager ipm, int userId) {
     95         Set<String> apps = new HashSet<>();
     96         List<ApplicationInfo> aInfos = null;
     97         try {
     98             aInfos = ipm.getInstalledApplications(
     99                     PackageManager.MATCH_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