• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.content.pm.parsing;
18 
19 import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
20 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
21 import static android.content.pm.PackageManager.FEATURE_WATCH;
22 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
23 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
24 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
25 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED;
26 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
27 import static android.os.Build.VERSION_CODES.DONUT;
28 import static android.os.Build.VERSION_CODES.O;
29 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
30 
31 import android.annotation.AnyRes;
32 import android.annotation.CheckResult;
33 import android.annotation.IntRange;
34 import android.annotation.NonNull;
35 import android.annotation.Nullable;
36 import android.annotation.StyleableRes;
37 import android.app.ActivityThread;
38 import android.content.Intent;
39 import android.content.IntentFilter;
40 import android.content.pm.ApplicationInfo;
41 import android.content.pm.ConfigurationInfo;
42 import android.content.pm.FeatureGroupInfo;
43 import android.content.pm.FeatureInfo;
44 import android.content.pm.PackageInfo;
45 import android.content.pm.PackageManager;
46 import android.content.pm.PackageParser;
47 import android.content.pm.PackageParser.PackageParserException;
48 import android.content.pm.PackageParser.SigningDetails;
49 import android.content.pm.Signature;
50 import android.content.pm.parsing.component.ComponentParseUtils;
51 import android.content.pm.parsing.component.ParsedActivity;
52 import android.content.pm.parsing.component.ParsedActivityUtils;
53 import android.content.pm.parsing.component.ParsedAttribution;
54 import android.content.pm.parsing.component.ParsedAttributionUtils;
55 import android.content.pm.parsing.component.ParsedInstrumentation;
56 import android.content.pm.parsing.component.ParsedInstrumentationUtils;
57 import android.content.pm.parsing.component.ParsedIntentInfo;
58 import android.content.pm.parsing.component.ParsedIntentInfoUtils;
59 import android.content.pm.parsing.component.ParsedMainComponent;
60 import android.content.pm.parsing.component.ParsedPermission;
61 import android.content.pm.parsing.component.ParsedPermissionGroup;
62 import android.content.pm.parsing.component.ParsedPermissionUtils;
63 import android.content.pm.parsing.component.ParsedProcess;
64 import android.content.pm.parsing.component.ParsedProcessUtils;
65 import android.content.pm.parsing.component.ParsedProvider;
66 import android.content.pm.parsing.component.ParsedProviderUtils;
67 import android.content.pm.parsing.component.ParsedService;
68 import android.content.pm.parsing.component.ParsedServiceUtils;
69 import android.content.pm.parsing.result.ParseInput;
70 import android.content.pm.parsing.result.ParseInput.DeferredError;
71 import android.content.pm.parsing.result.ParseResult;
72 import android.content.pm.parsing.result.ParseTypeImpl;
73 import android.content.pm.permission.SplitPermissionInfoParcelable;
74 import android.content.pm.split.DefaultSplitAssetLoader;
75 import android.content.pm.split.SplitAssetDependencyLoader;
76 import android.content.pm.split.SplitAssetLoader;
77 import android.content.res.ApkAssets;
78 import android.content.res.AssetManager;
79 import android.content.res.Configuration;
80 import android.content.res.Resources;
81 import android.content.res.TypedArray;
82 import android.content.res.XmlResourceParser;
83 import android.net.Uri;
84 import android.os.Build;
85 import android.os.Bundle;
86 import android.os.FileUtils;
87 import android.os.RemoteException;
88 import android.os.Trace;
89 import android.os.ext.SdkExtensions;
90 import android.text.TextUtils;
91 import android.util.ArrayMap;
92 import android.util.ArraySet;
93 import android.util.AttributeSet;
94 import android.util.DisplayMetrics;
95 import android.util.Pair;
96 import android.util.Slog;
97 import android.util.SparseArray;
98 import android.util.SparseIntArray;
99 import android.util.TypedValue;
100 import android.util.apk.ApkSignatureVerifier;
101 
102 import com.android.internal.R;
103 import com.android.internal.os.ClassLoaderFactory;
104 import com.android.internal.util.ArrayUtils;
105 import com.android.internal.util.XmlUtils;
106 
107 import libcore.io.IoUtils;
108 import libcore.util.EmptyArray;
109 
110 import org.xmlpull.v1.XmlPullParser;
111 import org.xmlpull.v1.XmlPullParserException;
112 
113 import java.io.File;
114 import java.io.IOException;
115 import java.security.PublicKey;
116 import java.util.ArrayList;
117 import java.util.Arrays;
118 import java.util.List;
119 import java.util.Map;
120 import java.util.Set;
121 import java.util.StringTokenizer;
122 
123 /**
124  * TODO(b/135203078): Differentiate between parse_ methods and some add_ method for whether it
125  * mutates the passed-in component or not. Or consolidate so all parse_ methods mutate.
126  *
127  * @hide
128  */
129 public class ParsingPackageUtils {
130 
131     public static final String TAG = ParsingUtils.TAG;
132 
133     /**
134      * @see #parseDefault(ParseInput, File, int, boolean)
135      */
136     @NonNull
parseDefaultOneTime(File file, @PackageParser.ParseFlags int parseFlags, boolean collectCertificates)137     public static ParseResult<ParsingPackage> parseDefaultOneTime(File file,
138             @PackageParser.ParseFlags int parseFlags, boolean collectCertificates) {
139         ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
140         return parseDefault(input, file, parseFlags, collectCertificates);
141     }
142 
143     /**
144      * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off
145      * request, without caching the input object and without querying the internal system state
146      * for feature support.
147      */
148     @NonNull
parseDefault(ParseInput input, File file, @PackageParser.ParseFlags int parseFlags, boolean collectCertificates)149     public static ParseResult<ParsingPackage> parseDefault(ParseInput input, File file,
150             @PackageParser.ParseFlags int parseFlags, boolean collectCertificates) {
151         ParseResult<ParsingPackage> result;
152 
153         ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, new Callback() {
154             @Override
155             public boolean hasFeature(String feature) {
156                 // Assume the device doesn't support anything. This will affect permission parsing
157                 // and will force <uses-permission/> declarations to include all requiredNotFeature
158                 // permissions and exclude all requiredFeature permissions. This mirrors the old
159                 // behavior.
160                 return false;
161             }
162 
163             @Override
164             public ParsingPackage startParsingPackage(
165                     @NonNull String packageName,
166                     @NonNull String baseCodePath,
167                     @NonNull String codePath,
168                     @NonNull TypedArray manifestArray, boolean isCoreApp) {
169                 return new ParsingPackageImpl(packageName, baseCodePath, codePath, manifestArray);
170             }
171         });
172         try {
173             result = parser.parsePackage(input, file, parseFlags);
174             if (result.isError()) {
175                 return result;
176             }
177         } catch (PackageParser.PackageParserException e) {
178             return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
179                     "Error parsing package", e);
180         }
181 
182         try {
183             ParsingPackage pkg = result.getResult();
184             if (collectCertificates) {
185                 pkg.setSigningDetails(
186                         ParsingPackageUtils.getSigningDetails(pkg, false /* skipVerify */));
187             }
188 
189             return input.success(pkg);
190         } catch (PackageParser.PackageParserException e) {
191             return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
192                     "Error collecting package certificates", e);
193         }
194     }
195 
196     private boolean mOnlyCoreApps;
197     private String[] mSeparateProcesses;
198     private DisplayMetrics mDisplayMetrics;
199     private Callback mCallback;
200 
ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses, DisplayMetrics displayMetrics, @NonNull Callback callback)201     public ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses,
202             DisplayMetrics displayMetrics, @NonNull Callback callback) {
203         mOnlyCoreApps = onlyCoreApps;
204         mSeparateProcesses = separateProcesses;
205         mDisplayMetrics = displayMetrics;
206         mCallback = callback;
207     }
208 
209     /**
210      * Parse the package at the given location. Automatically detects if the
211      * package is a monolithic style (single APK file) or cluster style
212      * (directory of APKs).
213      * <p>
214      * This performs sanity checking on cluster style packages, such as
215      * requiring identical package name and version codes, a single base APK,
216      * and unique split names.
217      * <p>
218      * Note that this <em>does not</em> perform signature verification; that
219      * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
220      *
221      * If {@code useCaches} is true, the package parser might return a cached
222      * result from a previous parse of the same {@code packageFile} with the same
223      * {@code flags}. Note that this method does not check whether {@code packageFile}
224      * has changed since the last parse, it's up to callers to do so.
225      *
226      * @see PackageParser#parsePackageLite(File, int)
227      */
parsePackage(ParseInput input, File packageFile, int flags)228     public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
229             int flags)
230             throws PackageParserException {
231         if (packageFile.isDirectory()) {
232             return parseClusterPackage(input, packageFile, flags);
233         } else {
234             return parseMonolithicPackage(input, packageFile, flags);
235         }
236     }
237 
238     /**
239      * Parse all APKs contained in the given directory, treating them as a
240      * single package. This also performs sanity checking, such as requiring
241      * identical package name and version codes, a single base APK, and unique
242      * split names.
243      * <p>
244      * Note that this <em>does not</em> perform signature verification; that
245      * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
246      */
parseClusterPackage(ParseInput input, File packageDir, int flags)247     private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
248             int flags) {
249         ParseResult<PackageParser.PackageLite> liteResult =
250                 ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);
251         if (liteResult.isError()) {
252             return input.error(liteResult);
253         }
254 
255         final PackageParser.PackageLite lite = liteResult.getResult();
256         if (mOnlyCoreApps && !lite.coreApp) {
257             return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
258                     "Not a coreApp: " + packageDir);
259         }
260 
261         // Build the split dependency tree.
262         SparseArray<int[]> splitDependencies = null;
263         final SplitAssetLoader assetLoader;
264         if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
265             try {
266                 splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
267                 assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
268             } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
269                 return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
270             }
271         } else {
272             assetLoader = new DefaultSplitAssetLoader(lite, flags);
273         }
274 
275         try {
276             final AssetManager assets = assetLoader.getBaseAssetManager();
277             final File baseApk = new File(lite.baseCodePath);
278             ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
279                     lite.codePath, assets, flags);
280             if (result.isError()) {
281                 return input.error(result);
282             }
283 
284             ParsingPackage pkg = result.getResult();
285             if (!ArrayUtils.isEmpty(lite.splitNames)) {
286                 pkg.asSplit(
287                         lite.splitNames,
288                         lite.splitCodePaths,
289                         lite.splitRevisionCodes,
290                         splitDependencies
291                 );
292                 final int num = lite.splitNames.length;
293 
294                 for (int i = 0; i < num; i++) {
295                     final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
296                     parseSplitApk(input, pkg, i, splitAssets, flags);
297                 }
298             }
299 
300             pkg.setUse32BitAbi(lite.use32bitAbi);
301             return input.success(pkg);
302         } catch (PackageParserException e) {
303             return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
304                     "Failed to load assets: " + lite.baseCodePath, e);
305         } finally {
306             IoUtils.closeQuietly(assetLoader);
307         }
308     }
309 
310     /**
311      * Parse the given APK file, treating it as as a single monolithic package.
312      * <p>
313      * Note that this <em>does not</em> perform signature verification; that
314      * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
315      */
parseMonolithicPackage(ParseInput input, File apkFile, int flags)316     private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
317             int flags) throws PackageParserException {
318         ParseResult<PackageParser.PackageLite> liteResult =
319                 ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
320         if (liteResult.isError()) {
321             return input.error(liteResult);
322         }
323 
324         final PackageParser.PackageLite lite = liteResult.getResult();
325         if (mOnlyCoreApps && !lite.coreApp) {
326             return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
327                     "Not a coreApp: " + apkFile);
328         }
329 
330         final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
331         try {
332             ParseResult<ParsingPackage> result = parseBaseApk(input,
333                     apkFile,
334                     apkFile.getCanonicalPath(),
335                     assetLoader.getBaseAssetManager(), flags);
336             if (result.isError()) {
337                 return input.error(result);
338             }
339 
340             return input.success(result.getResult()
341                     .setUse32BitAbi(lite.use32bitAbi));
342         } catch (IOException e) {
343             return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
344                     "Failed to get path: " + apkFile, e);
345         } finally {
346             IoUtils.closeQuietly(assetLoader);
347         }
348     }
349 
parseBaseApk(ParseInput input, File apkFile, String codePath, AssetManager assets, int flags)350     private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
351             String codePath, AssetManager assets, int flags) {
352         final String apkPath = apkFile.getAbsolutePath();
353 
354         String volumeUuid = null;
355         if (apkPath.startsWith(PackageParser.MNT_EXPAND)) {
356             final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length());
357             volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end);
358         }
359 
360         if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
361 
362         final int cookie = assets.findCookieForPath(apkPath);
363         if (cookie == 0) {
364             return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
365                     "Failed adding asset path: " + apkPath);
366         }
367 
368         try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
369                 PackageParser.ANDROID_MANIFEST_FILENAME)) {
370             final Resources res = new Resources(assets, mDisplayMetrics, null);
371 
372             ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
373                     parser, flags);
374             if (result.isError()) {
375                 return input.error(result.getErrorCode(),
376                         apkPath + " (at " + parser.getPositionDescription() + "): "
377                                 + result.getErrorMessage());
378             }
379 
380             final ParsingPackage pkg = result.getResult();
381             if (assets.containsAllocatedTable()) {
382                 final ParseResult<?> deferResult = input.deferError(
383                         "Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires"
384                                 + " the resources.arsc of installed APKs to be stored uncompressed"
385                                 + " and aligned on a 4-byte boundary",
386                         DeferredError.RESOURCES_ARSC_COMPRESSED);
387                 if (deferResult.isError()) {
388                     return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
389                             deferResult.getErrorMessage());
390                 }
391             }
392 
393             ApkAssets apkAssets = assets.getApkAssets()[0];
394             if (apkAssets.definesOverlayable()) {
395                 SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
396                 int size = packageNames.size();
397                 for (int index = 0; index < size; index++) {
398                     String packageName = packageNames.get(index);
399                     Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
400                     if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
401                         for (String overlayable : overlayableToActor.keySet()) {
402                             pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
403                         }
404                     }
405                 }
406             }
407 
408             pkg.setVolumeUuid(volumeUuid);
409 
410             if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) {
411                 pkg.setSigningDetails(getSigningDetails(pkg, false));
412             } else {
413                 pkg.setSigningDetails(SigningDetails.UNKNOWN);
414             }
415 
416             return input.success(pkg);
417         } catch (Exception e) {
418             return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
419                     "Failed to read manifest from " + apkPath, e);
420         }
421     }
422 
parseSplitApk(ParseInput input, ParsingPackage pkg, int splitIndex, AssetManager assets, int flags)423     private ParseResult<ParsingPackage> parseSplitApk(ParseInput input,
424             ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) {
425         final String apkPath = pkg.getSplitCodePaths()[splitIndex];
426 
427         if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
428 
429         // This must always succeed, as the path has been added to the AssetManager before.
430         final int cookie = assets.findCookieForPath(apkPath);
431         if (cookie == 0) {
432             return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
433                     "Failed adding asset path: " + apkPath);
434         }
435         try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
436                 PackageParser.ANDROID_MANIFEST_FILENAME)) {
437             Resources res = new Resources(assets, mDisplayMetrics, null);
438             ParseResult<ParsingPackage> parseResult = parseSplitApk(input, pkg, res,
439                     parser, flags, splitIndex);
440             if (parseResult.isError()) {
441                 return input.error(parseResult.getErrorCode(),
442                         apkPath + " (at " + parser.getPositionDescription() + "): "
443                                 + parseResult.getErrorMessage());
444             }
445 
446             return parseResult;
447         } catch (Exception e) {
448             return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
449                     "Failed to read manifest from " + apkPath, e);
450         }
451     }
452 
453     /**
454      * Parse the manifest of a <em>base APK</em>. When adding new features you
455      * need to consider whether they should be supported by split APKs and child
456      * packages.
457      *
458      * @param apkPath The package apk file path
459      * @param res     The resources from which to resolve values
460      * @param parser  The manifest parser
461      * @param flags   Flags how to parse
462      * @return Parsed package or null on error.
463      */
parseBaseApk(ParseInput input, String apkPath, String codePath, Resources res, XmlResourceParser parser, int flags)464     private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
465             String codePath, Resources res, XmlResourceParser parser, int flags)
466             throws XmlPullParserException, IOException, PackageParserException {
467         final String splitName;
468         final String pkgName;
469 
470         ParseResult<Pair<String, String>> packageSplitResult =
471                 ApkLiteParseUtils.parsePackageSplitNames(input, parser, parser);
472         if (packageSplitResult.isError()) {
473             return input.error(packageSplitResult);
474         }
475 
476         Pair<String, String> packageSplit = packageSplitResult.getResult();
477         pkgName = packageSplit.first;
478         splitName = packageSplit.second;
479 
480         if (!TextUtils.isEmpty(splitName)) {
481             return input.error(
482                     PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
483                     "Expected base APK, but found split " + splitName
484             );
485         }
486 
487         final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
488         try {
489             final boolean isCoreApp =
490                     parser.getAttributeBooleanValue(null, "coreApp", false);
491             final ParsingPackage pkg = mCallback.startParsingPackage(
492                     pkgName, apkPath, codePath, manifestArray, isCoreApp);
493             final ParseResult<ParsingPackage> result =
494                     parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
495             if (result.isError()) {
496                 return result;
497             }
498 
499             return input.success(pkg);
500         } finally {
501             manifestArray.recycle();
502         }
503     }
504 
505     /**
506      * Parse the manifest of a <em>split APK</em>.
507      * <p>
508      * Note that split APKs have many more restrictions on what they're capable
509      * of doing, so many valid features of a base APK have been carefully
510      * omitted here.
511      *
512      * @param pkg builder to fill
513      * @return false on failure
514      */
parseSplitApk(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex)515     private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg,
516             Resources res, XmlResourceParser parser, int flags, int splitIndex)
517             throws XmlPullParserException, IOException, PackageParserException {
518         AttributeSet attrs = parser;
519 
520         // We parsed manifest tag earlier; just skip past it
521         PackageParser.parsePackageSplitNames(parser, attrs);
522 
523         int type;
524 
525         boolean foundApp = false;
526 
527         int outerDepth = parser.getDepth();
528         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
529             if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) {
530                 continue;
531             }
532 
533             final ParseResult result;
534             String tagName = parser.getName();
535             if (PackageParser.TAG_APPLICATION.equals(tagName)) {
536                 if (foundApp) {
537                     if (PackageParser.RIGID_PARSER) {
538                         result = input.error("<manifest> has more than one <application>");
539                     } else {
540                         Slog.w(TAG, "<manifest> has more than one <application>");
541                         result = input.success(null);
542                     }
543                 } else {
544                     foundApp = true;
545                     result = parseSplitApplication(input, pkg, res, parser, flags, splitIndex);
546                 }
547             } else {
548                 result = ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
549             }
550 
551             if (result.isError()) {
552                 return input.error(result);
553             }
554         }
555 
556         if (!foundApp) {
557             ParseResult<?> deferResult = input.deferError(
558                     "<manifest> does not contain an <application>", DeferredError.MISSING_APP_TAG);
559             if (deferResult.isError()) {
560                 return input.error(deferResult);
561             }
562         }
563 
564         return input.success(pkg);
565     }
566 
567     /**
568      * Parse the {@code application} XML tree at the current parse location in a
569      * <em>split APK</em> manifest.
570      * <p>
571      * Note that split APKs have many more restrictions on what they're capable
572      * of doing, so many valid features of a base APK have been carefully
573      * omitted here.
574      */
parseSplitApplication(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex)575     private ParseResult<ParsingPackage> parseSplitApplication(ParseInput input,
576             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex)
577             throws XmlPullParserException, IOException {
578         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
579         try {
580             pkg.setSplitHasCode(splitIndex, sa.getBoolean(
581                     R.styleable.AndroidManifestApplication_hasCode, true));
582 
583             final String classLoaderName = sa.getString(
584                     R.styleable.AndroidManifestApplication_classLoader);
585             if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName(
586                     classLoaderName)) {
587                 pkg.setSplitClassLoaderName(splitIndex, classLoaderName);
588             } else {
589                 return input.error("Invalid class loader name: " + classLoaderName);
590             }
591         } finally {
592             sa.recycle();
593         }
594         final int depth = parser.getDepth();
595         int type;
596         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
597                 && (type != XmlPullParser.END_TAG
598                 || parser.getDepth() > depth)) {
599             if (type != XmlPullParser.START_TAG) {
600                 continue;
601             }
602 
603             ParsedMainComponent mainComponent = null;
604 
605             final ParseResult result;
606             String tagName = parser.getName();
607             boolean isActivity = false;
608             switch (tagName) {
609                 case "activity":
610                     isActivity = true;
611                     // fall-through
612                 case "receiver":
613                     ParseResult<ParsedActivity> activityResult =
614                             ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
615                                     res,
616                                     parser, flags, PackageParser.sUseRoundIcon, input);
617                     if (activityResult.isSuccess()) {
618                         ParsedActivity activity = activityResult.getResult();
619                         if (isActivity) {
620                             pkg.addActivity(activity);
621                         } else {
622                             pkg.addReceiver(activity);
623                         }
624                         mainComponent = activity;
625                     }
626                     result = activityResult;
627                     break;
628                 case "service":
629                     ParseResult<ParsedService> serviceResult = ParsedServiceUtils.parseService(
630                             mSeparateProcesses, pkg, res, parser, flags,
631                             PackageParser.sUseRoundIcon, input);
632                     if (serviceResult.isSuccess()) {
633                         ParsedService service = serviceResult.getResult();
634                         pkg.addService(service);
635                         mainComponent = service;
636                     }
637                     result = serviceResult;
638                     break;
639                 case "provider":
640                     ParseResult<ParsedProvider> providerResult =
641                             ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
642                                     flags, PackageParser.sUseRoundIcon, input);
643                     if (providerResult.isSuccess()) {
644                         ParsedProvider provider = providerResult.getResult();
645                         pkg.addProvider(provider);
646                         mainComponent = provider;
647                     }
648                     result = providerResult;
649                     break;
650                 case "activity-alias":
651                     activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser,
652                             PackageParser.sUseRoundIcon, input);
653                     if (activityResult.isSuccess()) {
654                         ParsedActivity activity = activityResult.getResult();
655                         pkg.addActivity(activity);
656                         mainComponent = activity;
657                     }
658 
659                     result = activityResult;
660                     break;
661                 default:
662                     result = parseSplitBaseAppChildTags(input, tagName, pkg, res, parser);
663                     break;
664             }
665 
666             if (result.isError()) {
667                 return input.error(result);
668             }
669 
670             if (mainComponent != null && mainComponent.getSplitName() == null) {
671                 // If the loaded component did not specify a split, inherit the split name
672                 // based on the split it is defined in.
673                 // This is used to later load the correct split when starting this
674                 // component.
675                 mainComponent.setSplitName(pkg.getSplitNames()[splitIndex]);
676             }
677         }
678 
679         return input.success(pkg);
680     }
681 
682     /**
683      * For parsing non-MainComponents. Main ones have an order and some special handling which is
684      * done directly in {@link #parseSplitApplication(ParseInput, ParsingPackage, Resources,
685      * XmlResourceParser, int, int)}.
686      */
parseSplitBaseAppChildTags(ParseInput input, String tag, ParsingPackage pkg, Resources res, XmlResourceParser parser)687     private ParseResult parseSplitBaseAppChildTags(ParseInput input, String tag, ParsingPackage pkg,
688             Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
689         switch (tag) {
690             case "meta-data":
691                 // note: application meta-data is stored off to the side, so it can
692                 // remain null in the primary copy (we like to avoid extra copies because
693                 // it can be large)
694                 ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
695                         pkg.getMetaData(), input);
696                 if (metaDataResult.isSuccess()) {
697                     pkg.setMetaData(metaDataResult.getResult());
698                 }
699                 return metaDataResult;
700             case "uses-static-library":
701                 return parseUsesStaticLibrary(input, pkg, res, parser);
702             case "uses-library":
703                 return parseUsesLibrary(input, pkg, res, parser);
704             case "uses-package":
705                 // Dependencies for app installers; we don't currently try to
706                 // enforce this.
707                 return input.success(null);
708             default:
709                 return ParsingUtils.unknownTag("<application>", pkg, parser, input);
710         }
711     }
712 
parseBaseApkTags(ParseInput input, ParsingPackage pkg, TypedArray sa, Resources res, XmlResourceParser parser, int flags)713     private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
714             TypedArray sa, Resources res, XmlResourceParser parser, int flags)
715             throws XmlPullParserException, IOException {
716         ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
717         if (sharedUserResult.isError()) {
718             return sharedUserResult;
719         }
720 
721         pkg.setInstallLocation(anInteger(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION,
722                 R.styleable.AndroidManifest_installLocation, sa))
723                 .setTargetSandboxVersion(anInteger(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX,
724                         R.styleable.AndroidManifest_targetSandboxVersion, sa))
725                 /* Set the global "on SD card" flag */
726                 .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0);
727 
728         boolean foundApp = false;
729         final int depth = parser.getDepth();
730         int type;
731         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
732                 && (type != XmlPullParser.END_TAG
733                 || parser.getDepth() > depth)) {
734             if (type != XmlPullParser.START_TAG) {
735                 continue;
736             }
737 
738             String tagName = parser.getName();
739             final ParseResult result;
740 
741             // TODO(b/135203078): Convert to instance methods to share variables
742             // <application> has special logic, so it's handled outside the general method
743             if (PackageParser.TAG_APPLICATION.equals(tagName)) {
744                 if (foundApp) {
745                     if (PackageParser.RIGID_PARSER) {
746                         result = input.error("<manifest> has more than one <application>");
747                     } else {
748                         Slog.w(TAG, "<manifest> has more than one <application>");
749                         result = input.success(null);
750                     }
751                 } else {
752                     foundApp = true;
753                     result = parseBaseApplication(input, pkg, res, parser, flags);
754                 }
755             } else {
756                 result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
757             }
758 
759             if (result.isError()) {
760                 return input.error(result);
761             }
762         }
763 
764         if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) {
765             ParseResult<?> deferResult = input.deferError(
766                     "<manifest> does not contain an <application> or <instrumentation>",
767                     DeferredError.MISSING_APP_TAG);
768             if (deferResult.isError()) {
769                 return input.error(deferResult);
770             }
771         }
772 
773         if (!ParsedAttribution.isCombinationValid(pkg.getAttributions())) {
774             return input.error(
775                     INSTALL_PARSE_FAILED_BAD_MANIFEST,
776                     "Combination <feature> tags are not valid"
777             );
778         }
779 
780         convertNewPermissions(pkg);
781 
782         convertSplitPermissions(pkg);
783 
784         // At this point we can check if an application is not supporting densities and hence
785         // cannot be windowed / resized. Note that an SDK version of 0 is common for
786         // pre-Doughnut applications.
787         if (pkg.getTargetSdkVersion() < DONUT
788                 || (!pkg.isSupportsSmallScreens()
789                 && !pkg.isSupportsNormalScreens()
790                 && !pkg.isSupportsLargeScreens()
791                 && !pkg.isSupportsExtraLargeScreens()
792                 && !pkg.isResizeable()
793                 && !pkg.isAnyDensity())) {
794             adjustPackageToBeUnresizeableAndUnpipable(pkg);
795         }
796 
797         return input.success(pkg);
798     }
799 
parseBaseApkTag(String tag, ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)800     private ParseResult parseBaseApkTag(String tag, ParseInput input,
801             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
802             throws IOException, XmlPullParserException {
803         switch (tag) {
804             case PackageParser.TAG_OVERLAY:
805                 return parseOverlay(input, pkg, res, parser);
806             case PackageParser.TAG_KEY_SETS:
807                 return parseKeySets(input, pkg, res, parser);
808             case "feature": // TODO moltmann: Remove
809             case PackageParser.TAG_ATTRIBUTION:
810                 return parseAttribution(input, pkg, res, parser);
811             case PackageParser.TAG_PERMISSION_GROUP:
812                 return parsePermissionGroup(input, pkg, res, parser);
813             case PackageParser.TAG_PERMISSION:
814                 return parsePermission(input, pkg, res, parser);
815             case PackageParser.TAG_PERMISSION_TREE:
816                 return parsePermissionTree(input, pkg, res, parser);
817             case PackageParser.TAG_USES_PERMISSION:
818             case PackageParser.TAG_USES_PERMISSION_SDK_M:
819             case PackageParser.TAG_USES_PERMISSION_SDK_23:
820                 return parseUsesPermission(input, pkg, res, parser);
821             case PackageParser.TAG_USES_CONFIGURATION:
822                 return parseUsesConfiguration(input, pkg, res, parser);
823             case PackageParser.TAG_USES_FEATURE:
824                 return parseUsesFeature(input, pkg, res, parser);
825             case PackageParser.TAG_FEATURE_GROUP:
826                 return parseFeatureGroup(input, pkg, res, parser);
827             case PackageParser.TAG_USES_SDK:
828                 return parseUsesSdk(input, pkg, res, parser);
829             case PackageParser.TAG_SUPPORT_SCREENS:
830                 return parseSupportScreens(input, pkg, res, parser);
831             case PackageParser.TAG_PROTECTED_BROADCAST:
832                 return parseProtectedBroadcast(input, pkg, res, parser);
833             case PackageParser.TAG_INSTRUMENTATION:
834                 return parseInstrumentation(input, pkg, res, parser);
835             case PackageParser.TAG_ORIGINAL_PACKAGE:
836                 return parseOriginalPackage(input, pkg, res, parser);
837             case PackageParser.TAG_ADOPT_PERMISSIONS:
838                 return parseAdoptPermissions(input, pkg, res, parser);
839             case PackageParser.TAG_USES_GL_TEXTURE:
840             case PackageParser.TAG_COMPATIBLE_SCREENS:
841             case PackageParser.TAG_SUPPORTS_INPUT:
842             case PackageParser.TAG_EAT_COMMENT:
843                 // Just skip this tag
844                 XmlUtils.skipCurrentTag(parser);
845                 return input.success(pkg);
846             case PackageParser.TAG_RESTRICT_UPDATE:
847                 return parseRestrictUpdateHash(flags, input, pkg, res, parser);
848             case PackageParser.TAG_QUERIES:
849                 return parseQueries(input, pkg, res, parser);
850             default:
851                 return ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
852         }
853     }
854 
parseSharedUser(ParseInput input, ParsingPackage pkg, TypedArray sa)855     private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input,
856             ParsingPackage pkg, TypedArray sa) {
857         String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa);
858         if (TextUtils.isEmpty(str)) {
859             return input.success(pkg);
860         }
861 
862         if (!"android".equals(pkg.getPackageName())) {
863             ParseResult<?> nameResult = validateName(input, str, true, true);
864             if (nameResult.isError()) {
865                 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
866                         "<manifest> specifies bad sharedUserId name \"" + str + "\": "
867                                 + nameResult.getErrorMessage());
868             }
869         }
870 
871         return input.success(pkg
872                 .setSharedUserId(str.intern())
873                 .setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa)));
874     }
875 
parseKeySets(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)876     private static ParseResult<ParsingPackage> parseKeySets(ParseInput input,
877             ParsingPackage pkg, Resources res, XmlResourceParser parser)
878             throws XmlPullParserException, IOException {
879         // we've encountered the 'key-sets' tag
880         // all the keys and keysets that we want must be defined here
881         // so we're going to iterate over the parser and pull out the things we want
882         int outerDepth = parser.getDepth();
883         int currentKeySetDepth = -1;
884         int type;
885         String currentKeySet = null;
886         ArrayMap<String, PublicKey> publicKeys = new ArrayMap<>();
887         ArraySet<String> upgradeKeySets = new ArraySet<>();
888         ArrayMap<String, ArraySet<String>> definedKeySets = new ArrayMap<>();
889         ArraySet<String> improperKeySets = new ArraySet<>();
890         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
891                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
892             if (type == XmlPullParser.END_TAG) {
893                 if (parser.getDepth() == currentKeySetDepth) {
894                     currentKeySet = null;
895                     currentKeySetDepth = -1;
896                 }
897                 continue;
898             }
899             String tagName = parser.getName();
900             switch (tagName) {
901                 case "key-set": {
902                     if (currentKeySet != null) {
903                         return input.error("Improperly nested 'key-set' tag at "
904                                 + parser.getPositionDescription());
905                     }
906                     TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestKeySet);
907                     try {
908                         final String keysetName = sa.getNonResourceString(
909                                 R.styleable.AndroidManifestKeySet_name);
910                         definedKeySets.put(keysetName, new ArraySet<>());
911                         currentKeySet = keysetName;
912                         currentKeySetDepth = parser.getDepth();
913                     } finally {
914                         sa.recycle();
915                     }
916                 } break;
917                 case "public-key": {
918                     if (currentKeySet == null) {
919                         return input.error("Improperly nested 'key-set' tag at "
920                                 + parser.getPositionDescription());
921                     }
922                     TypedArray sa = res.obtainAttributes(parser,
923                             R.styleable.AndroidManifestPublicKey);
924                     try {
925                         final String publicKeyName = nonResString(
926                                 R.styleable.AndroidManifestPublicKey_name, sa);
927                         final String encodedKey = nonResString(
928                                 R.styleable.AndroidManifestPublicKey_value, sa);
929                         if (encodedKey == null && publicKeys.get(publicKeyName) == null) {
930                             return input.error("'public-key' " + publicKeyName
931                                     + " must define a public-key value on first use at "
932                                     + parser.getPositionDescription());
933                         } else if (encodedKey != null) {
934                             PublicKey currentKey = PackageParser.parsePublicKey(encodedKey);
935                             if (currentKey == null) {
936                                 Slog.w(TAG, "No recognized valid key in 'public-key' tag at "
937                                         + parser.getPositionDescription() + " key-set "
938                                         + currentKeySet
939                                         + " will not be added to the package's defined key-sets.");
940                                 improperKeySets.add(currentKeySet);
941                                 XmlUtils.skipCurrentTag(parser);
942                                 continue;
943                             }
944                             if (publicKeys.get(publicKeyName) == null
945                                     || publicKeys.get(publicKeyName).equals(currentKey)) {
946 
947                                 /* public-key first definition, or matches old definition */
948                                 publicKeys.put(publicKeyName, currentKey);
949                             } else {
950                                 return input.error("Value of 'public-key' " + publicKeyName
951                                         + " conflicts with previously defined value at "
952                                         + parser.getPositionDescription());
953                             }
954                         }
955                         definedKeySets.get(currentKeySet).add(publicKeyName);
956                         XmlUtils.skipCurrentTag(parser);
957                     } finally {
958                         sa.recycle();
959                     }
960                 } break;
961                 case "upgrade-key-set": {
962                     TypedArray sa = res.obtainAttributes(parser,
963                             R.styleable.AndroidManifestUpgradeKeySet);
964                     try {
965                         String name = sa.getNonResourceString(
966                                 R.styleable.AndroidManifestUpgradeKeySet_name);
967                         upgradeKeySets.add(name);
968                         XmlUtils.skipCurrentTag(parser);
969                     } finally {
970                         sa.recycle();
971                     }
972                 } break;
973                 default:
974                     ParseResult result = ParsingUtils.unknownTag("<key-sets>", pkg, parser,
975                             input);
976                     if (result.isError()) {
977                         return input.error(result);
978                     }
979                     break;
980             }
981         }
982         String packageName = pkg.getPackageName();
983         Set<String> publicKeyNames = publicKeys.keySet();
984         if (publicKeyNames.removeAll(definedKeySets.keySet())) {
985             return input.error("Package" + packageName
986                     + " AndroidManifest.xml 'key-set' and 'public-key' names must be distinct.");
987         }
988 
989         for (ArrayMap.Entry<String, ArraySet<String>> e : definedKeySets.entrySet()) {
990             final String keySetName = e.getKey();
991             if (e.getValue().size() == 0) {
992                 Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
993                         + "'key-set' " + keySetName + " has no valid associated 'public-key'."
994                         + " Not including in package's defined key-sets.");
995                 continue;
996             } else if (improperKeySets.contains(keySetName)) {
997                 Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
998                         + "'key-set' " + keySetName + " contained improper 'public-key'"
999                         + " tags. Not including in package's defined key-sets.");
1000                 continue;
1001             }
1002 
1003             for (String s : e.getValue()) {
1004                 pkg.addKeySet(keySetName, publicKeys.get(s));
1005             }
1006         }
1007         if (pkg.getKeySetMapping().keySet().containsAll(upgradeKeySets)) {
1008             pkg.setUpgradeKeySets(upgradeKeySets);
1009         } else {
1010             return input.error("Package" + packageName
1011                     + " AndroidManifest.xml does not define all 'upgrade-key-set's .");
1012         }
1013 
1014         return input.success(pkg);
1015     }
1016 
parseAttribution(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1017     private static ParseResult<ParsingPackage> parseAttribution(ParseInput input,
1018             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1019             throws IOException, XmlPullParserException {
1020         ParseResult<ParsedAttribution> result = ParsedAttributionUtils.parseAttribution(res,
1021                 parser, input);
1022         if (result.isError()) {
1023             return input.error(result);
1024         }
1025         return input.success(pkg.addAttribution(result.getResult()));
1026     }
1027 
parsePermissionGroup(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1028     private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input,
1029             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1030             throws XmlPullParserException, IOException {
1031         ParseResult<ParsedPermissionGroup> result = ParsedPermissionUtils.parsePermissionGroup(
1032                 pkg, res, parser, PackageParser.sUseRoundIcon, input);
1033         if (result.isError()) {
1034             return input.error(result);
1035         }
1036         return input.success(pkg.addPermissionGroup(result.getResult()));
1037     }
1038 
parsePermission(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1039     private static ParseResult<ParsingPackage> parsePermission(ParseInput input,
1040             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1041             throws XmlPullParserException, IOException {
1042         ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermission(
1043                 pkg, res, parser, PackageParser.sUseRoundIcon, input);
1044         if (result.isError()) {
1045             return input.error(result);
1046         }
1047         return input.success(pkg.addPermission(result.getResult()));
1048     }
1049 
parsePermissionTree(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1050     private static ParseResult<ParsingPackage> parsePermissionTree(ParseInput input,
1051             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1052             throws XmlPullParserException, IOException {
1053         ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermissionTree(
1054                 pkg, res, parser, PackageParser.sUseRoundIcon, input);
1055         if (result.isError()) {
1056             return input.error(result);
1057         }
1058         return input.success(pkg.addPermission(result.getResult()));
1059     }
1060 
parseUsesPermission(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1061     private ParseResult<ParsingPackage> parseUsesPermission(ParseInput input,
1062             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1063             throws IOException, XmlPullParserException {
1064         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesPermission);
1065         try {
1066             // Note: don't allow this value to be a reference to a resource
1067             // that may change.
1068             String name = sa.getNonResourceString(
1069                     R.styleable.AndroidManifestUsesPermission_name);
1070 
1071             int maxSdkVersion = 0;
1072             TypedValue val = sa.peekValue(
1073                     R.styleable.AndroidManifestUsesPermission_maxSdkVersion);
1074             if (val != null) {
1075                 if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {
1076                     maxSdkVersion = val.data;
1077                 }
1078             }
1079 
1080             final String requiredFeature = sa.getNonConfigurationString(
1081                     R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
1082 
1083             final String requiredNotfeature = sa.getNonConfigurationString(
1084                     R.styleable.AndroidManifestUsesPermission_requiredNotFeature,
1085                     0);
1086 
1087             XmlUtils.skipCurrentTag(parser);
1088 
1089             // Can only succeed from here on out
1090             ParseResult<ParsingPackage> success = input.success(pkg);
1091 
1092             if (name == null) {
1093                 return success;
1094             }
1095 
1096             if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
1097                 return success;
1098             }
1099 
1100             // Only allow requesting this permission if the platform supports the given feature.
1101             if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(
1102                     requiredFeature)) {
1103                 return success;
1104             }
1105 
1106             // Only allow requesting this permission if the platform doesn't support the given
1107             // feature.
1108             if (requiredNotfeature != null && mCallback != null
1109                     && mCallback.hasFeature(requiredNotfeature)) {
1110                 return success;
1111             }
1112 
1113             if (!pkg.getRequestedPermissions().contains(name)) {
1114                 pkg.addRequestedPermission(name.intern());
1115             } else {
1116                 Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
1117                         + name + " in package: " + pkg.getPackageName() + " at: "
1118                         + parser.getPositionDescription());
1119             }
1120 
1121             return success;
1122         } finally {
1123             sa.recycle();
1124         }
1125     }
1126 
parseUsesConfiguration(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1127     private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input,
1128             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
1129         ConfigurationInfo cPref = new ConfigurationInfo();
1130         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesConfiguration);
1131         try {
1132             cPref.reqTouchScreen = sa.getInt(
1133                     R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
1134                     Configuration.TOUCHSCREEN_UNDEFINED);
1135             cPref.reqKeyboardType = sa.getInt(
1136                     R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
1137                     Configuration.KEYBOARD_UNDEFINED);
1138             if (sa.getBoolean(
1139                     R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
1140                     false)) {
1141                 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
1142             }
1143             cPref.reqNavigation = sa.getInt(
1144                     R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
1145                     Configuration.NAVIGATION_UNDEFINED);
1146             if (sa.getBoolean(
1147                     R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
1148                     false)) {
1149                 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
1150             }
1151             pkg.addConfigPreference(cPref);
1152             return input.success(pkg);
1153         } finally {
1154             sa.recycle();
1155         }
1156     }
1157 
parseUsesFeature(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1158     private static ParseResult<ParsingPackage> parseUsesFeature(ParseInput input,
1159             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
1160         FeatureInfo fi = parseFeatureInfo(res, parser);
1161         pkg.addReqFeature(fi);
1162 
1163         if (fi.name == null) {
1164             ConfigurationInfo cPref = new ConfigurationInfo();
1165             cPref.reqGlEsVersion = fi.reqGlEsVersion;
1166             pkg.addConfigPreference(cPref);
1167         }
1168 
1169         return input.success(pkg);
1170     }
1171 
parseFeatureInfo(Resources res, AttributeSet attrs)1172     private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) {
1173         FeatureInfo fi = new FeatureInfo();
1174         TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestUsesFeature);
1175         try {
1176             // Note: don't allow this value to be a reference to a resource
1177             // that may change.
1178             fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name);
1179             fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0);
1180             if (fi.name == null) {
1181                 fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion,
1182                         FeatureInfo.GL_ES_VERSION_UNDEFINED);
1183             }
1184             if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) {
1185                 fi.flags |= FeatureInfo.FLAG_REQUIRED;
1186             }
1187             return fi;
1188         } finally {
1189             sa.recycle();
1190         }
1191     }
1192 
parseFeatureGroup(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1193     private static ParseResult<ParsingPackage> parseFeatureGroup(ParseInput input,
1194             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1195             throws IOException, XmlPullParserException {
1196         FeatureGroupInfo group = new FeatureGroupInfo();
1197         ArrayList<FeatureInfo> features = null;
1198         final int depth = parser.getDepth();
1199         int type;
1200         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1201                 && (type != XmlPullParser.END_TAG
1202                 || parser.getDepth() > depth)) {
1203             if (type != XmlPullParser.START_TAG) {
1204                 continue;
1205             }
1206 
1207             final String innerTagName = parser.getName();
1208             if (innerTagName.equals("uses-feature")) {
1209                 FeatureInfo featureInfo = parseFeatureInfo(res, parser);
1210                 // FeatureGroups are stricter and mandate that
1211                 // any <uses-feature> declared are mandatory.
1212                 featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
1213                 features = ArrayUtils.add(features, featureInfo);
1214             } else {
1215                 Slog.w(TAG,
1216                         "Unknown element under <feature-group>: " + innerTagName +
1217                                 " at " + pkg.getBaseCodePath() + " " +
1218                                 parser.getPositionDescription());
1219             }
1220         }
1221 
1222         if (features != null) {
1223             group.features = new FeatureInfo[features.size()];
1224             group.features = features.toArray(group.features);
1225         }
1226 
1227         pkg.addFeatureGroup(group);
1228         return input.success(pkg);
1229     }
1230 
parseUsesSdk(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1231     private static ParseResult<ParsingPackage> parseUsesSdk(ParseInput input,
1232             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1233             throws IOException, XmlPullParserException {
1234         if (PackageParser.SDK_VERSION > 0) {
1235             TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
1236             try {
1237                 int minVers = 1;
1238                 String minCode = null;
1239                 int targetVers = 0;
1240                 String targetCode = null;
1241 
1242                 TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion);
1243                 if (val != null) {
1244                     if (val.type == TypedValue.TYPE_STRING && val.string != null) {
1245                         minCode = val.string.toString();
1246                     } else {
1247                         // If it's not a string, it's an integer.
1248                         minVers = val.data;
1249                     }
1250                 }
1251 
1252                 val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
1253                 if (val != null) {
1254                     if (val.type == TypedValue.TYPE_STRING && val.string != null) {
1255                         targetCode = val.string.toString();
1256                         if (minCode == null) {
1257                             minCode = targetCode;
1258                         }
1259                     } else {
1260                         // If it's not a string, it's an integer.
1261                         targetVers = val.data;
1262                     }
1263                 } else {
1264                     targetVers = minVers;
1265                     targetCode = minCode;
1266                 }
1267 
1268                 ParseResult<Integer> targetSdkVersionResult = computeTargetSdkVersion(
1269                         targetVers, targetCode, PackageParser.SDK_CODENAMES, input);
1270                 if (targetSdkVersionResult.isError()) {
1271                     return input.error(targetSdkVersionResult);
1272                 }
1273 
1274                 int targetSdkVersion = targetSdkVersionResult.getResult();
1275 
1276                 ParseResult<?> deferResult =
1277                         input.enableDeferredError(pkg.getPackageName(), targetSdkVersion);
1278                 if (deferResult.isError()) {
1279                     return input.error(deferResult);
1280                 }
1281 
1282                 ParseResult<Integer> minSdkVersionResult = computeMinSdkVersion(minVers, minCode,
1283                         PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, input);
1284                 if (minSdkVersionResult.isError()) {
1285                     return input.error(minSdkVersionResult);
1286                 }
1287 
1288                 int minSdkVersion = minSdkVersionResult.getResult();
1289 
1290                 pkg.setMinSdkVersion(minSdkVersion)
1291                         .setTargetSdkVersion(targetSdkVersion);
1292 
1293                 int type;
1294                 final int innerDepth = parser.getDepth();
1295                 SparseIntArray minExtensionVersions = null;
1296                 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1297                         && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1298                     if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1299                         continue;
1300                     }
1301 
1302                     final ParseResult result;
1303                     if (parser.getName().equals("extension-sdk")) {
1304                         if (minExtensionVersions == null) {
1305                             minExtensionVersions = new SparseIntArray();
1306                         }
1307                         result = parseExtensionSdk(input, res, parser, minExtensionVersions);
1308                         XmlUtils.skipCurrentTag(parser);
1309                     } else {
1310                         result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input);
1311                     }
1312 
1313                     if (result.isError()) {
1314                         return input.error(result);
1315                     }
1316                 }
1317                 pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions));
1318             } finally {
1319                 sa.recycle();
1320             }
1321         }
1322         return input.success(pkg);
1323     }
1324 
1325     @Nullable
exactSizedCopyOfSparseArray(@ullable SparseIntArray input)1326     private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) {
1327         if (input == null) {
1328             return null;
1329         }
1330         SparseIntArray output = new SparseIntArray(input.size());
1331         for (int i = 0; i < input.size(); i++) {
1332             output.put(input.keyAt(i), input.valueAt(i));
1333         }
1334         return output;
1335     }
1336 
parseExtensionSdk( ParseInput input, Resources res, XmlResourceParser parser, SparseIntArray minExtensionVersions)1337     private static ParseResult<SparseIntArray> parseExtensionSdk(
1338             ParseInput input, Resources res, XmlResourceParser parser,
1339             SparseIntArray minExtensionVersions) {
1340         int sdkVersion;
1341         int minVersion;
1342         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk);
1343         try {
1344             sdkVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1);
1345             minVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_minExtensionVersion, -1);
1346         } finally {
1347             sa.recycle();
1348         }
1349 
1350         if (sdkVersion < 0) {
1351             return input.error(
1352                     PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1353                     "<extension-sdk> must specify an sdkVersion >= 0");
1354         }
1355         if (minVersion < 0) {
1356             return input.error(
1357                     PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1358                     "<extension-sdk> must specify minExtensionVersion >= 0");
1359         }
1360 
1361         try {
1362             int version = SdkExtensions.getExtensionVersion(sdkVersion);
1363             if (version < minVersion) {
1364                 return input.error(
1365                         PackageManager.INSTALL_FAILED_OLDER_SDK,
1366                         "Package requires " + sdkVersion + " extension version " + minVersion
1367                                 + " which exceeds device version " + version);
1368             }
1369         } catch (RuntimeException e) {
1370             return input.error(
1371                     PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1372                     "Specified sdkVersion " + sdkVersion + " is not valid");
1373         }
1374         minExtensionVersions.put(sdkVersion, minVersion);
1375         return input.success(minExtensionVersions);
1376     }
1377 
1378     /**
1379      * {@link ParseResult} version of
1380      * {@link PackageParser#computeMinSdkVersion(int, String, int, String[], String[])}
1381      */
computeMinSdkVersion(@ntRangefrom = 1) int minVers, @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion, @NonNull String[] platformSdkCodenames, @NonNull ParseInput input)1382     public static ParseResult<Integer> computeMinSdkVersion(@IntRange(from = 1) int minVers,
1383             @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion,
1384             @NonNull String[] platformSdkCodenames, @NonNull ParseInput input) {
1385         // If it's a release SDK, make sure we meet the minimum SDK requirement.
1386         if (minCode == null) {
1387             if (minVers <= platformSdkVersion) {
1388                 return input.success(minVers);
1389             }
1390 
1391             // We don't meet the minimum SDK requirement.
1392             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
1393                     "Requires newer sdk version #" + minVers
1394                             + " (current version is #" + platformSdkVersion + ")");
1395         }
1396 
1397         // If it's a pre-release SDK and the codename matches this platform, we
1398         // definitely meet the minimum SDK requirement.
1399         if (matchTargetCode(platformSdkCodenames, minCode)) {
1400             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
1401         }
1402 
1403         // Otherwise, we're looking at an incompatible pre-release SDK.
1404         if (platformSdkCodenames.length > 0) {
1405             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
1406                     "Requires development platform " + minCode
1407                             + " (current platform is any of "
1408                             + Arrays.toString(platformSdkCodenames) + ")");
1409         } else {
1410             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
1411                     "Requires development platform " + minCode
1412                             + " but this is a release platform.");
1413         }
1414     }
1415 
1416     /**
1417      * {@link ParseResult} version of
1418      * {@link PackageParser#computeTargetSdkVersion(int, String, String[], String[])}
1419      */
computeTargetSdkVersion(@ntRangefrom = 0) int targetVers, @Nullable String targetCode, @NonNull String[] platformSdkCodenames, @NonNull ParseInput input)1420     public static ParseResult<Integer> computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
1421             @Nullable String targetCode, @NonNull String[] platformSdkCodenames,
1422             @NonNull ParseInput input) {
1423         // If it's a release SDK, return the version number unmodified.
1424         if (targetCode == null) {
1425             return input.success(targetVers);
1426         }
1427 
1428         // If it's a pre-release SDK and the codename matches this platform, it
1429         // definitely targets this SDK.
1430         if (matchTargetCode(platformSdkCodenames, targetCode)) {
1431             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
1432         }
1433 
1434         // Otherwise, we're looking at an incompatible pre-release SDK.
1435         if (platformSdkCodenames.length > 0) {
1436             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
1437                     "Requires development platform " + targetCode
1438                             + " (current platform is any of "
1439                             + Arrays.toString(platformSdkCodenames) + ")");
1440         } else {
1441             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
1442                     "Requires development platform " + targetCode
1443                             + " but this is a release platform.");
1444         }
1445     }
1446 
1447     /**
1448      * Matches a given {@code targetCode} against a set of release codeNames. Target codes can
1449      * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form
1450      * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}).
1451      */
matchTargetCode(@onNull String[] codeNames, @NonNull String targetCode)1452     private static boolean matchTargetCode(@NonNull String[] codeNames,
1453             @NonNull String targetCode) {
1454         final String targetCodeName;
1455         final int targetCodeIdx = targetCode.indexOf('.');
1456         if (targetCodeIdx == -1) {
1457             targetCodeName = targetCode;
1458         } else {
1459             targetCodeName = targetCode.substring(0, targetCodeIdx);
1460         }
1461         return ArrayUtils.contains(codeNames, targetCodeName);
1462     }
1463 
parseRestrictUpdateHash(int flags, ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1464     private static ParseResult<ParsingPackage> parseRestrictUpdateHash(int flags, ParseInput input,
1465             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
1466         if ((flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
1467             TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestRestrictUpdate);
1468             try {
1469                 final String hash = sa.getNonConfigurationString(
1470                         R.styleable.AndroidManifestRestrictUpdate_hash,
1471                         0);
1472 
1473                 if (hash != null) {
1474                     final int hashLength = hash.length();
1475                     final byte[] hashBytes = new byte[hashLength / 2];
1476                     for (int i = 0; i < hashLength; i += 2) {
1477                         hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16)
1478                                 << 4)
1479                                 + Character.digit(hash.charAt(i + 1), 16));
1480                     }
1481                     pkg.setRestrictUpdateHash(hashBytes);
1482                 } else {
1483                     pkg.setRestrictUpdateHash(null);
1484                 }
1485             } finally {
1486                 sa.recycle();
1487             }
1488         }
1489         return input.success(pkg);
1490     }
1491 
parseQueries(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1492     private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
1493             Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
1494         final int depth = parser.getDepth();
1495         int type;
1496         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1497                 && (type != XmlPullParser.END_TAG
1498                 || parser.getDepth() > depth)) {
1499             if (type != XmlPullParser.START_TAG) {
1500                 continue;
1501             }
1502             if (parser.getName().equals("intent")) {
1503                 ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(null,
1504                         pkg, res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, input);
1505                 if (result.isError()) {
1506                     return input.error(result);
1507                 }
1508 
1509                 ParsedIntentInfo intentInfo = result.getResult();
1510 
1511                 Uri data = null;
1512                 String dataType = null;
1513                 String host = null;
1514                 final int numActions = intentInfo.countActions();
1515                 final int numSchemes = intentInfo.countDataSchemes();
1516                 final int numTypes = intentInfo.countDataTypes();
1517                 final int numHosts = intentInfo.getHosts().length;
1518                 if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {
1519                     return input.error("intent tags must contain either an action or data.");
1520                 }
1521                 if (numActions > 1) {
1522                     return input.error("intent tag may have at most one action.");
1523                 }
1524                 if (numTypes > 1) {
1525                     return input.error("intent tag may have at most one data type.");
1526                 }
1527                 if (numSchemes > 1) {
1528                     return input.error("intent tag may have at most one data scheme.");
1529                 }
1530                 if (numHosts > 1) {
1531                     return input.error("intent tag may have at most one data host.");
1532                 }
1533                 Intent intent = new Intent();
1534                 for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
1535                     intent.addCategory(intentInfo.getCategory(i));
1536                 }
1537                 if (numHosts == 1) {
1538                     host = intentInfo.getHosts()[0];
1539                 }
1540                 if (numSchemes == 1) {
1541                     data = new Uri.Builder()
1542                             .scheme(intentInfo.getDataScheme(0))
1543                             .authority(host)
1544                             .path(IntentFilter.WILDCARD_PATH)
1545                             .build();
1546                 }
1547                 if (numTypes == 1) {
1548                     dataType = intentInfo.getDataType(0);
1549                     // The dataType may have had the '/' removed for the dynamic mimeType feature.
1550                     // If we detect that case, we add the * back.
1551                     if (!dataType.contains("/")) {
1552                         dataType = dataType + "/*";
1553                     }
1554                     if (data == null) {
1555                         data = new Uri.Builder()
1556                                 .scheme("content")
1557                                 .authority(IntentFilter.WILDCARD)
1558                                 .path(IntentFilter.WILDCARD_PATH)
1559                                 .build();
1560                     }
1561                 }
1562                 intent.setDataAndType(data, dataType);
1563                 if (numActions == 1) {
1564                     intent.setAction(intentInfo.getAction(0));
1565                 }
1566                 pkg.addQueriesIntent(intent);
1567             } else if (parser.getName().equals("package")) {
1568                 final TypedArray sa = res.obtainAttributes(parser,
1569                         R.styleable.AndroidManifestQueriesPackage);
1570                 final String packageName = sa.getNonConfigurationString(
1571                         R.styleable.AndroidManifestQueriesPackage_name, 0);
1572                 if (TextUtils.isEmpty(packageName)) {
1573                     return input.error("Package name is missing from package tag.");
1574                 }
1575                 pkg.addQueriesPackage(packageName.intern());
1576             } else if (parser.getName().equals("provider")) {
1577                 final TypedArray sa = res.obtainAttributes(parser,
1578                         R.styleable.AndroidManifestQueriesProvider);
1579                 try {
1580                     final String authorities = sa.getNonConfigurationString(
1581                             R.styleable.AndroidManifestQueriesProvider_authorities, 0);
1582                     if (TextUtils.isEmpty(authorities)) {
1583                         return input.error(
1584                                 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1585                                 "Authority missing from provider tag."
1586                         );
1587                     }
1588                     StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";");
1589                     while (authoritiesTokenizer.hasMoreElements()) {
1590                         pkg.addQueriesProvider(authoritiesTokenizer.nextToken());
1591                     }
1592                 } finally {
1593                     sa.recycle();
1594                 }
1595             }
1596         }
1597         return input.success(pkg);
1598     }
1599 
1600     /**
1601      * Parse the {@code application} XML tree at the current parse location in a
1602      * <em>base APK</em> manifest.
1603      * <p>
1604      * When adding new features, carefully consider if they should also be
1605      * supported by split APKs.
1606      *
1607      * This method should avoid using a getter for fields set by this method. Prefer assigning
1608      * a local variable and using it. Otherwise there's an ordering problem which can be broken
1609      * if any code moves around.
1610      */
parseBaseApplication(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)1611     private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
1612             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
1613             throws XmlPullParserException, IOException {
1614         final String pkgName = pkg.getPackageName();
1615         int targetSdk = pkg.getTargetSdkVersion();
1616 
1617         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
1618         try {
1619             // TODO(b/135203078): Remove this and force unit tests to mock an empty manifest
1620             // This case can only happen in unit tests where we sometimes need to create fakes
1621             // of various package parser data structures.
1622             if (sa == null) {
1623                 return input.error("<application> does not contain any attributes");
1624             }
1625 
1626             String name = sa.getNonConfigurationString(R.styleable.AndroidManifestApplication_name,
1627                     0);
1628             if (name != null) {
1629                 String packageName = pkg.getPackageName();
1630                 String outInfoName = ParsingUtils.buildClassName(packageName, name);
1631                 if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
1632                     return input.error("<application> invalid android:name");
1633                 } else if (outInfoName == null) {
1634                     return input.error("Empty class name in package " + packageName);
1635                 }
1636 
1637                 pkg.setClassName(outInfoName);
1638             }
1639 
1640             TypedValue labelValue = sa.peekValue(R.styleable.AndroidManifestApplication_label);
1641             if (labelValue != null) {
1642                 pkg.setLabelRes(labelValue.resourceId);
1643                 if (labelValue.resourceId == 0) {
1644                     pkg.setNonLocalizedLabel(labelValue.coerceToString());
1645                 }
1646             }
1647 
1648             parseBaseAppBasicFlags(pkg, sa);
1649 
1650             String manageSpaceActivity = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
1651                     R.styleable.AndroidManifestApplication_manageSpaceActivity, sa);
1652             if (manageSpaceActivity != null) {
1653                 String manageSpaceActivityName = ParsingUtils.buildClassName(pkgName,
1654                         manageSpaceActivity);
1655 
1656                 if (manageSpaceActivityName == null) {
1657                     return input.error("Empty class name in package " + pkgName);
1658                 }
1659 
1660                 pkg.setManageSpaceActivityName(manageSpaceActivityName);
1661             }
1662 
1663             if (pkg.isAllowBackup()) {
1664                 // backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
1665                 // and restoreAnyVersion are only relevant if backup is possible for the
1666                 // given application.
1667                 String backupAgent = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
1668                         R.styleable.AndroidManifestApplication_backupAgent, sa);
1669                 if (backupAgent != null) {
1670                     String backupAgentName = ParsingUtils.buildClassName(pkgName, backupAgent);
1671                     if (backupAgentName == null) {
1672                         return input.error("Empty class name in package " + pkgName);
1673                     }
1674 
1675                     if (PackageParser.DEBUG_BACKUP) {
1676                         Slog.v(TAG, "android:backupAgent = " + backupAgentName
1677                                 + " from " + pkgName + "+" + backupAgent);
1678                     }
1679 
1680                     pkg.setBackupAgentName(backupAgentName)
1681                             .setKillAfterRestore(bool(true,
1682                                     R.styleable.AndroidManifestApplication_killAfterRestore, sa))
1683                             .setRestoreAnyVersion(bool(false,
1684                                     R.styleable.AndroidManifestApplication_restoreAnyVersion, sa))
1685                             .setFullBackupOnly(bool(false,
1686                                     R.styleable.AndroidManifestApplication_fullBackupOnly, sa))
1687                             .setBackupInForeground(bool(false,
1688                                     R.styleable.AndroidManifestApplication_backupInForeground, sa));
1689                 }
1690 
1691                 TypedValue v = sa.peekValue(
1692                         R.styleable.AndroidManifestApplication_fullBackupContent);
1693                 int fullBackupContent = 0;
1694 
1695                 if (v != null) {
1696                     fullBackupContent = v.resourceId;
1697 
1698                     if (v.resourceId == 0) {
1699                         if (PackageParser.DEBUG_BACKUP) {
1700                             Slog.v(TAG, "fullBackupContent specified as boolean=" +
1701                                     (v.data == 0 ? "false" : "true"));
1702                         }
1703                         // "false" => -1, "true" => 0
1704                         fullBackupContent = v.data == 0 ? -1 : 0;
1705                     }
1706 
1707                     pkg.setFullBackupContent(fullBackupContent);
1708                 }
1709                 if (PackageParser.DEBUG_BACKUP) {
1710                     Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
1711                 }
1712             }
1713 
1714             if (sa.getBoolean(R.styleable.AndroidManifestApplication_persistent, false)) {
1715                 // Check if persistence is based on a feature being present
1716                 final String requiredFeature = sa.getNonResourceString(R.styleable
1717                         .AndroidManifestApplication_persistentWhenFeatureAvailable);
1718                 pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature));
1719             }
1720 
1721             // TODO(b/135203078): Should parsing code be responsible for this? Maybe move to a
1722             //  util or just have PackageImpl return true if either flag is set
1723             // Debuggable implies profileable
1724             pkg.setProfileableByShell(pkg.isProfileableByShell() || pkg.isDebuggable());
1725 
1726             if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
1727                 pkg.setResizeableActivity(sa.getBoolean(
1728                         R.styleable.AndroidManifestApplication_resizeableActivity, true));
1729             } else {
1730                 pkg.setResizeableActivityViaSdkVersion(
1731                         targetSdk >= Build.VERSION_CODES.N);
1732             }
1733 
1734             String taskAffinity;
1735             if (targetSdk >= Build.VERSION_CODES.FROYO) {
1736                 taskAffinity = sa.getNonConfigurationString(
1737                         R.styleable.AndroidManifestApplication_taskAffinity,
1738                         Configuration.NATIVE_CONFIG_VERSION);
1739             } else {
1740                 // Some older apps have been seen to use a resource reference
1741                 // here that on older builds was ignored (with a warning).  We
1742                 // need to continue to do this for them so they don't break.
1743                 taskAffinity = sa.getNonResourceString(
1744                         R.styleable.AndroidManifestApplication_taskAffinity);
1745             }
1746 
1747             ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
1748                     pkgName, pkgName, taskAffinity, input);
1749             if (taskAffinityResult.isError()) {
1750                 return input.error(taskAffinityResult);
1751             }
1752 
1753             pkg.setTaskAffinity(taskAffinityResult.getResult());
1754             String factory = sa.getNonResourceString(
1755                     R.styleable.AndroidManifestApplication_appComponentFactory);
1756             if (factory != null) {
1757                 String appComponentFactory = ParsingUtils.buildClassName(pkgName, factory);
1758                 if (appComponentFactory == null) {
1759                     return input.error("Empty class name in package " + pkgName);
1760                 }
1761 
1762                 pkg.setAppComponentFactory(appComponentFactory);
1763             }
1764 
1765             CharSequence pname;
1766             if (targetSdk >= Build.VERSION_CODES.FROYO) {
1767                 pname = sa.getNonConfigurationString(
1768                         R.styleable.AndroidManifestApplication_process,
1769                         Configuration.NATIVE_CONFIG_VERSION);
1770             } else {
1771                 // Some older apps have been seen to use a resource reference
1772                 // here that on older builds was ignored (with a warning).  We
1773                 // need to continue to do this for them so they don't break.
1774                 pname = sa.getNonResourceString(
1775                         R.styleable.AndroidManifestApplication_process);
1776             }
1777             ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
1778                     pkgName, null, pname, flags, mSeparateProcesses, input);
1779             if (processNameResult.isError()) {
1780                 return input.error(processNameResult);
1781             }
1782 
1783             String processName = processNameResult.getResult();
1784             pkg.setProcessName(processName);
1785 
1786             if (pkg.isCantSaveState()) {
1787                 // A heavy-weight application can not be in a custom process.
1788                 // We can do direct compare because we intern all strings.
1789                 if (processName != null && !processName.equals(pkgName)) {
1790                     return input.error(
1791                             "cantSaveState applications can not use custom processes");
1792                 }
1793             }
1794 
1795             String classLoaderName = pkg.getClassLoaderName();
1796             if (classLoaderName != null
1797                     && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
1798                 return input.error("Invalid class loader name: " + classLoaderName);
1799             }
1800 
1801             pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
1802         } finally {
1803             sa.recycle();
1804         }
1805 
1806         boolean hasActivityOrder = false;
1807         boolean hasReceiverOrder = false;
1808         boolean hasServiceOrder = false;
1809         final int depth = parser.getDepth();
1810         int type;
1811         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1812                 && (type != XmlPullParser.END_TAG
1813                 || parser.getDepth() > depth)) {
1814             if (type != XmlPullParser.START_TAG) {
1815                 continue;
1816             }
1817 
1818             final ParseResult result;
1819             String tagName = parser.getName();
1820             boolean isActivity = false;
1821             switch (tagName) {
1822                 case "activity":
1823                     isActivity = true;
1824                     // fall-through
1825                 case "receiver":
1826                     ParseResult<ParsedActivity> activityResult =
1827                             ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
1828                                     res, parser, flags, PackageParser.sUseRoundIcon, input);
1829 
1830                     if (activityResult.isSuccess()) {
1831                         ParsedActivity activity = activityResult.getResult();
1832                         if (isActivity) {
1833                             hasActivityOrder |= (activity.getOrder() != 0);
1834                             pkg.addActivity(activity);
1835                         } else {
1836                             hasReceiverOrder |= (activity.getOrder() != 0);
1837                             pkg.addReceiver(activity);
1838                         }
1839                     }
1840 
1841                     result = activityResult;
1842                     break;
1843                 case "service":
1844                     ParseResult<ParsedService> serviceResult =
1845                             ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
1846                                     flags, PackageParser.sUseRoundIcon, input);
1847                     if (serviceResult.isSuccess()) {
1848                         ParsedService service = serviceResult.getResult();
1849                         hasServiceOrder |= (service.getOrder() != 0);
1850                         pkg.addService(service);
1851                     }
1852 
1853                     result = serviceResult;
1854                     break;
1855                 case "provider":
1856                     ParseResult<ParsedProvider> providerResult =
1857                             ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
1858                                     flags, PackageParser.sUseRoundIcon, input);
1859                     if (providerResult.isSuccess()) {
1860                         pkg.addProvider(providerResult.getResult());
1861                     }
1862 
1863                     result = providerResult;
1864                     break;
1865                 case "activity-alias":
1866                     activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
1867                             parser, PackageParser.sUseRoundIcon, input);
1868                     if (activityResult.isSuccess()) {
1869                         ParsedActivity activity = activityResult.getResult();
1870                         hasActivityOrder |= (activity.getOrder() != 0);
1871                         pkg.addActivity(activity);
1872                     }
1873 
1874                     result = activityResult;
1875                     break;
1876                 default:
1877                     result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
1878                     break;
1879             }
1880 
1881             if (result.isError()) {
1882                 return input.error(result);
1883             }
1884         }
1885 
1886         if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) {
1887             // Add a hidden app detail activity to normal apps which forwards user to App Details
1888             // page.
1889             ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
1890             if (a.isError()) {
1891                 // Error should be impossible here, as the only failure case as of SDK R is a
1892                 // string validation error on a constant ":app_details" string passed in by the
1893                 // parsing code itself. For this reason, this is just a hard failure instead of
1894                 // deferred.
1895                 return input.error(a);
1896             }
1897 
1898             pkg.addActivity(a.getResult());
1899         }
1900 
1901         if (hasActivityOrder) {
1902             pkg.sortActivities();
1903         }
1904         if (hasReceiverOrder) {
1905             pkg.sortReceivers();
1906         }
1907         if (hasServiceOrder) {
1908             pkg.sortServices();
1909         }
1910 
1911         // Must be run after the entire {@link ApplicationInfo} has been fully processed and after
1912         // every activity info has had a chance to set it from its attributes.
1913         setMaxAspectRatio(pkg);
1914         setMinAspectRatio(pkg);
1915         setSupportsSizeChanges(pkg);
1916 
1917         pkg.setHasDomainUrls(hasDomainURLs(pkg));
1918 
1919         return input.success(pkg);
1920     }
1921 
1922     /**
1923      * Collection of single-line, no (or little) logic assignments. Separated for readability.
1924      *
1925      * Flags are separated by type and by default value. They are sorted alphabetically within each
1926      * section.
1927      */
parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa)1928     private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) {
1929         int targetSdk = pkg.getTargetSdkVersion();
1930         //@formatter:off
1931         // CHECKSTYLE:off
1932         pkg
1933                 // Default true
1934                 .setAllowBackup(bool(true, R.styleable.AndroidManifestApplication_allowBackup, sa))
1935                 .setAllowClearUserData(bool(true, R.styleable.AndroidManifestApplication_allowClearUserData, sa))
1936                 .setAllowClearUserDataOnFailedRestore(bool(true, R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore, sa))
1937                 .setAllowNativeHeapPointerTagging(bool(true, R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, sa))
1938                 .setEnabled(bool(true, R.styleable.AndroidManifestApplication_enabled, sa))
1939                 .setExtractNativeLibs(bool(true, R.styleable.AndroidManifestApplication_extractNativeLibs, sa))
1940                 .setHasCode(bool(true, R.styleable.AndroidManifestApplication_hasCode, sa))
1941                 // Default false
1942                 .setAllowTaskReparenting(bool(false, R.styleable.AndroidManifestApplication_allowTaskReparenting, sa))
1943                 .setCantSaveState(bool(false, R.styleable.AndroidManifestApplication_cantSaveState, sa))
1944                 .setCrossProfile(bool(false, R.styleable.AndroidManifestApplication_crossProfile, sa))
1945                 .setDebuggable(bool(false, R.styleable.AndroidManifestApplication_debuggable, sa))
1946                 .setDefaultToDeviceProtectedStorage(bool(false, R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage, sa))
1947                 .setDirectBootAware(bool(false, R.styleable.AndroidManifestApplication_directBootAware, sa))
1948                 .setForceQueryable(bool(false, R.styleable.AndroidManifestApplication_forceQueryable, sa))
1949                 .setGame(bool(false, R.styleable.AndroidManifestApplication_isGame, sa))
1950                 .setHasFragileUserData(bool(false, R.styleable.AndroidManifestApplication_hasFragileUserData, sa))
1951                 .setLargeHeap(bool(false, R.styleable.AndroidManifestApplication_largeHeap, sa))
1952                 .setMultiArch(bool(false, R.styleable.AndroidManifestApplication_multiArch, sa))
1953                 .setPreserveLegacyExternalStorage(bool(false, R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage, sa))
1954                 .setRequiredForAllUsers(bool(false, R.styleable.AndroidManifestApplication_requiredForAllUsers, sa))
1955                 .setSupportsRtl(bool(false, R.styleable.AndroidManifestApplication_supportsRtl, sa))
1956                 .setTestOnly(bool(false, R.styleable.AndroidManifestApplication_testOnly, sa))
1957                 .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
1958                 .setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
1959                 .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
1960                 .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa))
1961                 // targetSdkVersion gated
1962                 .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
1963                 .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
1964                 .setRequestLegacyExternalStorage(bool(targetSdk < Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, sa))
1965                 .setUsesCleartextTraffic(bool(targetSdk < Build.VERSION_CODES.P, R.styleable.AndroidManifestApplication_usesCleartextTraffic, sa))
1966                 // Ints Default 0
1967                 .setUiOptions(anInt(R.styleable.AndroidManifestApplication_uiOptions, sa))
1968                 // Ints
1969                 .setCategory(anInt(ApplicationInfo.CATEGORY_UNDEFINED, R.styleable.AndroidManifestApplication_appCategory, sa))
1970                 // Floats Default 0f
1971                 .setMaxAspectRatio(aFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, sa))
1972                 .setMinAspectRatio(aFloat(R.styleable.AndroidManifestApplication_minAspectRatio, sa))
1973                 // Resource ID
1974                 .setBanner(resId(R.styleable.AndroidManifestApplication_banner, sa))
1975                 .setDescriptionRes(resId(R.styleable.AndroidManifestApplication_description, sa))
1976                 .setIconRes(resId(R.styleable.AndroidManifestApplication_icon, sa))
1977                 .setLogo(resId(R.styleable.AndroidManifestApplication_logo, sa))
1978                 .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa))
1979                 .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa))
1980                 .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa))
1981                 // Strings
1982                 .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa))
1983                 .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa))
1984                 .setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa))
1985                 .setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa))
1986                 // Non-Config String
1987                 .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa));
1988         // CHECKSTYLE:on
1989         //@formatter:on
1990     }
1991 
1992     /**
1993      * For parsing non-MainComponents. Main ones have an order and some special handling which is
1994      * done directly in {@link #parseBaseApplication(ParseInput, ParsingPackage, Resources,
1995      * XmlResourceParser, int)}.
1996      */
1997     private ParseResult parseBaseAppChildTag(ParseInput input, String tag, ParsingPackage pkg,
1998             Resources res, XmlResourceParser parser, int flags)
1999             throws IOException, XmlPullParserException {
2000         switch (tag) {
2001             case "meta-data":
2002                 // TODO(b/135203078): I have no idea what this comment means
2003                 // note: application meta-data is stored off to the side, so it can
2004                 // remain null in the primary copy (we like to avoid extra copies because
2005                 // it can be large)
2006                 ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
2007                         pkg.getMetaData(), input);
2008                 if (metaDataResult.isSuccess()) {
2009                     pkg.setMetaData(metaDataResult.getResult());
2010                 }
2011 
2012                 return metaDataResult;
2013             case "static-library":
2014                 return parseStaticLibrary(pkg, res, parser, input);
2015             case "library":
2016                 return parseLibrary(pkg, res, parser, input);
2017             case "uses-static-library":
2018                 return parseUsesStaticLibrary(input, pkg, res, parser);
2019             case "uses-library":
2020                 return parseUsesLibrary(input, pkg, res, parser);
2021             case "processes":
2022                 return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
2023             case "uses-package":
2024                 // Dependencies for app installers; we don't currently try to
2025                 // enforce this.
2026                 return input.success(null);
2027             case "profileable":
2028                 return parseProfileable(input, pkg, res, parser);
2029             default:
2030                 return ParsingUtils.unknownTag("<application>", pkg, parser, input);
2031         }
2032     }
2033 
2034     @NonNull
2035     private static ParseResult<ParsingPackage> parseStaticLibrary(
2036             ParsingPackage pkg, Resources res,
2037             XmlResourceParser parser, ParseInput input) {
2038         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestStaticLibrary);
2039         try {
2040             // Note: don't allow this value to be a reference to a resource
2041             // that may change.
2042             String lname = sa.getNonResourceString(
2043                     R.styleable.AndroidManifestStaticLibrary_name);
2044             final int version = sa.getInt(
2045                     R.styleable.AndroidManifestStaticLibrary_version, -1);
2046             final int versionMajor = sa.getInt(
2047                     R.styleable.AndroidManifestStaticLibrary_versionMajor,
2048                     0);
2049 
2050             // Since the app canot run without a static lib - fail if malformed
2051             if (lname == null || version < 0) {
2052                 return input.error("Bad static-library declaration name: " + lname
2053                         + " version: " + version);
2054             } else if (pkg.getSharedUserId() != null) {
2055                 return input.error(
2056                         PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
2057                         "sharedUserId not allowed in static shared library"
2058                 );
2059             } else if (pkg.getStaticSharedLibName() != null) {
2060                 return input.error("Multiple static-shared libs for package "
2061                         + pkg.getPackageName());
2062             }
2063 
2064             return input.success(pkg.setStaticSharedLibName(lname.intern())
2065                     .setStaticSharedLibVersion(
2066                             PackageInfo.composeLongVersionCode(versionMajor, version))
2067                     .setStaticSharedLibrary(true));
2068         } finally {
2069             sa.recycle();
2070         }
2071     }
2072 
2073     @NonNull
2074     private static ParseResult<ParsingPackage> parseLibrary(
2075             ParsingPackage pkg, Resources res,
2076             XmlResourceParser parser, ParseInput input) {
2077         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestLibrary);
2078         try {
2079             // Note: don't allow this value to be a reference to a resource
2080             // that may change.
2081             String lname = sa.getNonResourceString(R.styleable.AndroidManifestLibrary_name);
2082 
2083             if (lname != null) {
2084                 lname = lname.intern();
2085                 if (!ArrayUtils.contains(pkg.getLibraryNames(), lname)) {
2086                     pkg.addLibraryName(lname);
2087                 }
2088             }
2089             return input.success(pkg);
2090         } finally {
2091             sa.recycle();
2092         }
2093     }
2094 
2095     @NonNull
2096     private static ParseResult<ParsingPackage> parseUsesStaticLibrary(ParseInput input,
2097             ParsingPackage pkg, Resources res, XmlResourceParser parser)
2098             throws XmlPullParserException, IOException {
2099         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesStaticLibrary);
2100         try {
2101             // Note: don't allow this value to be a reference to a resource that may change.
2102             String lname = sa.getNonResourceString(
2103                     R.styleable.AndroidManifestUsesLibrary_name);
2104             final int version = sa.getInt(
2105                     R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
2106             String certSha256Digest = sa.getNonResourceString(R.styleable
2107                     .AndroidManifestUsesStaticLibrary_certDigest);
2108 
2109             // Since an APK providing a static shared lib can only provide the lib - fail if
2110             // malformed
2111             if (lname == null || version < 0 || certSha256Digest == null) {
2112                 return input.error("Bad uses-static-library declaration name: " + lname
2113                         + " version: " + version + " certDigest" + certSha256Digest);
2114             }
2115 
2116             // Can depend only on one version of the same library
2117             List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
2118             if (usesStaticLibraries.contains(lname)) {
2119                 return input.error(
2120                         "Depending on multiple versions of static library " + lname);
2121             }
2122 
2123             lname = lname.intern();
2124             // We allow ":" delimiters in the SHA declaration as this is the format
2125             // emitted by the certtool making it easy for developers to copy/paste.
2126             certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
2127 
2128             // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
2129             String[] additionalCertSha256Digests = EmptyArray.STRING;
2130             if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
2131                 ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
2132                 if (certResult.isError()) {
2133                     return input.error(certResult);
2134                 }
2135                 additionalCertSha256Digests = certResult.getResult();
2136             }
2137 
2138             final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
2139             certSha256Digests[0] = certSha256Digest;
2140             System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
2141                     1, additionalCertSha256Digests.length);
2142 
2143             return input.success(pkg.addUsesStaticLibrary(lname)
2144                     .addUsesStaticLibraryVersion(version)
2145                     .addUsesStaticLibraryCertDigests(certSha256Digests));
2146         } finally {
2147             sa.recycle();
2148         }
2149     }
2150 
2151     @NonNull
2152     private static ParseResult<ParsingPackage> parseUsesLibrary(ParseInput input,
2153             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2154         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary);
2155         try {
2156             // Note: don't allow this value to be a reference to a resource
2157             // that may change.
2158             String lname = sa.getNonResourceString(R.styleable.AndroidManifestUsesLibrary_name);
2159             boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesLibrary_required, true);
2160 
2161             if (lname != null) {
2162                 lname = lname.intern();
2163                 if (req) {
2164                     // Upgrade to treat as stronger constraint
2165                     pkg.addUsesLibrary(lname)
2166                             .removeUsesOptionalLibrary(lname);
2167                 } else {
2168                     // Ignore if someone already defined as required
2169                     if (!ArrayUtils.contains(pkg.getUsesLibraries(), lname)) {
2170                         pkg.addUsesOptionalLibrary(lname);
2171                     }
2172                 }
2173             }
2174 
2175             return input.success(pkg);
2176         } finally {
2177             sa.recycle();
2178         }
2179     }
2180 
2181     @NonNull
2182     private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
2183             Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
2184             throws IOException, XmlPullParserException {
2185         ParseResult<ArrayMap<String, ParsedProcess>> result =
2186                 ParsedProcessUtils.parseProcesses(separateProcesses, pkg, res, parser, flags,
2187                         input);
2188         if (result.isError()) {
2189             return input.error(result);
2190         }
2191 
2192         return input.success(pkg.setProcesses(result.getResult()));
2193     }
2194 
2195     @NonNull
2196     private static ParseResult<ParsingPackage> parseProfileable(ParseInput input,
2197             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2198         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProfileable);
2199         try {
2200             return input.success(pkg.setProfileableByShell(pkg.isProfileableByShell()
2201                     || bool(false, R.styleable.AndroidManifestProfileable_shell, sa)));
2202         } finally {
2203             sa.recycle();
2204         }
2205     }
2206 
2207     private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input,
2208             Resources resources, XmlResourceParser parser)
2209             throws XmlPullParserException, IOException {
2210         String[] certSha256Digests = EmptyArray.STRING;
2211         final int depth = parser.getDepth();
2212         int type;
2213         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
2214                 && (type != XmlPullParser.END_TAG
2215                 || parser.getDepth() > depth)) {
2216             if (type != XmlPullParser.START_TAG) {
2217                 continue;
2218             }
2219 
2220             final String nodeName = parser.getName();
2221             if (nodeName.equals("additional-certificate")) {
2222                 TypedArray sa = resources.obtainAttributes(parser,
2223                         R.styleable.AndroidManifestAdditionalCertificate);
2224                 try {
2225                     String certSha256Digest = sa.getNonResourceString(
2226                             R.styleable.AndroidManifestAdditionalCertificate_certDigest);
2227 
2228                     if (TextUtils.isEmpty(certSha256Digest)) {
2229                         return input.error("Bad additional-certificate declaration with empty"
2230                                 + " certDigest:" + certSha256Digest);
2231                     }
2232 
2233 
2234                     // We allow ":" delimiters in the SHA declaration as this is the format
2235                     // emitted by the certtool making it easy for developers to copy/paste.
2236                     certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
2237                     certSha256Digests = ArrayUtils.appendElement(String.class,
2238                             certSha256Digests, certSha256Digest);
2239                 } finally {
2240                     sa.recycle();
2241                 }
2242             }
2243         }
2244 
2245         return input.success(certSha256Digests);
2246     }
2247 
2248     /**
2249      * Generate activity object that forwards user to App Details page automatically.
2250      * This activity should be invisible to user and user should not know or see it.
2251      */
2252     @NonNull
2253     private static ParseResult<ParsedActivity> generateAppDetailsHiddenActivity(ParseInput input,
2254             ParsingPackage pkg) {
2255         String packageName = pkg.getPackageName();
2256         ParseResult<String> result = ComponentParseUtils.buildTaskAffinityName(
2257                 packageName, packageName, ":app_details", input);
2258         if (result.isError()) {
2259             return input.error(result);
2260         }
2261 
2262         String taskAffinity = result.getResult();
2263 
2264         // Build custom App Details activity info instead of parsing it from xml
2265         return input.success(ParsedActivity.makeAppDetailsActivity(packageName,
2266                 pkg.getProcessName(), pkg.getUiOptions(), taskAffinity,
2267                 pkg.isBaseHardwareAccelerated()));
2268     }
2269 
2270     /**
2271      * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
2272      */
2273     private static boolean hasDomainURLs(ParsingPackage pkg) {
2274         final List<ParsedActivity> activities = pkg.getActivities();
2275         final int activitiesSize = activities.size();
2276         for (int index = 0; index < activitiesSize; index++) {
2277             ParsedActivity activity = activities.get(index);
2278             List<ParsedIntentInfo> filters = activity.getIntents();
2279             final int filtersSize = filters.size();
2280             for (int filtersIndex = 0; filtersIndex < filtersSize; filtersIndex++) {
2281                 ParsedIntentInfo aii = filters.get(filtersIndex);
2282                 if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
2283                 if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
2284                 if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
2285                         aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
2286                     return true;
2287                 }
2288             }
2289         }
2290         return false;
2291     }
2292 
2293     /**
2294      * Sets the max aspect ratio of every child activity that doesn't already have an aspect
2295      * ratio set.
2296      */
2297     private static void setMaxAspectRatio(ParsingPackage pkg) {
2298         // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
2299         // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
2300         float maxAspectRatio = pkg.getTargetSdkVersion() < O
2301                 ? PackageParser.DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
2302 
2303         float packageMaxAspectRatio = pkg.getMaxAspectRatio();
2304         if (packageMaxAspectRatio != 0) {
2305             // Use the application max aspect ration as default if set.
2306             maxAspectRatio = packageMaxAspectRatio;
2307         } else {
2308             Bundle appMetaData = pkg.getMetaData();
2309             if (appMetaData != null && appMetaData.containsKey(
2310                     PackageParser.METADATA_MAX_ASPECT_RATIO)) {
2311                 maxAspectRatio = appMetaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
2312                         maxAspectRatio);
2313             }
2314         }
2315 
2316         List<ParsedActivity> activities = pkg.getActivities();
2317         int activitiesSize = activities.size();
2318         for (int index = 0; index < activitiesSize; index++) {
2319             ParsedActivity activity = activities.get(index);
2320             // If the max aspect ratio for the activity has already been set, skip.
2321             if (activity.getMaxAspectRatio() != null) {
2322                 continue;
2323             }
2324 
2325             // By default we prefer to use a values defined on the activity directly than values
2326             // defined on the application. We do not check the styled attributes on the activity
2327             // as it would have already been set when we processed the activity. We wait to
2328             // process the meta data here since this method is called at the end of processing
2329             // the application and all meta data is guaranteed.
2330             final float activityAspectRatio = activity.getMetaData() != null
2331                     ? activity.getMetaData().getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
2332                     maxAspectRatio)
2333                     : maxAspectRatio;
2334 
2335             activity.setMaxAspectRatio(activity.getResizeMode(), activityAspectRatio);
2336         }
2337     }
2338 
2339     /**
2340      * Sets the min aspect ratio of every child activity that doesn't already have an aspect
2341      * ratio set.
2342      */
2343     private void setMinAspectRatio(ParsingPackage pkg) {
2344         final float minAspectRatio;
2345         float packageMinAspectRatio = pkg.getMinAspectRatio();
2346         if (packageMinAspectRatio != 0) {
2347             // Use the application max aspect ration as default if set.
2348             minAspectRatio = packageMinAspectRatio;
2349         } else {
2350             // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater.
2351             // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD,
2352             // except for watches which always supported 1:1.
2353             minAspectRatio = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.Q
2354                     ? 0
2355                     : (mCallback != null && mCallback.hasFeature(FEATURE_WATCH))
2356                             ? PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH
2357                             : PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO;
2358         }
2359 
2360         List<ParsedActivity> activities = pkg.getActivities();
2361         int activitiesSize = activities.size();
2362         for (int index = 0; index < activitiesSize; index++) {
2363             ParsedActivity activity = activities.get(index);
2364             if (activity.getMinAspectRatio() == null) {
2365                 activity.setMinAspectRatio(activity.getResizeMode(), minAspectRatio);
2366             }
2367         }
2368     }
2369 
2370     private void setSupportsSizeChanges(ParsingPackage pkg) {
2371         final Bundle appMetaData = pkg.getMetaData();
2372         final boolean supportsSizeChanges = appMetaData != null
2373                 && appMetaData.getBoolean(PackageParser.METADATA_SUPPORTS_SIZE_CHANGES, false);
2374 
2375         List<ParsedActivity> activities = pkg.getActivities();
2376         int activitiesSize = activities.size();
2377         for (int index = 0; index < activitiesSize; index++) {
2378             ParsedActivity activity = activities.get(index);
2379             if (supportsSizeChanges || (activity.getMetaData() != null
2380                     && activity.getMetaData().getBoolean(
2381                             PackageParser.METADATA_SUPPORTS_SIZE_CHANGES, false))) {
2382                 activity.setSupportsSizeChanges(true);
2383             }
2384         }
2385     }
2386 
2387     private static ParseResult<ParsingPackage> parseOverlay(ParseInput input, ParsingPackage pkg,
2388             Resources res, XmlResourceParser parser) {
2389         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay);
2390         try {
2391             String target = sa.getString(R.styleable.AndroidManifestResourceOverlay_targetPackage);
2392             int priority = anInt(0, R.styleable.AndroidManifestResourceOverlay_priority, sa);
2393 
2394             if (target == null) {
2395                 return input.error("<overlay> does not specify a target package");
2396             } else if (priority < 0 || priority > 9999) {
2397                 return input.error("<overlay> priority must be between 0 and 9999");
2398             }
2399 
2400             // check to see if overlay should be excluded based on system property condition
2401             String propName = sa.getString(
2402                     R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName);
2403             String propValue = sa.getString(
2404                     R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
2405             if (!PackageParser.checkRequiredSystemProperties(propName, propValue)) {
2406                 String message = "Skipping target and overlay pair " + target + " and "
2407                         + pkg.getBaseCodePath()
2408                         + ": overlay ignored due to required system property: "
2409                         + propName + " with value: " + propValue;
2410                 Slog.i(TAG, message);
2411                 return input.skip(message);
2412             }
2413 
2414             return input.success(pkg.setOverlay(true)
2415                     .setOverlayTarget(target)
2416                     .setOverlayPriority(priority)
2417                     .setOverlayTargetName(
2418                             sa.getString(R.styleable.AndroidManifestResourceOverlay_targetName))
2419                     .setOverlayCategory(
2420                             sa.getString(R.styleable.AndroidManifestResourceOverlay_category))
2421                     .setOverlayIsStatic(
2422                             bool(false, R.styleable.AndroidManifestResourceOverlay_isStatic, sa)));
2423         } finally {
2424             sa.recycle();
2425         }
2426     }
2427 
2428     private static ParseResult<ParsingPackage> parseProtectedBroadcast(ParseInput input,
2429             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2430         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProtectedBroadcast);
2431         try {
2432             // Note: don't allow this value to be a reference to a resource
2433             // that may change.
2434             String name = nonResString(R.styleable.AndroidManifestProtectedBroadcast_name, sa);
2435             if (name != null) {
2436                 pkg.addProtectedBroadcast(name);
2437             }
2438             return input.success(pkg);
2439         } finally {
2440             sa.recycle();
2441         }
2442     }
2443 
2444     private static ParseResult<ParsingPackage> parseSupportScreens(ParseInput input,
2445             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2446         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSupportsScreens);
2447         try {
2448             int requiresSmallestWidthDp = anInt(0,
2449                     R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, sa);
2450             int compatibleWidthLimitDp = anInt(0,
2451                     R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, sa);
2452             int largestWidthLimitDp = anInt(0,
2453                     R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, sa);
2454 
2455             // This is a trick to get a boolean and still able to detect
2456             // if a value was actually set.
2457             return input.success(pkg
2458                     .setSupportsSmallScreens(
2459                             anInt(1, R.styleable.AndroidManifestSupportsScreens_smallScreens, sa))
2460                     .setSupportsNormalScreens(
2461                             anInt(1, R.styleable.AndroidManifestSupportsScreens_normalScreens, sa))
2462                     .setSupportsLargeScreens(
2463                             anInt(1, R.styleable.AndroidManifestSupportsScreens_largeScreens, sa))
2464                     .setSupportsExtraLargeScreens(
2465                             anInt(1, R.styleable.AndroidManifestSupportsScreens_xlargeScreens, sa))
2466                     .setResizeable(
2467                             anInt(1, R.styleable.AndroidManifestSupportsScreens_resizeable, sa))
2468                     .setAnyDensity(
2469                             anInt(1, R.styleable.AndroidManifestSupportsScreens_anyDensity, sa))
2470                     .setRequiresSmallestWidthDp(requiresSmallestWidthDp)
2471                     .setCompatibleWidthLimitDp(compatibleWidthLimitDp)
2472                     .setLargestWidthLimitDp(largestWidthLimitDp));
2473         } finally {
2474             sa.recycle();
2475         }
2476     }
2477 
2478     private static ParseResult<ParsingPackage> parseInstrumentation(ParseInput input,
2479             ParsingPackage pkg, Resources res, XmlResourceParser parser)
2480             throws XmlPullParserException, IOException {
2481         ParseResult<ParsedInstrumentation> result = ParsedInstrumentationUtils.parseInstrumentation(
2482                 pkg, res, parser, PackageParser.sUseRoundIcon, input);
2483         if (result.isError()) {
2484             return input.error(result);
2485         }
2486         return input.success(pkg.addInstrumentation(result.getResult()));
2487     }
2488 
2489     private static ParseResult<ParsingPackage> parseOriginalPackage(ParseInput input,
2490             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2491         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
2492         try {
2493             String orig = sa.getNonConfigurationString(
2494                     R.styleable.AndroidManifestOriginalPackage_name,
2495                     0);
2496             if (!pkg.getPackageName().equals(orig)) {
2497                 if (pkg.getOriginalPackages().isEmpty()) {
2498                     pkg.setRealPackage(pkg.getPackageName());
2499                 }
2500                 pkg.addOriginalPackage(orig);
2501             }
2502             return input.success(pkg);
2503         } finally {
2504             sa.recycle();
2505         }
2506     }
2507 
2508     private static ParseResult<ParsingPackage> parseAdoptPermissions(ParseInput input,
2509             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2510         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
2511         try {
2512             String name = nonConfigString(0, R.styleable.AndroidManifestOriginalPackage_name, sa);
2513             if (name != null) {
2514                 pkg.addAdoptPermission(name);
2515             }
2516             return input.success(pkg);
2517         } finally {
2518             sa.recycle();
2519         }
2520     }
2521 
2522     private static void convertNewPermissions(ParsingPackage pkg) {
2523         final int NP = PackageParser.NEW_PERMISSIONS.length;
2524         StringBuilder newPermsMsg = null;
2525         for (int ip = 0; ip < NP; ip++) {
2526             final PackageParser.NewPermissionInfo npi
2527                     = PackageParser.NEW_PERMISSIONS[ip];
2528             if (pkg.getTargetSdkVersion() >= npi.sdkVersion) {
2529                 break;
2530             }
2531             if (!pkg.getRequestedPermissions().contains(npi.name)) {
2532                 if (newPermsMsg == null) {
2533                     newPermsMsg = new StringBuilder(128);
2534                     newPermsMsg.append(pkg.getPackageName());
2535                     newPermsMsg.append(": compat added ");
2536                 } else {
2537                     newPermsMsg.append(' ');
2538                 }
2539                 newPermsMsg.append(npi.name);
2540                 pkg.addRequestedPermission(npi.name)
2541                         .addImplicitPermission(npi.name);
2542             }
2543         }
2544         if (newPermsMsg != null) {
2545             Slog.i(TAG, newPermsMsg.toString());
2546         }
2547     }
2548 
2549     private static void convertSplitPermissions(ParsingPackage pkg) {
2550         List<SplitPermissionInfoParcelable> splitPermissions;
2551 
2552         try {
2553             splitPermissions = ActivityThread.getPermissionManager().getSplitPermissions();
2554         } catch (RemoteException e) {
2555             throw e.rethrowFromSystemServer();
2556         }
2557 
2558         final int listSize = splitPermissions.size();
2559         for (int is = 0; is < listSize; is++) {
2560             final SplitPermissionInfoParcelable spi = splitPermissions.get(is);
2561             List<String> requestedPermissions = pkg.getRequestedPermissions();
2562             if (pkg.getTargetSdkVersion() >= spi.getTargetSdk()
2563                     || !requestedPermissions.contains(spi.getSplitPermission())) {
2564                 continue;
2565             }
2566             final List<String> newPerms = spi.getNewPermissions();
2567             for (int in = 0; in < newPerms.size(); in++) {
2568                 final String perm = newPerms.get(in);
2569                 if (!requestedPermissions.contains(perm)) {
2570                     pkg.addRequestedPermission(perm)
2571                             .addImplicitPermission(perm);
2572                 }
2573             }
2574         }
2575     }
2576 
2577     /**
2578      * This is a pre-density application which will get scaled - instead of being pixel perfect.
2579      * This type of application is not resizable.
2580      *
2581      * @param pkg The package which needs to be marked as unresizable.
2582      */
2583     private static void adjustPackageToBeUnresizeableAndUnpipable(ParsingPackage pkg) {
2584         List<ParsedActivity> activities = pkg.getActivities();
2585         int activitiesSize = activities.size();
2586         for (int index = 0; index < activitiesSize; index++) {
2587             ParsedActivity activity = activities.get(index);
2588             activity.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
2589                     .setFlags(activity.getFlags() & ~FLAG_SUPPORTS_PICTURE_IN_PICTURE);
2590         }
2591     }
2592 
2593     private static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
2594             boolean requireFilename) {
2595         final int N = name.length();
2596         boolean hasSep = false;
2597         boolean front = true;
2598         for (int i = 0; i < N; i++) {
2599             final char c = name.charAt(i);
2600             if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2601                 front = false;
2602                 continue;
2603             }
2604             if (!front) {
2605                 if ((c >= '0' && c <= '9') || c == '_') {
2606                     continue;
2607                 }
2608             }
2609             if (c == '.') {
2610                 hasSep = true;
2611                 front = true;
2612                 continue;
2613             }
2614             return input.error("bad character '" + c + "'");
2615         }
2616         if (requireFilename && !FileUtils.isValidExtFilename(name)) {
2617             return input.error("Invalid filename");
2618         }
2619         return hasSep || !requireSeparator
2620                 ? input.success(null)
2621                 : input.error("must have at least one '.' separator");
2622     }
2623 
2624     public static ParseResult<Bundle> parseMetaData(ParsingPackage pkg, Resources res,
2625             XmlResourceParser parser, Bundle data, ParseInput input) {
2626         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData);
2627         try {
2628             if (data == null) {
2629                 data = new Bundle();
2630             }
2631 
2632             String name = TextUtils.safeIntern(
2633                     nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa));
2634             if (name == null) {
2635                 return input.error("<meta-data> requires an android:name attribute");
2636             }
2637 
2638             TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource);
2639             if (v != null && v.resourceId != 0) {
2640                 //Slog.i(TAG, "Meta data ref " + name + ": " + v);
2641                 data.putInt(name, v.resourceId);
2642             } else {
2643                 v = sa.peekValue(R.styleable.AndroidManifestMetaData_value);
2644                 //Slog.i(TAG, "Meta data " + name + ": " + v);
2645                 if (v != null) {
2646                     if (v.type == TypedValue.TYPE_STRING) {
2647                         CharSequence cs = v.coerceToString();
2648                         data.putString(name, cs != null ? cs.toString() : null);
2649                     } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
2650                         data.putBoolean(name, v.data != 0);
2651                     } else if (v.type >= TypedValue.TYPE_FIRST_INT
2652                             && v.type <= TypedValue.TYPE_LAST_INT) {
2653                         data.putInt(name, v.data);
2654                     } else if (v.type == TypedValue.TYPE_FLOAT) {
2655                         data.putFloat(name, v.getFloat());
2656                     } else {
2657                         if (!PackageParser.RIGID_PARSER) {
2658                             Slog.w(TAG,
2659                                     "<meta-data> only supports string, integer, float, color, "
2660                                             + "boolean, and resource reference types: "
2661                                             + parser.getName() + " at "
2662                                             + pkg.getBaseCodePath() + " "
2663                                             + parser.getPositionDescription());
2664                         } else {
2665                             return input.error("<meta-data> only supports string, integer, float, "
2666                                     + "color, boolean, and resource reference types");
2667                         }
2668                     }
2669                 } else {
2670                     return input.error("<meta-data> requires an android:value "
2671                             + "or android:resource attribute");
2672                 }
2673             }
2674             return input.success(data);
2675         } finally {
2676             sa.recycle();
2677         }
2678     }
2679 
2680     /**
2681      * Collect certificates from all the APKs described in the given package. Also asserts that
2682      * all APK contents are signed correctly and consistently.
2683      *
2684      * TODO(b/155513789): Remove this in favor of collecting certificates during the original parse
2685      *  call if requested. Leaving this as an optional method for the caller means we have to
2686      *  construct a dummy ParseInput.
2687      */
2688     @CheckResult
2689     public static SigningDetails getSigningDetails(ParsingPackageRead pkg, boolean skipVerify)
2690             throws PackageParserException {
2691         SigningDetails signingDetails = SigningDetails.UNKNOWN;
2692 
2693         ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
2694 
2695         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
2696         try {
2697             ParseResult<SigningDetails> result = getSigningDetails(
2698                     input,
2699                     pkg.getBaseCodePath(),
2700                     skipVerify,
2701                     pkg.isStaticSharedLibrary(),
2702                     signingDetails,
2703                     pkg.getTargetSdkVersion()
2704             );
2705             if (result.isError()) {
2706                 throw new PackageParser.PackageParserException(result.getErrorCode(),
2707                         result.getErrorMessage(), result.getException());
2708             }
2709 
2710             signingDetails = result.getResult();
2711 
2712             String[] splitCodePaths = pkg.getSplitCodePaths();
2713             if (!ArrayUtils.isEmpty(splitCodePaths)) {
2714                 for (int i = 0; i < splitCodePaths.length; i++) {
2715                     result = getSigningDetails(
2716                             input,
2717                             splitCodePaths[i],
2718                             skipVerify,
2719                             pkg.isStaticSharedLibrary(),
2720                             signingDetails,
2721                             pkg.getTargetSdkVersion()
2722                     );
2723                     if (result.isError()) {
2724                         throw new PackageParser.PackageParserException(result.getErrorCode(),
2725                                 result.getErrorMessage(), result.getException());
2726                     }
2727 
2728 
2729                     signingDetails = result.getResult();
2730                 }
2731             }
2732             return signingDetails;
2733         } finally {
2734             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
2735         }
2736     }
2737 
2738     @CheckResult
2739     public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
2740             String baseCodePath, boolean skipVerify, boolean isStaticSharedLibrary,
2741             @NonNull SigningDetails existingSigningDetails, int targetSdk) {
2742         int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
2743                 targetSdk);
2744         if (isStaticSharedLibrary) {
2745             // must use v2 signing scheme
2746             minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
2747         }
2748         SigningDetails verified;
2749         try {
2750             if (skipVerify) {
2751                 // systemDir APKs are already trusted, save time by not verifying; since the
2752                 // signature is not verified and some system apps can have their V2+ signatures
2753                 // stripped allow pulling the certs from the jar signature.
2754                 verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
2755                         baseCodePath, SigningDetails.SignatureSchemeVersion.JAR);
2756             } else {
2757                 verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
2758             }
2759         } catch (PackageParserException e) {
2760             return input.error(PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES,
2761                     "Failed collecting certificates for " + baseCodePath, e);
2762         }
2763 
2764         // Verify that entries are signed consistently with the first pkg
2765         // we encountered. Note that for splits, certificates may have
2766         // already been populated during an earlier parse of a base APK.
2767         if (existingSigningDetails == SigningDetails.UNKNOWN) {
2768             return input.success(verified);
2769         } else {
2770             if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
2771                 return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
2772                         baseCodePath + " has mismatched certificates");
2773             }
2774 
2775             return input.success(existingSigningDetails);
2776         }
2777     }
2778 
2779     /*
2780      The following set of methods makes code easier to read by re-ordering the TypedArray methods.
2781 
2782      The first parameter is the default, which is the most important to understand for someone
2783      reading through the parsing code.
2784 
2785      That's followed by the attribute name, which is usually irrelevant during reading because
2786      it'll look like setSomeValue(true, R.styleable.ReallyLongParentName_SomeValueAttr... and
2787      the "setSomeValue" part is enough to communicate what the line does.
2788 
2789      Last comes the TypedArray, which is by far the least important since each try-with-resources
2790      should only have 1.
2791     */
2792 
2793     // Note there is no variant of bool without a defaultValue parameter, since explicit true/false
2794     // is important to specify when adding an attribute.
2795     private static boolean bool(boolean defaultValue, @StyleableRes int attribute, TypedArray sa) {
2796         return sa.getBoolean(attribute, defaultValue);
2797     }
2798 
2799     private static float aFloat(float defaultValue, @StyleableRes int attribute, TypedArray sa) {
2800         return sa.getFloat(attribute, defaultValue);
2801     }
2802 
2803     private static float aFloat(@StyleableRes int attribute, TypedArray sa) {
2804         return sa.getFloat(attribute, 0f);
2805     }
2806 
2807     private static int anInt(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
2808         return sa.getInt(attribute, defaultValue);
2809     }
2810 
2811     private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
2812         return sa.getInteger(attribute, defaultValue);
2813     }
2814 
2815     private static int anInt(@StyleableRes int attribute, TypedArray sa) {
2816         return sa.getInt(attribute, 0);
2817     }
2818 
2819     @AnyRes
2820     private static int resId(@StyleableRes int attribute, TypedArray sa) {
2821         return sa.getResourceId(attribute, 0);
2822     }
2823 
2824     private static String string(@StyleableRes int attribute, TypedArray sa) {
2825         return sa.getString(attribute);
2826     }
2827 
2828     private static String nonConfigString(int allowedChangingConfigs, @StyleableRes int attribute,
2829             TypedArray sa) {
2830         return sa.getNonConfigurationString(attribute, allowedChangingConfigs);
2831     }
2832 
2833     private static String nonResString(@StyleableRes int index, TypedArray sa) {
2834         return sa.getNonResourceString(index);
2835     }
2836 
2837     /**
2838      * Callback interface for retrieving information that may be needed while parsing
2839      * a package.
2840      */
2841     public interface Callback {
2842         boolean hasFeature(String feature);
2843 
2844         ParsingPackage startParsingPackage(@NonNull String packageName,
2845                 @NonNull String baseCodePath, @NonNull String codePath,
2846                 @NonNull TypedArray manifestArray, boolean isCoreApp);
2847     }
2848 }
2849