Utils.java revision efef0fb3b283f6ef3c7e97a02e4d50cd35c27259
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.IOException;
68import java.lang.Integer;
69import java.lang.Math;
70import java.lang.String;
71import java.nio.charset.StandardCharsets;
72import java.util.HashSet;
73import java.util.List;
74import java.util.Set;
75import java.util.concurrent.TimeUnit;
76
77import com.android.internal.annotations.VisibleForTesting;
78import com.android.managedprovisioning.FinalizationActivity;
79import com.android.managedprovisioning.ProvisionLogger;
80import com.android.managedprovisioning.TrampolineActivity;
81import com.android.managedprovisioning.model.ProvisioningParams;
82import com.android.managedprovisioning.model.PackageDownloadInfo;
83
84/**
85 * Class containing various auxiliary methods.
86 */
87public class Utils {
88    private static final int ACCOUNT_COPY_TIMEOUT_SECONDS = 60 * 3;  // 3 minutes
89
90    private static final int THRESHOLD_BRIGHT_COLOR = 160; // A color needs a brightness of at least
91    // this value to be considered bright. (brightness being between 0 and 255).
92    public Utils() {}
93
94    /**
95     * Returns the currently installed system apps on a given user.
96     *
97     * <p>Calls into the {@link IPackageManager} to retrieve all installed packages on the given
98     * user and returns the package names of all system apps.
99     *
100     * @param ipm an {@link IPackageManager} object
101     * @param userId the id of the user we are interested in
102     */
103    public Set<String> getCurrentSystemApps(IPackageManager ipm, int userId) {
104        Set<String> apps = new HashSet<String>();
105        List<ApplicationInfo> aInfos = null;
106        try {
107            aInfos = ipm.getInstalledApplications(
108                    PackageManager.GET_UNINSTALLED_PACKAGES, userId).getList();
109        } catch (RemoteException neverThrown) {
110            ProvisionLogger.loge("This should not happen.", neverThrown);
111        }
112        for (ApplicationInfo aInfo : aInfos) {
113            if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
114                apps.add(aInfo.packageName);
115            }
116        }
117        return apps;
118    }
119
120    /**
121     * Disables a given component in a given user.
122     *
123     * @param toDisable the component that should be disabled
124     * @param userId the id of the user where the component should be disabled.
125     */
126    public void disableComponent(ComponentName toDisable, int userId) {
127        setComponentEnabledSetting(
128                IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
129                toDisable,
130                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
131                userId);
132    }
133
134    /**
135     * Enables a given component in a given user.
136     *
137     * @param toEnable the component that should be enabled
138     * @param userId the id of the user where the component should be disabled.
139     */
140    public void enableComponent(ComponentName toEnable, int userId) {
141        setComponentEnabledSetting(
142                IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
143                toEnable,
144                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
145                userId);
146    }
147
148    /**
149     * Disables a given component in a given user.
150     *
151     * @param ipm an {@link IPackageManager} object
152     * @param toDisable the component that should be disabled
153     * @param userId the id of the user where the component should be disabled.
154     */
155    @VisibleForTesting
156    void setComponentEnabledSetting(IPackageManager ipm, ComponentName toDisable,
157            int enabledSetting, int userId) {
158        try {
159            ipm.setComponentEnabledSetting(toDisable,
160                    enabledSetting, PackageManager.DONT_KILL_APP,
161                    userId);
162        } catch (RemoteException neverThrown) {
163            ProvisionLogger.loge("This should not happen.", neverThrown);
164        } catch (Exception e) {
165            ProvisionLogger.logw("Component not found, not changing enabled setting: "
166                + toDisable.toShortString());
167        }
168    }
169
170    /**
171     * Check the validity of the admin component name supplied, or try to infer this componentName
172     * from the package.
173     *
174     * We are supporting lookup by package name for legacy reasons.
175     *
176     * If mdmComponentName is supplied (not null):
177     * mdmPackageName is ignored.
178     * Check that the package of mdmComponentName is installed, that mdmComponentName is a
179     * receiver in this package, and return it. The receiver can be in disabled state.
180     *
181     * Otherwise:
182     * mdmPackageName must be supplied (not null).
183     * Check that this package is installed, try to infer a potential device admin in this package,
184     * and return it.
185     */
186    // TODO: Add unit tests
187    public ComponentName findDeviceAdmin(String mdmPackageName,
188            ComponentName mdmComponentName, Context c) throws IllegalProvisioningArgumentException {
189        if (mdmComponentName != null) {
190            mdmPackageName = mdmComponentName.getPackageName();
191        }
192        if (mdmPackageName == null) {
193            throw new IllegalProvisioningArgumentException("Neither the package name nor the"
194                    + " component name of the admin are supplied");
195        }
196        PackageInfo pi;
197        try {
198            pi = c.getPackageManager().getPackageInfo(mdmPackageName,
199                    PackageManager.GET_RECEIVERS | PackageManager.MATCH_DISABLED_COMPONENTS);
200        } catch (NameNotFoundException e) {
201            throw new IllegalProvisioningArgumentException("Mdm "+ mdmPackageName
202                    + " is not installed. ", e);
203        }
204        if (mdmComponentName != null) {
205            // If the component was specified in the intent: check that it is in the manifest.
206            checkAdminComponent(mdmComponentName, pi);
207            return mdmComponentName;
208        } else {
209            // Otherwise: try to find a potential device admin in the manifest.
210            return findDeviceAdminInPackage(mdmPackageName, pi);
211        }
212    }
213
214    /**
215     * Verifies that an admin component is part of a given package.
216     *
217     * @param mdmComponentName the admin component to be checked
218     * @param pi the {@link PackageInfo} of the package to be checked.
219     *
220     * @throws IllegalProvisioningArgumentException if the given component is not part of the
221     *         package
222     */
223    private void checkAdminComponent(ComponentName mdmComponentName, PackageInfo pi)
224            throws IllegalProvisioningArgumentException{
225        for (ActivityInfo ai : pi.receivers) {
226            if (mdmComponentName.getClassName().equals(ai.name)) {
227                return;
228            }
229        }
230        throw new IllegalProvisioningArgumentException("The component " + mdmComponentName
231                + " cannot be found");
232    }
233
234    private ComponentName findDeviceAdminInPackage(String mdmPackageName, PackageInfo pi)
235            throws IllegalProvisioningArgumentException {
236        ComponentName mdmComponentName = null;
237        for (ActivityInfo ai : pi.receivers) {
238            if (!TextUtils.isEmpty(ai.permission) &&
239                    ai.permission.equals(android.Manifest.permission.BIND_DEVICE_ADMIN)) {
240                if (mdmComponentName != null) {
241                    throw new IllegalProvisioningArgumentException("There are several "
242                            + "device admins in " + mdmPackageName + " but no one in specified");
243                } else {
244                    mdmComponentName = new ComponentName(mdmPackageName, ai.name);
245                }
246            }
247        }
248        if (mdmComponentName == null) {
249            throw new IllegalProvisioningArgumentException("There are no device admins in"
250                    + mdmPackageName);
251        }
252        return mdmComponentName;
253    }
254
255    /**
256     * Returns whether the current user is the system user.
257     */
258    public boolean isCurrentUserSystem() {
259        return UserHandle.myUserId() == UserHandle.USER_SYSTEM;
260    }
261
262    /**
263     * Returns whether the device is currently managed.
264     */
265    public boolean isDeviceManaged(Context context) {
266        DevicePolicyManager dpm =
267                (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
268        return dpm.isDeviceManaged();
269    }
270
271    /**
272     * Returns whether the calling user is a managed profile.
273     */
274    public boolean isManagedProfile(Context context) {
275        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
276        UserInfo user = um.getUserInfo(UserHandle.myUserId());
277        return user != null ? user.isManagedProfile() : false;
278    }
279
280    /**
281     * Returns true if the given package requires an update.
282     *
283     * <p>There are two cases where an update is required:
284     * 1. The package is not currently present on the device.
285     * 2. The package is present, but the version is below the minimum supported version.
286     *
287     * @param packageName the package to be checked for updates
288     * @param minSupportedVersion the minimum supported version
289     * @param context a {@link Context} object
290     */
291    public boolean packageRequiresUpdate(String packageName, int minSupportedVersion,
292            Context context) {
293        try {
294            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0);
295            // Always download packages if no minimum version given.
296            if (minSupportedVersion != PackageDownloadInfo.DEFAULT_MINIMUM_VERSION
297                    && packageInfo.versionCode >= minSupportedVersion) {
298                return false;
299            }
300        } catch (NameNotFoundException e) {
301            // Package not on device.
302        }
303
304        return true;
305    }
306
307    /**
308     * Transforms a string into a byte array.
309     *
310     * @param s the string to be transformed
311     */
312    public byte[] stringToByteArray(String s)
313        throws NumberFormatException {
314        try {
315            return Base64.decode(s, Base64.URL_SAFE);
316        } catch (IllegalArgumentException e) {
317            throw new NumberFormatException("Incorrect format. Should be Url-safe Base64 encoded.");
318        }
319    }
320
321    /**
322     * Transforms a byte array into a string.
323     *
324     * @param bytes the byte array to be transformed
325     */
326    public String byteArrayToString(byte[] bytes) {
327        return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
328    }
329
330    /**
331     * Sets user setup complete on a given user.
332     *
333     * <p>This will set USER_SETUP_COMPLETE to 1 on the given user.
334     */
335    public void markUserSetupComplete(Context context, int userId) {
336        ProvisionLogger.logd("Setting USER_SETUP_COMPLETE to 1 for user " + userId);
337        Secure.putIntForUser(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, userId);
338    }
339
340    /**
341     * Returns whether USER_SETUP_COMPLETE is set on the calling user.
342     */
343    public boolean isUserSetupCompleted(Context context) {
344        return Secure.getInt(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 0) != 0;
345    }
346
347    /**
348     * Returns whether DEVICE_PROVISIONED is set.
349     */
350    public boolean isDeviceProvisioned(Context context) {
351        return Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 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