1 /*
2  * Copyright (C) 2020 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.internal.content.om;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.pm.PackagePartitions;
22 import android.os.Build;
23 import android.os.Trace;
24 import android.util.ArrayMap;
25 import android.util.IndentingPrintWriter;
26 import android.util.Log;
27 
28 import com.android.apex.ApexInfo;
29 import com.android.apex.XmlParser;
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.internal.content.om.OverlayConfigParser.OverlayPartition;
32 import com.android.internal.content.om.OverlayConfigParser.ParsedConfiguration;
33 import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
34 import com.android.internal.util.Preconditions;
35 import com.android.internal.util.function.TriConsumer;
36 
37 import org.w3c.dom.Document;
38 import org.w3c.dom.Element;
39 import org.w3c.dom.Node;
40 import org.w3c.dom.NodeList;
41 import org.xml.sax.SAXException;
42 
43 import java.io.File;
44 import java.io.FileInputStream;
45 import java.io.IOException;
46 import java.io.PrintWriter;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Collections;
50 import java.util.Comparator;
51 import java.util.HashMap;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.function.Supplier;
55 
56 import javax.xml.parsers.DocumentBuilder;
57 import javax.xml.parsers.DocumentBuilderFactory;
58 import javax.xml.parsers.ParserConfigurationException;
59 
60 /**
61  * Responsible for reading overlay configuration files and handling queries of overlay mutability,
62  * default-enabled state, and priority.
63  *
64  * @see OverlayConfigParser
65  */
66 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
67 public class OverlayConfig {
68     static final String TAG = "OverlayConfig";
69 
70     // The default priority of an overlay that has not been configured. Overlays with default
71     // priority have a higher precedence than configured overlays.
72     @VisibleForTesting
73     public static final int DEFAULT_PRIORITY = Integer.MAX_VALUE;
74 
75     public static final String PARTITION_ORDER_FILE_PATH = "/product/overlay/partition_order.xml";
76 
77     @VisibleForTesting
78     public static final class Configuration {
79         @Nullable
80         public final ParsedConfiguration parsedConfig;
81 
82         public final int configIndex;
83 
Configuration(@ullable ParsedConfiguration parsedConfig, int configIndex)84         public Configuration(@Nullable ParsedConfiguration parsedConfig, int configIndex) {
85             this.parsedConfig = parsedConfig;
86             this.configIndex = configIndex;
87         }
88     }
89 
90     /**
91      * Interface for providing information on scanned packages.
92      * TODO(147840005): Remove this when android:isStatic and android:priority are fully deprecated
93      */
94     public interface PackageProvider {
95 
96         /** Performs the given action for each package. */
forEachPackage(TriConsumer<Package, Boolean, File> p)97         void forEachPackage(TriConsumer<Package, Boolean, File> p);
98 
99         interface Package {
100 
getBaseApkPath()101             String getBaseApkPath();
102 
getOverlayPriority()103             int getOverlayPriority();
104 
getOverlayTarget()105             String getOverlayTarget();
106 
getPackageName()107             String getPackageName();
108 
getTargetSdkVersion()109             int getTargetSdkVersion();
110 
isOverlayIsStatic()111             boolean isOverlayIsStatic();
112         }
113     }
114 
115     private static final Comparator<ParsedConfiguration> sStaticOverlayComparator = (c1, c2) -> {
116         final ParsedOverlayInfo o1 = c1.parsedInfo;
117         final ParsedOverlayInfo o2 = c2.parsedInfo;
118         Preconditions.checkArgument(o1.isStatic && o2.isStatic,
119                 "attempted to sort non-static overlay");
120 
121         if (!o1.targetPackageName.equals(o2.targetPackageName)) {
122             return o1.targetPackageName.compareTo(o2.targetPackageName);
123         }
124 
125         final int comparedPriority = o1.priority - o2.priority;
126         return comparedPriority == 0 ? o1.path.compareTo(o2.path) : comparedPriority;
127     };
128 
129     // Map of overlay package name to configured overlay settings
130     private final ArrayMap<String, Configuration> mConfigurations = new ArrayMap<>();
131 
132     // Singleton instance only assigned in system server
133     private static OverlayConfig sInstance;
134 
135     private final String mPartitionOrder;
136 
137     private final boolean mIsDefaultPartitionOrder;
138 
139     @VisibleForTesting
OverlayConfig(@ullable File rootDirectory, @Nullable Supplier<OverlayScanner> scannerFactory, @Nullable PackageProvider packageProvider)140     public OverlayConfig(@Nullable File rootDirectory,
141             @Nullable Supplier<OverlayScanner> scannerFactory,
142             @Nullable PackageProvider packageProvider) {
143         Preconditions.checkArgument((scannerFactory == null) != (packageProvider == null),
144                 "scannerFactory and packageProvider cannot be both null or both non-null");
145 
146         final ArrayList<OverlayPartition> partitions;
147         if (rootDirectory == null) {
148             partitions = new ArrayList<>(
149                     PackagePartitions.getOrderedPartitions(OverlayPartition::new));
150         } else {
151             // Rebase the system partitions and settings file on the specified root directory.
152             partitions = new ArrayList<>(PackagePartitions.getOrderedPartitions(
153                     p -> new OverlayPartition(
154                             new File(rootDirectory, p.getNonConicalFolder().getPath()),
155                             p)));
156         }
157         mIsDefaultPartitionOrder = !sortPartitions(PARTITION_ORDER_FILE_PATH, partitions);
158         mPartitionOrder = generatePartitionOrderString(partitions);
159 
160         ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions);
161 
162         final Map<String, ParsedOverlayInfo> packageManagerOverlayInfos =
163                 packageProvider == null ? null : getOverlayPackageInfos(packageProvider);
164 
165         final ArrayList<ParsedConfiguration> overlays = new ArrayList<>();
166         for (int i = 0, n = partitions.size(); i < n; i++) {
167             final OverlayPartition partition = partitions.get(i);
168             final OverlayScanner scanner = (scannerFactory == null) ? null : scannerFactory.get();
169             final ArrayList<ParsedConfiguration> partitionOverlays =
170                     OverlayConfigParser.getConfigurations(partition, scanner,
171                             packageManagerOverlayInfos,
172                             activeApexesPerPartition.getOrDefault(partition.type,
173                                     Collections.emptyList()));
174             if (partitionOverlays != null) {
175                 overlays.addAll(partitionOverlays);
176                 continue;
177             }
178 
179             // If the configuration file is not present, then use android:isStatic and
180             // android:priority to configure the overlays in the partition.
181             // TODO(147840005): Remove converting static overlays to immutable, default-enabled
182             //  overlays when android:siStatic and android:priority are fully deprecated.
183             final ArrayList<ParsedOverlayInfo> partitionOverlayInfos;
184             if (scannerFactory != null) {
185                 partitionOverlayInfos = new ArrayList<>(scanner.getAllParsedInfos());
186             } else {
187                 // Filter out overlays not present in the partition.
188                 partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos.values());
189                 for (int j = partitionOverlayInfos.size() - 1; j >= 0; j--) {
190                     if (!partition.containsFile(partitionOverlayInfos.get(j)
191                             .getOriginalPartitionPath())) {
192                         partitionOverlayInfos.remove(j);
193                     }
194                 }
195             }
196 
197             // Static overlays are configured as immutable, default-enabled overlays.
198             final ArrayList<ParsedConfiguration> partitionConfigs = new ArrayList<>();
199             for (int j = 0, m = partitionOverlayInfos.size(); j < m; j++) {
200                 final ParsedOverlayInfo p = partitionOverlayInfos.get(j);
201                 if (p.isStatic) {
202                     partitionConfigs.add(new ParsedConfiguration(p.packageName,
203                             true /* enabled */, false /* mutable */, partition.policy, p, null));
204                 }
205             }
206 
207             partitionConfigs.sort(sStaticOverlayComparator);
208             overlays.addAll(partitionConfigs);
209         }
210 
211         for (int i = 0, n = overlays.size(); i < n; i++) {
212             // Add the configurations to a map so definitions of an overlay in an earlier
213             // partition can be replaced by an overlay with the same package name in a later
214             // partition.
215             final ParsedConfiguration config = overlays.get(i);
216             mConfigurations.put(config.packageName, new Configuration(config, i));
217         }
218     }
219 
generatePartitionOrderString(List<OverlayPartition> partitions)220     private static String generatePartitionOrderString(List<OverlayPartition> partitions) {
221         if (partitions == null || partitions.size() == 0) {
222             return "";
223         }
224         StringBuilder partitionOrder = new StringBuilder();
225         partitionOrder.append(partitions.get(0).getName());
226         for (int i = 1; i < partitions.size(); i++) {
227             partitionOrder.append(", ").append(partitions.get(i).getName());
228         }
229         return partitionOrder.toString();
230     }
231 
parseAndValidatePartitionsOrderXml(String partitionOrderFilePath, Map<String, Integer> orderMap, List<OverlayPartition> partitions)232     private static boolean parseAndValidatePartitionsOrderXml(String partitionOrderFilePath,
233             Map<String, Integer> orderMap, List<OverlayPartition> partitions) {
234         try {
235             File file = new File(partitionOrderFilePath);
236             if (!file.exists()) {
237                 Log.w(TAG, "partition_order.xml does not exist.");
238                 return false;
239             }
240             var dbFactory = DocumentBuilderFactory.newInstance();
241             DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
242             Document doc = dBuilder.parse(file);
243             doc.getDocumentElement().normalize();
244 
245             Element root = doc.getDocumentElement();
246             if (!root.getNodeName().equals("partition-order")) {
247                 Log.w(TAG, "Invalid partition_order.xml, "
248                         + "xml root element is not partition-order");
249                 return false;
250             }
251 
252             NodeList partitionList = doc.getElementsByTagName("partition");
253             for (int order = 0; order < partitionList.getLength(); order++) {
254                 Node partitionNode = partitionList.item(order);
255                 if (partitionNode.getNodeType() == Node.ELEMENT_NODE) {
256                     Element partitionElement = (Element) partitionNode;
257                     String partitionName = partitionElement.getAttribute("name");
258                     if (orderMap.containsKey(partitionName)) {
259                         Log.w(TAG, "Invalid partition_order.xml, "
260                                 + "it has duplicate partition: " + partitionName);
261                         return false;
262                     }
263                     orderMap.put(partitionName, order);
264                 }
265             }
266 
267             if (orderMap.keySet().size() != partitions.size()) {
268                 Log.w(TAG, "Invalid partition_order.xml, partition_order.xml has "
269                         + orderMap.keySet().size() + " partitions, "
270                         + "which is different from SYSTEM_PARTITIONS");
271                 return false;
272             }
273             for (int i = 0; i < partitions.size(); i++) {
274                 if (!orderMap.keySet().contains(partitions.get(i).getName())) {
275                     Log.w(TAG, "Invalid Parsing partition_order.xml, "
276                             + "partition_order.xml does not have partition: "
277                             + partitions.get(i).getName());
278                     return false;
279                 }
280             }
281         } catch (ParserConfigurationException | SAXException | IOException e) {
282             Log.w(TAG, "Parsing or validating partition_order.xml failed, "
283                     + "exception thrown: " + e.getMessage());
284             return false;
285         }
286         Log.i(TAG, "Sorting partitions in the specified order from partitions_order.xml");
287         return true;
288     }
289 
290     /**
291      * Sort partitions by order in partition_order.xml if the file exists.
292      *
293      * @hide
294      */
295     @VisibleForTesting
sortPartitions(String partitionOrderFilePath, List<OverlayPartition> partitions)296     public static boolean sortPartitions(String partitionOrderFilePath,
297             List<OverlayPartition> partitions) {
298         Map<String, Integer> orderMap = new HashMap<>();
299         if (!parseAndValidatePartitionsOrderXml(partitionOrderFilePath, orderMap, partitions)) {
300             return false;
301         }
302 
303         Comparator<OverlayPartition> partitionComparator = Comparator.comparingInt(
304                 o -> orderMap.get(o.getName()));
305         Collections.sort(partitions, partitionComparator);
306 
307         return true;
308     }
309 
310     /**
311      * Creates an instance of OverlayConfig for use in the zygote process.
312      * This instance will not include information of static overlays existing outside of a partition
313      * overlay directory.
314      */
315     @NonNull
getZygoteInstance()316     public static OverlayConfig getZygoteInstance() {
317         Trace.traceBegin(Trace.TRACE_TAG_RRO, "OverlayConfig#getZygoteInstance");
318         try {
319             return new OverlayConfig(null /* rootDirectory */, OverlayScanner::new,
320                     null /* packageProvider */);
321         } finally {
322             Trace.traceEnd(Trace.TRACE_TAG_RRO);
323         }
324     }
325 
326     /**
327      * Initializes a singleton instance for use in the system process.
328      * Can only be called once. This instance is cached so future invocations of
329      * {@link #getSystemInstance()} will return the initialized instance.
330      */
331     @NonNull
initializeSystemInstance(PackageProvider packageProvider)332     public static OverlayConfig initializeSystemInstance(PackageProvider packageProvider) {
333         Trace.traceBegin(Trace.TRACE_TAG_RRO, "OverlayConfig#initializeSystemInstance");
334         try {
335             sInstance = new OverlayConfig(null, null, packageProvider);
336         } finally {
337             Trace.traceEnd(Trace.TRACE_TAG_RRO);
338         }
339         return sInstance;
340     }
341 
342     /**
343      * Retrieves the singleton instance initialized by
344      * {@link #initializeSystemInstance(PackageProvider)}.
345      */
346     @NonNull
getSystemInstance()347     public static OverlayConfig getSystemInstance() {
348         if (sInstance == null) {
349             throw new IllegalStateException("System instance not initialized");
350         }
351 
352         return sInstance;
353     }
354 
355     @VisibleForTesting
356     @Nullable
getConfiguration(@onNull String packageName)357     public Configuration getConfiguration(@NonNull String packageName) {
358         return mConfigurations.get(packageName);
359     }
360 
361     /**
362      * Returns whether the overlay is enabled by default.
363      * Overlays that are not configured are disabled by default.
364      *
365      * If an immutable overlay has its enabled state change, the new enabled state is applied to the
366      * overlay.
367      *
368      * When a mutable is first seen by the OverlayManagerService, the default-enabled state will be
369      * applied to the overlay. If the configured default-enabled state changes in a subsequent boot,
370      * the default-enabled state will not be applied to the overlay.
371      *
372      * The configured enabled state will only be applied when:
373      * <ul>
374      * <li> The device is factory reset
375      * <li> The overlay is removed from the device and added back to the device in a future OTA
376      * <li> The overlay changes its package name
377      * <li> The overlay changes its target package name or target overlayable name
378      * <li> An immutable overlay becomes mutable
379      * </ul>
380      */
isEnabled(String packageName)381     public boolean isEnabled(String packageName) {
382         final Configuration config = mConfigurations.get(packageName);
383         return config == null? OverlayConfigParser.DEFAULT_ENABLED_STATE
384                 : config.parsedConfig.enabled;
385     }
386 
387     /**
388      * Returns whether the overlay is mutable and can have its enabled state changed dynamically.
389      * Overlays that are not configured are mutable.
390      */
isMutable(String packageName)391     public boolean isMutable(String packageName) {
392         final Configuration config = mConfigurations.get(packageName);
393         return config == null ? OverlayConfigParser.DEFAULT_MUTABILITY
394                 : config.parsedConfig.mutable;
395     }
396 
397     /**
398      * Returns an integer corresponding to the priority of the overlay.
399      * When multiple overlays override the same resource, the overlay with the highest priority will
400      * will have its value chosen. Overlays that are not configured have a priority of
401      * {@link Integer#MAX_VALUE}.
402      */
getPriority(String packageName)403     public int getPriority(String packageName) {
404         final Configuration config = mConfigurations.get(packageName);
405         return config == null ? DEFAULT_PRIORITY : config.configIndex;
406     }
407 
408     @NonNull
getSortedOverlays()409     private ArrayList<Configuration> getSortedOverlays() {
410         final ArrayList<Configuration> sortedOverlays = new ArrayList<>();
411         for (int i = 0, n = mConfigurations.size(); i < n; i++) {
412             sortedOverlays.add(mConfigurations.valueAt(i));
413         }
414         sortedOverlays.sort(Comparator.comparingInt(o -> o.configIndex));
415         return sortedOverlays;
416     }
417 
418     @NonNull
getOverlayPackageInfos( @onNull PackageProvider packageManager)419     private static Map<String, ParsedOverlayInfo> getOverlayPackageInfos(
420             @NonNull PackageProvider packageManager) {
421         final HashMap<String, ParsedOverlayInfo> overlays = new HashMap<>();
422         packageManager.forEachPackage((PackageProvider.Package p, Boolean isSystem,
423                 @Nullable File preInstalledApexPath) -> {
424             if (p.getOverlayTarget() != null && isSystem) {
425                 overlays.put(p.getPackageName(), new ParsedOverlayInfo(p.getPackageName(),
426                         p.getOverlayTarget(), p.getTargetSdkVersion(), p.isOverlayIsStatic(),
427                         p.getOverlayPriority(), new File(p.getBaseApkPath()),
428                         preInstalledApexPath));
429             }
430         });
431         return overlays;
432     }
433 
434     /** Returns a map of PartitionType to List of active APEX module names. */
435     @NonNull
getActiveApexes( @onNull List<OverlayPartition> partitions)436     private static ArrayMap<Integer, List<String>> getActiveApexes(
437             @NonNull List<OverlayPartition> partitions) {
438         // An Overlay in an APEX, which is an update of an APEX in a given partition,
439         // is considered as belonging to that partition.
440         ArrayMap<Integer, List<String>> result = new ArrayMap<>();
441         for (OverlayPartition partition : partitions) {
442             result.put(partition.type, new ArrayList<String>());
443         }
444         // Read from apex-info-list because ApexManager is not accessible to zygote.
445         File apexInfoList = new File("/apex/apex-info-list.xml");
446         if (apexInfoList.exists() && apexInfoList.canRead()) {
447             try (FileInputStream stream = new FileInputStream(apexInfoList)) {
448                 List<ApexInfo> apexInfos = XmlParser.readApexInfoList(stream).getApexInfo();
449                 for (ApexInfo info : apexInfos) {
450                     if (info.getIsActive()) {
451                         for (OverlayPartition partition : partitions) {
452                             if (partition.containsPath(info.getPreinstalledModulePath())) {
453                                 result.get(partition.type).add(info.getModuleName());
454                                 break;
455                             }
456                         }
457                     }
458                 }
459             } catch (Exception e) {
460                 Log.w(TAG, "Error reading apex-info-list: " + e);
461             }
462         }
463         return result;
464     }
465 
466     /** Represents a single call to idmap create-multiple. */
467     @VisibleForTesting
468     public static class IdmapInvocation {
469         public final boolean enforceOverlayable;
470         public final String policy;
471         public final ArrayList<String> overlayPaths = new ArrayList<>();
472 
IdmapInvocation(boolean enforceOverlayable, @NonNull String policy)473         IdmapInvocation(boolean enforceOverlayable, @NonNull String policy) {
474             this.enforceOverlayable = enforceOverlayable;
475             this.policy = policy;
476         }
477 
478         @Override
toString()479         public String toString() {
480             return getClass().getSimpleName() + String.format("{enforceOverlayable=%s, policy=%s"
481                             + ", overlayPaths=[%s]}", enforceOverlayable, policy,
482                     String.join(", ", overlayPaths));
483         }
484     }
485 
486     /**
487      * Retrieves a list of immutable framework overlays in order of least precedence to greatest
488      * precedence.
489      */
490     @VisibleForTesting
getImmutableFrameworkOverlayIdmapInvocations()491     public ArrayList<IdmapInvocation> getImmutableFrameworkOverlayIdmapInvocations() {
492         final ArrayList<IdmapInvocation> idmapInvocations = new ArrayList<>();
493         final ArrayList<Configuration> sortedConfigs = getSortedOverlays();
494         for (int i = 0, n = sortedConfigs.size(); i < n; i++) {
495             final Configuration overlay = sortedConfigs.get(i);
496             if (overlay.parsedConfig.mutable || !overlay.parsedConfig.enabled
497                     || !"android".equals(overlay.parsedConfig.parsedInfo.targetPackageName)) {
498                 continue;
499             }
500 
501             // Only enforce that overlays targeting packages with overlayable declarations abide by
502             // those declarations if the target sdk of the overlay is at least Q (when overlayable
503             // was introduced).
504             final boolean enforceOverlayable = overlay.parsedConfig.parsedInfo.targetSdkVersion
505                     >= Build.VERSION_CODES.Q;
506 
507             // Determine if the idmap for the current overlay can be generated in the last idmap
508             // create-multiple invocation.
509             IdmapInvocation invocation = null;
510             if (!idmapInvocations.isEmpty()) {
511                 final IdmapInvocation last = idmapInvocations.get(idmapInvocations.size() - 1);
512                 if (last.enforceOverlayable == enforceOverlayable
513                         && last.policy.equals(overlay.parsedConfig.policy)) {
514                     invocation = last;
515                 }
516             }
517 
518             if (invocation == null) {
519                 invocation = new IdmapInvocation(enforceOverlayable, overlay.parsedConfig.policy);
520                 idmapInvocations.add(invocation);
521             }
522 
523             invocation.overlayPaths.add(overlay.parsedConfig.parsedInfo.path.getAbsolutePath());
524         }
525         return idmapInvocations;
526     }
527 
528     /**
529      * Creates idmap files for immutable overlays targeting the framework packages. Currently the
530      * android package is the only preloaded system package. Only the zygote can invoke this method.
531      *
532      * @return the paths of the created idmap files
533      */
534     @NonNull
createImmutableFrameworkIdmapsInZygote()535     public String[] createImmutableFrameworkIdmapsInZygote() {
536         final String targetPath = "/system/framework/framework-res.apk";
537         final ArrayList<String> idmapPaths = new ArrayList<>();
538         final ArrayList<IdmapInvocation> idmapInvocations =
539                 getImmutableFrameworkOverlayIdmapInvocations();
540 
541         for (int i = 0, n = idmapInvocations.size(); i < n; i++) {
542             final IdmapInvocation invocation = idmapInvocations.get(i);
543             final String[] idmaps = createIdmap(targetPath,
544                     invocation.overlayPaths.toArray(new String[0]),
545                     new String[]{OverlayConfigParser.OverlayPartition.POLICY_PUBLIC,
546                             invocation.policy},
547                     invocation.enforceOverlayable);
548 
549             if (idmaps == null) {
550                 Log.w(TAG, "'idmap2 create-multiple' failed: no mutable=\"false\" overlays"
551                         + " targeting \"android\" will be loaded");
552                 return new String[0];
553             }
554 
555             idmapPaths.addAll(Arrays.asList(idmaps));
556         }
557 
558         return idmapPaths.toArray(new String[0]);
559     }
560 
561     /** Dump all overlay configurations to the Printer. */
dump(@onNull PrintWriter writer)562     public void dump(@NonNull PrintWriter writer) {
563         final IndentingPrintWriter ipw = new IndentingPrintWriter(writer);
564         ipw.println("Overlay configurations:");
565         ipw.increaseIndent();
566 
567         final ArrayList<Configuration> configurations = new ArrayList<>(mConfigurations.values());
568         configurations.sort(Comparator.comparingInt(o -> o.configIndex));
569         for (int i = 0; i < configurations.size(); i++) {
570             final Configuration configuration = configurations.get(i);
571             ipw.print(configuration.configIndex);
572             ipw.print(", ");
573             ipw.print(configuration.parsedConfig);
574             ipw.println();
575         }
576         ipw.decreaseIndent();
577         ipw.println();
578     }
579 
580     /**
581      * For each overlay APK, this creates the idmap file that allows the overlay to override the
582      * target package.
583      *
584      * @return the paths of the created idmap
585      */
createIdmap(@onNull String targetPath, @NonNull String[] overlayPath, @NonNull String[] policies, boolean enforceOverlayable)586     private static native String[] createIdmap(@NonNull String targetPath,
587             @NonNull String[] overlayPath, @NonNull String[] policies, boolean enforceOverlayable);
588 
589     /**
590      * @hide
591      */
isDefaultPartitionOrder()592     public boolean isDefaultPartitionOrder() {
593         return mIsDefaultPartitionOrder;
594     }
595 
596     /**
597      * @hide
598      */
getPartitionOrder()599     public String getPartitionOrder() {
600         return mPartitionOrder;
601     }
602 
603 }
604