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.task; 18 19 import android.app.AppGlobals; 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.ApplicationInfo; 25 import android.content.pm.ComponentInfo; 26 import android.content.pm.IPackageDeleteObserver; 27 import android.content.pm.IPackageManager; 28 import android.content.pm.PackageInfo; 29 import android.content.pm.PackageManager.NameNotFoundException; 30 import android.content.pm.PackageManager; 31 import android.content.pm.ResolveInfo; 32 import android.content.res.Resources; 33 import android.os.IBinder; 34 import android.os.RemoteException; 35 import android.os.ServiceManager; 36 import android.util.Xml; 37 import android.view.inputmethod.InputMethodInfo; 38 import android.view.inputmethod.InputMethodManager; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.internal.util.FastXmlSerializer; 42 import com.android.internal.view.IInputMethodManager; 43 import com.android.managedprovisioning.ProvisionLogger; 44 import com.android.managedprovisioning.R; 45 import com.android.managedprovisioning.common.Utils; 46 47 import java.io.File; 48 import java.io.FileInputStream; 49 import java.io.FileOutputStream; 50 import java.io.IOException; 51 52 import java.util.ArrayList; 53 import java.util.Arrays; 54 import java.util.Collections; 55 import java.util.HashSet; 56 import java.util.List; 57 import java.util.Set; 58 import java.util.concurrent.atomic.AtomicInteger; 59 60 import org.xmlpull.v1.XmlPullParser; 61 import org.xmlpull.v1.XmlPullParserException; 62 import org.xmlpull.v1.XmlSerializer; 63 64 /** 65 * Deletes all system apps with a launcher that are not in the required set of packages. 66 * Furthermore deletes all disallowed apps. 67 * 68 * Note: If an app is mistakenly listed as both required and disallowed, it will be treated as 69 * required. 70 * 71 * This task may be run when a profile (both for managed device and managed profile) is created. 72 * In that case the newProfile flag should be true. 73 * 74 * It should also be run after a system update with newProfile false, if 75 * {@link #shouldDeleteNonRequiredApps} returns true. Note that only newly installed system apps 76 * will be deleted. 77 */ 78 public class DeleteNonRequiredAppsTask { 79 private final Callback mCallback; 80 private final Context mContext; 81 private final String mMdmPackageName; 82 private final IPackageManager mIPackageManager; 83 private final IInputMethodManager mIInputMethodManager; 84 private final PackageManager mPm; 85 private final List<String> mRequiredAppsList; 86 private final List<String> mDisallowedAppsList; 87 private final List<String> mVendorRequiredAppsList; 88 private final List<String> mVendorDisallowedAppsList; 89 private final int mUserId; 90 private final int mProvisioningType; 91 private final boolean mNewProfile; // If we are provisioning a new managed profile/device. 92 private final boolean mLeaveAllSystemAppsEnabled; 93 94 private static final String TAG_SYSTEM_APPS = "system-apps"; 95 private static final String TAG_PACKAGE_LIST_ITEM = "item"; 96 private static final String ATTR_VALUE = "value"; 97 98 public static final int DEVICE_OWNER = 0; 99 public static final int PROFILE_OWNER = 1; 100 public static final int MANAGED_USER = 2; 101 102 private final Utils mUtils = new Utils(); 103 104 /** 105 * Provisioning type should be either {@link #DEVICE_OWNER}, {@link #PROFILE_OWNER} or 106 * {@link #MANAGED_USER}. 107 **/ DeleteNonRequiredAppsTask(Context context, String mdmPackageName, int provisioningType, boolean newProfile, int userId, boolean leaveAllSystemAppsEnabled, Callback callback)108 public DeleteNonRequiredAppsTask(Context context, String mdmPackageName, int provisioningType, 109 boolean newProfile, int userId, boolean leaveAllSystemAppsEnabled, Callback callback) { 110 this(context, AppGlobals.getPackageManager(), getIInputMethodManager(), mdmPackageName, 111 provisioningType, newProfile, userId, leaveAllSystemAppsEnabled, callback); 112 } 113 114 @VisibleForTesting DeleteNonRequiredAppsTask(Context context, IPackageManager iPm, IInputMethodManager iimm, String mdmPackageName, int provisioningType, boolean newProfile, int userId, boolean leaveAllSystemAppsEnabled, Callback callback)115 DeleteNonRequiredAppsTask(Context context, IPackageManager iPm, IInputMethodManager iimm, 116 String mdmPackageName, int provisioningType, boolean newProfile, int userId, 117 boolean leaveAllSystemAppsEnabled, Callback callback) { 118 119 mCallback = callback; 120 mContext = context; 121 mMdmPackageName = mdmPackageName; 122 mProvisioningType = provisioningType; 123 mUserId = userId; 124 mNewProfile = newProfile; 125 mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled; 126 mPm = context.getPackageManager(); 127 mIPackageManager = iPm; 128 mIInputMethodManager = iimm; 129 130 int requiredAppsListArray; 131 int vendorRequiredAppsListArray; 132 int disallowedAppsListArray; 133 int vendorDisallowedAppsListArray; 134 if (mProvisioningType == DEVICE_OWNER) { 135 requiredAppsListArray = R.array.required_apps_managed_device; 136 disallowedAppsListArray = R.array.disallowed_apps_managed_device; 137 vendorRequiredAppsListArray = R.array.vendor_required_apps_managed_device; 138 vendorDisallowedAppsListArray = R.array.vendor_disallowed_apps_managed_device; 139 } else if (mProvisioningType == PROFILE_OWNER) { 140 requiredAppsListArray = R.array.required_apps_managed_profile; 141 disallowedAppsListArray = R.array.disallowed_apps_managed_profile; 142 vendorRequiredAppsListArray = R.array.vendor_required_apps_managed_profile; 143 vendorDisallowedAppsListArray = R.array.vendor_disallowed_apps_managed_profile; 144 } else if (mProvisioningType == MANAGED_USER) { 145 requiredAppsListArray = R.array.required_apps_managed_user; 146 disallowedAppsListArray = R.array.disallowed_apps_managed_user; 147 vendorRequiredAppsListArray = R.array.vendor_required_apps_managed_user; 148 vendorDisallowedAppsListArray = R.array.vendor_disallowed_apps_managed_user; 149 } else { 150 throw new IllegalArgumentException("Provisioning type " + mProvisioningType + 151 " not supported."); 152 } 153 154 Resources resources = mContext.getResources(); 155 mRequiredAppsList = Arrays.asList(resources.getStringArray(requiredAppsListArray)); 156 mDisallowedAppsList = Arrays.asList(resources.getStringArray(disallowedAppsListArray)); 157 mVendorRequiredAppsList = Arrays.asList( 158 resources.getStringArray(vendorRequiredAppsListArray)); 159 mVendorDisallowedAppsList = Arrays.asList( 160 resources.getStringArray(vendorDisallowedAppsListArray)); 161 } 162 run()163 public void run() { 164 if (mLeaveAllSystemAppsEnabled) { 165 ProvisionLogger.logd("Not deleting non-required apps."); 166 mCallback.onSuccess(); 167 return; 168 } 169 ProvisionLogger.logd("Deleting non required apps."); 170 171 Set<String> packagesToDelete = getPackagesToDelete(); 172 removeNonInstalledPackages(packagesToDelete); 173 174 if (packagesToDelete.isEmpty()) { 175 mCallback.onSuccess(); 176 return; 177 } 178 179 PackageDeleteObserver packageDeleteObserver = 180 new PackageDeleteObserver(packagesToDelete.size()); 181 for (String packageName : packagesToDelete) { 182 ProvisionLogger.logd("Deleting package [" + packageName + "] as user " + mUserId); 183 mPm.deletePackageAsUser(packageName, packageDeleteObserver, 184 PackageManager.DELETE_SYSTEM_APP, mUserId); 185 } 186 } 187 getPackagesToDelete()188 private Set<String> getPackagesToDelete() { 189 Set<String> packagesToDelete = getCurrentAppsWithLauncher(); 190 // Newly installed system apps are uninstalled when they are not required and are either 191 // disallowed or have a launcher icon. 192 packagesToDelete.removeAll(getRequiredApps()); 193 // Don't delete the system input method packages in case of Device owner provisioning. 194 if (mProvisioningType == DEVICE_OWNER || mProvisioningType == MANAGED_USER) { 195 packagesToDelete.removeAll(getSystemInputMethods()); 196 } 197 packagesToDelete.addAll(getDisallowedApps()); 198 199 // Only consider new system apps. 200 packagesToDelete.retainAll(getNewSystemApps()); 201 return packagesToDelete; 202 } 203 getNewSystemApps()204 private Set<String> getNewSystemApps() { 205 File systemAppsFile = getSystemAppsFile(mContext, mUserId); 206 systemAppsFile.getParentFile().mkdirs(); // Creating the folder if it does not exist 207 208 Set<String> currentSystemApps = mUtils.getCurrentSystemApps(mIPackageManager, mUserId); 209 final Set<String> previousSystemApps; 210 if (mNewProfile) { 211 // Provisioning case. 212 previousSystemApps = Collections.<String>emptySet(); 213 } else if (!systemAppsFile.exists()) { 214 // OTA case. 215 ProvisionLogger.loge("Could not find the system apps file " + 216 systemAppsFile.getAbsolutePath()); 217 mCallback.onError(); 218 return Collections.<String>emptySet(); 219 } else { 220 previousSystemApps = readSystemApps(systemAppsFile); 221 } 222 223 writeSystemApps(currentSystemApps, systemAppsFile); 224 Set<String> newApps = currentSystemApps; 225 newApps.removeAll(previousSystemApps); 226 return newApps; 227 } 228 229 /** 230 * Remove all packages from the set that are not installed. 231 */ removeNonInstalledPackages(Set<String> packages)232 private void removeNonInstalledPackages(Set<String> packages) { 233 Set<String> toBeRemoved = new HashSet<String>(); 234 for (String packageName : packages) { 235 try { 236 PackageInfo info = mPm.getPackageInfoAsUser(packageName, 0 /* default flags */, 237 mUserId); 238 if (info == null) { 239 toBeRemoved.add(packageName); 240 } 241 } catch (PackageManager.NameNotFoundException e) { 242 toBeRemoved.add(packageName); 243 } 244 } 245 packages.removeAll(toBeRemoved); 246 } 247 248 /** 249 * Returns if this task should be run on OTA. 250 * This is indicated by the presence of the system apps file. 251 */ shouldDeleteNonRequiredApps(Context context, int userId)252 public static boolean shouldDeleteNonRequiredApps(Context context, int userId) { 253 return getSystemAppsFile(context, userId).exists(); 254 } 255 getSystemAppsFile(Context context, int userId)256 static File getSystemAppsFile(Context context, int userId) { 257 return new File(context.getFilesDir() + File.separator + "system_apps" 258 + File.separator + "user" + userId + ".xml"); 259 } 260 getCurrentAppsWithLauncher()261 private Set<String> getCurrentAppsWithLauncher() { 262 Intent launcherIntent = new Intent(Intent.ACTION_MAIN); 263 launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER); 264 List<ResolveInfo> resolveInfos = mPm.queryIntentActivitiesAsUser(launcherIntent, 265 PackageManager.MATCH_UNINSTALLED_PACKAGES 266 | PackageManager.MATCH_DISABLED_COMPONENTS 267 | PackageManager.MATCH_DIRECT_BOOT_AWARE 268 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 269 mUserId); 270 Set<String> apps = new HashSet<String>(); 271 for (ResolveInfo resolveInfo : resolveInfos) { 272 apps.add(resolveInfo.activityInfo.packageName); 273 } 274 return apps; 275 } 276 getSystemInputMethods()277 private Set<String> getSystemInputMethods() { 278 // InputMethodManager is final so it cannot be mocked. 279 // So, we're using IInputMethodManager directly because it can be mocked. 280 List<InputMethodInfo> inputMethods = null; 281 try { 282 inputMethods = mIInputMethodManager.getInputMethodList(); 283 } catch (RemoteException e) { 284 ProvisionLogger.loge("Could not communicate with IInputMethodManager", e); 285 return Collections.<String>emptySet(); 286 } 287 Set<String> systemInputMethods = new HashSet<String>(); 288 for (InputMethodInfo inputMethodInfo : inputMethods) { 289 ApplicationInfo applicationInfo = inputMethodInfo.getServiceInfo().applicationInfo; 290 if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 291 systemInputMethods.add(inputMethodInfo.getPackageName()); 292 } 293 } 294 return systemInputMethods; 295 } 296 writeSystemApps(Set<String> packageNames, File systemAppsFile)297 private void writeSystemApps(Set<String> packageNames, File systemAppsFile) { 298 try { 299 FileOutputStream stream = new FileOutputStream(systemAppsFile, false); 300 XmlSerializer serializer = new FastXmlSerializer(); 301 serializer.setOutput(stream, "utf-8"); 302 serializer.startDocument(null, true); 303 serializer.startTag(null, TAG_SYSTEM_APPS); 304 for (String packageName : packageNames) { 305 serializer.startTag(null, TAG_PACKAGE_LIST_ITEM); 306 serializer.attribute(null, ATTR_VALUE, packageName); 307 serializer.endTag(null, TAG_PACKAGE_LIST_ITEM); 308 } 309 serializer.endTag(null, TAG_SYSTEM_APPS); 310 serializer.endDocument(); 311 stream.close(); 312 } catch (IOException e) { 313 ProvisionLogger.loge("IOException trying to write the system apps", e); 314 } 315 } 316 readSystemApps(File systemAppsFile)317 private Set<String> readSystemApps(File systemAppsFile) { 318 Set<String> result = new HashSet<String>(); 319 if (!systemAppsFile.exists()) { 320 return result; 321 } 322 try { 323 FileInputStream stream = new FileInputStream(systemAppsFile); 324 325 XmlPullParser parser = Xml.newPullParser(); 326 parser.setInput(stream, null); 327 328 int type = parser.next(); 329 int outerDepth = parser.getDepth(); 330 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 331 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 332 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 333 continue; 334 } 335 String tag = parser.getName(); 336 if (tag.equals(TAG_PACKAGE_LIST_ITEM)) { 337 result.add(parser.getAttributeValue(null, ATTR_VALUE)); 338 } else { 339 ProvisionLogger.loge("Unknown tag: " + tag); 340 } 341 } 342 stream.close(); 343 } catch (IOException e) { 344 ProvisionLogger.loge("IOException trying to read the system apps", e); 345 } catch (XmlPullParserException e) { 346 ProvisionLogger.loge("XmlPullParserException trying to read the system apps", e); 347 } 348 return result; 349 } 350 getRequiredApps()351 protected Set<String> getRequiredApps() { 352 HashSet<String> requiredApps = new HashSet<String>(); 353 requiredApps.addAll(mRequiredAppsList); 354 requiredApps.addAll(mVendorRequiredAppsList); 355 requiredApps.add(mMdmPackageName); 356 return requiredApps; 357 } 358 getDisallowedApps()359 private Set<String> getDisallowedApps() { 360 HashSet<String> disallowedApps = new HashSet<String>(); 361 disallowedApps.addAll(mDisallowedAppsList); 362 disallowedApps.addAll(mVendorDisallowedAppsList); 363 return disallowedApps; 364 } 365 366 /** 367 * Runs the next task when all packages have been deleted or shuts down the activity if package 368 * deletion fails. 369 */ 370 class PackageDeleteObserver extends IPackageDeleteObserver.Stub { 371 private final AtomicInteger mPackageCount = new AtomicInteger(0); 372 PackageDeleteObserver(int packageCount)373 public PackageDeleteObserver(int packageCount) { 374 this.mPackageCount.set(packageCount); 375 } 376 377 @Override packageDeleted(String packageName, int returnCode)378 public void packageDeleted(String packageName, int returnCode) { 379 if (returnCode != PackageManager.DELETE_SUCCEEDED) { 380 ProvisionLogger.logw( 381 "Could not finish the provisioning: package deletion failed"); 382 mCallback.onError(); 383 return; 384 } 385 int currentPackageCount = mPackageCount.decrementAndGet(); 386 if (currentPackageCount == 0) { 387 ProvisionLogger.logi("All non-required system apps with launcher icon, " 388 + "and all disallowed apps have been uninstalled."); 389 mCallback.onSuccess(); 390 } 391 } 392 } 393 getIInputMethodManager()394 private static IInputMethodManager getIInputMethodManager() { 395 IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE); 396 return IInputMethodManager.Stub.asInterface(b); 397 } 398 399 public abstract static class Callback { onSuccess()400 public abstract void onSuccess(); onError()401 public abstract void onError(); 402 } 403 } 404