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