Sander Alewijnse | d5e4c42 | 2014-11-25 17:56:16 +0000 | [diff] [blame] | 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 | |
Joe Delfino | 84e56f5 | 2015-03-27 09:56:18 -0400 | [diff] [blame] | 19 | import android.app.admin.DevicePolicyManager; |
Alan Treadway | 9a42f2b | 2015-02-25 15:12:31 +0000 | [diff] [blame] | 20 | import android.content.ComponentName; |
Nicolas Prevot | 0b44725 | 2015-03-09 14:59:02 +0000 | [diff] [blame] | 21 | import android.content.Context; |
Alan Treadway | 9a42f2b | 2015-02-25 15:12:31 +0000 | [diff] [blame] | 22 | import android.content.Intent; |
Nicolas Prevot | 0b44725 | 2015-03-09 14:59:02 +0000 | [diff] [blame] | 23 | import android.content.pm.ActivityInfo; |
Sander Alewijnse | d5e4c42 | 2014-11-25 17:56:16 +0000 | [diff] [blame] | 24 | import android.content.pm.ApplicationInfo; |
| 25 | import android.content.pm.IPackageManager; |
Nicolas Prevot | 0b44725 | 2015-03-09 14:59:02 +0000 | [diff] [blame] | 26 | import android.content.pm.PackageInfo; |
Sander Alewijnse | d5e4c42 | 2014-11-25 17:56:16 +0000 | [diff] [blame] | 27 | import android.content.pm.PackageManager; |
Nicolas Prevot | 0b44725 | 2015-03-09 14:59:02 +0000 | [diff] [blame] | 28 | import android.content.pm.PackageManager.NameNotFoundException; |
Joe Delfino | 84e56f5 | 2015-03-27 09:56:18 -0400 | [diff] [blame] | 29 | import android.content.pm.UserInfo; |
Alan Treadway | 9a42f2b | 2015-02-25 15:12:31 +0000 | [diff] [blame] | 30 | import android.graphics.drawable.Drawable; |
Joe Delfino | 84e56f5 | 2015-03-27 09:56:18 -0400 | [diff] [blame] | 31 | import android.os.Binder; |
Sander Alewijnse | d5e4c42 | 2014-11-25 17:56:16 +0000 | [diff] [blame] | 32 | import android.os.RemoteException; |
| 33 | import android.os.ServiceManager; |
Joe Delfino | d0f2928 | 2015-03-19 08:26:09 -0400 | [diff] [blame] | 34 | import android.os.UserHandle; |
Joe Delfino | 84e56f5 | 2015-03-27 09:56:18 -0400 | [diff] [blame] | 35 | import android.os.UserManager; |
Nicolas Prevot | 0b44725 | 2015-03-09 14:59:02 +0000 | [diff] [blame] | 36 | import android.text.TextUtils; |
Sander Alewijnse | 3efa83a | 2015-04-14 14:46:18 +0100 | [diff] [blame] | 37 | import android.util.Base64; |
Sander Alewijnse | d5e4c42 | 2014-11-25 17:56:16 +0000 | [diff] [blame] | 38 | |
Sander Alewijnse | d5e4c42 | 2014-11-25 17:56:16 +0000 | [diff] [blame] | 39 | import java.util.HashSet; |
Alan Treadway | 9a42f2b | 2015-02-25 15:12:31 +0000 | [diff] [blame] | 40 | import java.util.List; |
Sander Alewijnse | d5e4c42 | 2014-11-25 17:56:16 +0000 | [diff] [blame] | 41 | import java.util.Set; |
| 42 | |
Alan Treadway | 9a42f2b | 2015-02-25 15:12:31 +0000 | [diff] [blame] | 43 | import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; |
| 44 | import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME; |
| 45 | |
Sander Alewijnse | d5e4c42 | 2014-11-25 17:56:16 +0000 | [diff] [blame] | 46 | /** |
| 47 | * Class containing various auxiliary methods. |
| 48 | */ |
| 49 | public class Utils { |
Alan Treadway | 9a42f2b | 2015-02-25 15:12:31 +0000 | [diff] [blame] | 50 | private Utils() {} |
| 51 | |
Sander Alewijnse | d5e4c42 | 2014-11-25 17:56:16 +0000 | [diff] [blame] | 52 | public static Set<String> getCurrentSystemApps(int userId) { |
| 53 | IPackageManager ipm = IPackageManager.Stub.asInterface(ServiceManager |
| 54 | .getService("package")); |
| 55 | Set<String> apps = new HashSet<String>(); |
| 56 | List<ApplicationInfo> aInfos = null; |
| 57 | try { |
| 58 | aInfos = ipm.getInstalledApplications( |
| 59 | PackageManager.GET_UNINSTALLED_PACKAGES, userId).getList(); |
| 60 | } catch (RemoteException neverThrown) { |
| 61 | ProvisionLogger.loge("This should not happen.", neverThrown); |
| 62 | } |
| 63 | for (ApplicationInfo aInfo : aInfos) { |
| 64 | if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { |
| 65 | apps.add(aInfo.packageName); |
| 66 | } |
| 67 | } |
| 68 | return apps; |
| 69 | } |
| 70 | |
| 71 | public static void disableComponent(ComponentName toDisable, int userId) { |
| 72 | try { |
| 73 | IPackageManager ipm = IPackageManager.Stub.asInterface(ServiceManager |
| 74 | .getService("package")); |
| 75 | |
| 76 | ipm.setComponentEnabledSetting(toDisable, |
| 77 | PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP, |
| 78 | userId); |
| 79 | } catch (RemoteException neverThrown) { |
| 80 | ProvisionLogger.loge("This should not happen.", neverThrown); |
| 81 | } catch (Exception e) { |
| 82 | ProvisionLogger.logw("Component not found, not disabling it: " |
| 83 | + toDisable.toShortString()); |
| 84 | } |
| 85 | } |
Nicolas Prevot | 0b44725 | 2015-03-09 14:59:02 +0000 | [diff] [blame] | 86 | |
| 87 | public static ComponentName findDeviceAdminFromIntent(Intent intent, Context c) |
| 88 | throws IllegalProvisioningArgumentException { |
| 89 | ComponentName mdmComponentName = intent.getParcelableExtra( |
| 90 | EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME); |
| 91 | String mdmPackageName = intent.getStringExtra( |
| 92 | EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME); |
| 93 | return findDeviceAdmin(mdmPackageName, mdmComponentName, c); |
| 94 | } |
| 95 | |
| 96 | /** |
| 97 | * Exception thrown when the provisioning has failed completely. |
| 98 | * |
| 99 | * We're using a custom exception to avoid catching subsequent exceptions that might be |
| 100 | * significant. |
| 101 | */ |
| 102 | public static class IllegalProvisioningArgumentException extends Exception { |
| 103 | public IllegalProvisioningArgumentException(String message) { |
| 104 | super(message); |
| 105 | } |
| 106 | |
| 107 | public IllegalProvisioningArgumentException(String message, Throwable t) { |
| 108 | super(message, t); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * Check the validity of the admin component name supplied, or try to infer this componentName |
| 114 | * from the package. |
| 115 | * |
| 116 | * We are supporting lookup by package name for legacy reasons. |
| 117 | * |
| 118 | * If mdmComponentName is supplied (not null): |
| 119 | * mdmPackageName is ignored. |
| 120 | * Check that the package of mdmComponentName is installed, that mdmComponentName is a |
| 121 | * receiver in this package, and return it. |
| 122 | * |
| 123 | * Otherwise: |
| 124 | * mdmPackageName must be supplied (not null). |
| 125 | * Check that this package is installed, try to infer a potential device admin in this package, |
| 126 | * and return it. |
| 127 | */ |
| 128 | public static ComponentName findDeviceAdmin(String mdmPackageName, |
| 129 | ComponentName mdmComponentName, Context c) throws IllegalProvisioningArgumentException { |
| 130 | if (mdmComponentName != null) { |
| 131 | mdmPackageName = mdmComponentName.getPackageName(); |
| 132 | } |
| 133 | if (mdmPackageName == null) { |
| 134 | throw new IllegalProvisioningArgumentException("Neither the package name nor the" |
| 135 | + " component name of the admin are supplied"); |
| 136 | } |
| 137 | PackageInfo pi; |
| 138 | try { |
| 139 | pi = c.getPackageManager().getPackageInfo(mdmPackageName, |
| 140 | PackageManager.GET_RECEIVERS); |
| 141 | } catch (NameNotFoundException e) { |
| 142 | throw new IllegalProvisioningArgumentException("Mdm "+ mdmPackageName |
| 143 | + " is not installed. ", e); |
| 144 | } |
| 145 | if (mdmComponentName != null) { |
| 146 | // If the component was specified in the intent: check that it is in the manifest. |
| 147 | checkAdminComponent(mdmComponentName, pi); |
| 148 | return mdmComponentName; |
| 149 | } else { |
| 150 | // Otherwise: try to find a potential device admin in the manifest. |
| 151 | return findDeviceAdminInPackage(mdmPackageName, pi); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | private static void checkAdminComponent(ComponentName mdmComponentName, PackageInfo pi) |
| 156 | throws IllegalProvisioningArgumentException{ |
| 157 | for (ActivityInfo ai : pi.receivers) { |
| 158 | if (mdmComponentName.getClassName().equals(ai.name)) { |
| 159 | return; |
| 160 | } |
| 161 | } |
| 162 | throw new IllegalProvisioningArgumentException("The component " + mdmComponentName |
| 163 | + " cannot be found"); |
| 164 | } |
| 165 | |
| 166 | private static ComponentName findDeviceAdminInPackage(String mdmPackageName, PackageInfo pi) |
| 167 | throws IllegalProvisioningArgumentException { |
| 168 | ComponentName mdmComponentName = null; |
| 169 | for (ActivityInfo ai : pi.receivers) { |
| 170 | if (!TextUtils.isEmpty(ai.permission) && |
| 171 | ai.permission.equals(android.Manifest.permission.BIND_DEVICE_ADMIN)) { |
| 172 | if (mdmComponentName != null) { |
| 173 | throw new IllegalProvisioningArgumentException("There are several " |
| 174 | + "device admins in " + mdmPackageName + " but no one in specified"); |
| 175 | } else { |
| 176 | mdmComponentName = new ComponentName(mdmPackageName, ai.name); |
| 177 | } |
| 178 | } |
| 179 | } |
| 180 | if (mdmComponentName == null) { |
| 181 | throw new IllegalProvisioningArgumentException("There are no device admins in" |
| 182 | + mdmPackageName); |
| 183 | } |
| 184 | return mdmComponentName; |
| 185 | } |
Alan Treadway | 9a42f2b | 2015-02-25 15:12:31 +0000 | [diff] [blame] | 186 | |
| 187 | public static MdmPackageInfo getMdmPackageInfo(PackageManager pm, String packageName) { |
| 188 | if (packageName != null) { |
| 189 | try { |
| 190 | ApplicationInfo ai = pm.getApplicationInfo(packageName, /* default flags */ 0); |
| 191 | if (ai != null) { |
| 192 | return new MdmPackageInfo(pm.getApplicationIcon(packageName), |
| 193 | pm.getApplicationLabel(ai).toString()); |
| 194 | } |
| 195 | } catch (PackageManager.NameNotFoundException e) { |
| 196 | // Package does not exist, ignore. Should never happen. |
| 197 | ProvisionLogger.loge("Package does not exist. Should never happen."); |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | return null; |
| 202 | } |
| 203 | |
| 204 | /** |
| 205 | * Information relating to the currently installed MDM package manager. |
| 206 | */ |
| 207 | public static final class MdmPackageInfo { |
| 208 | private final Drawable packageIcon; |
| 209 | private final String appLabel; |
| 210 | |
| 211 | private MdmPackageInfo(Drawable packageIcon, String appLabel) { |
| 212 | this.packageIcon = packageIcon; |
| 213 | this.appLabel = appLabel; |
| 214 | } |
| 215 | |
| 216 | public String getAppLabel() { |
| 217 | return appLabel; |
| 218 | } |
| 219 | |
| 220 | public Drawable getPackageIcon() { |
| 221 | return packageIcon; |
| 222 | } |
| 223 | } |
Joe Delfino | d0f2928 | 2015-03-19 08:26:09 -0400 | [diff] [blame] | 224 | |
| 225 | public static boolean isCurrentUserOwner() { |
| 226 | return UserHandle.myUserId() == UserHandle.USER_OWNER; |
| 227 | } |
Joe Delfino | 84e56f5 | 2015-03-27 09:56:18 -0400 | [diff] [blame] | 228 | |
| 229 | public static boolean hasDeviceOwner(Context context) { |
| 230 | DevicePolicyManager dpm = |
| 231 | (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); |
| 232 | return !TextUtils.isEmpty(dpm.getDeviceOwner()); |
| 233 | } |
| 234 | |
| 235 | public static boolean isManagedProfile(Context context) { |
| 236 | UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); |
| 237 | UserInfo user = um.getUserInfo(UserHandle.myUserId()); |
| 238 | return user != null ? user.isManagedProfile() : false; |
| 239 | } |
| 240 | |
Sander Alewijnse | 74d6c14 | 2015-04-13 11:51:19 +0100 | [diff] [blame] | 241 | /** |
| 242 | * Returns true if the given package does not exist on the device or if its version code is less |
| 243 | * than the given version, and false otherwise. |
| 244 | */ |
| 245 | public static boolean packageRequiresUpdate(String packageName, int minSupportedVersion, |
| 246 | Context context) { |
| 247 | try { |
| 248 | PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0); |
| 249 | if (packageInfo.versionCode >= minSupportedVersion) { |
| 250 | return false; |
| 251 | } |
| 252 | } catch (NameNotFoundException e) { |
| 253 | // Package not on device. |
| 254 | } |
| 255 | |
| 256 | return true; |
| 257 | } |
| 258 | |
Sander Alewijnse | 3efa83a | 2015-04-14 14:46:18 +0100 | [diff] [blame] | 259 | public static byte[] stringToByteArray(String s) |
| 260 | throws NumberFormatException { |
| 261 | try { |
| 262 | return Base64.decode(s, Base64.URL_SAFE); |
| 263 | } catch (IllegalArgumentException e) { |
| 264 | throw new NumberFormatException("Incorrect format. Should be Url-safe Base64 encoded."); |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | public static String byteArrayToString(byte[] bytes) { |
| 269 | return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP); |
| 270 | } |
Sander Alewijnse | d5e4c42 | 2014-11-25 17:56:16 +0000 | [diff] [blame] | 271 | } |