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.ApplicationExitInfo;
31 import android.app.ResourcesManager;
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.pm.pkg.parsing.ParsingPackageUtils;
51 import com.android.internal.policy.AttributeCache;
52 import com.android.internal.util.IndentingPrintWriter;
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.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         synchronized (mPm.mLock) {
151             ver = mPm.mSettings.findOrCreateVersion(volumeUuid);
152             packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid);
153         }
154 
155         for (PackageStateInternal ps : packages) {
156             freezers.add(mPm.freezePackage(ps.getPackageName(), UserHandle.USER_ALL,
157                     "loadPrivatePackagesInner", ApplicationExitInfo.REASON_OTHER,
158                     null /* request */));
159             try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
160                 final AndroidPackage pkg;
161                 try {
162                     pkg = mPm.initPackageTracedLI(
163                             ps.getPath(), parseFlags, SCAN_INITIAL);
164                     loaded.add(pkg);
165 
166                 } catch (PackageManagerException e) {
167                     Slog.w(TAG, "Failed to scan " + ps.getPath() + ": " + e.getMessage());
168                 }
169 
170                 if (!PackagePartitions.FINGERPRINT.equals(ver.fingerprint)) {
171                     appDataHelper.clearAppDataLIF(
172                             ps.getPkg(), UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
173                             | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY
174                             | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
175                 }
176             }
177         }
178 
179         // Reconcile app data for all started/unlocked users
180         final StorageManager sm = mPm.mInjector.getSystemService(StorageManager.class);
181         UserManagerInternal umInternal = mPm.mInjector.getUserManagerInternal();
182         StorageManagerInternal smInternal = mPm.mInjector.getLocalService(
183                 StorageManagerInternal.class);
184         for (UserInfo user : mPm.mUserManager.getUsers(false /* includeDying */)) {
185             final int flags;
186             if (StorageManager.isCeStorageUnlocked(user.id)
187                     && smInternal.isCeStoragePrepared(user.id)) {
188                 flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
189             } else if (umInternal.isUserRunning(user.id)) {
190                 flags = StorageManager.FLAG_STORAGE_DE;
191             } else {
192                 continue;
193             }
194 
195             try {
196                 sm.prepareUserStorage(volumeUuid, user.id, flags);
197                 try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
198                     appDataHelper.reconcileAppsDataLI(volumeUuid, user.id, flags,
199                             true /* migrateAppData */);
200                 }
201             } catch (RuntimeException e) {
202                 // The volume was probably already unmounted.  We'll probably process the unmount
203                 // event momentarily.  TODO(b/256909937): ignoring errors from prepareUserStorage()
204                 // is very dangerous.  Instead, we should fix the race condition that allows this
205                 // code to run on an unmounted volume in the first place.
206                 Slog.w(TAG, "Failed to prepare storage: " + e);
207             }
208         }
209 
210         synchronized (mPm.mLock) {
211             final boolean isUpgrade = !PackagePartitions.FINGERPRINT.equals(ver.fingerprint);
212             if (isUpgrade) {
213                 logCriticalInfo(Log.INFO, "Partitions fingerprint changed from " + ver.fingerprint
214                         + " to " + PackagePartitions.FINGERPRINT + "; regranting permissions for "
215                         + volumeUuid);
216             }
217             mPm.mPermissionManager.onStorageVolumeMounted(volumeUuid, isUpgrade);
218 
219             // Yay, everything is now upgraded
220             ver.forceCurrent();
221 
222             mPm.writeSettingsLPrTEMP();
223         }
224 
225         for (PackageFreezer freezer : freezers) {
226             freezer.close();
227         }
228 
229         if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
230         mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
231                 true /* mediaStatus */, false /* replacing */, loaded);
232         synchronized (mLoadedVolumes) {
233             mLoadedVolumes.add(vol.getId());
234         }
235     }
236 
unloadPrivatePackages(final VolumeInfo vol)237     private void unloadPrivatePackages(final VolumeInfo vol) {
238         mPm.mHandler.post(() -> unloadPrivatePackagesInner(vol));
239     }
240 
unloadPrivatePackagesInner(VolumeInfo vol)241     private void unloadPrivatePackagesInner(VolumeInfo vol) {
242         final String volumeUuid = vol.fsUuid;
243         if (TextUtils.isEmpty(volumeUuid)) {
244             Slog.e(TAG, "Unloading internal storage is probably a mistake; ignoring");
245             return;
246         }
247 
248         final int[] userIds = mPm.mUserManager.getUserIds();
249         final ArrayList<AndroidPackage> unloaded = new ArrayList<>();
250         try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
251             synchronized (mPm.mLock) {
252                 final List<? extends PackageStateInternal> packages =
253                         mPm.mSettings.getVolumePackagesLPr(volumeUuid);
254                 for (PackageStateInternal ps : packages) {
255                     if (ps.getPkg() == null) continue;
256 
257                     final AndroidPackage pkg = ps.getPkg();
258                     final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
259 
260                     try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.getPackageName(),
261                              UserHandle.USER_ALL, deleteFlags,
262                             "unloadPrivatePackagesInner", ApplicationExitInfo.REASON_OTHER)) {
263                         if (mDeletePackageHelper.deletePackageLIF(ps.getPackageName(), null, false,
264                                 userIds, deleteFlags, new PackageRemovedInfo(), false)) {
265                             unloaded.add(pkg);
266                         } else {
267                             Slog.w(TAG, "Failed to unload " + ps.getPath());
268                         }
269                     }
270 
271                     // Try very hard to release any references to this package
272                     // so we don't risk the system server being killed due to
273                     // open FDs
274                     AttributeCache.instance().removePackage(ps.getPackageName());
275                 }
276 
277                 mPm.writeSettingsLPrTEMP();
278             }
279         }
280 
281         if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
282         mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
283                 false /* mediaStatus */, false /* replacing */, unloaded);
284         synchronized (mLoadedVolumes) {
285             mLoadedVolumes.remove(vol.getId());
286         }
287 
288         // Try very hard to release any references to this path so we don't risk
289         // the system server being killed due to open FDs
290         ResourcesManager.getInstance().invalidatePath(vol.getPath().getAbsolutePath());
291 
292         for (int i = 0; i < 3; i++) {
293             System.gc();
294             System.runFinalization();
295         }
296     }
297 
298     /**
299      * Examine all apps present on given mounted volume, and destroy apps that
300      * aren't expected, either due to uninstallation or reinstallation on
301      * another volume.
302      */
reconcileApps(@onNull Computer snapshot, String volumeUuid)303     public void reconcileApps(@NonNull Computer snapshot, String volumeUuid) {
304         List<String> absoluteCodePaths = collectAbsoluteCodePaths(snapshot);
305         List<File> filesToDelete = null;
306 
307         final File[] files = FileUtils.listFilesOrEmpty(
308                 Environment.getDataAppDirectory(volumeUuid));
309         for (File file : files) {
310             final boolean isPackage = (isApkFile(file) || file.isDirectory())
311                     && !PackageInstallerService.isStageName(file.getName());
312             if (!isPackage) {
313                 // Ignore entries which are not packages
314                 continue;
315             }
316 
317             String absolutePath = file.getAbsolutePath();
318 
319             boolean pathValid = false;
320             final int absoluteCodePathCount = absoluteCodePaths.size();
321             for (int i = 0; i < absoluteCodePathCount; i++) {
322                 String absoluteCodePath = absoluteCodePaths.get(i);
323                 if (absoluteCodePath.startsWith(absolutePath)) {
324                     pathValid = true;
325                     break;
326                 }
327             }
328 
329             if (!pathValid) {
330                 if (filesToDelete == null) {
331                     filesToDelete = new ArrayList<>();
332                 }
333                 filesToDelete.add(file);
334             }
335         }
336 
337         if (filesToDelete != null) {
338             final int fileToDeleteCount = filesToDelete.size();
339             for (int i = 0; i < fileToDeleteCount; i++) {
340                 File fileToDelete = filesToDelete.get(i);
341                 logCriticalInfo(Log.WARN, "Destroying orphaned at " + fileToDelete);
342                 mRemovePackageHelper.removeCodePath(fileToDelete);
343             }
344         }
345     }
346 
collectAbsoluteCodePaths(@onNull Computer snapshot)347     private List<String> collectAbsoluteCodePaths(@NonNull Computer snapshot) {
348         List<String> codePaths = new ArrayList<>();
349         final ArrayMap<String, ? extends PackageStateInternal> packageStates =
350                 snapshot.getPackageStates();
351         final int packageCount = packageStates.size();
352         for (int i = 0; i < packageCount; i++) {
353             final PackageStateInternal ps = packageStates.valueAt(i);
354             codePaths.add(ps.getPath().getAbsolutePath());
355         }
356         return codePaths;
357     }
358 
dumpLoadedVolumes(@onNull PrintWriter pw, @NonNull DumpState dumpState)359     public void dumpLoadedVolumes(@NonNull PrintWriter pw, @NonNull DumpState dumpState) {
360         if (dumpState.onTitlePrinted()) {
361             pw.println();
362         }
363         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
364         ipw.println();
365         ipw.println("Loaded volumes:");
366         ipw.increaseIndent();
367         synchronized (mLoadedVolumes) {
368             if (mLoadedVolumes.size() == 0) {
369                 ipw.println("(none)");
370             } else {
371                 for (int i = 0; i < mLoadedVolumes.size(); i++) {
372                     ipw.println(mLoadedVolumes.valueAt(i));
373                 }
374             }
375         }
376         ipw.decreaseIndent();
377     }
378 }
379