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.parsing.ApkLiteParseUtils.isApkFile; 20 import static android.os.storage.StorageManager.FLAG_STORAGE_CE; 21 import static android.os.storage.StorageManager.FLAG_STORAGE_DE; 22 import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; 23 24 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; 25 import static com.android.server.pm.PackageManagerService.SCAN_INITIAL; 26 import static com.android.server.pm.PackageManagerService.TAG; 27 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; 28 29 import android.annotation.NonNull; 30 import android.app.ResourcesManager; 31 import android.content.IIntentReceiver; 32 import android.content.pm.PackageManager; 33 import android.content.pm.PackagePartitions; 34 import android.content.pm.UserInfo; 35 import android.content.pm.VersionedPackage; 36 import android.os.Environment; 37 import android.os.FileUtils; 38 import android.os.UserHandle; 39 import android.os.storage.StorageEventListener; 40 import android.os.storage.StorageManager; 41 import android.os.storage.StorageManagerInternal; 42 import android.os.storage.VolumeInfo; 43 import android.text.TextUtils; 44 import android.util.ArrayMap; 45 import android.util.ArraySet; 46 import android.util.Log; 47 import android.util.Slog; 48 49 import com.android.internal.annotations.GuardedBy; 50 import com.android.internal.policy.AttributeCache; 51 import com.android.internal.util.IndentingPrintWriter; 52 import com.android.server.pm.parsing.pkg.AndroidPackage; 53 import com.android.server.pm.pkg.PackageStateInternal; 54 import com.android.server.pm.pkg.parsing.ParsingPackageUtils; 55 56 import java.io.File; 57 import java.io.PrintWriter; 58 import java.util.ArrayList; 59 import java.util.List; 60 61 /** Helper class to handle storage events and private apps loading */ 62 public final class StorageEventHelper extends StorageEventListener { 63 private final PackageManagerService mPm; 64 private final BroadcastHelper mBroadcastHelper; 65 private final DeletePackageHelper mDeletePackageHelper; 66 private final RemovePackageHelper mRemovePackageHelper; 67 68 @GuardedBy("mLoadedVolumes") 69 final ArraySet<String> mLoadedVolumes = new ArraySet<>(); 70 71 // TODO(b/198166813): remove PMS dependency StorageEventHelper(PackageManagerService pm, DeletePackageHelper deletePackageHelper, RemovePackageHelper removePackageHelper)72 public StorageEventHelper(PackageManagerService pm, DeletePackageHelper deletePackageHelper, 73 RemovePackageHelper removePackageHelper) { 74 mPm = pm; 75 mBroadcastHelper = new BroadcastHelper(mPm.mInjector); 76 mDeletePackageHelper = deletePackageHelper; 77 mRemovePackageHelper = removePackageHelper; 78 } 79 80 @Override onVolumeStateChanged(VolumeInfo vol, int oldState, int newState)81 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 82 if (vol.type == VolumeInfo.TYPE_PRIVATE) { 83 if (vol.state == VolumeInfo.STATE_MOUNTED) { 84 final String volumeUuid = vol.getFsUuid(); 85 86 // Clean up any users or apps that were removed or recreated 87 // while this volume was missing 88 mPm.mUserManager.reconcileUsers(volumeUuid); 89 reconcileApps(mPm.snapshotComputer(), volumeUuid); 90 91 // Clean up any install sessions that expired or were 92 // cancelled while this volume was missing 93 mPm.mInstallerService.onPrivateVolumeMounted(volumeUuid); 94 95 loadPrivatePackages(vol); 96 97 } else if (vol.state == VolumeInfo.STATE_EJECTING) { 98 unloadPrivatePackages(vol); 99 } 100 } 101 } 102 103 @Override onVolumeForgotten(String fsUuid)104 public void onVolumeForgotten(String fsUuid) { 105 if (TextUtils.isEmpty(fsUuid)) { 106 Slog.e(TAG, "Forgetting internal storage is probably a mistake; ignoring"); 107 return; 108 } 109 110 // Remove any apps installed on the forgotten volume 111 synchronized (mPm.mLock) { 112 final List<? extends PackageStateInternal> packages = 113 mPm.mSettings.getVolumePackagesLPr(fsUuid); 114 for (PackageStateInternal ps : packages) { 115 Slog.d(TAG, "Destroying " + ps.getPackageName() 116 + " because volume was forgotten"); 117 mPm.deletePackageVersioned(new VersionedPackage(ps.getPackageName(), 118 PackageManager.VERSION_CODE_HIGHEST), 119 new PackageManager.LegacyPackageDeleteObserver(null).getBinder(), 120 UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS); 121 // Try very hard to release any references to this package 122 // so we don't risk the system server being killed due to 123 // open FDs 124 AttributeCache.instance().removePackage(ps.getPackageName()); 125 } 126 127 mPm.mSettings.onVolumeForgotten(fsUuid); 128 mPm.writeSettingsLPrTEMP(); 129 } 130 } 131 loadPrivatePackages(final VolumeInfo vol)132 private void loadPrivatePackages(final VolumeInfo vol) { 133 mPm.mHandler.post(() -> loadPrivatePackagesInner(vol)); 134 } 135 loadPrivatePackagesInner(VolumeInfo vol)136 private void loadPrivatePackagesInner(VolumeInfo vol) { 137 final String volumeUuid = vol.fsUuid; 138 if (TextUtils.isEmpty(volumeUuid)) { 139 Slog.e(TAG, "Loading internal storage is probably a mistake; ignoring"); 140 return; 141 } 142 143 final AppDataHelper appDataHelper = new AppDataHelper(mPm); 144 final ArrayList<PackageFreezer> freezers = new ArrayList<>(); 145 final ArrayList<AndroidPackage> loaded = new ArrayList<>(); 146 final int parseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE; 147 148 final Settings.VersionInfo ver; 149 final List<? extends PackageStateInternal> packages; 150 final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm); 151 synchronized (mPm.mLock) { 152 ver = mPm.mSettings.findOrCreateVersion(volumeUuid); 153 packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid); 154 } 155 156 for (PackageStateInternal ps : packages) { 157 freezers.add(mPm.freezePackage(ps.getPackageName(), "loadPrivatePackagesInner")); 158 synchronized (mPm.mInstallLock) { 159 final AndroidPackage pkg; 160 try { 161 pkg = installPackageHelper.scanSystemPackageTracedLI( 162 ps.getPath(), parseFlags, SCAN_INITIAL, null); 163 loaded.add(pkg); 164 165 } catch (PackageManagerException e) { 166 Slog.w(TAG, "Failed to scan " + ps.getPath() + ": " + e.getMessage()); 167 } 168 169 if (!PackagePartitions.FINGERPRINT.equals(ver.fingerprint)) { 170 appDataHelper.clearAppDataLIF( 171 ps.getPkg(), UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE 172 | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY 173 | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES); 174 } 175 } 176 } 177 178 // Reconcile app data for all started/unlocked users 179 final StorageManager sm = mPm.mInjector.getSystemService(StorageManager.class); 180 UserManagerInternal umInternal = mPm.mInjector.getUserManagerInternal(); 181 StorageManagerInternal smInternal = mPm.mInjector.getLocalService( 182 StorageManagerInternal.class); 183 for (UserInfo user : mPm.mUserManager.getUsers(false /* includeDying */)) { 184 final int flags; 185 if (StorageManager.isUserKeyUnlocked(user.id) 186 && smInternal.isCeStoragePrepared(user.id)) { 187 flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; 188 } else if (umInternal.isUserRunning(user.id)) { 189 flags = StorageManager.FLAG_STORAGE_DE; 190 } else { 191 continue; 192 } 193 194 try { 195 sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags); 196 synchronized (mPm.mInstallLock) { 197 appDataHelper.reconcileAppsDataLI(volumeUuid, user.id, flags, 198 true /* migrateAppData */); 199 } 200 } catch (IllegalStateException e) { 201 // Device was probably ejected, and we'll process that event momentarily 202 Slog.w(TAG, "Failed to prepare storage: " + e); 203 } 204 } 205 206 synchronized (mPm.mLock) { 207 final boolean isUpgrade = !PackagePartitions.FINGERPRINT.equals(ver.fingerprint); 208 if (isUpgrade) { 209 logCriticalInfo(Log.INFO, "Build fingerprint changed from " + ver.fingerprint 210 + " to " + PackagePartitions.FINGERPRINT + "; regranting permissions for " 211 + volumeUuid); 212 } 213 mPm.mPermissionManager.onStorageVolumeMounted(volumeUuid, isUpgrade); 214 215 // Yay, everything is now upgraded 216 ver.forceCurrent(); 217 218 mPm.writeSettingsLPrTEMP(); 219 } 220 221 for (PackageFreezer freezer : freezers) { 222 freezer.close(); 223 } 224 225 if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded); 226 sendResourcesChangedBroadcast(true, false, loaded, null); 227 synchronized (mLoadedVolumes) { 228 mLoadedVolumes.add(vol.getId()); 229 } 230 } 231 unloadPrivatePackages(final VolumeInfo vol)232 private void unloadPrivatePackages(final VolumeInfo vol) { 233 mPm.mHandler.post(() -> unloadPrivatePackagesInner(vol)); 234 } 235 unloadPrivatePackagesInner(VolumeInfo vol)236 private void unloadPrivatePackagesInner(VolumeInfo vol) { 237 final String volumeUuid = vol.fsUuid; 238 if (TextUtils.isEmpty(volumeUuid)) { 239 Slog.e(TAG, "Unloading internal storage is probably a mistake; ignoring"); 240 return; 241 } 242 243 final int[] userIds = mPm.mUserManager.getUserIds(); 244 final ArrayList<AndroidPackage> unloaded = new ArrayList<>(); 245 synchronized (mPm.mInstallLock) { 246 synchronized (mPm.mLock) { 247 final List<? extends PackageStateInternal> packages = 248 mPm.mSettings.getVolumePackagesLPr(volumeUuid); 249 for (PackageStateInternal ps : packages) { 250 if (ps.getPkg() == null) continue; 251 252 final AndroidPackage pkg = ps.getPkg(); 253 final int deleteFlags = PackageManager.DELETE_KEEP_DATA; 254 final PackageRemovedInfo outInfo = new PackageRemovedInfo(mPm); 255 256 try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.getPackageName(), 257 deleteFlags, "unloadPrivatePackagesInner")) { 258 if (mDeletePackageHelper.deletePackageLIF(ps.getPackageName(), null, false, 259 userIds, deleteFlags, outInfo, false)) { 260 unloaded.add(pkg); 261 } else { 262 Slog.w(TAG, "Failed to unload " + ps.getPath()); 263 } 264 } 265 266 // Try very hard to release any references to this package 267 // so we don't risk the system server being killed due to 268 // open FDs 269 AttributeCache.instance().removePackage(ps.getPackageName()); 270 } 271 272 mPm.writeSettingsLPrTEMP(); 273 } 274 } 275 276 if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded); 277 sendResourcesChangedBroadcast(false, false, unloaded, null); 278 synchronized (mLoadedVolumes) { 279 mLoadedVolumes.remove(vol.getId()); 280 } 281 282 // Try very hard to release any references to this path so we don't risk 283 // the system server being killed due to open FDs 284 ResourcesManager.getInstance().invalidatePath(vol.getPath().getAbsolutePath()); 285 286 for (int i = 0; i < 3; i++) { 287 System.gc(); 288 System.runFinalization(); 289 } 290 } 291 sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing, ArrayList<AndroidPackage> packages, IIntentReceiver finishedReceiver)292 private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing, 293 ArrayList<AndroidPackage> packages, IIntentReceiver finishedReceiver) { 294 final int size = packages.size(); 295 final String[] packageNames = new String[size]; 296 final int[] packageUids = new int[size]; 297 for (int i = 0; i < size; i++) { 298 final AndroidPackage pkg = packages.get(i); 299 packageNames[i] = pkg.getPackageName(); 300 packageUids[i] = pkg.getUid(); 301 } 302 mBroadcastHelper.sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, 303 packageUids, finishedReceiver); 304 } 305 306 /** 307 * Examine all apps present on given mounted volume, and destroy apps that 308 * aren't expected, either due to uninstallation or reinstallation on 309 * another volume. 310 */ reconcileApps(@onNull Computer snapshot, String volumeUuid)311 public void reconcileApps(@NonNull Computer snapshot, String volumeUuid) { 312 List<String> absoluteCodePaths = collectAbsoluteCodePaths(snapshot); 313 List<File> filesToDelete = null; 314 315 final File[] files = FileUtils.listFilesOrEmpty( 316 Environment.getDataAppDirectory(volumeUuid)); 317 for (File file : files) { 318 final boolean isPackage = (isApkFile(file) || file.isDirectory()) 319 && !PackageInstallerService.isStageName(file.getName()); 320 if (!isPackage) { 321 // Ignore entries which are not packages 322 continue; 323 } 324 325 String absolutePath = file.getAbsolutePath(); 326 327 boolean pathValid = false; 328 final int absoluteCodePathCount = absoluteCodePaths.size(); 329 for (int i = 0; i < absoluteCodePathCount; i++) { 330 String absoluteCodePath = absoluteCodePaths.get(i); 331 if (absoluteCodePath.startsWith(absolutePath)) { 332 pathValid = true; 333 break; 334 } 335 } 336 337 if (!pathValid) { 338 if (filesToDelete == null) { 339 filesToDelete = new ArrayList<>(); 340 } 341 filesToDelete.add(file); 342 } 343 } 344 345 if (filesToDelete != null) { 346 final int fileToDeleteCount = filesToDelete.size(); 347 for (int i = 0; i < fileToDeleteCount; i++) { 348 File fileToDelete = filesToDelete.get(i); 349 logCriticalInfo(Log.WARN, "Destroying orphaned at " + fileToDelete); 350 synchronized (mPm.mInstallLock) { 351 mRemovePackageHelper.removeCodePathLI(fileToDelete); 352 } 353 } 354 } 355 } 356 collectAbsoluteCodePaths(@onNull Computer snapshot)357 private List<String> collectAbsoluteCodePaths(@NonNull Computer snapshot) { 358 List<String> codePaths = new ArrayList<>(); 359 final ArrayMap<String, ? extends PackageStateInternal> packageStates = 360 snapshot.getPackageStates(); 361 final int packageCount = packageStates.size(); 362 for (int i = 0; i < packageCount; i++) { 363 final PackageStateInternal ps = packageStates.valueAt(i); 364 codePaths.add(ps.getPath().getAbsolutePath()); 365 } 366 return codePaths; 367 } 368 dumpLoadedVolumes(@onNull PrintWriter pw, @NonNull DumpState dumpState)369 public void dumpLoadedVolumes(@NonNull PrintWriter pw, @NonNull DumpState dumpState) { 370 if (dumpState.onTitlePrinted()) { 371 pw.println(); 372 } 373 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); 374 ipw.println(); 375 ipw.println("Loaded volumes:"); 376 ipw.increaseIndent(); 377 synchronized (mLoadedVolumes) { 378 if (mLoadedVolumes.size() == 0) { 379 ipw.println("(none)"); 380 } else { 381 for (int i = 0; i < mLoadedVolumes.size(); i++) { 382 ipw.println(mLoadedVolumes.valueAt(i)); 383 } 384 } 385 } 386 ipw.decreaseIndent(); 387 } 388 } 389