1 /*
2  * Copyright (C) 2019 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 android.content.pm.parsing;
18 
19 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
20 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
21 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
22 
23 import android.annotation.NonNull;
24 import android.app.admin.DeviceAdminReceiver;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.SigningDetails;
28 import android.content.pm.VerifierInfo;
29 import android.content.pm.parsing.result.ParseInput;
30 import android.content.pm.parsing.result.ParseResult;
31 import android.content.res.ApkAssets;
32 import android.content.res.XmlResourceParser;
33 import android.os.Build;
34 import android.os.Trace;
35 import android.text.TextUtils;
36 import android.util.ArrayMap;
37 import android.util.ArraySet;
38 import android.util.AttributeSet;
39 import android.util.Pair;
40 import android.util.Slog;
41 
42 import com.android.internal.pm.pkg.component.flags.Flags;
43 import com.android.internal.util.ArrayUtils;
44 
45 import libcore.io.IoUtils;
46 
47 import org.xmlpull.v1.XmlPullParser;
48 import org.xmlpull.v1.XmlPullParserException;
49 
50 import java.io.File;
51 import java.io.FileDescriptor;
52 import java.io.IOException;
53 import java.security.PublicKey;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.Comparator;
57 import java.util.List;
58 import java.util.Objects;
59 import java.util.Set;
60 
61 /** @hide */
62 public class ApkLiteParseUtils {
63 
64     private static final String TAG = "ApkLiteParseUtils";
65 
66     private static final int PARSE_DEFAULT_INSTALL_LOCATION =
67             PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
68 
69     private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
70 
71     public static final String APK_FILE_EXTENSION = ".apk";
72 
73 
74     // Constants copied from services.jar side since they're not accessible
75     private static final String ANDROID_RES_NAMESPACE =
76             "http://schemas.android.com/apk/res/android";
77     public static final int DEFAULT_MIN_SDK_VERSION = 1;
78     private static final int DEFAULT_TARGET_SDK_VERSION = 0;
79     public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
80     private static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
81     private static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
82     private static final String TAG_APPLICATION = "application";
83     private static final String TAG_PACKAGE_VERIFIER = "package-verifier";
84     private static final String TAG_PROFILEABLE = "profileable";
85     private static final String TAG_RECEIVER = "receiver";
86     private static final String TAG_OVERLAY = "overlay";
87     private static final String TAG_USES_SDK = "uses-sdk";
88     private static final String TAG_USES_SPLIT = "uses-split";
89     private static final String TAG_MANIFEST = "manifest";
90     private static final String TAG_SDK_LIBRARY = "sdk-library";
91     private static final int SDK_VERSION = Build.VERSION.SDK_INT;
92     private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
93     private static final String TAG_PROCESSES = "processes";
94     private static final String TAG_PROCESS = "process";
95 
96     /**
97      * Parse only lightweight details about the package at the given location.
98      * Automatically detects if the package is a monolithic style (single APK
99      * file) or cluster style (directory of APKs).
100      * <p>
101      * This performs validity checking on cluster style packages, such as
102      * requiring identical package name and version codes, a single base APK,
103      * and unique split names.
104      */
parsePackageLite(ParseInput input, File packageFile, int flags)105     public static ParseResult<PackageLite> parsePackageLite(ParseInput input,
106             File packageFile, int flags) {
107         if (packageFile.isDirectory()) {
108             return parseClusterPackageLite(input, packageFile, flags);
109         } else {
110             return parseMonolithicPackageLite(input, packageFile, flags);
111         }
112     }
113 
114     /**
115      * Parse lightweight details about a single APK file.
116      */
parseMonolithicPackageLite(ParseInput input, File packageFile, int flags)117     public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
118             File packageFile, int flags) {
119         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
120         try {
121             final ParseResult<ApkLite> result = parseApkLite(input, packageFile, flags);
122             if (result.isError()) {
123                 return input.error(result);
124             }
125 
126             final ApkLite baseApk = result.getResult();
127             final String packagePath = packageFile.getAbsolutePath();
128             return input.success(
129                     new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */,
130                             null /* isFeatureSplits */, null /* usesSplitNames */,
131                             null /* configForSplit */, null /* splitApkPaths */,
132                             null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(),
133                             null /* requiredSplitTypes */, null /* splitTypes */));
134         } finally {
135             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
136         }
137     }
138 
139     /**
140      * Parse lightweight details about a single APK file passed as an FD.
141      */
parseMonolithicPackageLite(ParseInput input, FileDescriptor packageFd, String debugPathName, int flags)142     public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
143             FileDescriptor packageFd, String debugPathName, int flags) {
144         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
145         try {
146             final ParseResult<ApkLite> result = parseApkLite(input, packageFd, debugPathName,
147                     flags);
148             if (result.isError()) {
149                 return input.error(result);
150             }
151 
152             final ApkLite baseApk = result.getResult();
153             final String packagePath = debugPathName;
154             return input.success(
155                     new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */,
156                             null /* isFeatureSplits */, null /* usesSplitNames */,
157                             null /* configForSplit */, null /* splitApkPaths */,
158                             null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(),
159                             null /* requiredSplitTypes */, null /* splitTypes */));
160         } finally {
161             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
162         }
163     }
164 
165     /**
166      * Parse lightweight details about a directory of APKs.
167      *
168      * @param packageDir is the folder that contains split apks for a regular app
169      */
parseClusterPackageLite(ParseInput input, File packageDir, int flags)170     public static ParseResult<PackageLite> parseClusterPackageLite(ParseInput input,
171             File packageDir, int flags) {
172         final File[] files;
173         files = packageDir.listFiles();
174         if (ArrayUtils.isEmpty(files)) {
175             return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
176                     "No packages found in split");
177         }
178         // Apk directory is directly nested under the current directory
179         if (files.length == 1 && files[0].isDirectory()) {
180             return parseClusterPackageLite(input, files[0], flags);
181         }
182 
183         String packageName = null;
184         int versionCode = 0;
185         ApkLite baseApk = null;
186 
187         final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
188         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
189         try {
190             for (File file : files) {
191                 if (!isApkFile(file)) {
192                     continue;
193                 }
194 
195                 final ParseResult<ApkLite> result = parseApkLite(input, file, flags);
196                 if (result.isError()) {
197                     return input.error(result);
198                 }
199 
200                 final ApkLite lite = result.getResult();
201                 // Assert that all package names and version codes are
202                 // consistent with the first one we encounter.
203                 if (packageName == null) {
204                     packageName = lite.getPackageName();
205                     versionCode = lite.getVersionCode();
206                 } else {
207                     if (!packageName.equals(lite.getPackageName())) {
208                         return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
209                                 "Inconsistent package " + lite.getPackageName() + " in " + file
210                                         + "; expected " + packageName);
211                     }
212                     if (versionCode != lite.getVersionCode()) {
213                         return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
214                                 "Inconsistent version " + lite.getVersionCode() + " in " + file
215                                         + "; expected " + versionCode);
216                     }
217                 }
218 
219                 // Assert that each split is defined only once
220                 ApkLite prev = apks.put(lite.getSplitName(), lite);
221                 if (prev != null) {
222                     return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
223                             "Split name " + lite.getSplitName()
224                                     + " defined more than once; most recent was " + file
225                                     + ", previous was " + prev.getPath());
226                 }
227             }
228             baseApk = apks.remove(null);
229         } finally {
230             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
231         }
232         return composePackageLiteFromApks(input, packageDir, baseApk, apks);
233     }
234 
235     /**
236      * Utility method that retrieves lightweight details about the package by given location,
237      * base APK, and split APKs.
238      *
239      * @param packageDir Path to the package
240      * @param baseApk Parsed base APK
241      * @param splitApks Parsed split APKs
242      * @return PackageLite
243      */
composePackageLiteFromApks(ParseInput input, File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks)244     public static ParseResult<PackageLite> composePackageLiteFromApks(ParseInput input,
245             File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks) {
246         return composePackageLiteFromApks(input, packageDir, baseApk, splitApks, false);
247     }
248 
249     /**
250      * Utility method that retrieves lightweight details about the package by given location,
251      * base APK, and split APKs.
252      *
253      * @param packageDir Path to the package
254      * @param baseApk Parsed base APK
255      * @param splitApks Parsed split APKs
256      * @param apkRenamed Indicate whether the APKs are renamed after parsed.
257      * @return PackageLite
258      */
composePackageLiteFromApks( ParseInput input, File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks, boolean apkRenamed)259     public static ParseResult<PackageLite> composePackageLiteFromApks(
260             ParseInput input, File packageDir, ApkLite baseApk,
261             ArrayMap<String, ApkLite> splitApks, boolean apkRenamed) {
262         if (baseApk == null) {
263             return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
264                     "Missing base APK in " + packageDir);
265         }
266         // Always apply deterministic ordering based on splitName
267         final int size = ArrayUtils.size(splitApks);
268 
269         String[] splitNames = null;
270         Set<String>[] requiredSplitTypes = null;
271         Set<String>[] splitTypes = null;
272         boolean[] isFeatureSplits = null;
273         String[] usesSplitNames = null;
274         String[] configForSplits = null;
275         String[] splitCodePaths = null;
276         int[] splitRevisionCodes = null;
277         if (size > 0) {
278             splitNames = new String[size];
279             requiredSplitTypes = new Set[size];
280             splitTypes = new Set[size];
281             isFeatureSplits = new boolean[size];
282             usesSplitNames = new String[size];
283             configForSplits = new String[size];
284             splitCodePaths = new String[size];
285             splitRevisionCodes = new int[size];
286 
287             splitNames = splitApks.keySet().toArray(splitNames);
288             Arrays.sort(splitNames, sSplitNameComparator);
289 
290             for (int i = 0; i < size; i++) {
291                 final ApkLite apk = splitApks.get(splitNames[i]);
292                 requiredSplitTypes[i] = apk.getRequiredSplitTypes();
293                 splitTypes[i] = apk.getSplitTypes();
294                 usesSplitNames[i] = apk.getUsesSplitName();
295                 isFeatureSplits[i] = apk.isFeatureSplit();
296                 configForSplits[i] = apk.getConfigForSplit();
297                 splitCodePaths[i] = apkRenamed ? new File(packageDir,
298                         splitNameToFileName(apk)).getAbsolutePath() : apk.getPath();
299                 splitRevisionCodes[i] = apk.getRevisionCode();
300             }
301         }
302 
303         final String codePath = packageDir.getAbsolutePath();
304         final String baseCodePath = apkRenamed ? new File(packageDir,
305                 splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.getPath();
306         return input.success(
307                 new PackageLite(codePath, baseCodePath, baseApk, splitNames, isFeatureSplits,
308                         usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes,
309                         baseApk.getTargetSdkVersion(), requiredSplitTypes, splitTypes));
310     }
311 
312     /**
313      * Utility method that retrieves canonical file name by given split name from parsed APK.
314      *
315      * @param apk Parsed APK
316      * @return The canonical file name
317      */
splitNameToFileName(@onNull ApkLite apk)318     public static String splitNameToFileName(@NonNull ApkLite apk) {
319         Objects.requireNonNull(apk);
320         final String fileName = apk.getSplitName() == null ? "base" : "split_" + apk.getSplitName();
321         return fileName + APK_FILE_EXTENSION;
322     }
323 
324     /**
325      * Utility method that retrieves lightweight details about a single APK
326      * file, including package name, split name, and install location.
327      *
328      * @param apkFile path to a single APK
329      * @param flags optional parse flags, such as
330      *            {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES}
331      */
parseApkLite(ParseInput input, File apkFile, int flags)332     public static ParseResult<ApkLite> parseApkLite(ParseInput input, File apkFile, int flags) {
333         return parseApkLiteInner(input, apkFile, null, null, flags);
334     }
335 
336     /**
337      * Utility method that retrieves lightweight details about a single APK
338      * file, including package name, split name, and install location.
339      *
340      * @param fd already open file descriptor of an apk file
341      * @param debugPathName arbitrary text name for this file, for debug output
342      * @param flags optional parse flags, such as
343      *            {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES}
344      */
parseApkLite(ParseInput input, FileDescriptor fd, String debugPathName, int flags)345     public static ParseResult<ApkLite> parseApkLite(ParseInput input,
346             FileDescriptor fd, String debugPathName, int flags) {
347         return parseApkLiteInner(input, null, fd, debugPathName, flags);
348     }
349 
parseApkLiteInner(ParseInput input, File apkFile, FileDescriptor fd, String debugPathName, int flags)350     private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input,
351             File apkFile, FileDescriptor fd, String debugPathName, int flags) {
352         final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
353 
354         XmlResourceParser parser = null;
355         ApkAssets apkAssets = null;
356         try {
357             try {
358                 apkAssets = fd != null
359                         ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
360                         : ApkAssets.loadFromPath(apkPath);
361             } catch (IOException e) {
362                 return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
363                         "Failed to parse " + apkPath, e);
364             }
365 
366             parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
367 
368             final SigningDetails signingDetails;
369             if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
370                 final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
371                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
372                 try {
373                     final ParseResult<SigningDetails> result =
374                             FrameworkParsingPackageUtils.getSigningDetails(input,
375                                     apkFile.getAbsolutePath(),
376                                     skipVerify, /* isStaticSharedLibrary */ false,
377                                     SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
378                     if (result.isError()) {
379                         return input.error(result);
380                     }
381                     signingDetails = result.getResult();
382                 } finally {
383                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
384                 }
385             } else {
386                 signingDetails = SigningDetails.UNKNOWN;
387             }
388 
389             return parseApkLite(input, apkPath, parser, signingDetails, flags);
390         } catch (XmlPullParserException | IOException | RuntimeException e) {
391             Slog.w(TAG, "Failed to parse " + apkPath, e);
392             return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
393                     "Failed to parse " + apkPath, e);
394         } finally {
395             IoUtils.closeQuietly(parser);
396             if (apkAssets != null) {
397                 try {
398                     apkAssets.close();
399                 } catch (Throwable ignored) {
400                 }
401             }
402             // TODO(b/72056911): Implement AutoCloseable on ApkAssets.
403         }
404     }
405 
parseApkLite(ParseInput input, String codePath, XmlResourceParser parser, SigningDetails signingDetails, int flags)406     private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath,
407             XmlResourceParser parser, SigningDetails signingDetails, int flags)
408             throws IOException, XmlPullParserException {
409         ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser);
410         if (result.isError()) {
411             return input.error(result);
412         }
413         Pair<String, String> packageSplit = result.getResult();
414 
415         final ParseResult<Pair<Set<String>, Set<String>>> requiredSplitTypesResult =
416                 parseRequiredSplitTypes(input, parser);
417         if (requiredSplitTypesResult.isError()) {
418             return input.error(result);
419         }
420         Pair<Set<String>, Set<String>> requiredSplitTypes = requiredSplitTypesResult.getResult();
421 
422         int installLocation = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE,
423                 "installLocation", PARSE_DEFAULT_INSTALL_LOCATION);
424         int versionCode = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "versionCode", 0);
425         int versionCodeMajor = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE,
426                 "versionCodeMajor",
427                 0);
428         int revisionCode = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "revisionCode", 0);
429         boolean coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
430         boolean updatableSystem = parser.getAttributeBooleanValue(null, "updatableSystem", true);
431         boolean isolatedSplits = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
432                 "isolatedSplits", false);
433         boolean isFeatureSplit = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
434                 "isFeatureSplit", false);
435         boolean isSplitRequired = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
436                 "isSplitRequired", false);
437         String configForSplit = parser.getAttributeValue(null, "configForSplit");
438         String emergencyInstaller = parser.getAttributeValue(null, "emergencyInstaller");
439 
440         int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION;
441         int minSdkVersion = DEFAULT_MIN_SDK_VERSION;
442         boolean debuggable = false;
443         boolean profilableByShell = false;
444         boolean multiArch = false;
445         boolean use32bitAbi = false;
446         boolean extractNativeLibs = true;
447         boolean useEmbeddedDex = false;
448         String usesSplitName = null;
449         String targetPackage = null;
450         boolean overlayIsStatic = false;
451         int overlayPriority = 0;
452         int rollbackDataPolicy = 0;
453 
454         String requiredSystemPropertyName = null;
455         String requiredSystemPropertyValue = null;
456 
457         boolean hasDeviceAdminReceiver = false;
458 
459         boolean isSdkLibrary = false;
460 
461         // Only search the tree when the tag is the direct child of <manifest> tag
462         int type;
463         final int searchDepth = parser.getDepth() + 1;
464 
465         final List<VerifierInfo> verifiers = new ArrayList<>();
466         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
467                 && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
468             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
469                 continue;
470             }
471 
472             if (parser.getDepth() != searchDepth) {
473                 continue;
474             }
475 
476             if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
477                 final VerifierInfo verifier = parseVerifier(parser);
478                 if (verifier != null) {
479                     verifiers.add(verifier);
480                 }
481             } else if (TAG_APPLICATION.equals(parser.getName())) {
482                 debuggable = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "debuggable",
483                         false);
484                 multiArch = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "multiArch",
485                         false);
486                 use32bitAbi = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "use32bitAbi",
487                         false);
488                 extractNativeLibs = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
489                         "extractNativeLibs", true);
490                 useEmbeddedDex = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
491                         "useEmbeddedDex", false);
492 
493                 rollbackDataPolicy = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE,
494                         "rollbackDataPolicy", 0);
495                 String permission = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
496                         "permission");
497                 boolean hasBindDeviceAdminPermission =
498                         android.Manifest.permission.BIND_DEVICE_ADMIN.equals(permission);
499 
500                 final int innerDepth = parser.getDepth();
501                 int innerType;
502                 while ((innerType = parser.next()) != XmlPullParser.END_DOCUMENT
503                         && (innerType != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
504                     if (innerType == XmlPullParser.END_TAG || innerType == XmlPullParser.TEXT) {
505                         continue;
506                     }
507 
508                     if (parser.getDepth() != innerDepth + 1) {
509                         // Search only under <application>.
510                         continue;
511                     }
512 
513                     switch (parser.getName()) {
514                         case TAG_PROFILEABLE:
515                             profilableByShell = parser.getAttributeBooleanValue(
516                                     ANDROID_RES_NAMESPACE, "shell", profilableByShell);
517                             break;
518                         case TAG_RECEIVER:
519                             hasDeviceAdminReceiver |= isDeviceAdminReceiver(parser,
520                                     hasBindDeviceAdminPermission);
521                             break;
522                         case TAG_SDK_LIBRARY:
523                             isSdkLibrary = true;
524                             break;
525                         case TAG_PROCESSES:
526                             final int processesDepth = parser.getDepth();
527                             int processesType;
528                             while ((processesType = parser.next()) != XmlPullParser.END_DOCUMENT
529                                     && (processesType != XmlPullParser.END_TAG
530                                     || parser.getDepth() > processesDepth)) {
531                                 if (processesType == XmlPullParser.END_TAG
532                                         || processesType == XmlPullParser.TEXT) {
533                                     continue;
534                                 }
535 
536                                 if (parser.getDepth() != processesDepth + 1) {
537                                     // Search only under <processes>.
538                                     continue;
539                                 }
540 
541                                 if (parser.getName().equals(TAG_PROCESS)
542                                         && Flags.enablePerProcessUseEmbeddedDexAttr()) {
543                                     useEmbeddedDex |= parser.getAttributeBooleanValue(
544                                             ANDROID_RES_NAMESPACE, "useEmbeddedDex", false);
545                                 }
546                             }
547                     }
548                 }
549             } else if (TAG_OVERLAY.equals(parser.getName())) {
550                 requiredSystemPropertyName = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
551                         "requiredSystemPropertyName");
552                 requiredSystemPropertyValue = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
553                         "requiredSystemPropertyValue");
554                 targetPackage = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "targetPackage");
555                 overlayIsStatic = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "isStatic",
556                         false);
557                 overlayPriority = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "priority", 0);
558             } else if (TAG_USES_SPLIT.equals(parser.getName())) {
559                 if (usesSplitName != null) {
560                     Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
561                     continue;
562                 }
563 
564                 usesSplitName = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "name");
565                 if (usesSplitName == null) {
566                     return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
567                             "<uses-split> tag requires 'android:name' attribute");
568                 }
569             } else if (TAG_USES_SDK.equals(parser.getName())) {
570                 // Mirrors FrameworkParsingPackageUtils#parseUsesSdk until lite and full parsing is combined
571                 String minSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
572                         "minSdkVersion");
573                 String targetSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
574                         "targetSdkVersion");
575 
576                 int minVer = DEFAULT_MIN_SDK_VERSION;
577                 String minCode = null;
578                 boolean minAssigned = false;
579                 int targetVer = DEFAULT_TARGET_SDK_VERSION;
580                 String targetCode = null;
581 
582                 if (!TextUtils.isEmpty(minSdkVersionString)) {
583                     try {
584                         minVer = Integer.parseInt(minSdkVersionString);
585                         minAssigned = true;
586                     } catch (NumberFormatException ignored) {
587                         minCode = minSdkVersionString;
588                         minAssigned = !TextUtils.isEmpty(minCode);
589                     }
590                 }
591 
592                 if (!TextUtils.isEmpty(targetSdkVersionString)) {
593                     try {
594                         targetVer = Integer.parseInt(targetSdkVersionString);
595                     } catch (NumberFormatException ignored) {
596                         targetCode = targetSdkVersionString;
597                         if (!minAssigned) {
598                             minCode = targetCode;
599                         }
600                     }
601                 } else {
602                     targetVer = minVer;
603                     targetCode = minCode;
604                 }
605 
606                 boolean allowUnknownCodenames = false;
607                 if ((flags & FrameworkParsingPackageUtils.PARSE_APK_IN_APEX) != 0) {
608                     allowUnknownCodenames = true;
609                 }
610 
611                 ParseResult<Integer> targetResult = FrameworkParsingPackageUtils.computeTargetSdkVersion(
612                         targetVer, targetCode, SDK_CODENAMES, input,
613                         allowUnknownCodenames);
614                 if (targetResult.isError()) {
615                     return input.error(targetResult);
616                 }
617                 targetSdkVersion = targetResult.getResult();
618 
619                 ParseResult<Integer> minResult = FrameworkParsingPackageUtils.computeMinSdkVersion(
620                         minVer, minCode, SDK_VERSION, SDK_CODENAMES, input);
621                 if (minResult.isError()) {
622                     return input.error(minResult);
623                 }
624                 minSdkVersion = minResult.getResult();
625             }
626         }
627 
628         // Check to see if overlay should be excluded based on system property condition
629         if ((flags & FrameworkParsingPackageUtils.PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY)
630                 == 0 && !FrameworkParsingPackageUtils.checkRequiredSystemProperties(
631                 requiredSystemPropertyName, requiredSystemPropertyValue)) {
632             String message = "Skipping target and overlay pair " + targetPackage + " and "
633                     + codePath + ": overlay ignored due to required system property: "
634                     + requiredSystemPropertyName + " with value: " + requiredSystemPropertyValue;
635             Slog.i(TAG, message);
636             return input.skip(message);
637         }
638 
639         return input.success(
640                 new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
641                         configForSplit, usesSplitName, isSplitRequired, versionCode,
642                         versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
643                         coreApp, debuggable, profilableByShell, multiArch, use32bitAbi,
644                         useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage,
645                         overlayIsStatic, overlayPriority, requiredSystemPropertyName,
646                         requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
647                         rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
648                         hasDeviceAdminReceiver, isSdkLibrary, updatableSystem, emergencyInstaller));
649     }
650 
isDeviceAdminReceiver( XmlResourceParser parser, boolean applicationHasBindDeviceAdminPermission)651     private static boolean isDeviceAdminReceiver(
652             XmlResourceParser parser, boolean applicationHasBindDeviceAdminPermission)
653             throws XmlPullParserException, IOException {
654         String permission = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
655                 "permission");
656         if (!applicationHasBindDeviceAdminPermission
657                 && !android.Manifest.permission.BIND_DEVICE_ADMIN.equals(permission)) {
658             return false;
659         }
660 
661         boolean hasDeviceAdminReceiver = false;
662         final int depth = parser.getDepth();
663         int type;
664         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
665                 && (type != XmlPullParser.END_TAG || parser.getDepth() > depth)) {
666             if (type == XmlPullParser.END_TAG
667                     || type == XmlPullParser.TEXT) {
668                 continue;
669             }
670             if (parser.getDepth() != depth + 1) {
671                 // Search only under <receiver>.
672                 continue;
673             }
674             if (!hasDeviceAdminReceiver && "meta-data".equals(parser.getName())) {
675                 String name = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
676                         "name");
677                 if (DeviceAdminReceiver.DEVICE_ADMIN_META_DATA.equals(name)) {
678                     hasDeviceAdminReceiver = true;
679                 }
680             }
681         }
682         return hasDeviceAdminReceiver;
683     }
684 
parsePackageSplitNames(ParseInput input, XmlResourceParser parser)685     public static ParseResult<Pair<String, String>> parsePackageSplitNames(ParseInput input,
686             XmlResourceParser parser) throws IOException, XmlPullParserException {
687         int type;
688         while ((type = parser.next()) != XmlPullParser.START_TAG
689                 && type != XmlPullParser.END_DOCUMENT) {
690         }
691 
692         if (type != XmlPullParser.START_TAG) {
693             return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
694                     "No start tag found");
695         }
696         if (!parser.getName().equals(TAG_MANIFEST)) {
697             return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
698                     "No <manifest> tag");
699         }
700 
701         final String packageName = parser.getAttributeValue(null, "package");
702         if (!"android".equals(packageName)) {
703             final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input,
704                     packageName, true, true);
705             if (nameResult.isError()) {
706                 return input.error(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
707                         "Invalid manifest package: " + nameResult.getErrorMessage());
708             }
709         }
710 
711         String splitName = parser.getAttributeValue(null, "split");
712         if (splitName != null) {
713             if (splitName.length() == 0) {
714                 splitName = null;
715             } else {
716                 final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input,
717                         splitName, false, false);
718                 if (nameResult.isError()) {
719                     return input.error(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
720                             "Invalid manifest split: " + nameResult.getErrorMessage());
721                 }
722             }
723         }
724 
725         return input.success(Pair.create(packageName.intern(),
726                 (splitName != null) ? splitName.intern() : splitName));
727     }
728 
729     /**
730      * Utility method that parses attributes android:requiredSplitTypes and android:splitTypes.
731      */
parseRequiredSplitTypes( ParseInput input, XmlResourceParser parser)732     public static ParseResult<Pair<Set<String>, Set<String>>> parseRequiredSplitTypes(
733             ParseInput input, XmlResourceParser parser) {
734         Set<String> requiredSplitTypes = null;
735         Set<String> splitTypes = null;
736         String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "requiredSplitTypes");
737         if (!TextUtils.isEmpty(value)) {
738             final ParseResult<Set<String>> result = separateAndValidateSplitTypes(input, value);
739             if (result.isError()) {
740                 return input.error(result);
741             }
742             requiredSplitTypes = result.getResult();
743         }
744 
745         value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "splitTypes");
746         if (!TextUtils.isEmpty(value)) {
747             final ParseResult<Set<String>> result = separateAndValidateSplitTypes(input, value);
748             if (result.isError()) {
749                 return input.error(result);
750             }
751             splitTypes = result.getResult();
752         }
753 
754         return input.success(Pair.create(requiredSplitTypes, splitTypes));
755     }
756 
separateAndValidateSplitTypes(ParseInput input, String values)757     private static ParseResult<Set<String>> separateAndValidateSplitTypes(ParseInput input,
758             String values) {
759         final Set<String> ret = new ArraySet<>();
760         for (String value : values.trim().split(",")) {
761             final String type = value.trim();
762             // Using requireFilename as true because it limits length of the name to the
763             // {@link #MAX_FILE_NAME_SIZE}.
764             final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, type,
765                     false /* requireSeparator */, true /* requireFilename */);
766             if (nameResult.isError()) {
767                 return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
768                         "Invalid manifest split types: " + nameResult.getErrorMessage());
769             }
770             if (!ret.add(type)) {
771                 Slog.w(TAG, type + " was defined multiple times");
772             }
773         }
774         return input.success(ret);
775     }
776 
parseVerifier(AttributeSet attrs)777     public static VerifierInfo parseVerifier(AttributeSet attrs) {
778         String packageName = attrs.getAttributeValue(ANDROID_RES_NAMESPACE, "name");
779         String encodedPublicKey = attrs.getAttributeValue(ANDROID_RES_NAMESPACE, "publicKey");
780 
781         if (packageName == null || packageName.length() == 0) {
782             Slog.i(TAG, "verifier package name was null; skipping");
783             return null;
784         }
785 
786         final PublicKey publicKey = FrameworkParsingPackageUtils.parsePublicKey(encodedPublicKey);
787         if (publicKey == null) {
788             Slog.i(TAG, "Unable to parse verifier public key for " + packageName);
789             return null;
790         }
791 
792         return new VerifierInfo(packageName, publicKey);
793     }
794 
795     /**
796      * Used to sort a set of APKs based on their split names, always placing the
797      * base APK (with {@code null} split name) first.
798      */
799     private static class SplitNameComparator implements Comparator<String> {
800         @Override
compare(String lhs, String rhs)801         public int compare(String lhs, String rhs) {
802             if (lhs == null) {
803                 return -1;
804             } else if (rhs == null) {
805                 return 1;
806             } else {
807                 return lhs.compareTo(rhs);
808             }
809         }
810     }
811 
812     /**
813      * Check if the given file is an APK file.
814      *
815      * @param file the file to check.
816      * @return {@code true} if the given file is an APK file.
817      */
isApkFile(File file)818     public static boolean isApkFile(File file) {
819         return isApkPath(file.getName());
820     }
821 
822     /**
823      * Check if the given path ends with APK file extension.
824      *
825      * @param path the path to check.
826      * @return {@code true} if the given path ends with APK file extension.
827      */
isApkPath(String path)828     public static boolean isApkPath(String path) {
829         return path.endsWith(APK_FILE_EXTENSION);
830     }
831 }
832