Utils.java revision ea9199539d215f0caa3427a6e117867eeae9813b
1/*
2 * Copyright 2014, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.managedprovisioning.common;
18
19import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
20import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
21import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
22import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE;
23import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
24import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC;
25import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED;
26import static java.nio.charset.StandardCharsets.UTF_8;
27
28import android.accounts.Account;
29import android.accounts.AccountManager;
30import android.accounts.AccountManagerFuture;
31import android.accounts.AuthenticatorException;
32import android.accounts.OperationCanceledException;
33import android.app.admin.DevicePolicyManager;
34import android.content.ComponentName;
35import android.content.Context;
36import android.content.Intent;
37import android.content.pm.ActivityInfo;
38import android.content.pm.ApplicationInfo;
39import android.content.pm.IPackageManager;
40import android.content.pm.PackageInfo;
41import android.content.pm.PackageManager;
42import android.content.pm.PackageManager.NameNotFoundException;
43import android.content.pm.ResolveInfo;
44import android.content.pm.UserInfo;
45import android.graphics.Color;
46import android.net.ConnectivityManager;
47import android.net.NetworkInfo;
48import android.net.wifi.WifiManager;
49import android.nfc.NdefMessage;
50import android.nfc.NdefRecord;
51import android.nfc.NfcAdapter;
52import android.os.Build;
53import android.os.Bundle;
54import android.os.Parcelable;
55import android.os.Process;
56import android.os.RemoteException;
57import android.os.ServiceManager;
58import android.os.SystemProperties;
59import android.os.UserHandle;
60import android.os.UserManager;
61import android.os.storage.StorageManager;
62import android.provider.Settings.Global;
63import android.provider.Settings.Secure;
64import android.text.TextUtils;
65import android.util.Base64;
66
67import java.io.FileInputStream;
68import java.io.IOException;
69import java.io.InputStream;
70import java.lang.Integer;
71import java.lang.Math;
72import java.lang.String;
73import java.nio.charset.StandardCharsets;
74import java.security.MessageDigest;
75import java.security.NoSuchAlgorithmException;
76import java.util.HashSet;
77import java.util.List;
78import java.util.Set;
79import java.util.concurrent.TimeUnit;
80
81import com.android.internal.annotations.VisibleForTesting;
82import com.android.managedprovisioning.FinalizationActivity;
83import com.android.managedprovisioning.ProvisionLogger;
84import com.android.managedprovisioning.TrampolineActivity;
85import com.android.managedprovisioning.model.ProvisioningParams;
86import com.android.managedprovisioning.model.PackageDownloadInfo;
87
88/**
89 * Class containing various auxiliary methods.
90 */
91public class Utils {
92    private static final int THRESHOLD_BRIGHT_COLOR = 160; // A color needs a brightness of at least
93    // this value to be considered bright. (brightness being between 0 and 255).
94
95    public static final String SHA256_TYPE = "SHA-256";
96    public static final String SHA1_TYPE = "SHA-1";
97
98    public Utils() {}
99
100    /**
101     * Returns the currently installed system apps on a given user.
102     *
103     * <p>Calls into the {@link IPackageManager} to retrieve all installed packages on the given
104     * user and returns the package names of all system apps.
105     *
106     * @param ipm an {@link IPackageManager} object
107     * @param userId the id of the user we are interested in
108     */
109    public Set<String> getCurrentSystemApps(IPackageManager ipm, int userId) {
110        Set<String> apps = new HashSet<String>();
111        List<ApplicationInfo> aInfos = null;
112        try {
113            aInfos = ipm.getInstalledApplications(
114                    PackageManager.GET_UNINSTALLED_PACKAGES, userId).getList();
115        } catch (RemoteException neverThrown) {
116            ProvisionLogger.loge("This should not happen.", neverThrown);
117        }
118        for (ApplicationInfo aInfo : aInfos) {
119            if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
120                apps.add(aInfo.packageName);
121            }
122        }
123        return apps;
124    }
125
126    /**
127     * Disables a given component in a given user.
128     *
129     * @param toDisable the component that should be disabled
130     * @param userId the id of the user where the component should be disabled.
131     */
132    public void disableComponent(ComponentName toDisable, int userId) {
133        setComponentEnabledSetting(
134                IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
135                toDisable,
136                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
137                userId);
138    }
139
140    /**
141     * Enables a given component in a given user.
142     *
143     * @param toEnable the component that should be enabled
144     * @param userId the id of the user where the component should be disabled.
145     */
146    public void enableComponent(ComponentName toEnable, int userId) {
147        setComponentEnabledSetting(
148                IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
149                toEnable,
150                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
151                userId);
152    }
153
154    /**
155     * Disables a given component in a given user.
156     *
157     * @param ipm an {@link IPackageManager} object
158     * @param toDisable the component that should be disabled
159     * @param userId the id of the user where the component should be disabled.
160     */
161    @VisibleForTesting
162    void setComponentEnabledSetting(IPackageManager ipm, ComponentName toDisable,
163            int enabledSetting, int userId) {
164        try {
165            ipm.setComponentEnabledSetting(toDisable,
166                    enabledSetting, PackageManager.DONT_KILL_APP,
167                    userId);
168        } catch (RemoteException neverThrown) {
169            ProvisionLogger.loge("This should not happen.", neverThrown);
170        } catch (Exception e) {
171            ProvisionLogger.logw("Component not found, not changing enabled setting: "
172                + toDisable.toShortString());
173        }
174    }
175
176    /**
177     * Check the validity of the admin component name supplied, or try to infer this componentName
178     * from the package.
179     *
180     * We are supporting lookup by package name for legacy reasons.
181     *
182     * If mdmComponentName is supplied (not null):
183     * mdmPackageName is ignored.
184     * Check that the package of mdmComponentName is installed, that mdmComponentName is a
185     * receiver in this package, and return it. The receiver can be in disabled state.
186     *
187     * Otherwise:
188     * mdmPackageName must be supplied (not null).
189     * Check that this package is installed, try to infer a potential device admin in this package,
190     * and return it.
191     */
192    // TODO: Add unit tests
193    public ComponentName findDeviceAdmin(String mdmPackageName,
194            ComponentName mdmComponentName, Context c) throws IllegalProvisioningArgumentException {
195        if (mdmComponentName != null) {
196            mdmPackageName = mdmComponentName.getPackageName();
197        }
198        if (mdmPackageName == null) {
199            throw new IllegalProvisioningArgumentException("Neither the package name nor the"
200                    + " component name of the admin are supplied");
201        }
202        PackageInfo pi;
203        try {
204            pi = c.getPackageManager().getPackageInfo(mdmPackageName,
205                    PackageManager.GET_RECEIVERS | PackageManager.MATCH_DISABLED_COMPONENTS);
206        } catch (NameNotFoundException e) {
207            throw new IllegalProvisioningArgumentException("Mdm "+ mdmPackageName
208                    + " is not installed. ", e);
209        }
210        if (mdmComponentName != null) {
211            // If the component was specified in the intent: check that it is in the manifest.
212            checkAdminComponent(mdmComponentName, pi);
213            return mdmComponentName;
214        } else {
215            // Otherwise: try to find a potential device admin in the manifest.
216            return findDeviceAdminInPackage(mdmPackageName, pi);
217        }
218    }
219
220    /**
221     * Verifies that an admin component is part of a given package.
222     *
223     * @param mdmComponentName the admin component to be checked
224     * @param pi the {@link PackageInfo} of the package to be checked.
225     *
226     * @throws IllegalProvisioningArgumentException if the given component is not part of the
227     *         package
228     */
229    private void checkAdminComponent(ComponentName mdmComponentName, PackageInfo pi)
230            throws IllegalProvisioningArgumentException{
231        for (ActivityInfo ai : pi.receivers) {
232            if (mdmComponentName.getClassName().equals(ai.name)) {
233                return;
234            }
235        }
236        throw new IllegalProvisioningArgumentException("The component " + mdmComponentName
237                + " cannot be found");
238    }
239
240    private ComponentName findDeviceAdminInPackage(String mdmPackageName, PackageInfo pi)
241            throws IllegalProvisioningArgumentException {
242        ComponentName mdmComponentName = null;
243        for (ActivityInfo ai : pi.receivers) {
244            if (!TextUtils.isEmpty(ai.permission) &&
245                    ai.permission.equals(android.Manifest.permission.BIND_DEVICE_ADMIN)) {
246                if (mdmComponentName != null) {
247                    throw new IllegalProvisioningArgumentException("There are several "
248                            + "device admins in " + mdmPackageName + " but no one in specified");
249                } else {
250                    mdmComponentName = new ComponentName(mdmPackageName, ai.name);
251                }
252            }
253        }
254        if (mdmComponentName == null) {
255            throw new IllegalProvisioningArgumentException("There are no device admins in"
256                    + mdmPackageName);
257        }
258        return mdmComponentName;
259    }
260
261    /**
262     * Returns whether the current user is the system user.
263     */
264    public boolean isCurrentUserSystem() {
265        return UserHandle.myUserId() == UserHandle.USER_SYSTEM;
266    }
267
268    /**
269     * Returns whether the device is currently managed.
270     */
271    public boolean isDeviceManaged(Context context) {
272        DevicePolicyManager dpm =
273                (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
274        return dpm.isDeviceManaged();
275    }
276
277    /**
278     * Returns true if the given package requires an update.
279     *
280     * <p>There are two cases where an update is required:
281     * 1. The package is not currently present on the device.
282     * 2. The package is present, but the version is below the minimum supported version.
283     *
284     * @param packageName the package to be checked for updates
285     * @param minSupportedVersion the minimum supported version
286     * @param context a {@link Context} object
287     */
288    public boolean packageRequiresUpdate(String packageName, int minSupportedVersion,
289            Context context) {
290        try {
291            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0);
292            // Always download packages if no minimum version given.
293            if (minSupportedVersion != PackageDownloadInfo.DEFAULT_MINIMUM_VERSION
294                    && packageInfo.versionCode >= minSupportedVersion) {
295                return false;
296            }
297        } catch (NameNotFoundException e) {
298            // Package not on device.
299        }
300
301        return true;
302    }
303
304    /**
305     * Returns whether USER_SETUP_COMPLETE is set on the calling user.
306     */
307    public boolean isUserSetupCompleted(Context context) {
308        return Secure.getInt(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 0) != 0;
309    }
310
311    /**
312     * Returns whether DEVICE_PROVISIONED is set.
313     */
314    public boolean isDeviceProvisioned(Context context) {
315        return Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0;
316    }
317
318    /**
319     * Sets whether package verification is enabled or not.
320     */
321    public void setPackageVerifierEnabled(Context context, boolean packageVerifierEnabled) {
322        Global.putInt(context.getContentResolver(), Global.PACKAGE_VERIFIER_ENABLE,
323                packageVerifierEnabled ? 1 : 0);
324    }
325
326    /**
327     * Returns whether package verification is enabled or not.
328     */
329    public boolean isPackageVerifierEnabled(Context context) {
330        return Global.getInt(context.getContentResolver(), Global.PACKAGE_VERIFIER_ENABLE, 0) != 0;
331    }
332
333    /**
334     * Set the current users userProvisioningState depending on the following factors:
335     * <ul>
336     *     <li>We're setting up a managed-profile - need to set state on two users.</li>
337     *     <li>User-setup has previously been completed or not - skip states relating to
338     *     communicating with setup-wizard</li>
339     *     <li>DPC requested we skip the rest of setup-wizard.</li>
340     * </ul>
341     *
342     * @param context a {@link Context} object
343     * @param params configuration for current provisioning attempt
344     */
345    // TODO: Add unit tests
346    public void markUserProvisioningStateInitiallyDone(Context context,
347            ProvisioningParams params) {
348        int currentUserId = UserHandle.myUserId();
349        int managedProfileUserId = UserHandle.USER_NULL;
350        DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
351
352        // new provisioning state for current user, if non-null
353        Integer newState = null;
354         // New provisioning state for managed-profile of current user, if non-null.
355        Integer newProfileState = null;
356
357        boolean userSetupCompleted = isUserSetupCompleted(context);
358        if (params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) {
359            // Managed profiles are a special case as two users are involved.
360            managedProfileUserId = getManagedProfile(context).getIdentifier();
361            if (userSetupCompleted) {
362                // SUW on current user is complete, so nothing much to do beyond indicating we're
363                // all done.
364                newProfileState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED;
365            } else {
366                // We're still in SUW, so indicate that a managed-profile was setup on current user,
367                // and that we're awaiting finalization on both.
368                newState = DevicePolicyManager.STATE_USER_PROFILE_COMPLETE;
369                newProfileState = DevicePolicyManager.STATE_USER_SETUP_COMPLETE;
370            }
371        } else if (userSetupCompleted) {
372            // User setup was previously completed this is an unexpected case.
373            ProvisionLogger.logw("user_setup_complete set, but provisioning was started");
374        } else if (params.skipUserSetup) {
375            // DPC requested setup-wizard is skipped, indicate this to SUW.
376            newState = DevicePolicyManager.STATE_USER_SETUP_COMPLETE;
377        } else {
378            // DPC requested setup-wizard is not skipped, indicate this to SUW.
379            newState = DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE;
380        }
381
382        if (newState != null) {
383            setUserProvisioningState(dpm, newState, currentUserId);
384        }
385        if (newProfileState != null) {
386            setUserProvisioningState(dpm, newProfileState, managedProfileUserId);
387        }
388        if (!userSetupCompleted) {
389            // We expect a PROVISIONING_FINALIZATION intent to finish setup if we're still in
390            // user-setup.
391            FinalizationActivity.storeProvisioningParams(context, params);
392        }
393    }
394
395    /**
396     * Finalize the current users userProvisioningState depending on the following factors:
397     * <ul>
398     *     <li>We're setting up a managed-profile - need to set state on two users.</li>
399     * </ul>
400     *
401     * @param context a {@link Context} object
402     * @param params configuration for current provisioning attempt - if null (because
403     *               ManagedProvisioning wasn't used for first phase of provisioning) aassumes we
404     *               can just mark current user as being in finalized provisioning state
405     */
406    // TODO: Add unit tests
407    public void markUserProvisioningStateFinalized(Context context,
408            ProvisioningParams params) {
409        int currentUserId = UserHandle.myUserId();
410        int managedProfileUserId = -1;
411        DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
412        Integer newState = null;
413        Integer newProfileState = null;
414
415        if (params != null && params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) {
416            // Managed profiles are a special case as two users are involved.
417            managedProfileUserId = getManagedProfile(context).getIdentifier();
418
419            newState = DevicePolicyManager.STATE_USER_UNMANAGED;
420            newProfileState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED;
421        } else {
422            newState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED;
423        }
424
425        if (newState != null) {
426            setUserProvisioningState(dpm, newState, currentUserId);
427        }
428        if (newProfileState != null) {
429            setUserProvisioningState(dpm, newProfileState, managedProfileUserId);
430        }
431    }
432
433    private void setUserProvisioningState(DevicePolicyManager dpm, int state, int userId) {
434        ProvisionLogger.logi("Setting userProvisioningState for user " + userId + " to: " + state);
435        dpm.setUserProvisioningState(state, userId);
436    }
437
438    /**
439     * Returns the first existing managed profile if any present, null otherwise.
440     *
441     * <p>Note that we currently only support one managed profile per device.
442     */
443    // TODO: Add unit tests
444    public UserHandle getManagedProfile(Context context) {
445        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
446        int currentUserId = userManager.getUserHandle();
447        List<UserInfo> userProfiles = userManager.getProfiles(currentUserId);
448        for (UserInfo profile : userProfiles) {
449            if (profile.isManagedProfile()) {
450                return new UserHandle(profile.id);
451            }
452        }
453        return null;
454    }
455
456    /**
457     * Returns the user id of an already existing managed profile or -1 if none exists.
458     */
459    // TODO: Add unit tests
460    public int alreadyHasManagedProfile(Context context) {
461        UserHandle managedUser = getManagedProfile(context);
462        if (managedUser != null) {
463            return managedUser.getIdentifier();
464        } else {
465            return -1;
466        }
467    }
468
469    /**
470     * Removes an account.
471     *
472     * <p>This removes the given account from the calling user's list of accounts.
473     *
474     * @param context a {@link Context} object
475     * @param account the account to be removed
476     */
477    // TODO: Add unit tests
478    public void removeAccount(Context context, Account account) {
479        try {
480            AccountManager accountManager =
481                    (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
482            AccountManagerFuture<Bundle> bundle = accountManager.removeAccount(account,
483                    null, null /* callback */, null /* handler */);
484            // Block to get the result of the removeAccount operation
485            if (bundle.getResult().getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
486                ProvisionLogger.logw("Account removed from the primary user.");
487            } else {
488                Intent removeIntent = (Intent) bundle.getResult().getParcelable(
489                        AccountManager.KEY_INTENT);
490                if (removeIntent != null) {
491                    ProvisionLogger.logi("Starting activity to remove account");
492                    TrampolineActivity.startActivity(context, removeIntent);
493                } else {
494                    ProvisionLogger.logw("Could not remove account from the primary user.");
495                }
496            }
497        } catch (OperationCanceledException | AuthenticatorException | IOException e) {
498            ProvisionLogger.logw("Exception removing account from the primary user.", e);
499        }
500    }
501
502    /**
503     * Returns whether FRP is supported on the device.
504     */
505    public boolean isFrpSupported(Context context) {
506        Object pdbManager = context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
507        return pdbManager != null;
508    }
509
510    /**
511     * Translates a given managed provisioning intent to its corresponding provisioning flow, using
512     * the action from the intent.
513     *
514     * <p/>This is necessary because, unlike other provisioning actions which has 1:1 mapping, there
515     * are multiple actions that can trigger the device owner provisioning flow. This includes
516     * {@link ACTION_PROVISION_MANAGED_DEVICE}, {@link ACTION_NDEF_DISCOVERED} and
517     * {@link ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}. These 3 actions are equivalent
518     * excepts they are sent from a different source.
519     *
520     * @return the appropriate DevicePolicyManager declared action for the given incoming intent.
521     * @throws IllegalProvisioningArgumentException if intent is malformed
522     */
523    // TODO: Add unit tests
524    public String mapIntentToDpmAction(Intent intent)
525            throws IllegalProvisioningArgumentException {
526        if (intent == null || intent.getAction() == null) {
527            throw new IllegalProvisioningArgumentException("Null intent action.");
528        }
529
530        // Map the incoming intent to a DevicePolicyManager.ACTION_*, as there is a N:1 mapping in
531        // some cases.
532        String dpmProvisioningAction;
533        switch (intent.getAction()) {
534            // Trivial cases.
535            case ACTION_PROVISION_MANAGED_DEVICE:
536            case ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE:
537            case ACTION_PROVISION_MANAGED_USER:
538            case ACTION_PROVISION_MANAGED_PROFILE:
539                dpmProvisioningAction = intent.getAction();
540                break;
541
542            // NFC cases which need to take mime-type into account.
543            case ACTION_NDEF_DISCOVERED:
544                String mimeType = intent.getType();
545                switch (mimeType) {
546                    case MIME_TYPE_PROVISIONING_NFC:
547                        dpmProvisioningAction = ACTION_PROVISION_MANAGED_DEVICE;
548                        break;
549
550                    default:
551                        throw new IllegalProvisioningArgumentException(
552                                "Unknown NFC bump mime-type: " + mimeType);
553                }
554                break;
555
556            // Device owner provisioning from a trusted app.
557            // TODO (b/27217042): review for new management modes in split system-user model
558            case ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE:
559                dpmProvisioningAction = ACTION_PROVISION_MANAGED_DEVICE;
560                break;
561
562            default:
563                throw new IllegalProvisioningArgumentException("Unknown intent action "
564                        + intent.getAction());
565        }
566        return dpmProvisioningAction;
567    }
568
569    /**
570     * Sends an intent to trigger a factory reset.
571     */
572    // TODO: Move the FR intent into a Globals class.
573    public void sendFactoryResetBroadcast(Context context, String reason) {
574        Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
575        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
576        intent.putExtra(Intent.EXTRA_REASON, reason);
577        context.sendBroadcast(intent);
578    }
579
580    /**
581     * Returns whether the given provisioning action is a profile owner action.
582     */
583    // TODO: Move the list of device owner actions into a Globals class.
584    public final boolean isProfileOwnerAction(String action) {
585        return action.equals(ACTION_PROVISION_MANAGED_PROFILE)
586                || action.equals(ACTION_PROVISION_MANAGED_USER);
587    }
588
589    /**
590     * Returns whether the given provisioning action is a device owner action.
591     */
592    // TODO: Move the list of device owner actions into a Globals class.
593    public final boolean isDeviceOwnerAction(String action) {
594        return action.equals(ACTION_PROVISION_MANAGED_DEVICE)
595                || action.equals(ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE);
596    }
597
598    /**
599     * Returns whether the device currently has connectivity.
600     */
601    public boolean isConnectedToNetwork(Context context) {
602        NetworkInfo info = getActiveNetworkInfo(context);
603        return info != null && info.isConnected();
604    }
605
606    /**
607     * Returns whether the device is currently connected to a wifi.
608     */
609    public boolean isConnectedToWifi(Context context) {
610        NetworkInfo info = getActiveNetworkInfo(context);
611        return info != null
612                && info.isConnected()
613                && info.getType() == ConnectivityManager.TYPE_WIFI;
614    }
615
616    private NetworkInfo getActiveNetworkInfo(Context context) {
617        ConnectivityManager cm =
618                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
619        if (cm != null) {
620            return cm.getActiveNetworkInfo();
621        }
622        return null;
623    }
624
625    /**
626     * Returns whether encryption is required on this device.
627     *
628     * <p>Encryption is required if the device is not currently encrypted and the persistent
629     * system flag {@code persist.sys.no_req_encrypt} is not set.
630     */
631    public boolean isEncryptionRequired() {
632        return !isPhysicalDeviceEncrypted()
633                && !SystemProperties.getBoolean("persist.sys.no_req_encrypt", false);
634    }
635
636    /**
637     * Returns whether the device is currently encrypted.
638     */
639    public boolean isPhysicalDeviceEncrypted() {
640        return StorageManager.isEncrypted();
641    }
642
643    /**
644     * Returns the wifi pick intent.
645     */
646    // TODO: Move this intent into a Globals class.
647    public Intent getWifiPickIntent() {
648        Intent wifiIntent = new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK);
649        wifiIntent.putExtra("extra_prefs_show_button_bar", true);
650        wifiIntent.putExtra("wifi_enable_next_on_connect", true);
651        return wifiIntent;
652    }
653
654    /**
655     * Returns whether the device has a split system user.
656     *
657     * <p>Split system user means that user 0 is system only and all meat users are separate from
658     * the system user.
659     */
660    public boolean isSplitSystemUser() {
661        return UserManager.isSplitSystemUser();
662    }
663
664    /**
665     * Returns whether the currently chosen launcher supports managed profiles.
666     *
667     * <p>A launcher is deemed to support managed profiles when its target API version is at least
668     * {@link Build.VERSION_CODES#LOLLIPOP}.
669     */
670    public boolean currentLauncherSupportsManagedProfiles(Context context) {
671        Intent intent = new Intent(Intent.ACTION_MAIN);
672        intent.addCategory(Intent.CATEGORY_HOME);
673
674        PackageManager pm = context.getPackageManager();
675        ResolveInfo launcherResolveInfo = pm.resolveActivity(intent,
676                PackageManager.MATCH_DEFAULT_ONLY);
677        if (launcherResolveInfo == null) {
678            return false;
679        }
680        try {
681            // If the user has not chosen a default launcher, then launcherResolveInfo will be
682            // referring to the resolver activity. It is fine to create a managed profile in
683            // this case since there will always be at least one launcher on the device that
684            // supports managed profile feature.
685            ApplicationInfo launcherAppInfo = pm.getApplicationInfo(
686                    launcherResolveInfo.activityInfo.packageName, 0 /* default flags */);
687            return versionNumberAtLeastL(launcherAppInfo.targetSdkVersion);
688        } catch (PackageManager.NameNotFoundException e) {
689            return false;
690        }
691    }
692
693    /**
694     * Returns whether the given version number is at least lollipop.
695     *
696     * @param versionNumber the version number to be verified.
697     */
698    private boolean versionNumberAtLeastL(int versionNumber) {
699        return versionNumber >= Build.VERSION_CODES.LOLLIPOP;
700    }
701
702    /**
703     * Computes the sha 256 hash of a byte array.
704     */
705    public byte[] computeHashOfByteArray(byte[] bytes) throws NoSuchAlgorithmException {
706        MessageDigest md = MessageDigest.getInstance(SHA256_TYPE);
707        md.update(bytes);
708        return md.digest();
709    }
710
711    /**
712     * Computes a hash of a file with a spcific hash algorithm.
713     */
714    public byte[] computeHashOfFile(String fileLocation, String hashType) {
715        InputStream fis = null;
716        MessageDigest md;
717        byte hash[] = null;
718        try {
719            md = MessageDigest.getInstance(hashType);
720        } catch (NoSuchAlgorithmException e) {
721            ProvisionLogger.loge("Hashing algorithm " + hashType + " not supported.", e);
722            return null;
723        }
724        try {
725            fis = new FileInputStream(fileLocation);
726
727            byte[] buffer = new byte[256];
728            int n = 0;
729            while (n != -1) {
730                n = fis.read(buffer);
731                if (n > 0) {
732                    md.update(buffer, 0, n);
733                }
734            }
735            hash = md.digest();
736        } catch (IOException e) {
737            ProvisionLogger.loge("IO error.", e);
738        } finally {
739            // Close input stream quietly.
740            try {
741                if (fis != null) {
742                    fis.close();
743                }
744            } catch (IOException e) {
745                // Ignore.
746            }
747        }
748        return hash;
749    }
750}
751