Home | History | Annotate | Download | only in common
      1 /*
      2  * Copyright 2014, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.managedprovisioning.common;
     18 
     19 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
     20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
     21 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
     22 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE;
     23 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
     24 import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC;
     25 import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED;
     26 import static java.nio.charset.StandardCharsets.UTF_8;
     27 
     28 import android.accounts.Account;
     29 import android.accounts.AccountManager;
     30 import android.accounts.AccountManagerFuture;
     31 import android.accounts.AuthenticatorException;
     32 import android.accounts.OperationCanceledException;
     33 import android.app.admin.DevicePolicyManager;
     34 import android.content.ComponentName;
     35 import android.content.Context;
     36 import android.content.Intent;
     37 import android.content.pm.ActivityInfo;
     38 import android.content.pm.ApplicationInfo;
     39 import android.content.pm.IPackageManager;
     40 import android.content.pm.PackageInfo;
     41 import android.content.pm.PackageManager;
     42 import android.content.pm.PackageManager.NameNotFoundException;
     43 import android.content.pm.ResolveInfo;
     44 import android.content.pm.UserInfo;
     45 import android.graphics.Color;
     46 import android.net.ConnectivityManager;
     47 import android.net.NetworkInfo;
     48 import android.net.wifi.WifiManager;
     49 import android.nfc.NdefMessage;
     50 import android.nfc.NdefRecord;
     51 import android.nfc.NfcAdapter;
     52 import android.os.Build;
     53 import android.os.Bundle;
     54 import android.os.Parcelable;
     55 import android.os.Process;
     56 import android.os.RemoteException;
     57 import android.os.ServiceManager;
     58 import android.os.SystemProperties;
     59 import android.os.UserHandle;
     60 import android.os.UserManager;
     61 import android.os.storage.StorageManager;
     62 import android.provider.Settings.Global;
     63 import android.provider.Settings.Secure;
     64 import android.text.TextUtils;
     65 import android.util.Base64;
     66 
     67 import java.io.IOException;
     68 import java.lang.Integer;
     69 import java.lang.Math;
     70 import java.lang.String;
     71 import java.nio.charset.StandardCharsets;
     72 import java.util.HashSet;
     73 import java.util.List;
     74 import java.util.Set;
     75 import java.util.concurrent.TimeUnit;
     76 
     77 import com.android.internal.annotations.VisibleForTesting;
     78 import com.android.managedprovisioning.FinalizationActivity;
     79 import com.android.managedprovisioning.ProvisionLogger;
     80 import com.android.managedprovisioning.TrampolineActivity;
     81 import com.android.managedprovisioning.model.ProvisioningParams;
     82 import com.android.managedprovisioning.model.PackageDownloadInfo;
     83 
     84 /**
     85  * Class containing various auxiliary methods.
     86  */
     87 public 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