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.InstructionSets.getDexCodeInstructionSets; 27 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; 28 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE; 29 import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX; 30 import static com.android.server.pm.PackageManagerService.TAG; 31 32 import android.annotation.NonNull; 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.util.ArrayUtils; 48 import com.android.server.pm.Installer.LegacyDexoptDisabledException; 49 import com.android.server.pm.parsing.PackageCacher; 50 import com.android.server.pm.parsing.pkg.AndroidPackageUtils; 51 import com.android.server.pm.parsing.pkg.PackageImpl; 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 import com.android.server.pm.pkg.component.ParsedInstrumentation; 56 57 import java.io.File; 58 import java.util.Collections; 59 import java.util.List; 60 61 /** 62 * Removes a package from internal data structures, deletes it data directories if requested, 63 * and clears its app profiles 64 */ 65 final class RemovePackageHelper { 66 private final PackageManagerService mPm; 67 private final IncrementalManager mIncrementalManager; 68 private final Installer mInstaller; 69 private final UserManagerInternal mUserManagerInternal; 70 private final PermissionManagerServiceInternal mPermissionManager; 71 private final SharedLibrariesImpl mSharedLibraries; 72 private final AppDataHelper mAppDataHelper; 73 74 // TODO(b/198166813): remove PMS dependency RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper)75 RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) { 76 mPm = pm; 77 mIncrementalManager = mPm.mInjector.getIncrementalManager(); 78 mInstaller = mPm.mInjector.getInstaller(); 79 mUserManagerInternal = mPm.mInjector.getUserManagerInternal(); 80 mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal(); 81 mSharedLibraries = mPm.mInjector.getSharedLibrariesImpl(); 82 mAppDataHelper = appDataHelper; 83 } 84 RemovePackageHelper(PackageManagerService pm)85 RemovePackageHelper(PackageManagerService pm) { 86 this(pm, new AppDataHelper(pm)); 87 } 88 removeCodePath(File codePath)89 public void removeCodePath(File codePath) { 90 synchronized (mPm.mInstallLock) { 91 removeCodePathLI(codePath); 92 } 93 } 94 95 @GuardedBy("mPm.mInstallLock") removeCodePathLI(File codePath)96 private void removeCodePathLI(File codePath) { 97 if (codePath == null || !codePath.exists()) { 98 return; 99 } 100 if (codePath.isDirectory()) { 101 final File codePathParent = codePath.getParentFile(); 102 final boolean needRemoveParent = codePathParent.getName().startsWith(RANDOM_DIR_PREFIX); 103 try { 104 final boolean isIncremental = (mIncrementalManager != null && isIncrementalPath( 105 codePath.getAbsolutePath())); 106 if (isIncremental) { 107 if (needRemoveParent) { 108 mIncrementalManager.rmPackageDir(codePathParent); 109 } else { 110 mIncrementalManager.rmPackageDir(codePath); 111 } 112 } 113 114 final String packageName = codePath.getName(); 115 mInstaller.rmPackageDir(packageName, codePath.getAbsolutePath()); 116 if (needRemoveParent) { 117 mInstaller.rmPackageDir(packageName, codePathParent.getAbsolutePath()); 118 removeCachedResult(codePathParent); 119 } 120 } catch (Installer.InstallerException e) { 121 Slog.w(TAG, "Failed to remove code path", e); 122 } 123 } else { 124 codePath.delete(); 125 } 126 } 127 removeCachedResult(@onNull File codePath)128 private void removeCachedResult(@NonNull File codePath) { 129 if (mPm.getCacheDir() == null) { 130 return; 131 } 132 133 final PackageCacher cacher = new PackageCacher(mPm.getCacheDir()); 134 // Find and delete the cached result belong to the given codePath. 135 cacher.cleanCachedResult(codePath); 136 } 137 removePackage(AndroidPackage pkg, boolean chatty)138 public void removePackage(AndroidPackage pkg, boolean chatty) { 139 synchronized (mPm.mInstallLock) { 140 removePackageLI(pkg, chatty); 141 } 142 } 143 144 @GuardedBy("mPm.mInstallLock") removePackageLI(AndroidPackage pkg, boolean chatty)145 private void removePackageLI(AndroidPackage pkg, boolean chatty) { 146 // Remove the parent package setting 147 PackageStateInternal ps = mPm.snapshotComputer() 148 .getPackageStateInternal(pkg.getPackageName()); 149 if (ps != null) { 150 removePackageLI(ps.getPackageName(), chatty); 151 } else if (DEBUG_REMOVE && chatty) { 152 Log.d(TAG, "Not removing package " + pkg.getPackageName() + "; mExtras == null"); 153 } 154 } 155 156 @GuardedBy("mPm.mInstallLock") removePackageLI(String packageName, boolean chatty)157 private void removePackageLI(String packageName, boolean chatty) { 158 if (DEBUG_INSTALL) { 159 if (chatty) { 160 Log.d(TAG, "Removing package " + packageName); 161 } 162 } 163 164 // writer 165 synchronized (mPm.mLock) { 166 final AndroidPackage removedPackage = mPm.mPackages.remove(packageName); 167 if (removedPackage != null) { 168 // TODO: Use PackageState for isSystem 169 cleanPackageDataStructuresLILPw(removedPackage, 170 AndroidPackageUtils.isSystem(removedPackage), chatty); 171 } 172 } 173 } 174 175 @GuardedBy("mPm.mLock") cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean isSystemApp, boolean chatty)176 private void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean isSystemApp, 177 boolean chatty) { 178 mPm.mComponentResolver.removeAllComponents(pkg, chatty); 179 mPermissionManager.onPackageRemoved(pkg); 180 mPm.getPackageProperty().removeAllProperties(pkg); 181 182 final int instrumentationSize = ArrayUtils.size(pkg.getInstrumentations()); 183 StringBuilder r = null; 184 int i; 185 for (i = 0; i < instrumentationSize; i++) { 186 ParsedInstrumentation a = pkg.getInstrumentations().get(i); 187 mPm.getInstrumentation().remove(a.getComponentName()); 188 if (DEBUG_REMOVE && chatty) { 189 if (r == null) { 190 r = new StringBuilder(256); 191 } else { 192 r.append(' '); 193 } 194 r.append(a.getName()); 195 } 196 } 197 if (r != null) { 198 if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r); 199 } 200 201 r = null; 202 if (isSystemApp) { 203 // Only system apps can hold shared libraries. 204 final int libraryNamesSize = pkg.getLibraryNames().size(); 205 for (i = 0; i < libraryNamesSize; i++) { 206 String name = pkg.getLibraryNames().get(i); 207 if (mSharedLibraries.removeSharedLibrary(name, 0)) { 208 if (DEBUG_REMOVE && chatty) { 209 if (r == null) { 210 r = new StringBuilder(256); 211 } else { 212 r.append(' '); 213 } 214 r.append(name); 215 } 216 } 217 } 218 } 219 220 r = null; 221 222 // Any package can hold SDK or static shared libraries. 223 if (pkg.getSdkLibraryName() != null) { 224 if (mSharedLibraries.removeSharedLibrary( 225 pkg.getSdkLibraryName(), pkg.getSdkLibVersionMajor())) { 226 if (DEBUG_REMOVE && chatty) { 227 if (r == null) { 228 r = new StringBuilder(256); 229 } else { 230 r.append(' '); 231 } 232 r.append(pkg.getSdkLibraryName()); 233 } 234 } 235 } 236 if (pkg.getStaticSharedLibraryName() != null) { 237 if (mSharedLibraries.removeSharedLibrary(pkg.getStaticSharedLibraryName(), 238 pkg.getStaticSharedLibraryVersion())) { 239 if (DEBUG_REMOVE && chatty) { 240 if (r == null) { 241 r = new StringBuilder(256); 242 } else { 243 r.append(' '); 244 } 245 r.append(pkg.getStaticSharedLibraryName()); 246 } 247 } 248 } 249 250 if (r != null) { 251 if (DEBUG_REMOVE) Log.d(TAG, " Libraries: " + r); 252 } 253 } 254 removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles, PackageRemovedInfo outInfo, int flags, boolean writeSettings)255 public void removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles, 256 PackageRemovedInfo outInfo, int flags, boolean writeSettings) { 257 synchronized (mPm.mInstallLock) { 258 removePackageDataLIF(deletedPs, allUserHandles, outInfo, flags, writeSettings); 259 } 260 } 261 262 /* 263 * This method deletes the package from internal data structures. If the DELETE_KEEP_DATA 264 * flag is not set, the data directory is removed as well. 265 * make sure this flag is set for partially installed apps. If not its meaningless to 266 * delete a partially installed application. 267 */ 268 @GuardedBy("mPm.mInstallLock") removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles, PackageRemovedInfo outInfo, int flags, boolean writeSettings)269 public void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles, 270 PackageRemovedInfo outInfo, int flags, boolean writeSettings) { 271 String packageName = deletedPs.getPackageName(); 272 if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs); 273 // Retrieve object to delete permissions for shared user later on 274 final AndroidPackage deletedPkg = deletedPs.getPkg(); 275 if (outInfo != null) { 276 outInfo.mRemovedPackage = packageName; 277 outInfo.mInstallerPackageName = deletedPs.getInstallSource().mInstallerPackageName; 278 outInfo.mIsStaticSharedLib = deletedPkg != null 279 && deletedPkg.getStaticSharedLibraryName() != null; 280 outInfo.populateUsers(deletedPs.queryInstalledUsers( 281 mUserManagerInternal.getUserIds(), true), deletedPs); 282 outInfo.mIsExternal = deletedPs.isExternalStorage(); 283 outInfo.mRemovedPackageVersionCode = deletedPs.getVersionCode(); 284 } 285 286 removePackageLI(deletedPs.getPackageName(), (flags & PackageManager.DELETE_CHATTY) != 0); 287 288 if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { 289 final AndroidPackage resolvedPkg; 290 if (deletedPkg != null) { 291 resolvedPkg = deletedPkg; 292 } else { 293 // We don't have a parsed package when it lives on an ejected 294 // adopted storage device, so fake something together 295 resolvedPkg = PackageImpl.buildFakeForDeletion(deletedPs.getPackageName(), 296 deletedPs.getVolumeUuid()); 297 } 298 mAppDataHelper.destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL, 299 FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); 300 mAppDataHelper.destroyAppProfilesLIF(resolvedPkg); 301 if (outInfo != null) { 302 outInfo.mDataRemoved = true; 303 } 304 } 305 306 int removedAppId = -1; 307 308 // writer 309 boolean installedStateChanged = false; 310 if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { 311 final SparseBooleanArray changedUsers = new SparseBooleanArray(); 312 synchronized (mPm.mLock) { 313 mPm.mDomainVerificationManager.clearPackage(deletedPs.getPackageName()); 314 mPm.mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName); 315 mPm.mInjector.getUpdateOwnershipHelper().removeUpdateOwnerDenyList(packageName); 316 final Computer snapshot = mPm.snapshotComputer(); 317 mPm.mAppsFilter.removePackage(snapshot, 318 snapshot.getPackageStateInternal(packageName)); 319 removedAppId = mPm.mSettings.removePackageLPw(packageName); 320 if (outInfo != null) { 321 outInfo.mRemovedAppId = removedAppId; 322 } 323 if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) { 324 // If we don't have a disabled system package to reinstall, the package is 325 // really gone and its permission state should be removed. 326 SharedUserSetting sus = mPm.mSettings.getSharedUserSettingLPr(deletedPs); 327 List<AndroidPackage> sharedUserPkgs = 328 sus != null ? sus.getPackages() : Collections.emptyList(); 329 mPermissionManager.onPackageUninstalled(packageName, deletedPs.getAppId(), 330 deletedPs, deletedPkg, sharedUserPkgs, UserHandle.USER_ALL); 331 // After permissions are handled, check if the shared user can be migrated 332 if (sus != null) { 333 mPm.mSettings.checkAndConvertSharedUserSettingsLPw(sus); 334 } 335 } 336 mPm.clearPackagePreferredActivitiesLPw( 337 deletedPs.getPackageName(), changedUsers, UserHandle.USER_ALL); 338 339 mPm.mSettings.removeRenamedPackageLPw(deletedPs.getRealName()); 340 } 341 if (changedUsers.size() > 0) { 342 final PreferredActivityHelper preferredActivityHelper = 343 new PreferredActivityHelper(mPm); 344 preferredActivityHelper.updateDefaultHomeNotLocked(mPm.snapshotComputer(), 345 changedUsers); 346 mPm.postPreferredActivityChangedBroadcast(UserHandle.USER_ALL); 347 } 348 } 349 // make sure to preserve per-user disabled state if this removal was just 350 // a downgrade of a system app to the factory package 351 if (outInfo != null && outInfo.mOrigUsers != null) { 352 if (DEBUG_REMOVE) { 353 Slog.d(TAG, "Propagating install state across downgrade"); 354 } 355 for (int userId : allUserHandles) { 356 final boolean installed = ArrayUtils.contains(outInfo.mOrigUsers, userId); 357 if (DEBUG_REMOVE) { 358 Slog.d(TAG, " user " + userId + " => " + installed); 359 } 360 if (installed != deletedPs.getInstalled(userId)) { 361 installedStateChanged = true; 362 } 363 deletedPs.setInstalled(installed, userId); 364 if (installed) { 365 deletedPs.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); 366 } 367 } 368 } 369 synchronized (mPm.mLock) { 370 // can downgrade to reader 371 if (writeSettings) { 372 // Save settings now 373 mPm.writeSettingsLPrTEMP(); 374 } 375 if (installedStateChanged) { 376 mPm.mSettings.writeKernelMappingLPr(deletedPs); 377 } 378 } 379 380 if (removedAppId != -1) { 381 // A user ID was deleted here. Go through all users and remove it from KeyStore. 382 final int appIdToRemove = removedAppId; 383 mPm.mInjector.getBackgroundHandler().post(() -> { 384 try { 385 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, 386 "clearKeystoreData:" + appIdToRemove); 387 mAppDataHelper.clearKeystoreData(UserHandle.USER_ALL, appIdToRemove); 388 } finally { 389 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 390 } 391 }); 392 } 393 } 394 cleanUpResources(File codeFile, String[] instructionSets)395 void cleanUpResources(File codeFile, String[] instructionSets) { 396 synchronized (mPm.mInstallLock) { 397 cleanUpResourcesLI(codeFile, instructionSets); 398 } 399 } 400 401 // Need installer lock especially for dex file removal. 402 @GuardedBy("mPm.mInstallLock") cleanUpResourcesLI(File codeFile, String[] instructionSets)403 private void cleanUpResourcesLI(File codeFile, String[] instructionSets) { 404 // Try enumerating all code paths before deleting 405 List<String> allCodePaths = Collections.EMPTY_LIST; 406 if (codeFile != null && codeFile.exists()) { 407 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 408 final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite( 409 input.reset(), codeFile, /* flags */ 0); 410 if (result.isSuccess()) { 411 // Ignore error; we tried our best 412 allCodePaths = result.getResult().getAllApkPaths(); 413 } 414 } 415 416 removeCodePathLI(codeFile); 417 removeDexFilesLI(allCodePaths, instructionSets); 418 } 419 420 @GuardedBy("mPm.mInstallLock") removeDexFilesLI(List<String> allCodePaths, String[] instructionSets)421 private void removeDexFilesLI(List<String> allCodePaths, String[] instructionSets) { 422 if (!allCodePaths.isEmpty()) { 423 if (instructionSets == null) { 424 throw new IllegalStateException("instructionSet == null"); 425 } 426 // TODO(b/265813358): ART Service currently doesn't support deleting optimized artifacts 427 // relative to an arbitrary APK path. Skip this and rely on its file GC instead. 428 if (!DexOptHelper.useArtService()) { 429 String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); 430 for (String codePath : allCodePaths) { 431 for (String dexCodeInstructionSet : dexCodeInstructionSets) { 432 try { 433 mPm.mInstaller.rmdex(codePath, dexCodeInstructionSet); 434 } catch (LegacyDexoptDisabledException e) { 435 throw new RuntimeException(e); 436 } catch (Installer.InstallerException ignored) { 437 } 438 } 439 } 440 } 441 } 442 } 443 cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath)444 void cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath) { 445 final String toPathName = new File(fromCodePath).getName(); 446 final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid), toPathName); 447 Slog.d(TAG, "Cleaning up " + packageName + " on " + volumeUuid); 448 final int[] userIds = mPm.mUserManager.getUserIds(); 449 synchronized (mPm.mInstallLock) { 450 // Clean up both app data and code 451 // All package moves are frozen until finished 452 453 // We purposefully exclude FLAG_STORAGE_EXTERNAL here, since 454 // this task was only focused on moving data on internal storage. 455 // We don't want ART profiles cleared, because they don't move, 456 // so we would be deleting the only copy (b/149200535). 457 final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE 458 | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES; 459 for (int userId : userIds) { 460 try { 461 mPm.mInstaller.destroyAppData(volumeUuid, packageName, userId, flags, 462 0); 463 } catch (Installer.InstallerException e) { 464 Slog.w(TAG, String.valueOf(e)); 465 } 466 } 467 removeCodePathLI(codeFile); 468 } 469 } 470 } 471