1 /* 2 * Copyright (C) 2021 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.server.pm; 18 19 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; 20 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; 21 import static android.os.incremental.IncrementalManager.isIncrementalPath; 22 import static android.os.storage.StorageManager.FLAG_STORAGE_CE; 23 import static android.os.storage.StorageManager.FLAG_STORAGE_DE; 24 import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; 25 26 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; 27 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE; 28 import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX; 29 import static com.android.server.pm.PackageManagerService.TAG; 30 31 import android.annotation.NonNull; 32 import android.annotation.Nullable; 33 import android.content.pm.PackageManager; 34 import android.content.pm.parsing.ApkLiteParseUtils; 35 import android.content.pm.parsing.PackageLite; 36 import android.content.pm.parsing.result.ParseResult; 37 import android.content.pm.parsing.result.ParseTypeImpl; 38 import android.os.Environment; 39 import android.os.Trace; 40 import android.os.UserHandle; 41 import android.os.incremental.IncrementalManager; 42 import android.util.Log; 43 import android.util.Slog; 44 import android.util.SparseBooleanArray; 45 46 import com.android.internal.annotations.GuardedBy; 47 import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; 48 import com.android.internal.pm.parsing.pkg.PackageImpl; 49 import com.android.internal.pm.pkg.component.ParsedInstrumentation; 50 import com.android.internal.util.ArrayUtils; 51 import com.android.server.pm.parsing.PackageCacher; 52 import com.android.server.pm.permission.PermissionManagerServiceInternal; 53 import com.android.server.pm.pkg.AndroidPackage; 54 import com.android.server.pm.pkg.PackageStateInternal; 55 56 import java.io.File; 57 import java.util.Collections; 58 import java.util.List; 59 60 /** 61 * Removes a package from internal data structures, deletes it data directories if requested, 62 * and clears its app profiles 63 */ 64 final class RemovePackageHelper { 65 private final PackageManagerService mPm; 66 private final IncrementalManager mIncrementalManager; 67 private final Installer mInstaller; 68 private final PermissionManagerServiceInternal mPermissionManager; 69 private final SharedLibrariesImpl mSharedLibraries; 70 private final AppDataHelper mAppDataHelper; 71 private final BroadcastHelper mBroadcastHelper; 72 73 // TODO(b/198166813): remove PMS dependency RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper, BroadcastHelper broadcastHelper)74 RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper, 75 BroadcastHelper broadcastHelper) { 76 mPm = pm; 77 mIncrementalManager = mPm.mInjector.getIncrementalManager(); 78 mInstaller = mPm.mInjector.getInstaller(); 79 mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal(); 80 mSharedLibraries = mPm.mInjector.getSharedLibrariesImpl(); 81 mAppDataHelper = appDataHelper; 82 mBroadcastHelper = broadcastHelper; 83 } 84 removeCodePath(File codePath)85 public void removeCodePath(File codePath) { 86 try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { 87 removeCodePathLI(codePath); 88 } 89 } 90 91 @GuardedBy("mPm.mInstallLock") removeCodePathLI(File codePath)92 private void removeCodePathLI(File codePath) { 93 if (codePath == null || !codePath.exists()) { 94 return; 95 } 96 if (codePath.isDirectory()) { 97 final File codePathParent = codePath.getParentFile(); 98 final boolean needRemoveParent = codePathParent.getName().startsWith(RANDOM_DIR_PREFIX); 99 try { 100 final boolean isIncremental = (mIncrementalManager != null && isIncrementalPath( 101 codePath.getAbsolutePath())); 102 if (isIncremental) { 103 if (needRemoveParent) { 104 mIncrementalManager.rmPackageDir(codePathParent); 105 } else { 106 mIncrementalManager.rmPackageDir(codePath); 107 } 108 } 109 110 final String packageName = codePath.getName(); 111 mInstaller.rmPackageDir(packageName, codePath.getAbsolutePath()); 112 if (needRemoveParent) { 113 mInstaller.rmPackageDir(packageName, codePathParent.getAbsolutePath()); 114 removeCachedResult(codePathParent); 115 } 116 } catch (Installer.InstallerException e) { 117 Slog.w(TAG, "Failed to remove code path", e); 118 } 119 } else { 120 codePath.delete(); 121 } 122 } 123 removeCachedResult(@onNull File codePath)124 private void removeCachedResult(@NonNull File codePath) { 125 if (mPm.getCacheDir() == null) { 126 return; 127 } 128 129 final PackageCacher cacher = new PackageCacher(mPm.getCacheDir()); 130 // Find and delete the cached result belong to the given codePath. 131 cacher.cleanCachedResult(codePath); 132 } 133 134 // Used for system apps only removePackage(AndroidPackage pkg, boolean chatty)135 public void removePackage(AndroidPackage pkg, boolean chatty) { 136 try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { 137 removePackageLI(pkg, chatty); 138 } 139 } 140 141 @GuardedBy("mPm.mInstallLock") removePackageLI(AndroidPackage pkg, boolean chatty)142 private void removePackageLI(AndroidPackage pkg, boolean chatty) { 143 // Remove the parent package setting 144 PackageStateInternal ps = mPm.snapshotComputer() 145 .getPackageStateInternal(pkg.getPackageName()); 146 if (ps != null) { 147 removePackageLI(ps.getPackageName(), chatty); 148 } else if (DEBUG_REMOVE && chatty) { 149 Log.d(TAG, "Not removing package " + pkg.getPackageName() + "; mExtras == null"); 150 } 151 } 152 153 @GuardedBy("mPm.mInstallLock") removePackageLI(String packageName, boolean chatty)154 private void removePackageLI(String packageName, boolean chatty) { 155 if (DEBUG_INSTALL) { 156 if (chatty) { 157 Log.d(TAG, "Removing package " + packageName); 158 } 159 } 160 161 // writer 162 synchronized (mPm.mLock) { 163 final AndroidPackage removedPackage = mPm.mPackages.remove(packageName); 164 if (removedPackage != null) { 165 // TODO: Use PackageState for isSystem 166 cleanPackageDataStructuresLILPw(removedPackage, 167 AndroidPackageLegacyUtils.isSystem(removedPackage), chatty); 168 } 169 } 170 } 171 172 @GuardedBy("mPm.mLock") cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean isSystemApp, boolean chatty)173 private void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean isSystemApp, 174 boolean chatty) { 175 mPm.mComponentResolver.removeAllComponents(pkg, chatty); 176 mPermissionManager.onPackageRemoved(pkg); 177 mPm.getPackageProperty().removeAllProperties(pkg); 178 179 final int instrumentationSize = ArrayUtils.size(pkg.getInstrumentations()); 180 StringBuilder r = null; 181 int i; 182 for (i = 0; i < instrumentationSize; i++) { 183 ParsedInstrumentation a = pkg.getInstrumentations().get(i); 184 mPm.getInstrumentation().remove(a.getComponentName()); 185 if (DEBUG_REMOVE && chatty) { 186 if (r == null) { 187 r = new StringBuilder(256); 188 } else { 189 r.append(' '); 190 } 191 r.append(a.getName()); 192 } 193 } 194 if (r != null) { 195 if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r); 196 } 197 198 r = null; 199 if (isSystemApp) { 200 // Only system apps can hold shared libraries. 201 final int libraryNamesSize = pkg.getLibraryNames().size(); 202 for (i = 0; i < libraryNamesSize; i++) { 203 String name = pkg.getLibraryNames().get(i); 204 if (mSharedLibraries.removeSharedLibrary(name, 0)) { 205 if (DEBUG_REMOVE && chatty) { 206 if (r == null) { 207 r = new StringBuilder(256); 208 } else { 209 r.append(' '); 210 } 211 r.append(name); 212 } 213 } 214 } 215 } 216 217 r = null; 218 219 // Any package can hold SDK or static shared libraries. 220 if (pkg.getSdkLibraryName() != null) { 221 if (mSharedLibraries.removeSharedLibrary( 222 pkg.getSdkLibraryName(), pkg.getSdkLibVersionMajor())) { 223 if (DEBUG_REMOVE && chatty) { 224 if (r == null) { 225 r = new StringBuilder(256); 226 } else { 227 r.append(' '); 228 } 229 r.append(pkg.getSdkLibraryName()); 230 } 231 } 232 } 233 if (pkg.getStaticSharedLibraryName() != null) { 234 if (mSharedLibraries.removeSharedLibrary(pkg.getStaticSharedLibraryName(), 235 pkg.getStaticSharedLibraryVersion())) { 236 if (DEBUG_REMOVE && chatty) { 237 if (r == null) { 238 r = new StringBuilder(256); 239 } else { 240 r.append(' '); 241 } 242 r.append(pkg.getStaticSharedLibraryName()); 243 } 244 } 245 } 246 247 if (r != null) { 248 if (DEBUG_REMOVE) Log.d(TAG, " Libraries: " + r); 249 } 250 } 251 252 /** 253 * This method clears the data and states stored in the system that are related to the 254 * package being deleted and the target user, including the data directory. 255 * If the DELETE_KEEP_DATA flag is set, everything is preserved except ART profiles. 256 * Make sure this flag is set for partially installed apps. If not it's meaningless to 257 * delete a partially installed application. 258 */ clearPackageStateForUserLIF(PackageSetting ps, int userId, int flags)259 public void clearPackageStateForUserLIF(PackageSetting ps, int userId, int flags) { 260 final String packageName = ps.getPackageName(); 261 // Step 1: always destroy app profiles. 262 mAppDataHelper.destroyAppProfilesLIF(packageName); 263 264 final AndroidPackage pkg; 265 final SharedUserSetting sus; 266 synchronized (mPm.mLock) { 267 pkg = mPm.mPackages.get(packageName); 268 sus = mPm.mSettings.getSharedUserSettingLPr(ps); 269 } 270 271 final AndroidPackage resolvedPkg; 272 if (pkg != null) { 273 resolvedPkg = pkg; 274 } else { 275 // We don't have a parsed package when it lives on an ejected 276 // adopted storage device, so fake something together 277 resolvedPkg = PackageImpl.buildFakeForDeletion(packageName, ps.getVolumeUuid()); 278 } 279 280 int appDataDeletionFlags = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL; 281 // Personal data is preserved if the DELETE_KEEP_DATA flag is on 282 if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) { 283 if ((flags & PackageManager.DELETE_ARCHIVE) != 0) { 284 mAppDataHelper.clearAppDataLIF(resolvedPkg, userId, 285 appDataDeletionFlags | Installer.FLAG_CLEAR_CACHE_ONLY); 286 mAppDataHelper.clearAppDataLIF(resolvedPkg, userId, 287 appDataDeletionFlags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); 288 } 289 return; 290 } 291 292 // Step 2: destroy app data. 293 mAppDataHelper.destroyAppDataLIF(resolvedPkg, userId, appDataDeletionFlags); 294 if (userId != UserHandle.USER_ALL) { 295 ps.setCeDataInode(-1, userId); 296 ps.setDeDataInode(-1, userId); 297 } 298 299 final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm, 300 mBroadcastHelper); 301 if (userId == UserHandle.USER_ALL) { 302 if (DEBUG_REMOVE) { 303 Slog.d(TAG, "Clear package:" + packageName + " state for all users"); 304 } 305 // Step 3: inform DomainVerificationManager. 306 mPm.mDomainVerificationManager.clearPackage(packageName); 307 synchronized (mPm.mLock) { 308 // Step 3.1 (only for USER_ALL): notify KeySetManagerService. 309 mPm.mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName); 310 // Step 3.2 (only for USER_ALL): update installer ownership. 311 mPm.mInjector.getUpdateOwnershipHelper().removeUpdateOwnerDenyList(packageName); 312 // Step 3.3 (only for USER_ALL): update AppsFilter. 313 final Computer snapshot = mPm.snapshotComputer(); 314 mPm.mAppsFilter.removePackage(snapshot, 315 snapshot.getPackageStateInternal(packageName)); 316 // Step 4: clear perferred activities. 317 final SparseBooleanArray changedUsers = new SparseBooleanArray(); 318 mPm.clearPackagePreferredActivitiesLPw( 319 packageName, changedUsers, UserHandle.USER_ALL); 320 mPm.mInjector.getBackgroundHandler().post(() -> { 321 if (changedUsers.size() > 0) { 322 preferredActivityHelper.updateDefaultHomeNotLocked(mPm.snapshotComputer(), 323 changedUsers); 324 mBroadcastHelper.sendPreferredActivityChangedBroadcast(UserHandle.USER_ALL); 325 } 326 }); 327 // Step 5: inform PermissionManager. 328 // This has to be done after the removal from mSettings in removePackageDataLIF. 329 } 330 } else { 331 if (DEBUG_REMOVE) { 332 Slog.d(TAG, "Clear package:" + packageName + " state for user:" + userId); 333 } 334 // Step 3: inform DomainVerificationManager. 335 mPm.mDomainVerificationManager.clearPackageForUser(packageName, userId); 336 // Step 4: clear perferred activities. 337 preferredActivityHelper.clearPackagePreferredActivities(packageName, userId); 338 // Step 5: inform PermissionManager. 339 List<AndroidPackage> sharedUserPkgs = 340 sus != null ? sus.getPackages() : Collections.emptyList(); 341 mPermissionManager.onPackageUninstalled(packageName, ps.getAppId(), ps, pkg, 342 sharedUserPkgs, userId); 343 } 344 345 // Step 6: detroy keystore data. 346 mPm.mInjector.getBackgroundHandler().post(() -> { 347 try { 348 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, 349 "clearKeystoreData:" + ps.getAppId() + " for user: " + userId); 350 mAppDataHelper.clearKeystoreData(userId, ps.getAppId()); 351 } finally { 352 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 353 } 354 }); 355 } 356 357 // Called to clean up disabled system packages removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles)358 public void removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles) { 359 try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { 360 removePackageDataLIF(deletedPs, UserHandle.USER_ALL, allUserHandles, 361 new PackageRemovedInfo(), /* flags= */ 0, /* writeSettings= */ false); 362 } 363 } 364 365 /** 366 * This method deletes the package from internal data structures such as mPackages / mSettings. 367 * 368 * @param targetUserId indicates the target user of the deletion. It equals to 369 * {@link UserHandle.USER_ALL} if the deletion was initiated for all users, 370 * otherwise it equals to the specific user id that the deletion was meant 371 * for. 372 */ 373 @GuardedBy("mPm.mInstallLock") removePackageDataLIF(final PackageSetting deletedPs, int targetUserId, @NonNull int[] allUserHandles, @NonNull PackageRemovedInfo outInfo, int flags, boolean writeSettings)374 public void removePackageDataLIF(final PackageSetting deletedPs, int targetUserId, 375 @NonNull int[] allUserHandles, 376 @NonNull PackageRemovedInfo outInfo, int flags, boolean writeSettings) { 377 String packageName = deletedPs.getPackageName(); 378 if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs); 379 final boolean shouldDeletePackageSetting = 380 shouldDeletePackageSetting(deletedPs, targetUserId, allUserHandles, flags); 381 // Retrieve object to delete permissions for shared user later on 382 final AndroidPackage deletedPkg = deletedPs.getPkg(); 383 384 // Delete all the data and states related to this package. 385 clearPackageStateForUserLIF(deletedPs, 386 shouldDeletePackageSetting ? UserHandle.USER_ALL : targetUserId, flags); 387 388 // Delete from mPackages 389 removePackageLI(packageName, (flags & PackageManager.DELETE_CHATTY) != 0); 390 if (!deletedPs.isSystem()) { 391 // A non-system app's AndroidPackage object has been removed from the service. 392 // Explicitly nullify the corresponding app's PackageSetting's pkg object to 393 // prevent any future usage of it, in case the PackageSetting object will remain because 394 // of DELETE_KEEP_DATA. 395 deletedPs.setPkg(null); 396 } 397 398 if (shouldDeletePackageSetting) { 399 // Delete from mSettings 400 final SparseBooleanArray changedUsers = new SparseBooleanArray(); 401 synchronized (mPm.mLock) { 402 outInfo.mIsAppIdRemoved = mPm.mSettings.removePackageAndAppIdLPw(packageName); 403 if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) { 404 final SharedUserSetting sus = mPm.mSettings.getSharedUserSettingLPr(deletedPs); 405 // If we don't have a disabled system package to reinstall, the package is 406 // really gone and its permission state should be removed. 407 final List<AndroidPackage> sharedUserPkgs = 408 sus != null ? sus.getPackages() : Collections.emptyList(); 409 mPermissionManager.onPackageUninstalled(packageName, deletedPs.getAppId(), 410 deletedPs, deletedPkg, sharedUserPkgs, UserHandle.USER_ALL); 411 // After permissions are handled, check if the shared user can be migrated 412 if (sus != null) { 413 mPm.mSettings.checkAndConvertSharedUserSettingsLPw(sus); 414 } 415 } 416 mPm.mSettings.removeRenamedPackageLPw(deletedPs.getRealName()); 417 } 418 } else if (!deletedPs.isSystem() && !outInfo.mIsUpdate 419 && outInfo.mRemovedUsers != null && !deletedPs.isExternalStorage()) { 420 // For non-system uninstalls with DELETE_KEEP_DATA, set the installed state to false 421 // for affected users. This does not apply to app updates where the old apk is replaced 422 // but the old data remains. 423 if (DEBUG_REMOVE) { 424 Slog.d(TAG, "Updating installed state to false because of DELETE_KEEP_DATA"); 425 } 426 final boolean isArchive = (flags & PackageManager.DELETE_ARCHIVE) != 0; 427 final long currentTimeMillis = System.currentTimeMillis(); 428 for (int userId : outInfo.mRemovedUsers) { 429 if (DEBUG_REMOVE) { 430 final boolean wasInstalled = deletedPs.getInstalled(userId); 431 Slog.d(TAG, " user " + userId + ": " + wasInstalled + " => " + false); 432 } 433 deletedPs.setInstalled(/* installed= */ false, userId); 434 } 435 } 436 437 // make sure to preserve per-user installed state if this removal was just 438 // a downgrade of a system app to the factory package 439 boolean installedStateChanged = false; 440 if (outInfo.mOrigUsers != null && deletedPs.isSystem()) { 441 if (DEBUG_REMOVE) { 442 Slog.d(TAG, "Propagating install state across downgrade"); 443 } 444 for (int userId : allUserHandles) { 445 final boolean installed = ArrayUtils.contains(outInfo.mOrigUsers, userId); 446 if (DEBUG_REMOVE) { 447 Slog.d(TAG, " user " + userId + " => " + installed); 448 } 449 if (installed != deletedPs.getInstalled(userId)) { 450 installedStateChanged = true; 451 } 452 deletedPs.setInstalled(installed, userId); 453 if (installed) { 454 deletedPs.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); 455 } 456 } 457 } 458 synchronized (mPm.mLock) { 459 // can downgrade to reader 460 if (writeSettings) { 461 // Save settings now 462 mPm.writeSettingsLPrTEMP(); 463 } 464 if (installedStateChanged) { 465 mPm.mSettings.writeKernelMappingLPr(deletedPs); 466 } 467 } 468 } 469 shouldDeletePackageSetting(PackageSetting deletedPs, int userId, int[] allUserHandles, int flags)470 private static boolean shouldDeletePackageSetting(PackageSetting deletedPs, int userId, 471 int[] allUserHandles, int flags) { 472 if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) { 473 return false; 474 } 475 if (userId == UserHandle.USER_ALL) { 476 // Deleting for ALL. Let's wipe the PackageSetting. 477 return true; 478 } 479 if (deletedPs.hasDataOnAnyOtherUser(allUserHandles, userId)) { 480 // We arrived here because we are uninstalling the package for a specified user, and the 481 // package isn't installed on any other user. Before we proceed to completely delete the 482 // PackageSetting from mSettings, let's first check if data exists on any other user. 483 // If so, do not wipe the PackageSetting. 484 return false; 485 } 486 return true; 487 } 488 cleanUpResources(@ullable String packageName, @Nullable File codeFile, @Nullable String[] instructionSets)489 void cleanUpResources(@Nullable String packageName, @Nullable File codeFile, 490 @Nullable String[] instructionSets) { 491 try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { 492 cleanUpResourcesLI(codeFile, instructionSets); 493 } 494 if (packageName == null) { 495 return; 496 } 497 synchronized (mPm.mLock) { 498 PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); 499 if (ps != null) { 500 ps.removeOldPath(codeFile); 501 } 502 } 503 } 504 505 // Need installer lock especially for dex file removal. 506 @GuardedBy("mPm.mInstallLock") cleanUpResourcesLI(@ullable File codeFile, @Nullable String[] instructionSets)507 private void cleanUpResourcesLI(@Nullable File codeFile, @Nullable String[] instructionSets) { 508 // Try enumerating all code paths before deleting 509 List<String> allCodePaths = Collections.EMPTY_LIST; 510 if (codeFile != null && codeFile.exists()) { 511 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 512 final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite( 513 input.reset(), codeFile, /* flags */ 0); 514 if (result.isSuccess()) { 515 // Ignore error; we tried our best 516 allCodePaths = result.getResult().getAllApkPaths(); 517 } 518 } 519 520 removeCodePathLI(codeFile); 521 522 // TODO(b/265813358): ART Service currently doesn't support deleting optimized artifacts 523 // relative to an arbitrary APK path. Skip this and rely on its file GC instead. 524 } 525 cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath)526 void cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath) { 527 final String toPathName = new File(fromCodePath).getName(); 528 final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid), toPathName); 529 Slog.d(TAG, "Cleaning up " + packageName + " on " + volumeUuid); 530 final int[] userIds = mPm.mUserManager.getUserIds(); 531 try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { 532 // Clean up both app data and code 533 // All package moves are frozen until finished 534 535 // We purposefully exclude FLAG_STORAGE_EXTERNAL here, since 536 // this task was only focused on moving data on internal storage. 537 // We don't want ART profiles cleared, because they don't move, 538 // so we would be deleting the only copy (b/149200535). 539 final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE 540 | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES; 541 for (int userId : userIds) { 542 try { 543 mPm.mInstaller.destroyAppData(volumeUuid, packageName, userId, flags, 544 0); 545 } catch (Installer.InstallerException e) { 546 Slog.w(TAG, String.valueOf(e)); 547 } 548 } 549 removeCodePathLI(codeFile); 550 } 551 } 552 } 553