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