Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d4e7f93d authored by Ryan Mitchell's avatar Ryan Mitchell
Browse files

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
parent 57e977a5
Loading
Loading
Loading
Loading
+49 −34
Original line number Diff line number Diff line
@@ -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;
            }
            if (!mIsCanonical) {
                mFile = canonicalize(mFile);
                mIsCanonical = true;
            try {
                mFile = mFile.getCanonicalFile();
            } catch (IOException ignore) {
                // failed to look up canonical path, continue with original one
            }
            return mFile;
        }
+4 −2
Original line number Diff line number Diff line
@@ -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);
                    }
                }
+2 −2
Original line number Diff line number Diff line
@@ -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());
            }
        }
    }
+8 −8
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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;
            }
Loading