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 17package com.android.managedprovisioning.task; 18 19import android.app.AppGlobals; 20import android.content.BroadcastReceiver; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.ApplicationInfo; 25import android.content.pm.ComponentInfo; 26import android.content.pm.IPackageDeleteObserver; 27import android.content.pm.IPackageManager; 28import android.content.pm.PackageInfo; 29import android.content.pm.PackageManager.NameNotFoundException; 30import android.content.pm.PackageManager; 31import android.content.pm.ResolveInfo; 32import android.content.res.Resources; 33import android.os.IBinder; 34import android.os.RemoteException; 35import android.os.ServiceManager; 36import android.util.Xml; 37import android.view.inputmethod.InputMethodInfo; 38import android.view.inputmethod.InputMethodManager; 39 40import com.android.internal.annotations.VisibleForTesting; 41import com.android.internal.util.FastXmlSerializer; 42import com.android.internal.view.IInputMethodManager; 43import com.android.managedprovisioning.ProvisionLogger; 44import com.android.managedprovisioning.R; 45import com.android.managedprovisioning.common.Utils; 46 47import java.io.File; 48import java.io.FileInputStream; 49import java.io.FileOutputStream; 50import java.io.IOException; 51 52import java.util.ArrayList; 53import java.util.Arrays; 54import java.util.Collections; 55import java.util.HashSet; 56import java.util.List; 57import java.util.Set; 58import java.util.concurrent.atomic.AtomicInteger; 59 60import org.xmlpull.v1.XmlPullParser; 61import org.xmlpull.v1.XmlPullParserException; 62import 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 */ 78public 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 **/ 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 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 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 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 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 */ 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 */ 252 public static boolean shouldDeleteNonRequiredApps(Context context, int userId) { 253 return getSystemAppsFile(context, userId).exists(); 254 } 255 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 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 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 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 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 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 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 373 public PackageDeleteObserver(int packageCount) { 374 this.mPackageCount.set(packageCount); 375 } 376 377 @Override 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 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 { 400 public abstract void onSuccess(); 401 public abstract void onError(); 402 } 403} 404