Home | History | Annotate | Download | only in managedprovisioning
      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;
     18 
     19 import android.app.admin.DevicePolicyManager;
     20 import android.content.BroadcastReceiver;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.pm.ActivityInfo;
     25 import android.content.pm.ApplicationInfo;
     26 import android.content.pm.IPackageManager;
     27 import android.content.pm.PackageInfo;
     28 import android.content.pm.PackageManager;
     29 import android.content.pm.PackageManager.NameNotFoundException;
     30 import android.content.pm.UserInfo;
     31 import android.graphics.drawable.Drawable;
     32 import android.os.AsyncTask;
     33 import android.os.Binder;
     34 import android.os.Bundle;
     35 import android.os.RemoteException;
     36 import android.os.ServiceManager;
     37 import android.os.UserHandle;
     38 import android.os.UserManager;
     39 import android.provider.Settings.Global;
     40 import android.provider.Settings.Secure;
     41 import android.text.TextUtils;
     42 import android.util.Base64;
     43 
     44 import java.io.IOException;
     45 import java.util.HashSet;
     46 import java.util.List;
     47 import java.util.Set;
     48 
     49 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
     50 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
     51 
     52 import android.accounts.Account;
     53 import android.accounts.AccountManager;
     54 import android.accounts.AccountManagerFuture;
     55 import android.accounts.AuthenticatorException;
     56 import android.accounts.OperationCanceledException;
     57 
     58 /**
     59  * Class containing various auxiliary methods.
     60  */
     61 public class Utils {
     62     private Utils() {}
     63 
     64     public static Set<String> getCurrentSystemApps(int userId) {
     65         IPackageManager ipm = IPackageManager.Stub.asInterface(ServiceManager
     66                 .getService("package"));
     67         Set<String> apps = new HashSet<String>();
     68         List<ApplicationInfo> aInfos = null;
     69         try {
     70             aInfos = ipm.getInstalledApplications(
     71                     PackageManager.GET_UNINSTALLED_PACKAGES, userId).getList();
     72         } catch (RemoteException neverThrown) {
     73             ProvisionLogger.loge("This should not happen.", neverThrown);
     74         }
     75         for (ApplicationInfo aInfo : aInfos) {
     76             if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
     77                 apps.add(aInfo.packageName);
     78             }
     79         }
     80         return apps;
     81     }
     82 
     83     public static void disableComponent(ComponentName toDisable, int userId) {
     84         try {
     85             IPackageManager ipm = IPackageManager.Stub.asInterface(ServiceManager
     86                 .getService("package"));
     87 
     88             ipm.setComponentEnabledSetting(toDisable,
     89                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP,
     90                     userId);
     91         } catch (RemoteException neverThrown) {
     92             ProvisionLogger.loge("This should not happen.", neverThrown);
     93         } catch (Exception e) {
     94             ProvisionLogger.logw("Component not found, not disabling it: "
     95                 + toDisable.toShortString());
     96         }
     97     }
     98 
     99     /**
    100      * Exception thrown when the provisioning has failed completely.
    101      *
    102      * We're using a custom exception to avoid catching subsequent exceptions that might be
    103      * significant.
    104      */
    105     public static class IllegalProvisioningArgumentException extends Exception {
    106         public IllegalProvisioningArgumentException(String message) {
    107             super(message);
    108         }
    109 
    110         public IllegalProvisioningArgumentException(String message, Throwable t) {
    111             super(message, t);
    112         }
    113     }
    114 
    115     /**
    116      * Check the validity of the admin component name supplied, or try to infer this componentName
    117      * from the package.
    118      *
    119      * We are supporting lookup by package name for legacy reasons.
    120      *
    121      * If mdmComponentName is supplied (not null):
    122      * mdmPackageName is ignored.
    123      * Check that the package of mdmComponentName is installed, that mdmComponentName is a
    124      * receiver in this package, and return it.
    125      *
    126      * Otherwise:
    127      * mdmPackageName must be supplied (not null).
    128      * Check that this package is installed, try to infer a potential device admin in this package,
    129      * and return it.
    130      */
    131     public static ComponentName findDeviceAdmin(String mdmPackageName,
    132             ComponentName mdmComponentName, Context c) throws IllegalProvisioningArgumentException {
    133         if (mdmComponentName != null) {
    134             mdmPackageName = mdmComponentName.getPackageName();
    135         }
    136         if (mdmPackageName == null) {
    137             throw new IllegalProvisioningArgumentException("Neither the package name nor the"
    138                     + " component name of the admin are supplied");
    139         }
    140         PackageInfo pi;
    141         try {
    142             pi = c.getPackageManager().getPackageInfo(mdmPackageName,
    143                     PackageManager.GET_RECEIVERS);
    144         } catch (NameNotFoundException e) {
    145             throw new IllegalProvisioningArgumentException("Mdm "+ mdmPackageName
    146                     + " is not installed. ", e);
    147         }
    148         if (mdmComponentName != null) {
    149             // If the component was specified in the intent: check that it is in the manifest.
    150             checkAdminComponent(mdmComponentName, pi);
    151             return mdmComponentName;
    152         } else {
    153             // Otherwise: try to find a potential device admin in the manifest.
    154             return findDeviceAdminInPackage(mdmPackageName, pi);
    155         }
    156     }
    157 
    158     private static void checkAdminComponent(ComponentName mdmComponentName, PackageInfo pi)
    159             throws IllegalProvisioningArgumentException{
    160         for (ActivityInfo ai : pi.receivers) {
    161             if (mdmComponentName.getClassName().equals(ai.name)) {
    162                 return;
    163             }
    164         }
    165         throw new IllegalProvisioningArgumentException("The component " + mdmComponentName
    166                 + " cannot be found");
    167     }
    168 
    169     private static ComponentName findDeviceAdminInPackage(String mdmPackageName, PackageInfo pi)
    170             throws IllegalProvisioningArgumentException {
    171         ComponentName mdmComponentName = null;
    172         for (ActivityInfo ai : pi.receivers) {
    173             if (!TextUtils.isEmpty(ai.permission) &&
    174                     ai.permission.equals(android.Manifest.permission.BIND_DEVICE_ADMIN)) {
    175                 if (mdmComponentName != null) {
    176                     throw new IllegalProvisioningArgumentException("There are several "
    177                             + "device admins in " + mdmPackageName + " but no one in specified");
    178                 } else {
    179                     mdmComponentName = new ComponentName(mdmPackageName, ai.name);
    180                 }
    181             }
    182         }
    183         if (mdmComponentName == null) {
    184             throw new IllegalProvisioningArgumentException("There are no device admins in"
    185                     + mdmPackageName);
    186         }
    187         return mdmComponentName;
    188     }
    189 
    190     public static MdmPackageInfo getMdmPackageInfo(PackageManager pm, String packageName) {
    191         if (packageName != null) {
    192             try {
    193                 ApplicationInfo ai = pm.getApplicationInfo(packageName, /* default flags */ 0);
    194                 if (ai != null) {
    195                     return new MdmPackageInfo(pm.getApplicationIcon(packageName),
    196                             pm.getApplicationLabel(ai).toString());
    197                 }
    198             } catch (PackageManager.NameNotFoundException e) {
    199                 // Package does not exist, ignore. Should never happen.
    200                 ProvisionLogger.loge("Package does not exist. Should never happen.");
    201             }
    202         }
    203 
    204         return null;
    205     }
    206 
    207     /**
    208      * Information relating to the currently installed MDM package manager.
    209      */
    210     public static final class MdmPackageInfo {
    211         private final Drawable packageIcon;
    212         private final String appLabel;
    213 
    214         private MdmPackageInfo(Drawable packageIcon, String appLabel) {
    215             this.packageIcon = packageIcon;
    216             this.appLabel = appLabel;
    217         }
    218 
    219         public String getAppLabel() {
    220             return appLabel;
    221         }
    222 
    223         public Drawable getPackageIcon() {
    224             return packageIcon;
    225         }
    226     }
    227 
    228     public static boolean isCurrentUserOwner() {
    229         return UserHandle.myUserId() == UserHandle.USER_OWNER;
    230     }
    231 
    232     public static boolean hasDeviceOwner(Context context) {
    233         DevicePolicyManager dpm =
    234                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
    235         return !TextUtils.isEmpty(dpm.getDeviceOwner());
    236     }
    237 
    238     public static boolean hasDeviceInitializer(Context context) {
    239         DevicePolicyManager dpm =
    240                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
    241         return dpm != null && dpm.getDeviceInitializerApp() != null;
    242     }
    243 
    244     public static boolean isManagedProfile(Context context) {
    245         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    246         UserInfo user = um.getUserInfo(UserHandle.myUserId());
    247         return user != null ? user.isManagedProfile() : false;
    248     }
    249 
    250     /**
    251      * Returns true if the given package does not exist on the device or if its version code is less
    252      * than the given version, and false otherwise.
    253      */
    254     public static boolean packageRequiresUpdate(String packageName, int minSupportedVersion,
    255             Context context) {
    256         try {
    257             PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0);
    258             if (packageInfo.versionCode >= minSupportedVersion) {
    259                 return false;
    260             }
    261         } catch (NameNotFoundException e) {
    262             // Package not on device.
    263         }
    264 
    265         return true;
    266     }
    267 
    268     public static byte[] stringToByteArray(String s)
    269         throws NumberFormatException {
    270         try {
    271             return Base64.decode(s, Base64.URL_SAFE);
    272         } catch (IllegalArgumentException e) {
    273             throw new NumberFormatException("Incorrect format. Should be Url-safe Base64 encoded.");
    274         }
    275     }
    276 
    277     public static String byteArrayToString(byte[] bytes) {
    278         return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
    279     }
    280 
    281     public static void markDeviceProvisioned(Context context) {
    282         if (isCurrentUserOwner()) {
    283             // This only needs to be set once per device
    284             Global.putInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
    285         }
    286 
    287         // Setting this flag will either cause Setup Wizard to finish immediately when it starts (if
    288         // it is not already running), or when its next activity starts (if it is already running,
    289         // e.g. the non-NFC flow).
    290         // When either of these things happen, a home intent is fired. We catch that in
    291         // HomeReceiverActivity before sending the intent to notify the mdm that provisioning is
    292         // complete.
    293         // Note that, in the NFC flow or for secondary users, setting this flag will prevent the
    294         // user from seeing SUW, even if no other device initialization app was specified.
    295         Secure.putInt(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1);
    296     }
    297 
    298     public static boolean isUserSetupCompleted(Context context) {
    299         return Secure.getInt(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 0) != 0;
    300     }
    301 
    302     public static UserHandle getManagedProfile(Context context) {
    303         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
    304         int currentUserId = userManager.getUserHandle();
    305         List<UserInfo> userProfiles = userManager.getProfiles(currentUserId);
    306         for (UserInfo profile : userProfiles) {
    307             if (profile.isManagedProfile()) {
    308                 return new UserHandle(profile.id);
    309             }
    310         }
    311         return null;
    312     }
    313 
    314     /**
    315      * @return The User id of an already existing managed profile or -1 if none
    316      * exists
    317      */
    318     public static int alreadyHasManagedProfile(Context context) {
    319         UserHandle managedUser = getManagedProfile(context);
    320         if (managedUser != null) {
    321             return managedUser.getIdentifier();
    322         } else {
    323             return -1;
    324         }
    325     }
    326 
    327     public static void removeAccount(Context context, Account account) {
    328         try {
    329             AccountManager accountManager =
    330                     (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
    331             AccountManagerFuture<Bundle> bundle = accountManager.removeAccount(account,
    332                     null, null /* callback */, null /* handler */);
    333             // Block to get the result of the removeAccount operation
    334             if (bundle.getResult().getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
    335                 ProvisionLogger.logw("Account removed from the primary user.");
    336             } else {
    337                 Intent removeIntent = (Intent) bundle.getResult().getParcelable(
    338                         AccountManager.KEY_INTENT);
    339                 if (removeIntent != null) {
    340                     ProvisionLogger.logi("Starting activity to remove account");
    341                     TrampolineActivity.startActivity(context, removeIntent);
    342                 } else {
    343                     ProvisionLogger.logw("Could not remove account from the primary user.");
    344                 }
    345             }
    346         } catch (OperationCanceledException | AuthenticatorException | IOException e) {
    347             ProvisionLogger.logw("Exception removing account from the primary user.", e);
    348         }
    349     }
    350 
    351     public static boolean isFrpSupported(Context context) {
    352         Object pdbManager = context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
    353         return pdbManager != null;
    354     }
    355 
    356 }
    357