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.content.pm.PackageManager; 33 import android.os.Trace; 34 import android.os.UserHandle; 35 import android.os.incremental.IncrementalManager; 36 import android.util.Log; 37 import android.util.Slog; 38 import android.util.SparseBooleanArray; 39 40 import com.android.internal.annotations.GuardedBy; 41 import com.android.internal.util.ArrayUtils; 42 import com.android.server.pm.parsing.PackageCacher; 43 import com.android.server.pm.parsing.pkg.AndroidPackage; 44 import com.android.server.pm.parsing.pkg.PackageImpl; 45 import com.android.server.pm.permission.PermissionManagerServiceInternal; 46 import com.android.server.pm.pkg.PackageStateInternal; 47 import com.android.server.pm.pkg.component.ParsedInstrumentation; 48 49 import java.io.File; 50 import java.util.Collections; 51 import java.util.List; 52 53 /** 54 * Removes a package from internal data structures, deletes it data directories if requested, 55 * and clears its app profiles 56 */ 57 final class RemovePackageHelper { 58 private final PackageManagerService mPm; 59 private final IncrementalManager mIncrementalManager; 60 private final Installer mInstaller; 61 private final UserManagerInternal mUserManagerInternal; 62 private final PermissionManagerServiceInternal mPermissionManager; 63 private final SharedLibrariesImpl mSharedLibraries; 64 private final AppDataHelper mAppDataHelper; 65 66 // TODO(b/198166813): remove PMS dependency RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper)67 RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) { 68 mPm = pm; 69 mIncrementalManager = mPm.mInjector.getIncrementalManager(); 70 mInstaller = mPm.mInjector.getInstaller(); 71 mUserManagerInternal = mPm.mInjector.getUserManagerInternal(); 72 mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal(); 73 mSharedLibraries = mPm.mInjector.getSharedLibrariesImpl(); 74 mAppDataHelper = appDataHelper; 75 } 76 RemovePackageHelper(PackageManagerService pm)77 RemovePackageHelper(PackageManagerService pm) { 78 this(pm, new AppDataHelper(pm)); 79 } 80 81 @GuardedBy("mPm.mInstallLock") removeCodePathLI(File codePath)82 public void removeCodePathLI(File codePath) { 83 if (codePath.isDirectory()) { 84 final File codePathParent = codePath.getParentFile(); 85 final boolean needRemoveParent = codePathParent.getName().startsWith(RANDOM_DIR_PREFIX); 86 try { 87 final boolean isIncremental = (mIncrementalManager != null && isIncrementalPath( 88 codePath.getAbsolutePath())); 89 if (isIncremental) { 90 if (needRemoveParent) { 91 mIncrementalManager.rmPackageDir(codePathParent); 92 } else { 93 mIncrementalManager.rmPackageDir(codePath); 94 } 95 } 96 97 final String packageName = codePath.getName(); 98 mInstaller.rmPackageDir(packageName, codePath.getAbsolutePath()); 99 if (needRemoveParent) { 100 mInstaller.rmPackageDir(packageName, codePathParent.getAbsolutePath()); 101 removeCachedResult(codePathParent); 102 } 103 } catch (Installer.InstallerException e) { 104 Slog.w(TAG, "Failed to remove code path", e); 105 } 106 } else { 107 codePath.delete(); 108 } 109 } 110 removeCachedResult(@onNull File codePath)111 private void removeCachedResult(@NonNull File codePath) { 112 if (mPm.getCacheDir() == null) { 113 return; 114 } 115 116 final PackageCacher cacher = new PackageCacher(mPm.getCacheDir()); 117 // Find and delete the cached result belong to the given codePath. 118 cacher.cleanCachedResult(codePath); 119 } 120 removePackageLI(AndroidPackage pkg, boolean chatty)121 public void removePackageLI(AndroidPackage pkg, boolean chatty) { 122 // Remove the parent package setting 123 PackageStateInternal ps = mPm.snapshotComputer() 124 .getPackageStateInternal(pkg.getPackageName()); 125 if (ps != null) { 126 removePackageLI(ps.getPackageName(), chatty); 127 } else if (DEBUG_REMOVE && chatty) { 128 Log.d(TAG, "Not removing package " + pkg.getPackageName() + "; mExtras == null"); 129 } 130 } 131 removePackageLI(String packageName, boolean chatty)132 private void removePackageLI(String packageName, boolean chatty) { 133 if (DEBUG_INSTALL) { 134 if (chatty) { 135 Log.d(TAG, "Removing package " + packageName); 136 } 137 } 138 139 // writer 140 synchronized (mPm.mLock) { 141 final AndroidPackage removedPackage = mPm.mPackages.remove(packageName); 142 if (removedPackage != null) { 143 cleanPackageDataStructuresLILPw(removedPackage, chatty); 144 } 145 } 146 } 147 cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty)148 private void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) { 149 mPm.mComponentResolver.removeAllComponents(pkg, chatty); 150 mPermissionManager.onPackageRemoved(pkg); 151 mPm.getPackageProperty().removeAllProperties(pkg); 152 153 final int instrumentationSize = ArrayUtils.size(pkg.getInstrumentations()); 154 StringBuilder r = null; 155 int i; 156 for (i = 0; i < instrumentationSize; i++) { 157 ParsedInstrumentation a = pkg.getInstrumentations().get(i); 158 mPm.getInstrumentation().remove(a.getComponentName()); 159 if (DEBUG_REMOVE && chatty) { 160 if (r == null) { 161 r = new StringBuilder(256); 162 } else { 163 r.append(' '); 164 } 165 r.append(a.getName()); 166 } 167 } 168 if (r != null) { 169 if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r); 170 } 171 172 r = null; 173 if (pkg.isSystem()) { 174 // Only system apps can hold shared libraries. 175 final int libraryNamesSize = pkg.getLibraryNames().size(); 176 for (i = 0; i < libraryNamesSize; i++) { 177 String name = pkg.getLibraryNames().get(i); 178 if (mSharedLibraries.removeSharedLibraryLPw(name, 0)) { 179 if (DEBUG_REMOVE && chatty) { 180 if (r == null) { 181 r = new StringBuilder(256); 182 } else { 183 r.append(' '); 184 } 185 r.append(name); 186 } 187 } 188 } 189 } 190 191 r = null; 192 193 // Any package can hold SDK or static shared libraries. 194 if (pkg.getSdkLibName() != null) { 195 if (mSharedLibraries.removeSharedLibraryLPw( 196 pkg.getSdkLibName(), pkg.getSdkLibVersionMajor())) { 197 if (DEBUG_REMOVE && chatty) { 198 if (r == null) { 199 r = new StringBuilder(256); 200 } else { 201 r.append(' '); 202 } 203 r.append(pkg.getSdkLibName()); 204 } 205 } 206 } 207 if (pkg.getStaticSharedLibName() != null) { 208 if (mSharedLibraries.removeSharedLibraryLPw(pkg.getStaticSharedLibName(), 209 pkg.getStaticSharedLibVersion())) { 210 if (DEBUG_REMOVE && chatty) { 211 if (r == null) { 212 r = new StringBuilder(256); 213 } else { 214 r.append(' '); 215 } 216 r.append(pkg.getStaticSharedLibName()); 217 } 218 } 219 } 220 221 if (r != null) { 222 if (DEBUG_REMOVE) Log.d(TAG, " Libraries: " + r); 223 } 224 } 225 226 /* 227 * This method deletes the package from internal data structures. If the DELETE_KEEP_DATA 228 * flag is not set, the data directory is removed as well. 229 * make sure this flag is set for partially installed apps. If not its meaningless to 230 * delete a partially installed application. 231 */ removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles, PackageRemovedInfo outInfo, int flags, boolean writeSettings)232 public void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles, 233 PackageRemovedInfo outInfo, int flags, boolean writeSettings) { 234 String packageName = deletedPs.getPackageName(); 235 if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs); 236 // Retrieve object to delete permissions for shared user later on 237 final AndroidPackage deletedPkg = deletedPs.getPkg(); 238 if (outInfo != null) { 239 outInfo.mRemovedPackage = packageName; 240 outInfo.mInstallerPackageName = deletedPs.getInstallSource().installerPackageName; 241 outInfo.mIsStaticSharedLib = deletedPkg != null 242 && deletedPkg.getStaticSharedLibName() != null; 243 outInfo.populateUsers(deletedPs.queryInstalledUsers( 244 mUserManagerInternal.getUserIds(), true), deletedPs); 245 outInfo.mIsExternal = deletedPs.isExternalStorage(); 246 } 247 248 removePackageLI(deletedPs.getPackageName(), (flags & PackageManager.DELETE_CHATTY) != 0); 249 250 if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { 251 final AndroidPackage resolvedPkg; 252 if (deletedPkg != null) { 253 resolvedPkg = deletedPkg; 254 } else { 255 // We don't have a parsed package when it lives on an ejected 256 // adopted storage device, so fake something together 257 resolvedPkg = PackageImpl.buildFakeForDeletion(deletedPs.getPackageName(), 258 deletedPs.getVolumeUuid()); 259 } 260 mAppDataHelper.destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL, 261 FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); 262 mAppDataHelper.destroyAppProfilesLIF(resolvedPkg); 263 if (outInfo != null) { 264 outInfo.mDataRemoved = true; 265 } 266 } 267 268 int removedAppId = -1; 269 270 // writer 271 boolean installedStateChanged = false; 272 if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { 273 final SparseBooleanArray changedUsers = new SparseBooleanArray(); 274 synchronized (mPm.mLock) { 275 mPm.mDomainVerificationManager.clearPackage(deletedPs.getPackageName()); 276 mPm.mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName); 277 final Computer snapshot = mPm.snapshotComputer(); 278 mPm.mAppsFilter.removePackage(snapshot, 279 snapshot.getPackageStateInternal(packageName), false /* isReplace */); 280 removedAppId = mPm.mSettings.removePackageLPw(packageName); 281 if (outInfo != null) { 282 outInfo.mRemovedAppId = removedAppId; 283 } 284 if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) { 285 // If we don't have a disabled system package to reinstall, the package is 286 // really gone and its permission state should be removed. 287 SharedUserSetting sus = mPm.mSettings.getSharedUserSettingLPr(deletedPs); 288 List<AndroidPackage> sharedUserPkgs = 289 sus != null ? sus.getPackages() : Collections.emptyList(); 290 mPermissionManager.onPackageUninstalled(packageName, deletedPs.getAppId(), 291 deletedPkg, sharedUserPkgs, UserHandle.USER_ALL); 292 // After permissions are handled, check if the shared user can be migrated 293 if (sus != null) { 294 mPm.mSettings.checkAndConvertSharedUserSettingsLPw(sus); 295 } 296 } 297 mPm.clearPackagePreferredActivitiesLPw( 298 deletedPs.getPackageName(), changedUsers, UserHandle.USER_ALL); 299 300 mPm.mSettings.removeRenamedPackageLPw(deletedPs.getRealName()); 301 } 302 if (changedUsers.size() > 0) { 303 final PreferredActivityHelper preferredActivityHelper = 304 new PreferredActivityHelper(mPm); 305 preferredActivityHelper.updateDefaultHomeNotLocked(mPm.snapshotComputer(), 306 changedUsers); 307 mPm.postPreferredActivityChangedBroadcast(UserHandle.USER_ALL); 308 } 309 } 310 // make sure to preserve per-user disabled state if this removal was just 311 // a downgrade of a system app to the factory package 312 if (outInfo != null && outInfo.mOrigUsers != null) { 313 if (DEBUG_REMOVE) { 314 Slog.d(TAG, "Propagating install state across downgrade"); 315 } 316 for (int userId : allUserHandles) { 317 final boolean installed = ArrayUtils.contains(outInfo.mOrigUsers, userId); 318 if (DEBUG_REMOVE) { 319 Slog.d(TAG, " user " + userId + " => " + installed); 320 } 321 if (installed != deletedPs.getInstalled(userId)) { 322 installedStateChanged = true; 323 } 324 deletedPs.setInstalled(installed, userId); 325 if (installed) { 326 deletedPs.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); 327 } 328 } 329 } 330 synchronized (mPm.mLock) { 331 // can downgrade to reader 332 if (writeSettings) { 333 // Save settings now 334 mPm.writeSettingsLPrTEMP(); 335 } 336 if (installedStateChanged) { 337 mPm.mSettings.writeKernelMappingLPr(deletedPs); 338 } 339 } 340 341 if (removedAppId != -1) { 342 // A user ID was deleted here. Go through all users and remove it from KeyStore. 343 final int appIdToRemove = removedAppId; 344 mPm.mInjector.getBackgroundHandler().post(() -> { 345 try { 346 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, 347 "clearKeystoreData:" + appIdToRemove); 348 mAppDataHelper.clearKeystoreData(UserHandle.USER_ALL, appIdToRemove); 349 } finally { 350 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 351 } 352 }); 353 } 354 } 355 } 356