diff options
| author | 2020-05-15 10:33:57 -0700 | |
|---|---|---|
| committer | 2020-05-28 16:06:30 -0700 | |
| commit | d4e7f93df50950aff2f973c9c7e8408bd07ca2f6 (patch) | |
| tree | 020c4f305924971c72498617c08eea3602dbcfa7 | |
| parent | 57e977a5859ff24f5ff909046dccde5bce341f6d (diff) | |
Install system app in greatest priority partition
On Pixel 2 devices, /product is a symlink to /system/product. The
product partition has a higher partition precedence than the system
partition so the app should be installed as a system app on the product
partition.
This change also unifies methods for checking whether a file is within
a partition so we will paths will always be canonicalized before the
check.
Bug: 152522330
Test: update system app in system/product/privapp, uninstall updates,
verify that the app was scanned as privileged
Change-Id: I646a5f293b977a78daa2102b73f1d3122f774a2a
6 files changed, 66 insertions, 51 deletions
diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java index 9b8396e340ea..653b9ec9e8f2 100644 --- a/core/java/android/content/pm/PackagePartitions.java +++ b/core/java/android/content/pm/PackagePartitions.java @@ -93,15 +93,23 @@ public class PackagePartitions { return out; } + private static File canonicalize(File path) { + try { + return path.getCanonicalFile(); + } catch (IOException e) { + return path; + } + } + /** Represents a partition that contains application packages. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public static class SystemPartition { - @NonNull - public final File folder; - @PartitionType public final int type; + @NonNull + private final DeferredCanonicalFile mFolder; + @Nullable private final DeferredCanonicalFile mAppFolder; @@ -113,18 +121,18 @@ public class PackagePartitions { private SystemPartition(@NonNull File folder, @PartitionType int type, boolean containsPrivApp, boolean containsOverlay) { - this.folder = folder; this.type = type; + this.mFolder = new DeferredCanonicalFile(folder); this.mAppFolder = new DeferredCanonicalFile(folder, "app"); - this.mPrivAppFolder = containsPrivApp ? - new DeferredCanonicalFile(folder, "priv-app") : null; - this.mOverlayFolder = containsOverlay ? - new DeferredCanonicalFile(folder, "overlay") : null; + this.mPrivAppFolder = containsPrivApp ? new DeferredCanonicalFile(folder, "priv-app") + : null; + this.mOverlayFolder = containsOverlay ? new DeferredCanonicalFile(folder, "overlay") + : null; } public SystemPartition(@NonNull SystemPartition original) { - this.folder = original.folder; this.type = original.type; + this.mFolder = new DeferredCanonicalFile(original.mFolder.getFile()); this.mAppFolder = original.mAppFolder; this.mPrivAppFolder = original.mPrivAppFolder; this.mOverlayFolder = original.mOverlayFolder; @@ -139,6 +147,12 @@ public class PackagePartitions { partition.mOverlayFolder != null); } + /** Returns the canonical folder of the partition. */ + @NonNull + public File getFolder() { + return mFolder.getFile(); + } + /** Returns the canonical app folder of the partition. */ @Nullable public File getAppFolder() { @@ -157,30 +171,29 @@ public class PackagePartitions { return mOverlayFolder == null ? null : mOverlayFolder.getFile(); } + /** Returns whether the partition contains the specified file. */ + public boolean containsPath(@NonNull String path) { + return containsFile(new File(path)); + } + + /** Returns whether the partition contains the specified file. */ + public boolean containsFile(@NonNull File file) { + return FileUtils.contains(mFolder.getFile(), canonicalize(file)); + } + /** Returns whether the partition contains the specified file in its priv-app folder. */ public boolean containsPrivApp(@NonNull File scanFile) { - return FileUtils.contains(mPrivAppFolder.getFile(), scanFile); + return FileUtils.contains(mPrivAppFolder.getFile(), canonicalize(scanFile)); } /** Returns whether the partition contains the specified file in its app folder. */ public boolean containsApp(@NonNull File scanFile) { - return FileUtils.contains(mAppFolder.getFile(), scanFile); + return FileUtils.contains(mAppFolder.getFile(), canonicalize(scanFile)); } /** Returns whether the partition contains the specified file in its overlay folder. */ public boolean containsOverlay(@NonNull File scanFile) { - return FileUtils.contains(mOverlayFolder.getFile(), scanFile); - } - - /** Returns whether the partition contains the specified file. */ - public boolean containsPath(@NonNull String path) { - return path.startsWith(folder.getPath() + "/"); - } - - /** Returns whether the partition contains the specified file in its priv-app folder. */ - public boolean containsPrivPath(@NonNull String path) { - return mPrivAppFolder != null - && path.startsWith(mPrivAppFolder.getFile().getPath() + "/"); + return FileUtils.contains(mOverlayFolder.getFile(), canonicalize(scanFile)); } } @@ -190,22 +203,24 @@ public class PackagePartitions { * have the correct selinux policies. */ private static class DeferredCanonicalFile { - private boolean mIsCanonical; + private boolean mIsCanonical = false; + + @NonNull private File mFile; - private DeferredCanonicalFile(File dir, String fileName) { + + private DeferredCanonicalFile(@NonNull File dir) { + mFile = dir; + } + + private DeferredCanonicalFile(@NonNull File dir, @NonNull String fileName) { mFile = new File(dir, fileName); - mIsCanonical = false; } + @NonNull private File getFile() { - if (mIsCanonical) { - return mFile; - } - mIsCanonical = true; - try { - mFile = mFile.getCanonicalFile(); - } catch (IOException ignore) { - // failed to look up canonical path, continue with original one + if (!mIsCanonical) { + mFile = canonicalize(mFile); + mIsCanonical = true; } return mFile; } diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java index fbef027ac37f..3b5cf487c8da 100644 --- a/core/java/com/android/internal/content/om/OverlayConfig.java +++ b/core/java/com/android/internal/content/om/OverlayConfig.java @@ -110,7 +110,9 @@ public class OverlayConfig { } else { // Rebase the system partitions and settings file on the specified root directory. partitions = new ArrayList<>(PackagePartitions.getOrderedPartitions( - p -> new OverlayPartition(new File(rootDirectory, p.folder.getPath()), p))); + p -> new OverlayPartition( + new File(rootDirectory, p.getFolder().getPath()), + p))); } boolean foundConfigFile = false; @@ -143,7 +145,7 @@ public class OverlayConfig { // Filter out overlays not present in the partition. partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos); for (int j = partitionOverlayInfos.size() - 1; j >= 0; j--) { - if (!partition.containsPath(partitionOverlayInfos.get(j).path.getPath())) { + if (!partition.containsFile(partitionOverlayInfos.get(j).path)) { partitionOverlayInfos.remove(j); } } diff --git a/core/java/com/android/internal/content/om/OverlayConfigParser.java b/core/java/com/android/internal/content/om/OverlayConfigParser.java index 139607ff7196..a86e5950c2f6 100644 --- a/core/java/com/android/internal/content/om/OverlayConfigParser.java +++ b/core/java/com/android/internal/content/om/OverlayConfigParser.java @@ -27,8 +27,8 @@ import android.util.ArraySet; import android.util.Log; import android.util.Xml; -import com.android.internal.util.XmlUtils; import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo; +import com.android.internal.util.XmlUtils; import libcore.io.IoUtils; @@ -154,7 +154,7 @@ final class OverlayConfigParser { return POLICY_PRODUCT; default: throw new IllegalStateException("Unable to determine policy for " - + partition.folder); + + partition.getFolder()); } } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index aa7a1ad2b47a..fd861b56b729 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2711,13 +2711,13 @@ public class PackageManagerService extends IPackageManager.Stub return SCAN_AS_SYSTEM_EXT; default: throw new IllegalStateException("Unable to determine scan flag for " - + partition.folder); + + partition.getFolder()); } } @Override public String toString() { - return folder.getAbsolutePath() + ":" + scanFlag; + return getFolder().getAbsolutePath() + ":" + scanFlag; } } @@ -3283,7 +3283,7 @@ public class PackageManagerService extends IPackageManager.Stub @ParseFlags int reparseFlags = 0; @ScanFlags int rescanFlags = 0; - for (int i1 = 0, size = mDirsToScanAsSystem.size(); i1 < size; i1++) { + for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) { final ScanPartition partition = mDirsToScanAsSystem.get(i1); if (partition.containsPrivApp(scanFile)) { reparseFlags = systemParseFlags; @@ -18681,7 +18681,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { ScanPartition sp = SYSTEM_PARTITIONS.get(i); if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( - sp.folder.getAbsolutePath())) { + sp.getFolder().getAbsolutePath())) { return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX); } } @@ -18777,23 +18777,23 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, @Nullable PermissionsState origPermissionState, boolean writeSettings) throws PackageManagerException { + final File codePath = new File(codePathString); @ParseFlags int parseFlags = mDefParseFlags | PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM_DIR; @ScanFlags int scanFlags = SCAN_AS_SYSTEM; - for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) { + for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) { ScanPartition partition = mDirsToScanAsSystem.get(i); - if (partition.containsPath(codePathString)) { + if (partition.containsFile(codePath)) { scanFlags |= partition.scanFlag; - if (partition.containsPrivPath(codePathString)) { + if (partition.containsPrivApp(codePath)) { scanFlags |= SCAN_AS_PRIVILEGED; } break; } } - final File codePath = new File(codePathString); final AndroidPackage pkg = scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index a5b1bf98cdb7..936ba7f3b097 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3235,7 +3235,7 @@ public final class Settings { PackageManagerService.ScanPartition partition = PackageManagerService.SYSTEM_PARTITIONS.get(index); - File preferredDir = new File(partition.folder, "etc/preferred-apps"); + File preferredDir = new File(partition.getFolder(), "etc/preferred-apps"); if (!preferredDir.exists() || !preferredDir.isDirectory()) { continue; } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index 37c106094954..c4e25b53d5d5 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -122,10 +122,8 @@ public class PackageManagerServiceTest { final PackageManagerService.ScanPartition scanPartition = PackageManagerService.SYSTEM_PARTITIONS.get(i); for (int j = 0; j < appdir.length; j++) { - String canonical = new File("/" + partitions[i]).getCanonicalPath(); - String path = String.format("%s/%s/A.apk", canonical, appdir[j]); - - Assert.assertEquals(j == 1 && i != 3, scanPartition.containsPrivPath(path)); + File path = new File(String.format("%s/%s/A.apk", partitions[i], appdir[j])); + Assert.assertEquals(j == 1 && i != 3, scanPartition.containsPrivApp(path)); final int scanFlag = scanPartition.scanFlag; Assert.assertEquals(i == 1, scanFlag == PackageManagerService.SCAN_AS_VENDOR); |