From 110a12dff13276baa12e8587449a1a7f3a318451 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Thu, 2 Jun 2016 14:01:16 +0100 Subject: [PATCH] PackageManager: Rename foreign use markers on app installation. The foreign use markers contain the codePath for a given executable dex file, so we'll need to rename them when the codePath changes during an application update. If we don't do this, we might compile that are used widely across processes (such as GMS core) with suboptimal filters. This seems like a messy and brittle design in general, and will be rewritten post N so that we don't rely on file system topology. bug: 28998083 Change-Id: Ie4f3995ba52f098edb911b5a388c63696bbd77ac --- .../android/server/pm/PackageManagerService.java | 99 ++++++++++++++++++++-- 1 file changed, 90 insertions(+), 9 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 1ecbbbd8c216..27ca62af5955 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -7671,15 +7671,17 @@ public class PackageManagerService extends IPackageManager.Stub { return; } destroyAppProfilesLeafLIF(pkg); - destroyAppReferenceProfileLeafLIF(pkg, userId); + destroyAppReferenceProfileLeafLIF(pkg, userId, true /* removeBaseMarker */); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppProfilesLeafLIF(pkg.childPackages.get(i)); - destroyAppReferenceProfileLeafLIF(pkg.childPackages.get(i), userId); + destroyAppReferenceProfileLeafLIF(pkg.childPackages.get(i), userId, + true /* removeBaseMarker */); } } - private void destroyAppReferenceProfileLeafLIF(PackageParser.Package pkg, int userId) { + private void destroyAppReferenceProfileLeafLIF(PackageParser.Package pkg, int userId, + boolean removeBaseMarker) { if (pkg.isForwardLocked()) { return; } @@ -7696,11 +7698,13 @@ public class PackageManagerService extends IPackageManager.Stub { final String useMarker = path.replace('/', '@'); for (int realUserId : resolveUserIds(userId)) { File profileDir = Environment.getDataProfilesDeForeignDexDirectory(realUserId); - File foreignUseMark = new File(profileDir, useMarker); - if (foreignUseMark.exists()) { - if (!foreignUseMark.delete()) { - Slog.w(TAG, "Unable to delete foreign user mark for package: " - + pkg.packageName); + if (removeBaseMarker) { + File foreignUseMark = new File(profileDir, useMarker); + if (foreignUseMark.exists()) { + if (!foreignUseMark.delete()) { + Slog.w(TAG, "Unable to delete foreign user mark for package: " + + pkg.packageName); + } } } @@ -7738,7 +7742,10 @@ public class PackageManagerService extends IPackageManager.Stub { return; } clearAppProfilesLeafLIF(pkg); - destroyAppReferenceProfileLeafLIF(pkg, userId); + // We don't remove the base foreign use marker when clearing profiles because + // we will rename it when the app is updated. Unlike the actual profile contents, + // the foreign use marker is good across installs. + destroyAppReferenceProfileLeafLIF(pkg, userId, false /* removeBaseMarker */); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { clearAppProfilesLeafLIF(pkg.childPackages.get(i)); @@ -8612,6 +8619,10 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { // We don't expect installation to fail beyond this point + if (pkgSetting.pkg != null) { + maybeRenameForeignDexMarkers(pkgSetting.pkg, pkg, user); + } + // Add the new setting to mSettings mSettings.insertPackageSettingLPw(pkgSetting, pkg); // Add the new setting to mPackages @@ -8971,6 +8982,74 @@ public class PackageManagerService extends IPackageManager.Stub { return pkg; } + private void maybeRenameForeignDexMarkers(PackageParser.Package existing, + PackageParser.Package update, UserHandle user) { + if (existing.applicationInfo == null || update.applicationInfo == null) { + // This isn't due to an app installation. + return; + } + + final File oldCodePath = new File(existing.applicationInfo.getCodePath()); + final File newCodePath = new File(update.applicationInfo.getCodePath()); + + // The codePath hasn't changed, so there's nothing for us to do. + if (Objects.equals(oldCodePath, newCodePath)) { + return; + } + + File canonicalNewCodePath; + try { + canonicalNewCodePath = new File(PackageManagerServiceUtils.realpath(newCodePath)); + } catch (IOException e) { + Slog.w(TAG, "Failed to get canonical path.", e); + return; + } + + // This is a bit of a hack. The oldCodePath doesn't exist at this point (because + // we've already renamed / deleted it) so we cannot call realpath on it. Here we assume + // that the last component of the path (i.e, the name) doesn't need canonicalization + // (i.e, that it isn't ".", ".." or a symbolic link). This is a valid assumption for now + // but may change in the future. Hopefully this function won't exist at that point. + final File canonicalOldCodePath = new File(canonicalNewCodePath.getParentFile(), + oldCodePath.getName()); + + // Calculate the prefixes of the markers. These are just the paths with "/" replaced + // with "@". + String oldMarkerPrefix = canonicalOldCodePath.getAbsolutePath().replace('/', '@'); + if (!oldMarkerPrefix.endsWith("@")) { + oldMarkerPrefix += "@"; + } + String newMarkerPrefix = canonicalNewCodePath.getAbsolutePath().replace('/', '@'); + if (!newMarkerPrefix.endsWith("@")) { + newMarkerPrefix += "@"; + } + + List updatedPaths = update.getAllCodePathsExcludingResourceOnly(); + List markerSuffixes = new ArrayList(updatedPaths.size()); + for (String updatedPath : updatedPaths) { + String updatedPathName = new File(updatedPath).getName(); + markerSuffixes.add(updatedPathName.replace('/', '@')); + } + + for (int userId : resolveUserIds(user.getIdentifier())) { + File profileDir = Environment.getDataProfilesDeForeignDexDirectory(userId); + + for (String markerSuffix : markerSuffixes) { + File oldForeignUseMark = new File(profileDir, oldMarkerPrefix + markerSuffix); + File newForeignUseMark = new File(profileDir, newMarkerPrefix + markerSuffix); + if (oldForeignUseMark.exists()) { + try { + Os.rename(oldForeignUseMark.getAbsolutePath(), + newForeignUseMark.getAbsolutePath()); + } catch (ErrnoException e) { + Slog.w(TAG, "Failed to rename foreign use marker", e); + oldForeignUseMark.delete(); + } + } + } + } + } + /** * Derive the ABI of a non-system package located at {@code scanFile}. This information * is derived purely on the basis of the contents of {@code scanFile} and @@ -16302,6 +16381,8 @@ public class PackageManagerService extends IPackageManager.Stub { try (PackageFreezer freezer = freezePackage(packageName, "clearApplicationProfileData")) { synchronized (mInstallLock) { clearAppProfilesLIF(pkg, UserHandle.USER_ALL); + destroyAppReferenceProfileLeafLIF(pkg, UserHandle.USER_ALL, + true /* removeBaseMarker */); } } } -- 2.11.0