• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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