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

Commit 93a20533 authored by Brandon Liu's avatar Brandon Liu Committed by Android (Google) Code Review
Browse files

Merge "Provided a solution to sort partitions from order in...

Merge "Provided a solution to sort partitions from order in /product/overlay/partition_order.xml, added a debugging command to dump the partition order and how it get established." into main
parents f4da3797 abfa02a3
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -190,4 +190,15 @@ interface IOverlayManager {
     * @throws SecurityException if the transaction failed
     * @throws SecurityException if the transaction failed
     */
     */
    void commit(in OverlayManagerTransaction transaction);
    void commit(in OverlayManagerTransaction transaction);

    /**
     * Returns a String of a list of partitions from low priority to high.
     */
    String getPartitionOrder();

    /**
     * Returns a boolean which represent whether the partition list is sorted by default.
     * If not then it should be sorted by /product/overlay/partition_order.xml.
     */
    boolean isDefaultPartitionOrder();
}
}
+124 −0
Original line number Original line Diff line number Diff line
@@ -34,8 +34,15 @@ import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.TriConsumer;
import com.android.internal.util.function.TriConsumer;


import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
@@ -46,6 +53,10 @@ import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.function.Supplier;
import java.util.function.Supplier;


import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
/**
 * Responsible for reading overlay configuration files and handling queries of overlay mutability,
 * Responsible for reading overlay configuration files and handling queries of overlay mutability,
 * default-enabled state, and priority.
 * default-enabled state, and priority.
@@ -61,6 +72,8 @@ public class OverlayConfig {
    @VisibleForTesting
    @VisibleForTesting
    public static final int DEFAULT_PRIORITY = Integer.MAX_VALUE;
    public static final int DEFAULT_PRIORITY = Integer.MAX_VALUE;


    public static final String PARTITION_ORDER_FILE_PATH = "/product/overlay/partition_order.xml";

    @VisibleForTesting
    @VisibleForTesting
    public static final class Configuration {
    public static final class Configuration {
        @Nullable
        @Nullable
@@ -119,6 +132,10 @@ public class OverlayConfig {
    // Singleton instance only assigned in system server
    // Singleton instance only assigned in system server
    private static OverlayConfig sInstance;
    private static OverlayConfig sInstance;


    private final String mPartitionOrder;

    private final boolean mIsDefaultPartitionOrder;

    @VisibleForTesting
    @VisibleForTesting
    public OverlayConfig(@Nullable File rootDirectory,
    public OverlayConfig(@Nullable File rootDirectory,
            @Nullable Supplier<OverlayScanner> scannerFactory,
            @Nullable Supplier<OverlayScanner> scannerFactory,
@@ -137,6 +154,8 @@ public class OverlayConfig {
                            new File(rootDirectory, p.getNonConicalFolder().getPath()),
                            new File(rootDirectory, p.getNonConicalFolder().getPath()),
                            p)));
                            p)));
        }
        }
        mIsDefaultPartitionOrder = !sortPartitions(PARTITION_ORDER_FILE_PATH, partitions);
        mPartitionOrder = generatePartitionOrderString(partitions);


        ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions);
        ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions);


@@ -198,6 +217,96 @@ public class OverlayConfig {
        }
        }
    }
    }


    private static String generatePartitionOrderString(List<OverlayPartition> partitions) {
        if (partitions == null || partitions.size() == 0) {
            return "";
        }
        StringBuilder partitionOrder = new StringBuilder();
        partitionOrder.append(partitions.get(0).getName());
        for (int i = 1; i < partitions.size(); i++) {
            partitionOrder.append(", ").append(partitions.get(i).getName());
        }
        return partitionOrder.toString();
    }

    private static boolean parseAndValidatePartitionsOrderXml(String partitionOrderFilePath,
            Map<String, Integer> orderMap, List<OverlayPartition> partitions) {
        try {
            File file = new File(partitionOrderFilePath);
            if (!file.exists()) {
                Log.w(TAG, "partition_order.xml does not exist.");
                return false;
            }
            var dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc = dBuilder.parse(file);
            doc.getDocumentElement().normalize();

            Element root = doc.getDocumentElement();
            if (!root.getNodeName().equals("partition-order")) {
                Log.w(TAG, "Invalid partition_order.xml, "
                        + "xml root element is not partition-order");
                return false;
            }

            NodeList partitionList = doc.getElementsByTagName("partition");
            for (int order = 0; order < partitionList.getLength(); order++) {
                Node partitionNode = partitionList.item(order);
                if (partitionNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element partitionElement = (Element) partitionNode;
                    String partitionName = partitionElement.getAttribute("name");
                    if (orderMap.containsKey(partitionName)) {
                        Log.w(TAG, "Invalid partition_order.xml, "
                                + "it has duplicate partition: " + partitionName);
                        return false;
                    }
                    orderMap.put(partitionName, order);
                }
            }

            if (orderMap.keySet().size() != partitions.size()) {
                Log.w(TAG, "Invalid partition_order.xml, partition_order.xml has "
                        + orderMap.keySet().size() + " partitions, "
                        + "which is different from SYSTEM_PARTITIONS");
                return false;
            }
            for (int i = 0; i < partitions.size(); i++) {
                if (!orderMap.keySet().contains(partitions.get(i).getName())) {
                    Log.w(TAG, "Invalid Parsing partition_order.xml, "
                            + "partition_order.xml does not have partition: "
                            + partitions.get(i).getName());
                    return false;
                }
            }
        } catch (ParserConfigurationException | SAXException | IOException e) {
            Log.w(TAG, "Parsing or validating partition_order.xml failed, "
                    + "exception thrown: " + e.getMessage());
            return false;
        }
        Log.i(TAG, "Sorting partitions in the specified order from partitions_order.xml");
        return true;
    }

    /**
     * Sort partitions by order in partition_order.xml if the file exists.
     *
     * @hide
     */
    @VisibleForTesting
    public static boolean sortPartitions(String partitionOrderFilePath,
            List<OverlayPartition> partitions) {
        Map<String, Integer> orderMap = new HashMap<>();
        if (!parseAndValidatePartitionsOrderXml(partitionOrderFilePath, orderMap, partitions)) {
            return false;
        }

        Comparator<OverlayPartition> partitionComparator = Comparator.comparingInt(
                o -> orderMap.get(o.getName()));
        Collections.sort(partitions, partitionComparator);

        return true;
    }

    /**
    /**
     * Creates an instance of OverlayConfig for use in the zygote process.
     * Creates an instance of OverlayConfig for use in the zygote process.
     * This instance will not include information of static overlays existing outside of a partition
     * This instance will not include information of static overlays existing outside of a partition
@@ -476,4 +585,19 @@ public class OverlayConfig {
     */
     */
    private static native String[] createIdmap(@NonNull String targetPath,
    private static native String[] createIdmap(@NonNull String targetPath,
            @NonNull String[] overlayPath, @NonNull String[] policies, boolean enforceOverlayable);
            @NonNull String[] overlayPath, @NonNull String[] policies, boolean enforceOverlayable);

    /**
     * @hide
     */
    public boolean isDefaultPartitionOrder() {
        return mIsDefaultPartitionOrder;
    }

    /**
     * @hide
     */
    public String getPartitionOrder() {
        return mPartitionOrder;
    }

}
}
+15 −3
Original line number Original line Diff line number Diff line
@@ -28,6 +28,7 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.Log;
import android.util.Xml;
import android.util.Xml;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.XmlUtils;
@@ -54,8 +55,11 @@ import java.util.Map;
 *
 *
 * @see #parseOverlay(File, XmlPullParser, OverlayScanner, ParsingContext)
 * @see #parseOverlay(File, XmlPullParser, OverlayScanner, ParsingContext)
 * @see #parseMerge(File, XmlPullParser, OverlayScanner, ParsingContext)
 * @see #parseMerge(File, XmlPullParser, OverlayScanner, ParsingContext)
 *
 * @hide
 **/
 **/
final class OverlayConfigParser {
@VisibleForTesting
public final class OverlayConfigParser {


    /** Represents a part of a parsed overlay configuration XML file. */
    /** Represents a part of a parsed overlay configuration XML file. */
    public static class ParsedConfigFile {
    public static class ParsedConfigFile {
@@ -160,7 +164,11 @@ final class OverlayConfigParser {
        }
        }
    }
    }


    static class OverlayPartition extends SystemPartition {
    /**
     * @hide
     **/
    @VisibleForTesting
    public static class OverlayPartition extends SystemPartition {
        // Policies passed to idmap2 during idmap creation.
        // Policies passed to idmap2 during idmap creation.
        // Keep partition policy constants in sync with f/b/cmds/idmap2/include/idmap2/Policies.h.
        // Keep partition policy constants in sync with f/b/cmds/idmap2/include/idmap2/Policies.h.
        static final String POLICY_ODM = "odm";
        static final String POLICY_ODM = "odm";
@@ -173,7 +181,11 @@ final class OverlayConfigParser {
        @NonNull
        @NonNull
        public final String policy;
        public final String policy;


        OverlayPartition(@NonNull SystemPartition partition) {
        /**
         * @hide
         **/
        @VisibleForTesting
        public OverlayPartition(@NonNull SystemPartition partition) {
            super(partition);
            super(partition);
            this.policy = policyForPartition(partition);
            this.policy = policyForPartition(partition);
        }
        }
+132 −0
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;


import android.content.pm.PackagePartitions;
import android.os.FileUtils;
import android.os.FileUtils;
import android.os.SystemProperties;
import android.os.SystemProperties;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.Presubmit;
@@ -32,6 +33,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.coretests.R;
import com.android.frameworks.coretests.R;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.content.om.OverlayConfig.IdmapInvocation;
import com.android.internal.content.om.OverlayConfig.IdmapInvocation;
import com.android.internal.content.om.OverlayConfigParser.OverlayPartition;
import com.android.internal.content.om.OverlayScanner;
import com.android.internal.content.om.OverlayScanner;


import org.junit.Rule;
import org.junit.Rule;
@@ -46,6 +48,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;


@Presubmit
@Presubmit
@RunWith(AndroidJUnit4.class)
@RunWith(AndroidJUnit4.class)
@@ -88,6 +91,17 @@ public class OverlayConfigTest {
        assertEquals(configIndex, config.configIndex);
        assertEquals(configIndex, config.configIndex);
    }
    }


    private String generatePartitionOrderString(List<OverlayPartition> partitions) {
        StringBuilder partitionOrder = new StringBuilder();
        for (int i = 0; i < partitions.size(); i++) {
            partitionOrder.append(partitions.get(i).getName());
            if (i < partitions.size() - 1) {
                partitionOrder.append(", ");
            }
        }
        return partitionOrder.toString();
    }

    @Test
    @Test
    public void testImmutableAfterNonImmutableFails() throws IOException {
    public void testImmutableAfterNonImmutableFails() throws IOException {
        mExpectedException.expect(IllegalStateException.class);
        mExpectedException.expect(IllegalStateException.class);
@@ -685,4 +699,122 @@ public class OverlayConfigTest {
        OverlayConfig.Configuration o3 = overlayConfig.getConfiguration("three");
        OverlayConfig.Configuration o3 = overlayConfig.getConfiguration("three");
        assertNotNull(o3);
        assertNotNull(o3);
    }
    }

    @Test
    public void testSortPartitionsWithoutXml() throws IOException {
        ArrayList<OverlayPartition> partitions = new ArrayList<>(
                PackagePartitions.getOrderedPartitions(OverlayPartition::new));

        final OverlayConfig overlayConfig = createConfigImpl();
        String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(),
                "/product/overlay/partition_order.xml");
        assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions));
        assertEquals("system, vendor, odm, oem, product, system_ext",
                generatePartitionOrderString(partitions));
    }

    @Test
    public void testSortPartitionsWithInvalidXmlRootElement() throws IOException {
        ArrayList<OverlayPartition> partitions = new ArrayList<>(
                PackagePartitions.getOrderedPartitions(OverlayPartition::new));
        createFile("/product/overlay/partition_order.xml",
                "<partition-list>\n"
                        + "  <partition name=\"system_ext\"/>\n"
                        + "  <partition name=\"vendor\"/>\n"
                        + "  <partition name=\"oem\"/>\n"
                        + "  <partition name=\"odm\"/>\n"
                        + "  <partition name=\"product\"/>\n"
                        + "  <partition name=\"system\"/>\n"
                        + "</partition-list>\n");
        final OverlayConfig overlayConfig = createConfigImpl();
        String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(),
                "/product/overlay/partition_order.xml");
        assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions));
        assertEquals("system, vendor, odm, oem, product, system_ext",
                generatePartitionOrderString(partitions));
    }

    @Test
    public void testSortPartitionsWithInvalidPartition() throws IOException {
        ArrayList<OverlayPartition> partitions = new ArrayList<>(
                PackagePartitions.getOrderedPartitions(OverlayPartition::new));
        createFile("/product/overlay/partition_order.xml",
                "<partition-order>\n"
                        + "  <partition name=\"INVALID\"/>\n"
                        + "  <partition name=\"vendor\"/>\n"
                        + "  <partition name=\"oem\"/>\n"
                        + "  <partition name=\"odm\"/>\n"
                        + "  <partition name=\"product\"/>\n"
                        + "  <partition name=\"system\"/>\n"
                        + "</partition-order>\n");
        final OverlayConfig overlayConfig = createConfigImpl();
        String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(),
                "/product/overlay/partition_order.xml");
        assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions));
        assertEquals("system, vendor, odm, oem, product, system_ext",
                generatePartitionOrderString(partitions));
    }

    @Test
    public void testSortPartitionsWithDuplicatePartition() throws IOException {
        ArrayList<OverlayPartition> partitions = new ArrayList<>(
                PackagePartitions.getOrderedPartitions(OverlayPartition::new));
        createFile("/product/overlay/partition_order.xml",
                "<partition-order>\n"
                        + "  <partition name=\"system_ext\"/>\n"
                        + "  <partition name=\"system\"/>\n"
                        + "  <partition name=\"vendor\"/>\n"
                        + "  <partition name=\"oem\"/>\n"
                        + "  <partition name=\"odm\"/>\n"
                        + "  <partition name=\"product\"/>\n"
                        + "  <partition name=\"system\"/>\n"
                        + "</partition-order>\n");
        final OverlayConfig overlayConfig = createConfigImpl();
        String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(),
                "/product/overlay/partition_order.xml");
        assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions));
        assertEquals("system, vendor, odm, oem, product, system_ext",
                generatePartitionOrderString(partitions));
    }

    @Test
    public void testSortPartitionsWithMissingPartition() throws IOException {
        ArrayList<OverlayPartition> partitions = new ArrayList<>(
                PackagePartitions.getOrderedPartitions(OverlayPartition::new));
        createFile("/product/overlay/partition_order.xml",
                "<partition-order>\n"
                        + "  <partition name=\"vendor\"/>\n"
                        + "  <partition name=\"oem\"/>\n"
                        + "  <partition name=\"odm\"/>\n"
                        + "  <partition name=\"product\"/>\n"
                        + "  <partition name=\"system\"/>\n"
                        + "</partition-order>\n");
        final OverlayConfig overlayConfig = createConfigImpl();
        String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(),
                "/product/overlay/partition_order.xml");
        assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions));
        assertEquals("system, vendor, odm, oem, product, system_ext",
                generatePartitionOrderString(partitions));
    }

    @Test
    public void testSortPartitionsWithCorrectPartitionOrderXml() throws IOException {
        ArrayList<OverlayPartition> partitions = new ArrayList<>(
                PackagePartitions.getOrderedPartitions(OverlayPartition::new));
        createFile("/product/overlay/partition_order.xml",
                "<partition-order>\n"
                        + "  <partition name=\"system_ext\"/>\n"
                        + "  <partition name=\"vendor\"/>\n"
                        + "  <partition name=\"oem\"/>\n"
                        + "  <partition name=\"odm\"/>\n"
                        + "  <partition name=\"product\"/>\n"
                        + "  <partition name=\"system\"/>\n"
                        + "</partition-order>\n");
        final OverlayConfig overlayConfig = createConfigImpl();
        String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(),
                "/product/overlay/partition_order.xml");
        assertEquals(true, overlayConfig.sortPartitions(partitionOrderFilePath, partitions));
        assertEquals("system_ext, vendor, oem, odm, product, system",
                generatePartitionOrderString(partitions));
    }
}
}
+15 −0
Original line number Original line Diff line number Diff line
@@ -1120,6 +1120,21 @@ public final class OverlayManagerService extends SystemService {
            int callingUid = Binder.getCallingUid();
            int callingUid = Binder.getCallingUid();
            mActorEnforcer.enforceActor(overlayInfo, methodName, callingUid, realUserId);
            mActorEnforcer.enforceActor(overlayInfo, methodName, callingUid, realUserId);
        }
        }

        /**
         * @hide
         */
        public String getPartitionOrder() {
            return mImpl.getOverlayConfig().getPartitionOrder();
        }

        /**
         * @hide
         */
        public boolean isDefaultPartitionOrder() {
            return mImpl.getOverlayConfig().isDefaultPartitionOrder();
        }

    };
    };


    private static final class PackageManagerHelperImpl implements PackageManagerHelper {
    private static final class PackageManagerHelperImpl implements PackageManagerHelper {
Loading