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 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