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