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