• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Utils()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      */
getCurrentSystemApps(IPackageManager ipm, int userId)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      */
disableComponent(ComponentName toDisable, int userId)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      */
enableComponent(ComponentName toEnable, int userId)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
setComponentEnabledSetting(IPackageManager ipm, ComponentName toDisable, int enabledSetting, int userId)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
findDeviceAdmin(String dpcPackageName, ComponentName dpcComponentName, Context context, int userId)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
findDeviceAdminInPackageInfo(@onNull String dpcPackageName, @Nullable ComponentName dpcComponentName, @NonNull PackageInfo pi)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
findDeviceAdminInPackage(String packageName, PackageInfo packageInfo)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 
isComponentInPackageInfo(ComponentName dpcComponentName, PackageInfo pi)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      */
isPackageTestOnly(PackageManager pm, String packageName, int userHandle)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      */
isCurrentUserSystem()299     public boolean isCurrentUserSystem() {
300         return UserHandle.myUserId() == UserHandle.USER_SYSTEM;
301     }
302 
303     /**
304      * Returns whether the device is currently managed.
305      */
isDeviceManaged(Context context)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      */
packageRequiresUpdate(String packageName, int minSupportedVersion, Context context)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
getManagedProfile(Context context)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
alreadyHasManagedProfile(Context context)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
removeAccount(Context context, Account account)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      */
isFrpSupported(Context context)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
mapIntentToDpmAction(Intent intent)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.
sendFactoryResetBroadcast(Context context, String reason)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.
isProfileOwnerAction(String action)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.
isDeviceOwnerAction(String action)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      */
isConnectedToNetwork(Context context)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      */
isConnectedToWifi(Context context)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      */
getActiveNetworkInfo(Context context)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      */
isEncryptionRequired()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      */
isPhysicalDeviceEncrypted()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.
getWifiPickIntent()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      */
isSplitSystemUser()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      */
currentLauncherSupportsManagedProfiles(Context context)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      */
versionNumberAtLeastL(int versionNumber)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
computeHashOfByteArray(byte[] bytes)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
computeHashOfFile(String fileLocation, String hashType)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 
isBrightColor(int color)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      */
canResolveIntentAsUser(Context context, Intent intent, int userId)681     public boolean canResolveIntentAsUser(Context context, Intent intent, int userId) {
682         return intent != null
683                 && context.getPackageManager().resolveActivityAsUser(intent, 0, userId) != null;
684     }
685 
isPackageDeviceOwner(DevicePolicyManager dpm, String packageName)686     public boolean isPackageDeviceOwner(DevicePolicyManager dpm, String packageName) {
687         final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnCallingUser();
688         return deviceOwner != null && deviceOwner.getPackageName().equals(packageName);
689     }
690 
getAccentColor(Context context)691     public int getAccentColor(Context context) {
692         return getAttrColor(context, android.R.attr.colorAccent);
693     }
694 
getAttrColor(Context context, int attr)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