1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.pm.pkg.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.Flags.disallowSdkLibsToBeApps;
22 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
23 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
24 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
25 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
26 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
27 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED;
28 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
29 import static android.os.Build.VERSION_CODES.DONUT;
30 import static android.os.Build.VERSION_CODES.O;
31 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
32 
33 import static com.android.internal.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
34 
35 import android.annotation.AnyRes;
36 import android.annotation.CheckResult;
37 import android.annotation.IntDef;
38 import android.annotation.NonNull;
39 import android.annotation.Nullable;
40 import android.annotation.StyleableRes;
41 import android.app.ActivityThread;
42 import android.app.ResourcesManager;
43 import android.content.Intent;
44 import android.content.IntentFilter;
45 import android.content.pm.ApplicationInfo;
46 import android.content.pm.ConfigurationInfo;
47 import android.content.pm.FeatureGroupInfo;
48 import android.content.pm.FeatureInfo;
49 import android.content.pm.PackageInfo;
50 import android.content.pm.PackageManager;
51 import android.content.pm.PackageManager.Property;
52 import android.content.pm.Signature;
53 import android.content.pm.SigningDetails;
54 import android.content.pm.parsing.ApkLiteParseUtils;
55 import android.content.pm.parsing.FrameworkParsingPackageUtils;
56 import android.content.pm.parsing.PackageLite;
57 import android.content.pm.parsing.result.ParseInput;
58 import android.content.pm.parsing.result.ParseInput.DeferredError;
59 import android.content.pm.parsing.result.ParseResult;
60 import android.content.res.ApkAssets;
61 import android.content.res.AssetManager;
62 import android.content.res.Configuration;
63 import android.content.res.Resources;
64 import android.content.res.TypedArray;
65 import android.content.res.XmlResourceParser;
66 import android.net.Uri;
67 import android.os.Build;
68 import android.os.Bundle;
69 import android.os.Environment;
70 import android.os.Parcel;
71 import android.os.RemoteException;
72 import android.os.SystemProperties;
73 import android.os.Trace;
74 import android.os.UserHandle;
75 import android.os.ext.SdkExtensions;
76 import android.permission.PermissionManager;
77 import android.text.TextUtils;
78 import android.util.ArrayMap;
79 import android.util.ArraySet;
80 import android.util.AttributeSet;
81 import android.util.DisplayMetrics;
82 import android.util.Pair;
83 import android.util.Slog;
84 import android.util.SparseArray;
85 import android.util.SparseIntArray;
86 import android.util.TypedValue;
87 import android.util.apk.ApkSignatureVerifier;
88 
89 import com.android.internal.R;
90 import com.android.internal.os.ClassLoaderFactory;
91 import com.android.internal.pm.parsing.pkg.ParsedPackage;
92 import com.android.internal.pm.permission.CompatibilityPermissionInfo;
93 import com.android.internal.pm.pkg.component.AconfigFlags;
94 import com.android.internal.pm.pkg.component.ComponentMutateUtils;
95 import com.android.internal.pm.pkg.component.ComponentParseUtils;
96 import com.android.internal.pm.pkg.component.InstallConstraintsTagParser;
97 import com.android.internal.pm.pkg.component.ParsedActivity;
98 import com.android.internal.pm.pkg.component.ParsedActivityImpl;
99 import com.android.internal.pm.pkg.component.ParsedActivityUtils;
100 import com.android.internal.pm.pkg.component.ParsedApexSystemService;
101 import com.android.internal.pm.pkg.component.ParsedApexSystemServiceUtils;
102 import com.android.internal.pm.pkg.component.ParsedAttribution;
103 import com.android.internal.pm.pkg.component.ParsedAttributionUtils;
104 import com.android.internal.pm.pkg.component.ParsedComponent;
105 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
106 import com.android.internal.pm.pkg.component.ParsedInstrumentationUtils;
107 import com.android.internal.pm.pkg.component.ParsedIntentInfo;
108 import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl;
109 import com.android.internal.pm.pkg.component.ParsedIntentInfoUtils;
110 import com.android.internal.pm.pkg.component.ParsedMainComponent;
111 import com.android.internal.pm.pkg.component.ParsedPermission;
112 import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
113 import com.android.internal.pm.pkg.component.ParsedPermissionUtils;
114 import com.android.internal.pm.pkg.component.ParsedProcess;
115 import com.android.internal.pm.pkg.component.ParsedProcessUtils;
116 import com.android.internal.pm.pkg.component.ParsedProvider;
117 import com.android.internal.pm.pkg.component.ParsedProviderUtils;
118 import com.android.internal.pm.pkg.component.ParsedService;
119 import com.android.internal.pm.pkg.component.ParsedServiceUtils;
120 import com.android.internal.pm.pkg.component.ParsedUsesPermission;
121 import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl;
122 import com.android.internal.pm.split.DefaultSplitAssetLoader;
123 import com.android.internal.pm.split.SplitAssetDependencyLoader;
124 import com.android.internal.pm.split.SplitAssetLoader;
125 import com.android.internal.util.ArrayUtils;
126 import com.android.internal.util.XmlUtils;
127 
128 import libcore.io.IoUtils;
129 import libcore.util.EmptyArray;
130 import libcore.util.HexEncoding;
131 
132 import org.xmlpull.v1.XmlPullParser;
133 import org.xmlpull.v1.XmlPullParserException;
134 
135 import java.io.File;
136 import java.io.IOException;
137 import java.lang.annotation.Retention;
138 import java.lang.annotation.RetentionPolicy;
139 import java.security.PublicKey;
140 import java.util.ArrayList;
141 import java.util.List;
142 import java.util.Map;
143 import java.util.Objects;
144 import java.util.Set;
145 import java.util.StringTokenizer;
146 
147 /**
148  * TODO(b/135203078): Differentiate between parse_ methods and some add_ method for whether it
149  * mutates the passed-in component or not. Or consolidate so all parse_ methods mutate.
150  *
151  * @hide
152  */
153 public class ParsingPackageUtils {
154 
155     private static final String TAG = ParsingUtils.TAG;
156 
157     public static final boolean DEBUG_JAR = false;
158     public static final boolean DEBUG_BACKUP = false;
159     public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
160     public static final float ASPECT_RATIO_NOT_SET = -1f;
161 
162     /**
163      * File name in an APK for the Android manifest.
164      */
165     public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
166 
167     /**
168      * Path prefix for apps on expanded storage
169      */
170     public static final String MNT_EXPAND = "/mnt/expand/";
171 
172     public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
173     public static final String TAG_APPLICATION = "application";
174     public static final String TAG_ATTRIBUTION = "attribution";
175     public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
176     public static final String TAG_EAT_COMMENT = "eat-comment";
177     public static final String TAG_FEATURE_GROUP = "feature-group";
178     public static final String TAG_INSTALL_CONSTRAINTS = "install-constraints";
179     public static final String TAG_INSTRUMENTATION = "instrumentation";
180     public static final String TAG_KEY_SETS = "key-sets";
181     public static final String TAG_MANIFEST = "manifest";
182     public static final String TAG_ORIGINAL_PACKAGE = "original-package";
183     public static final String TAG_OVERLAY = "overlay";
184     public static final String TAG_PACKAGE = "package";
185     public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
186     public static final String TAG_PERMISSION = "permission";
187     public static final String TAG_PERMISSION_GROUP = "permission-group";
188     public static final String TAG_PERMISSION_TREE = "permission-tree";
189     public static final String TAG_PROFILEABLE = "profileable";
190     public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
191     public static final String TAG_QUERIES = "queries";
192     public static final String TAG_RECEIVER = "receiver";
193     public static final String TAG_RESTRICT_UPDATE = "restrict-update";
194     public static final String TAG_SUPPORTS_INPUT = "supports-input";
195     public static final String TAG_SUPPORT_SCREENS = "supports-screens";
196     public static final String TAG_USES_CONFIGURATION = "uses-configuration";
197     public static final String TAG_USES_FEATURE = "uses-feature";
198     public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
199     public static final String TAG_USES_PERMISSION = "uses-permission";
200     public static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";
201     public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
202     public static final String TAG_USES_SDK = "uses-sdk";
203     public static final String TAG_USES_SPLIT = "uses-split";
204 
205     public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
206     public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
207     public static final String METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES =
208             "android.can_display_on_remote_devices";
209     public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
210             "android.activity_window_layout_affinity";
211     public static final String METADATA_ACTIVITY_LAUNCH_MODE = "android.activity.launch_mode";
212 
213     public static final int SDK_VERSION = Build.VERSION.SDK_INT;
214     public static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
215 
216     public static boolean sCompatibilityModeEnabled = true;
217     public static boolean sUseRoundIcon = false;
218 
219     public static final int PARSE_DEFAULT_INSTALL_LOCATION =
220             PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
221     public static final int PARSE_DEFAULT_TARGET_SANDBOX = 1;
222 
223     /**
224      * If set to true, we will only allow package files that exactly match the DTD. Otherwise, we
225      * try to get as much from the package as we can without failing. This should normally be set to
226      * false, to support extensions to the DTD in future versions.
227      */
228     public static final boolean RIGID_PARSER = false;
229 
230     public static final int PARSE_MUST_BE_APK = 1 << 0;
231     public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
232     public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
233     public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
234     public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
235     public static final int PARSE_ENFORCE_CODE = 1 << 6;
236     /**
237      * This flag is applied in the ApkLiteParser. Used by OverlayConfigParser to ignore the checks
238      * of required system property within the overlay tag.
239      */
240     public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
241     public static final int PARSE_APK_IN_APEX = 1 << 9;
242     public static final int PARSE_APEX = 1 << 10;
243 
244     public static final int PARSE_CHATTY = 1 << 31;
245 
246     @IntDef(flag = true, prefix = { "PARSE_" }, value = {
247             PARSE_CHATTY,
248             PARSE_COLLECT_CERTIFICATES,
249             PARSE_ENFORCE_CODE,
250             PARSE_EXTERNAL_STORAGE,
251             PARSE_IGNORE_PROCESSES,
252             PARSE_IS_SYSTEM_DIR,
253             PARSE_MUST_BE_APK,
254             PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
255     })
256     @Retention(RetentionPolicy.SOURCE)
257     public @interface ParseFlags {}
258 
259     /**
260      * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off
261      * request, without caching the input object and without querying the internal system state for
262      * feature support.
263      */
264     @NonNull
parseDefault(ParseInput input, File file, @ParseFlags int parseFlags, @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions, boolean collectCertificates, Callback callback)265     public static ParseResult<ParsedPackage> parseDefault(ParseInput input, File file,
266             @ParseFlags int parseFlags,
267             @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
268             boolean collectCertificates, Callback callback) {
269 
270         ParsingPackageUtils parser = new ParsingPackageUtils(null /*separateProcesses*/,
271                 null /*displayMetrics*/, splitPermissions, callback);
272         var parseResult = parser.parsePackage(input, file, parseFlags);
273         if (parseResult.isError()) {
274             return input.error(parseResult);
275         }
276 
277         var pkg = parseResult.getResult().hideAsParsed();
278 
279         if (collectCertificates) {
280             final ParseResult<SigningDetails> ret =
281                     ParsingPackageUtils.getSigningDetails(input, pkg, false /*skipVerify*/);
282             if (ret.isError()) {
283                 return input.error(ret);
284             }
285             pkg.setSigningDetails(ret.getResult());
286         }
287 
288         return input.success(pkg);
289     }
290 
291     private final String[] mSeparateProcesses;
292     private final DisplayMetrics mDisplayMetrics;
293     @NonNull
294     private final List<PermissionManager.SplitPermissionInfo> mSplitPermissionInfos;
295     private final Callback mCallback;
296     private static final AconfigFlags sAconfigFlags = new AconfigFlags();
297 
ParsingPackageUtils(String[] separateProcesses, DisplayMetrics displayMetrics, @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions, @NonNull Callback callback)298     public ParsingPackageUtils(String[] separateProcesses, DisplayMetrics displayMetrics,
299             @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
300             @NonNull Callback callback) {
301         mSeparateProcesses = separateProcesses;
302         mDisplayMetrics = displayMetrics;
303         mSplitPermissionInfos = splitPermissions;
304         mCallback = callback;
305     }
306 
307     /**
308      * Parse the package at the given location. Automatically detects if the package is a monolithic
309      * style (single APK file) or cluster style (directory of APKs).
310      * <p>
311      * This performs validity checking on cluster style packages, such as requiring identical
312      * package name and version codes, a single base APK, and unique split names.
313      * <p>
314      * Note that this <em>does not</em> perform signature verification; that must be done separately
315      * in {@link #getSigningDetails(ParseInput, ParsedPackage, boolean)}.
316      * <p>
317      * If {@code useCaches} is true, the package parser might return a cached result from a previous
318      * parse of the same {@code packageFile} with the same {@code flags}. Note that this method does
319      * not check whether {@code packageFile} has changed since the last parse, it's up to callers to
320      * do so.
321      */
parsePackage(ParseInput input, File packageFile, int flags)322     public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags) {
323         if (packageFile.isDirectory()) {
324             return parseClusterPackage(input, packageFile,  flags);
325         } else {
326             return parseMonolithicPackage(input, packageFile, flags);
327         }
328     }
329 
330     /**
331      * Parse all APKs contained in the given directory, treating them as a
332      * single package. This also performs validity checking, such as requiring
333      * identical package name and version codes, a single base APK, and unique
334      * split names.
335      * <p>
336      * Note that this <em>does not</em> perform signature verification; that must be done separately
337      * in {@link #getSigningDetails(ParseInput, ParsedPackage, boolean)}.
338      */
parseClusterPackage(ParseInput input, File packageDir, int flags)339     private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
340             int flags) {
341         int liteParseFlags = 0;
342         if ((flags & PARSE_APK_IN_APEX) != 0) {
343             liteParseFlags |= PARSE_APK_IN_APEX;
344         }
345         if ((flags & PARSE_APEX) != 0) {
346             liteParseFlags |= PARSE_APEX;
347         }
348         final ParseResult<PackageLite> liteResult =
349                 ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, liteParseFlags);
350         if (liteResult.isError()) {
351             return input.error(liteResult);
352         }
353 
354         final PackageLite lite = liteResult.getResult();
355         // Build the split dependency tree.
356         SparseArray<int[]> splitDependencies = null;
357         final SplitAssetLoader assetLoader;
358         if (lite.isIsolatedSplits() && !ArrayUtils.isEmpty(lite.getSplitNames())) {
359             try {
360                 splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
361                 assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
362             } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
363                 return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
364             }
365         } else {
366             assetLoader = new DefaultSplitAssetLoader(lite, flags);
367         }
368 
369         try {
370             final File baseApk = new File(lite.getBaseApkPath());
371             boolean shouldSkipComponents = lite.isIsSdkLibrary() && disallowSdkLibsToBeApps();
372             final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
373                     lite.getPath(), assetLoader, flags, shouldSkipComponents);
374             if (result.isError()) {
375                 return input.error(result);
376             }
377 
378             ParsingPackage pkg = result.getResult();
379             if (!ArrayUtils.isEmpty(lite.getSplitNames())) {
380                 pkg.asSplit(
381                         lite.getSplitNames(),
382                         lite.getSplitApkPaths(),
383                         lite.getSplitRevisionCodes(),
384                         splitDependencies
385                 );
386                 final int num = lite.getSplitNames().length;
387 
388                 for (int i = 0; i < num; i++) {
389                     final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
390                     final ParseResult<ParsingPackage> split =
391                             parseSplitApk(input, pkg, i, splitAssets, flags);
392                     if (split.isError()) {
393                         return input.error(split);
394                     }
395                 }
396             }
397 
398             pkg.set32BitAbiPreferred(lite.isUse32bitAbi());
399             return input.success(pkg);
400         } catch (IllegalArgumentException e) {
401             return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
402                     : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
403         } finally {
404             IoUtils.closeQuietly(assetLoader);
405         }
406     }
407 
408     /**
409      * Parse the given APK file, treating it as as a single monolithic package.
410      * <p>
411      * Note that this <em>does not</em> perform signature verification; that must be done separately
412      * in {@link #getSigningDetails(ParseInput, ParsedPackage, boolean)}.
413      */
parseMonolithicPackage(ParseInput input, File apkFile, int flags)414     private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
415             int flags) {
416         // The signature parsing will be done later in method parseBaseApk.
417         int liteParseFlags = flags & ~PARSE_COLLECT_CERTIFICATES;
418         final ParseResult<PackageLite> liteResult =
419                 ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, liteParseFlags);
420         if (liteResult.isError()) {
421             return input.error(liteResult);
422         }
423 
424         final PackageLite lite = liteResult.getResult();
425         final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
426         try {
427             boolean shouldSkipComponents =  lite.isIsSdkLibrary() && disallowSdkLibsToBeApps();
428             final ParseResult<ParsingPackage> result = parseBaseApk(input,
429                     apkFile,
430                     apkFile.getCanonicalPath(),
431                     assetLoader, flags, shouldSkipComponents);
432             if (result.isError()) {
433                 return input.error(result);
434             }
435 
436             return input.success(result.getResult()
437                     .set32BitAbiPreferred(lite.isUse32bitAbi()));
438         } catch (IOException e) {
439             return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
440                     "Failed to get path: " + apkFile, e);
441         } finally {
442             IoUtils.closeQuietly(assetLoader);
443         }
444     }
445 
446     /**
447      * Creates ParsingPackage using only PackageLite.
448      * Missing fields will contain reasonable defaults.
449      * Used for packageless (aka archived) package installation.
450      */
parsePackageFromPackageLite(ParseInput input, PackageLite lite, int flags)451     public ParseResult<ParsingPackage> parsePackageFromPackageLite(ParseInput input,
452             PackageLite lite, int flags) {
453         final String volumeUuid = getVolumeUuid(lite.getPath());
454         final String pkgName = lite.getPackageName();
455 
456         final TypedArray manifestArray = null;
457         final ParsingPackage pkg = mCallback.startParsingPackage(pkgName,
458                 lite.getBaseApkPath(), lite.getPath(), manifestArray, lite.isCoreApp());
459 
460         final int targetSdk = lite.getTargetSdk();
461         final String versionName = null;
462         final int compileSdkVersion = 0;
463         final String compileSdkVersionCodeName = null;
464         final boolean isolatedSplitLoading = false;
465 
466         // Normally set from manifestArray.
467         pkg.setVersionCode(lite.getVersionCode());
468         pkg.setVersionCodeMajor(lite.getVersionCodeMajor());
469         pkg.setBaseRevisionCode(lite.getBaseRevisionCode());
470         pkg.setVersionName(versionName);
471         pkg.setCompileSdkVersion(compileSdkVersion);
472         pkg.setCompileSdkVersionCodeName(compileSdkVersionCodeName);
473         pkg.setIsolatedSplitLoading(isolatedSplitLoading);
474         pkg.setTargetSdkVersion(targetSdk);
475 
476         // parseBaseApkTags
477         pkg.setInstallLocation(lite.getInstallLocation())
478                 .setTargetSandboxVersion(PARSE_DEFAULT_TARGET_SANDBOX)
479                 /* Set the global "on SD card" flag */
480                 .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
481 
482         var archivedPackage = lite.getArchivedPackage();
483         if (archivedPackage == null) {
484             return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
485                     "archivePackage is missing");
486         }
487 
488         // parseBaseAppBasicFlags
489         pkg
490                 // Default true
491                 .setBackupAllowed(true)
492                 .setClearUserDataAllowed(true)
493                 .setClearUserDataOnFailedRestoreAllowed(true)
494                 .setAllowNativeHeapPointerTagging(true)
495                 .setEnabled(true)
496                 .setExtractNativeLibrariesRequested(true)
497                 // targetSdkVersion gated
498                 .setAllowAudioPlaybackCapture(targetSdk >= Build.VERSION_CODES.Q)
499                 .setHardwareAccelerated(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
500                 .setRequestLegacyExternalStorage(
501                         XmlUtils.convertValueToBoolean(archivedPackage.requestLegacyExternalStorage,
502                                 targetSdk < Build.VERSION_CODES.Q))
503                 .setCleartextTrafficAllowed(targetSdk < Build.VERSION_CODES.P)
504                 // Default false
505                 .setDefaultToDeviceProtectedStorage(XmlUtils.convertValueToBoolean(
506                         archivedPackage.defaultToDeviceProtectedStorage, false))
507                 .setUserDataFragile(
508                         XmlUtils.convertValueToBoolean(archivedPackage.userDataFragile, false))
509                 // Ints
510                 .setCategory(ApplicationInfo.CATEGORY_UNDEFINED)
511                 // Floats Default 0f
512                 .setMaxAspectRatio(0f)
513                 .setMinAspectRatio(0f);
514 
515         // No APK - no code.
516         pkg.setDeclaredHavingCode(false);
517 
518         final String taskAffinity = null;
519         ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
520                 pkgName, pkgName, taskAffinity, input);
521         if (taskAffinityResult.isError()) {
522             return input.error(taskAffinityResult);
523         }
524         pkg.setTaskAffinity(taskAffinityResult.getResult());
525 
526         final CharSequence pname = null;
527         ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
528                 pkgName, null /*defProc*/, pname, flags, mSeparateProcesses, input);
529         if (processNameResult.isError()) {
530             return input.error(processNameResult);
531         }
532         pkg.setProcessName(processNameResult.getResult());
533 
534         pkg.setGwpAsanMode(-1);
535         pkg.setMemtagMode(-1);
536 
537         afterParseBaseApplication(pkg);
538 
539         final ParseResult<ParsingPackage> result = validateBaseApkTags(input, pkg, flags);
540         if (result.isError()) {
541             return result;
542         }
543 
544         pkg.setVolumeUuid(volumeUuid);
545 
546         if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
547             pkg.setSigningDetails(lite.getSigningDetails());
548         } else {
549             pkg.setSigningDetails(SigningDetails.UNKNOWN);
550         }
551 
552         return input.success(pkg
553                 .set32BitAbiPreferred(lite.isUse32bitAbi()));
554     }
555 
556     private static String getVolumeUuid(final String apkPath) {
557         String volumeUuid = null;
558         if (apkPath.startsWith(MNT_EXPAND)) {
559             final int end = apkPath.indexOf('/', MNT_EXPAND.length());
560             volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
561         }
562         return volumeUuid;
563     }
564 
565     private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
566             String codePath, SplitAssetLoader assetLoader, int flags,
567             boolean shouldSkipComponents) {
568         final String apkPath = apkFile.getAbsolutePath();
569 
570         final String volumeUuid = getVolumeUuid(apkPath);
571 
572         if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
573 
574         final AssetManager assets;
575         try {
576             assets = assetLoader.getBaseAssetManager();
577         } catch (IllegalArgumentException e) {
578             return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
579                     : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
580         }
581         final int cookie = assets.findCookieForPath(apkPath);
582         if (cookie == 0) {
583             return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
584                     "Failed adding asset path: " + apkPath);
585         }
586 
587         try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
588                 ANDROID_MANIFEST_FILENAME)) {
589             final Resources res = new Resources(assets, mDisplayMetrics, null);
590 
591             ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
592                     parser, flags, shouldSkipComponents);
593             if (result.isError()) {
594                 return input.error(result.getErrorCode(),
595                         apkPath + " (at " + parser.getPositionDescription() + "): "
596                                 + result.getErrorMessage());
597             }
598 
599             final ParsingPackage pkg = result.getResult();
600             if (assets.containsAllocatedTable()) {
601                 final ParseResult<?> deferResult = input.deferError(
602                         "Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires"
603                                 + " the resources.arsc of installed APKs to be stored uncompressed"
604                                 + " and aligned on a 4-byte boundary",
605                         DeferredError.RESOURCES_ARSC_COMPRESSED);
606                 if (deferResult.isError()) {
607                     return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
608                             deferResult.getErrorMessage());
609                 }
610             }
611 
612             ApkAssets apkAssets = assetLoader.getBaseApkAssets();
613             boolean definesOverlayable = false;
614             try {
615                 definesOverlayable = apkAssets.definesOverlayable();
616             } catch (IOException ignored) {
617                 // Will fail if there's no packages in the ApkAssets, which can be treated as false
618             }
619 
620             if (definesOverlayable) {
621                 SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
622                 int size = packageNames.size();
623                 for (int index = 0; index < size; index++) {
624                     String packageName = packageNames.valueAt(index);
625                     Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
626                     if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
627                         for (String overlayable : overlayableToActor.keySet()) {
628                             pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
629                         }
630                     }
631                 }
632             }
633 
634             pkg.setVolumeUuid(volumeUuid);
635 
636             if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
637                 final ParseResult<SigningDetails> ret =
638                         getSigningDetails(input, pkg, false /*skipVerify*/);
639                 if (ret.isError()) {
640                     return input.error(ret);
641                 }
642                 pkg.setSigningDetails(ret.getResult());
643             } else {
644                 pkg.setSigningDetails(SigningDetails.UNKNOWN);
645             }
646 
647             return input.success(pkg);
648         } catch (Exception e) {
649             return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
650                     "Failed to read manifest from " + apkPath, e);
651         }
652     }
653 
654     private ParseResult<ParsingPackage> parseSplitApk(ParseInput input,
655             ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) {
656         final String apkPath = pkg.getSplitCodePaths()[splitIndex];
657 
658         if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
659 
660         // This must always succeed, as the path has been added to the AssetManager before.
661         final int cookie = assets.findCookieForPath(apkPath);
662         if (cookie == 0) {
663             return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
664                     "Failed adding asset path: " + apkPath);
665         }
666         try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
667                 ANDROID_MANIFEST_FILENAME)) {
668             Resources res = new Resources(assets, mDisplayMetrics, null);
669             ParseResult<ParsingPackage> parseResult = parseSplitApk(input, pkg, res,
670                     parser, flags, splitIndex);
671             if (parseResult.isError()) {
672                 return input.error(parseResult.getErrorCode(),
673                         apkPath + " (at " + parser.getPositionDescription() + "): "
674                                 + parseResult.getErrorMessage());
675             }
676 
677             return parseResult;
678         } catch (Exception e) {
679             return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
680                     "Failed to read manifest from " + apkPath, e);
681         }
682     }
683 
684     /**
685      * Parse the manifest of a <em>base APK</em>. When adding new features you need to consider
686      * whether they should be supported by split APKs and child packages.
687      *
688      * @param apkPath The package apk file path
689      * @param res     The resources from which to resolve values
690      * @param parser  The manifest parser
691      * @param flags   Flags how to parse
692      * @param shouldSkipComponents If the package is a sdk-library
693      * @return Parsed package or null on error.
694      */
695     private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
696             String codePath, Resources res, XmlResourceParser parser, int flags,
697             boolean shouldSkipComponents)
698             throws XmlPullParserException, IOException {
699         final String splitName;
700         final String pkgName;
701 
702         ParseResult<Pair<String, String>> packageSplitResult =
703                 ApkLiteParseUtils.parsePackageSplitNames(input, parser);
704         if (packageSplitResult.isError()) {
705             return input.error(packageSplitResult);
706         }
707 
708         Pair<String, String> packageSplit = packageSplitResult.getResult();
709         pkgName = packageSplit.first;
710         splitName = packageSplit.second;
711 
712         if (!TextUtils.isEmpty(splitName)) {
713             return input.error(
714                     PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
715                     "Expected base APK, but found split " + splitName
716             );
717         }
718 
719         final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
720         try {
721             final boolean isCoreApp = parser.getAttributeBooleanValue(null /*namespace*/,
722                     "coreApp", false);
723             final ParsingPackage pkg = mCallback.startParsingPackage(
724                     pkgName, apkPath, codePath, manifestArray, isCoreApp);
725             final ParseResult<ParsingPackage> result =
726                     parseBaseApkTags(input, pkg, manifestArray, res, parser, flags,
727                             shouldSkipComponents);
728             if (result.isError()) {
729                 return result;
730             }
731 
732             return input.success(pkg);
733         } finally {
734             manifestArray.recycle();
735         }
736     }
737 
738     /**
739      * Parse the manifest of a <em>split APK</em>.
740      * <p>
741      * Note that split APKs have many more restrictions on what they're capable of doing, so many
742      * valid features of a base APK have been carefully omitted here.
743      *
744      * @param pkg builder to fill
745      * @return false on failure
746      */
747     private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg,
748             Resources res, XmlResourceParser parser, int flags, int splitIndex)
749             throws XmlPullParserException, IOException {
750         // We parsed manifest tag earlier; just skip past it
751         final ParseResult<Pair<String, String>> packageSplitResult =
752                 ApkLiteParseUtils.parsePackageSplitNames(input, parser);
753         if (packageSplitResult.isError()) {
754             return input.error(packageSplitResult);
755         }
756 
757         int type;
758 
759         boolean foundApp = false;
760 
761         int outerDepth = parser.getDepth();
762         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
763             if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) {
764                 continue;
765             }
766             if (sAconfigFlags.skipCurrentElement(parser)) {
767                 continue;
768             }
769 
770             final ParseResult result;
771             String tagName = parser.getName();
772             if (TAG_APPLICATION.equals(tagName)) {
773                 if (foundApp) {
774                     if (RIGID_PARSER) {
775                         result = input.error("<manifest> has more than one <application>");
776                     } else {
777                         Slog.w(TAG, "<manifest> has more than one <application>");
778                         result = input.success(null);
779                     }
780                 } else {
781                     foundApp = true;
782                     result = parseSplitApplication(input, pkg, res, parser, flags, splitIndex);
783                 }
784             } else {
785                 result = ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
786             }
787 
788             if (result.isError()) {
789                 return input.error(result);
790             }
791         }
792 
793         if (!foundApp) {
794             ParseResult<?> deferResult = input.deferError(
795                     "<manifest> does not contain an <application>", DeferredError.MISSING_APP_TAG);
796             if (deferResult.isError()) {
797                 return input.error(deferResult);
798             }
799         }
800 
801         return input.success(pkg);
802     }
803 
804     /**
805      * Parse the {@code application} XML tree at the current parse location in a
806      * <em>split APK</em> manifest.
807      * <p>
808      * Note that split APKs have many more restrictions on what they're capable of doing, so many
809      * valid features of a base APK have been carefully omitted here.
810      */
811     private ParseResult<ParsingPackage> parseSplitApplication(ParseInput input,
812             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex)
813             throws XmlPullParserException, IOException {
814         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
815         try {
816             pkg.setSplitHasCode(splitIndex, sa.getBoolean(
817                     R.styleable.AndroidManifestApplication_hasCode, true));
818 
819             final String classLoaderName = sa.getString(
820                     R.styleable.AndroidManifestApplication_classLoader);
821             if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName(
822                     classLoaderName)) {
823                 pkg.setSplitClassLoaderName(splitIndex, classLoaderName);
824             } else {
825                 return input.error("Invalid class loader name: " + classLoaderName);
826             }
827         } finally {
828             sa.recycle();
829         }
830 
831         // If the loaded component did not specify a split, inherit the split name
832         // based on the split it is defined in.
833         // This is used to later load the correct split when starting this
834         // component.
835         String defaultSplitName = pkg.getSplitNames()[splitIndex];
836 
837         final int depth = parser.getDepth();
838         int type;
839         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
840                 && (type != XmlPullParser.END_TAG
841                 || parser.getDepth() > depth)) {
842             if (type != XmlPullParser.START_TAG) {
843                 continue;
844             }
845             if (sAconfigFlags.skipCurrentElement(parser)) {
846                 continue;
847             }
848 
849             ParsedMainComponent mainComponent = null;
850 
851             final ParseResult result;
852             String tagName = parser.getName();
853             boolean isActivity = false;
854             switch (tagName) {
855                 case "activity":
856                     isActivity = true;
857                     // fall-through
858                 case "receiver":
859                     ParseResult<ParsedActivity> activityResult =
860                             ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
861                                     res, parser, flags, sUseRoundIcon, defaultSplitName, input);
862                     if (activityResult.isSuccess()) {
863                         ParsedActivity activity = activityResult.getResult();
864                         if (isActivity) {
865                             pkg.addActivity(activity);
866                         } else {
867                             pkg.addReceiver(activity);
868                         }
869                         mainComponent = activity;
870                     }
871                     result = activityResult;
872                     break;
873                 case "service":
874                     ParseResult<ParsedService> serviceResult = ParsedServiceUtils.parseService(
875                             mSeparateProcesses, pkg, res, parser, flags, sUseRoundIcon,
876                             defaultSplitName, input);
877                     if (serviceResult.isSuccess()) {
878                         ParsedService service = serviceResult.getResult();
879                         pkg.addService(service);
880                         mainComponent = service;
881                     }
882                     result = serviceResult;
883                     break;
884                 case "provider":
885                     ParseResult<ParsedProvider> providerResult =
886                             ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
887                                     flags, sUseRoundIcon, defaultSplitName, input);
888                     if (providerResult.isSuccess()) {
889                         ParsedProvider provider = providerResult.getResult();
890                         pkg.addProvider(provider);
891                         mainComponent = provider;
892                     }
893                     result = providerResult;
894                     break;
895                 case "activity-alias":
896                     activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser,
897                             sUseRoundIcon, defaultSplitName, input);
898                     if (activityResult.isSuccess()) {
899                         ParsedActivity activity = activityResult.getResult();
900                         pkg.addActivity(activity);
901                         mainComponent = activity;
902                     }
903 
904                     result = activityResult;
905                     break;
906                 default:
907                     result = parseSplitBaseAppChildTags(input, tagName, pkg, res, parser);
908                     break;
909             }
910 
911             if (result.isError()) {
912                 return input.error(result);
913             }
914         }
915 
916         return input.success(pkg);
917     }
918 
919     /**
920      * For parsing non-MainComponents. Main ones have an order and some special handling which is
921      * done directly in {@link #parseSplitApplication(ParseInput, ParsingPackage, Resources,
922      * XmlResourceParser, int, int)}.
923      */
924     private ParseResult parseSplitBaseAppChildTags(ParseInput input, String tag, ParsingPackage pkg,
925             Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
926         switch (tag) {
927             case "meta-data":
928                 // note: application meta-data is stored off to the side, so it can
929                 // remain null in the primary copy (we like to avoid extra copies because
930                 // it can be large)
931                 ParseResult<Property> metaDataResult = parseMetaData(pkg, null /*component*/,
932                         res, parser, "<meta-data>", input);
933                 if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
934                     pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
935                 }
936                 return metaDataResult;
937             case "property":
938                 ParseResult<Property> propertyResult = parseMetaData(pkg, null /*component*/,
939                         res, parser, "<property>", input);
940                 if (propertyResult.isSuccess()) {
941                     pkg.addProperty(propertyResult.getResult());
942                 }
943                 return propertyResult;
944             case "uses-sdk-library":
945                 return parseUsesSdkLibrary(input, pkg, res, parser);
946             case "uses-static-library":
947                 return parseUsesStaticLibrary(input, pkg, res, parser);
948             case "uses-library":
949                 return parseUsesLibrary(input, pkg, res, parser);
950             case "uses-native-library":
951                 return parseUsesNativeLibrary(input, pkg, res, parser);
952             case "uses-package":
953                 // Dependencies for app installers; we don't currently try to
954                 // enforce this.
955                 return input.success(null);
956             default:
957                 return ParsingUtils.unknownTag("<application>", pkg, parser, input);
958         }
959     }
960     private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
961             TypedArray sa, Resources res, XmlResourceParser parser, int flags,
962             boolean shouldSkipComponents) throws XmlPullParserException, IOException {
963         ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
964         if (sharedUserResult.isError()) {
965             return sharedUserResult;
966         }
967 
968         final boolean updatableSystem = parser.getAttributeBooleanValue(null /*namespace*/,
969                 "updatableSystem", true);
970         final String emergencyInstaller = parser.getAttributeValue(null /*namespace*/,
971                 "emergencyInstaller");
972 
973         pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
974                         R.styleable.AndroidManifest_installLocation, sa))
975                 .setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
976                         R.styleable.AndroidManifest_targetSandboxVersion, sa))
977                 /* Set the global "on SD card" flag */
978                 .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0)
979                 .setUpdatableSystem(updatableSystem)
980                 .setEmergencyInstaller(emergencyInstaller);
981 
982         boolean foundApp = false;
983         final int depth = parser.getDepth();
984         int type;
985         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
986                 && (type != XmlPullParser.END_TAG
987                 || parser.getDepth() > depth)) {
988             if (type != XmlPullParser.START_TAG) {
989                 continue;
990             }
991             if (sAconfigFlags.skipCurrentElement(parser)) {
992                 continue;
993             }
994 
995             String tagName = parser.getName();
996             final ParseResult result;
997 
998             // <application> has special logic, so it's handled outside the general method
999             if (TAG_APPLICATION.equals(tagName)) {
1000                 if (foundApp) {
1001                     if (RIGID_PARSER) {
1002                         result = input.error("<manifest> has more than one <application>");
1003                     } else {
1004                         Slog.w(TAG, "<manifest> has more than one <application>");
1005                         result = input.success(null);
1006                     }
1007                 } else {
1008                     foundApp = true;
1009                     result = parseBaseApplication(input, pkg, res, parser, flags,
1010                             shouldSkipComponents);
1011                 }
1012             } else {
1013                 result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
1014             }
1015 
1016             if (result.isError()) {
1017                 return input.error(result);
1018             }
1019         }
1020 
1021         if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) {
1022             ParseResult<?> deferResult = input.deferError(
1023                     "<manifest> does not contain an <application> or <instrumentation>",
1024                     DeferredError.MISSING_APP_TAG);
1025             if (deferResult.isError()) {
1026                 return input.error(deferResult);
1027             }
1028         }
1029 
1030         return validateBaseApkTags(input, pkg, flags);
1031     }
1032 
1033     private ParseResult<ParsingPackage> validateBaseApkTags(ParseInput input, ParsingPackage pkg,
1034             int flags) {
1035         if (!ParsedAttributionUtils.isCombinationValid(pkg.getAttributions())) {
1036             return input.error(
1037                     INSTALL_PARSE_FAILED_BAD_MANIFEST,
1038                     "Combination <attribution> tags are not valid"
1039             );
1040         }
1041 
1042         if (ParsedPermissionUtils.declareDuplicatePermission(pkg)) {
1043             return input.error(
1044                     INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1045                     "Found duplicate permission with a different attribute value."
1046             );
1047         }
1048 
1049         convertCompatPermissions(pkg);
1050 
1051         convertSplitPermissions(pkg);
1052 
1053         // At this point we can check if an application is not supporting densities and hence
1054         // cannot be windowed / resized. Note that an SDK version of 0 is common for
1055         // pre-Doughnut applications.
1056         if (pkg.getTargetSdkVersion() < DONUT
1057                 || (!pkg.isSmallScreensSupported()
1058                 && !pkg.isNormalScreensSupported()
1059                 && !pkg.isLargeScreensSupported()
1060                 && !pkg.isExtraLargeScreensSupported()
1061                 && !pkg.isResizeable()
1062                 && !pkg.isAnyDensity())) {
1063             adjustPackageToBeUnresizeableAndUnpipable(pkg);
1064         }
1065 
1066         // An Apex package shouldn't have permission declarations
1067         final boolean isApex = (flags & PARSE_APEX) != 0;
1068         if (isApex && !pkg.getPermissions().isEmpty()) {
1069             return input.error(
1070                     INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1071                     pkg.getPackageName()
1072                             + " is an APEX package and shouldn't declare permissions."
1073             );
1074         }
1075 
1076         return input.success(pkg);
1077     }
1078 
1079     private ParseResult parseBaseApkTag(String tag, ParseInput input,
1080             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
1081             throws IOException, XmlPullParserException {
1082         switch (tag) {
1083             case TAG_OVERLAY:
1084                 return parseOverlay(input, pkg, res, parser);
1085             case TAG_KEY_SETS:
1086                 return parseKeySets(input, pkg, res, parser);
1087             case "feature": // TODO moltmann: Remove
1088             case TAG_ATTRIBUTION:
1089                 return parseAttribution(input, pkg, res, parser);
1090             case TAG_PERMISSION_GROUP:
1091                 return parsePermissionGroup(input, pkg, res, parser);
1092             case TAG_PERMISSION:
1093                 return parsePermission(input, pkg, res, parser);
1094             case TAG_PERMISSION_TREE:
1095                 return parsePermissionTree(input, pkg, res, parser);
1096             case TAG_USES_PERMISSION:
1097             case TAG_USES_PERMISSION_SDK_M:
1098             case TAG_USES_PERMISSION_SDK_23:
1099                 return parseUsesPermission(input, pkg, res, parser);
1100             case TAG_USES_CONFIGURATION:
1101                 return parseUsesConfiguration(input, pkg, res, parser);
1102             case TAG_USES_FEATURE:
1103                 return parseUsesFeature(input, pkg, res, parser);
1104             case TAG_FEATURE_GROUP:
1105                 return parseFeatureGroup(input, pkg, res, parser);
1106             case TAG_USES_SDK:
1107                 return parseUsesSdk(input, pkg, res, parser, flags);
1108             case TAG_SUPPORT_SCREENS:
1109                 return parseSupportScreens(input, pkg, res, parser);
1110             case TAG_PROTECTED_BROADCAST:
1111                 return parseProtectedBroadcast(input, pkg, res, parser);
1112             case TAG_INSTRUMENTATION:
1113                 return parseInstrumentation(input, pkg, res, parser);
1114             case TAG_ORIGINAL_PACKAGE:
1115                 return parseOriginalPackage(input, pkg, res, parser);
1116             case TAG_ADOPT_PERMISSIONS:
1117                 return parseAdoptPermissions(input, pkg, res, parser);
1118             case TAG_USES_GL_TEXTURE:
1119             case TAG_COMPATIBLE_SCREENS:
1120             case TAG_SUPPORTS_INPUT:
1121             case TAG_EAT_COMMENT:
1122                 // Just skip this tag
1123                 XmlUtils.skipCurrentTag(parser);
1124                 return input.success(pkg);
1125             case TAG_RESTRICT_UPDATE:
1126                 return parseRestrictUpdateHash(flags, input, pkg, res, parser);
1127             case TAG_INSTALL_CONSTRAINTS:
1128                 return parseInstallConstraints(input, pkg, res, parser,
1129                         mCallback.getInstallConstraintsAllowlist());
1130             case TAG_QUERIES:
1131                 return parseQueries(input, pkg, res, parser);
1132             default:
1133                 return ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
1134         }
1135     }
1136 
1137     private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input,
1138             ParsingPackage pkg, TypedArray sa) {
1139         String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa);
1140         if (TextUtils.isEmpty(str)) {
1141             return input.success(pkg);
1142         }
1143 
1144         if (!"android".equals(pkg.getPackageName())) {
1145             ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, str,
1146                     true, true);
1147             if (nameResult.isError()) {
1148                 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
1149                         "<manifest> specifies bad sharedUserId name \"" + str + "\": "
1150                                 + nameResult.getErrorMessage());
1151             }
1152         }
1153 
1154         boolean leaving = false;
1155         if (PackageManager.ENABLE_SHARED_UID_MIGRATION) {
1156             int max = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
1157             leaving = (max != 0) && (max < Build.VERSION.RESOURCES_SDK_INT);
1158         }
1159 
1160         return input.success(pkg
1161                 .setLeavingSharedUser(leaving)
1162                 .setSharedUserId(str.intern())
1163                 .setSharedUserLabelResourceId(
1164                         resId(R.styleable.AndroidManifest_sharedUserLabel, sa)));
1165     }
1166 
1167     private static ParseResult<ParsingPackage> parseKeySets(ParseInput input,
1168             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1169             throws XmlPullParserException, IOException {
1170         // we've encountered the 'key-sets' tag
1171         // all the keys and keysets that we want must be defined here
1172         // so we're going to iterate over the parser and pull out the things we want
1173         int outerDepth = parser.getDepth();
1174         int currentKeySetDepth = -1;
1175         int type;
1176         String currentKeySet = null;
1177         ArrayMap<String, PublicKey> publicKeys = new ArrayMap<>();
1178         ArraySet<String> upgradeKeySets = new ArraySet<>();
1179         ArrayMap<String, ArraySet<String>> definedKeySets = new ArrayMap<>();
1180         ArraySet<String> improperKeySets = new ArraySet<>();
1181         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1182                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1183             if (type == XmlPullParser.END_TAG) {
1184                 if (parser.getDepth() == currentKeySetDepth) {
1185                     currentKeySet = null;
1186                     currentKeySetDepth = -1;
1187                 }
1188                 continue;
1189             }
1190             String tagName = parser.getName();
1191             switch (tagName) {
1192                 case "key-set": {
1193                     if (currentKeySet != null) {
1194                         return input.error("Improperly nested 'key-set' tag at "
1195                                 + parser.getPositionDescription());
1196                     }
1197                     TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestKeySet);
1198                     try {
1199                         final String keysetName = sa.getNonResourceString(
1200                                 R.styleable.AndroidManifestKeySet_name);
1201                         definedKeySets.put(keysetName, new ArraySet<>());
1202                         currentKeySet = keysetName;
1203                         currentKeySetDepth = parser.getDepth();
1204                     } finally {
1205                         sa.recycle();
1206                     }
1207                 } break;
1208                 case "public-key": {
1209                     if (currentKeySet == null) {
1210                         return input.error("Improperly nested 'key-set' tag at "
1211                                 + parser.getPositionDescription());
1212                     }
1213                     TypedArray sa = res.obtainAttributes(parser,
1214                             R.styleable.AndroidManifestPublicKey);
1215                     try {
1216                         final String publicKeyName = nonResString(
1217                                 R.styleable.AndroidManifestPublicKey_name, sa);
1218                         final String encodedKey = nonResString(
1219                                 R.styleable.AndroidManifestPublicKey_value, sa);
1220                         if (encodedKey == null && publicKeys.get(publicKeyName) == null) {
1221                             return input.error("'public-key' " + publicKeyName
1222                                     + " must define a public-key value on first use at "
1223                                     + parser.getPositionDescription());
1224                         } else if (encodedKey != null) {
1225                             PublicKey currentKey =
1226                                     FrameworkParsingPackageUtils.parsePublicKey(encodedKey);
1227                             if (currentKey == null) {
1228                                 Slog.w(TAG, "No recognized valid key in 'public-key' tag at "
1229                                         + parser.getPositionDescription() + " key-set "
1230                                         + currentKeySet
1231                                         + " will not be added to the package's defined key-sets.");
1232                                 improperKeySets.add(currentKeySet);
1233                                 XmlUtils.skipCurrentTag(parser);
1234                                 continue;
1235                             }
1236                             if (publicKeys.get(publicKeyName) == null
1237                                     || publicKeys.get(publicKeyName).equals(currentKey)) {
1238 
1239                                 /* public-key first definition, or matches old definition */
1240                                 publicKeys.put(publicKeyName, currentKey);
1241                             } else {
1242                                 return input.error("Value of 'public-key' " + publicKeyName
1243                                         + " conflicts with previously defined value at "
1244                                         + parser.getPositionDescription());
1245                             }
1246                         }
1247                         definedKeySets.get(currentKeySet).add(publicKeyName);
1248                         XmlUtils.skipCurrentTag(parser);
1249                     } finally {
1250                         sa.recycle();
1251                     }
1252                 } break;
1253                 case "upgrade-key-set": {
1254                     TypedArray sa = res.obtainAttributes(parser,
1255                             R.styleable.AndroidManifestUpgradeKeySet);
1256                     try {
1257                         String name = sa.getNonResourceString(
1258                                 R.styleable.AndroidManifestUpgradeKeySet_name);
1259                         upgradeKeySets.add(name);
1260                         XmlUtils.skipCurrentTag(parser);
1261                     } finally {
1262                         sa.recycle();
1263                     }
1264                 } break;
1265                 default:
1266                     ParseResult result = ParsingUtils.unknownTag("<key-sets>", pkg, parser,
1267                             input);
1268                     if (result.isError()) {
1269                         return input.error(result);
1270                     }
1271                     break;
1272             }
1273         }
1274         String packageName = pkg.getPackageName();
1275         Set<String> publicKeyNames = publicKeys.keySet();
1276         if (publicKeyNames.removeAll(definedKeySets.keySet())) {
1277             return input.error("Package" + packageName
1278                     + " AndroidManifest.xml 'key-set' and 'public-key' names must be distinct.");
1279         }
1280 
1281         for (ArrayMap.Entry<String, ArraySet<String>> e : definedKeySets.entrySet()) {
1282             final String keySetName = e.getKey();
1283             if (e.getValue().size() == 0) {
1284                 Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
1285                         + "'key-set' " + keySetName + " has no valid associated 'public-key'."
1286                         + " Not including in package's defined key-sets.");
1287                 continue;
1288             } else if (improperKeySets.contains(keySetName)) {
1289                 Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
1290                         + "'key-set' " + keySetName + " contained improper 'public-key'"
1291                         + " tags. Not including in package's defined key-sets.");
1292                 continue;
1293             }
1294 
1295             for (String s : e.getValue()) {
1296                 pkg.addKeySet(keySetName, publicKeys.get(s));
1297             }
1298         }
1299         if (pkg.getKeySetMapping().keySet().containsAll(upgradeKeySets)) {
1300             pkg.setUpgradeKeySets(upgradeKeySets);
1301         } else {
1302             return input.error("Package" + packageName
1303                     + " AndroidManifest.xml does not define all 'upgrade-key-set's .");
1304         }
1305 
1306         return input.success(pkg);
1307     }
1308 
1309     private static ParseResult<ParsingPackage> parseAttribution(ParseInput input,
1310             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1311             throws IOException, XmlPullParserException {
1312         ParseResult<ParsedAttribution> result = ParsedAttributionUtils.parseAttribution(res,
1313                 parser, input);
1314         if (result.isError()) {
1315             return input.error(result);
1316         }
1317         return input.success(pkg.addAttribution(result.getResult()));
1318     }
1319 
1320     private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input,
1321             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1322             throws XmlPullParserException, IOException {
1323         ParseResult<ParsedPermissionGroup> result = ParsedPermissionUtils.parsePermissionGroup(
1324                 pkg, res, parser, sUseRoundIcon, input);
1325         if (result.isError()) {
1326             return input.error(result);
1327         }
1328         return input.success(pkg.addPermissionGroup(result.getResult()));
1329     }
1330 
1331     private static ParseResult<ParsingPackage> parsePermission(ParseInput input,
1332             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1333             throws XmlPullParserException, IOException {
1334         ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermission(
1335                 pkg, res, parser, sUseRoundIcon, input);
1336         if (result.isError()) {
1337             return input.error(result);
1338         }
1339         ParsedPermission permission = result.getResult();
1340         if (permission != null) {
1341             pkg.addPermission(permission);
1342         }
1343         return input.success(pkg);
1344     }
1345 
1346     private static ParseResult<ParsingPackage> parsePermissionTree(ParseInput input,
1347             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1348             throws XmlPullParserException, IOException {
1349         ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermissionTree(
1350                 pkg, res, parser, sUseRoundIcon, input);
1351         if (result.isError()) {
1352             return input.error(result);
1353         }
1354         return input.success(pkg.addPermission(result.getResult()));
1355     }
1356 
1357     private int parseMinOrMaxSdkVersion(TypedArray sa, int attr, int defaultValue) {
1358         int val = defaultValue;
1359         TypedValue peekVal = sa.peekValue(attr);
1360         if (peekVal != null) {
1361             if (peekVal.type >= TypedValue.TYPE_FIRST_INT
1362                     && peekVal.type <= TypedValue.TYPE_LAST_INT) {
1363                 val = peekVal.data;
1364             }
1365         }
1366         return val;
1367     }
1368 
1369     private ParseResult<ParsingPackage> parseUsesPermission(ParseInput input,
1370             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1371             throws IOException, XmlPullParserException {
1372         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesPermission);
1373         try {
1374             // Note: don't allow this value to be a reference to a resource
1375             // that may change.
1376             String name = sa.getNonResourceString(
1377                     R.styleable.AndroidManifestUsesPermission_name);
1378 
1379             int minSdkVersion =  parseMinOrMaxSdkVersion(sa,
1380                     R.styleable.AndroidManifestUsesPermission_minSdkVersion,
1381                     Integer.MIN_VALUE);
1382 
1383             int maxSdkVersion =  parseMinOrMaxSdkVersion(sa,
1384                     R.styleable.AndroidManifestUsesPermission_maxSdkVersion,
1385                     Integer.MAX_VALUE);
1386 
1387             final ArraySet<String> requiredFeatures = new ArraySet<>();
1388             String feature = sa.getNonConfigurationString(
1389                     com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature,
1390                     0);
1391             if (feature != null) {
1392                 requiredFeatures.add(feature);
1393             }
1394 
1395             final ArraySet<String> requiredNotFeatures = new ArraySet<>();
1396             feature = sa.getNonConfigurationString(
1397                     com.android.internal.R.styleable
1398                             .AndroidManifestUsesPermission_requiredNotFeature,
1399                     0);
1400             if (feature != null) {
1401                 requiredNotFeatures.add(feature);
1402             }
1403 
1404             final int usesPermissionFlags = sa.getInt(
1405                 com.android.internal.R.styleable.AndroidManifestUsesPermission_usesPermissionFlags,
1406                 0);
1407 
1408             final int outerDepth = parser.getDepth();
1409             int type;
1410             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1411                     && (type != XmlPullParser.END_TAG
1412                     || parser.getDepth() > outerDepth)) {
1413                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1414                     continue;
1415                 }
1416 
1417                 final ParseResult<?> result;
1418                 switch (parser.getName()) {
1419                     case "required-feature":
1420                         result = parseRequiredFeature(input, res, parser);
1421                         if (result.isSuccess()) {
1422                             requiredFeatures.add((String) result.getResult());
1423                         }
1424                         break;
1425 
1426                     case "required-not-feature":
1427                         result = parseRequiredNotFeature(input, res, parser);
1428                         if (result.isSuccess()) {
1429                             requiredNotFeatures.add((String) result.getResult());
1430                         }
1431                         break;
1432 
1433                     default:
1434                         result = ParsingUtils.unknownTag("<uses-permission>", pkg, parser, input);
1435                         break;
1436                 }
1437 
1438                 if (result.isError()) {
1439                     return input.error(result);
1440                 }
1441             }
1442 
1443             // Can only succeed from here on out
1444             ParseResult<ParsingPackage> success = input.success(pkg);
1445 
1446             if (name == null) {
1447                 return success;
1448             }
1449 
1450             if (Build.VERSION.RESOURCES_SDK_INT < minSdkVersion
1451                     || Build.VERSION.RESOURCES_SDK_INT > maxSdkVersion) {
1452                 return success;
1453             }
1454 
1455             if (mCallback != null) {
1456                 // Only allow requesting this permission if the platform supports all of the
1457                 // "required-feature"s.
1458                 for (int i = requiredFeatures.size() - 1; i >= 0; i--) {
1459                     if (!mCallback.hasFeature(requiredFeatures.valueAt(i))) {
1460                         return success;
1461                     }
1462                 }
1463 
1464                 // Only allow requesting this permission if the platform does not supports any of
1465                 // the "required-not-feature"s.
1466                 for (int i = requiredNotFeatures.size() - 1; i >= 0; i--) {
1467                     if (mCallback.hasFeature(requiredNotFeatures.valueAt(i))) {
1468                         return success;
1469                     }
1470                 }
1471             }
1472 
1473             // Quietly ignore duplicate permission requests, but fail loudly if
1474             // the two requests have conflicting flags
1475             boolean found = false;
1476             final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions();
1477             final int size = usesPermissions.size();
1478             for (int i = 0; i < size; i++) {
1479                 final ParsedUsesPermission usesPermission = usesPermissions.get(i);
1480                 if (Objects.equals(usesPermission.getName(), name)) {
1481                     if (usesPermission.getUsesPermissionFlags() != usesPermissionFlags) {
1482                         return input.error("Conflicting uses-permissions flags: "
1483                                 + name + " in package: " + pkg.getPackageName() + " at: "
1484                                 + parser.getPositionDescription());
1485                     } else {
Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: " + name + " in package: " + pkg.getPackageName() + " at: " + parser.getPositionDescription())1486                         Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
1487                                 + name + " in package: " + pkg.getPackageName() + " at: "
1488                                 + parser.getPositionDescription());
1489                     }
1490                     found = true;
1491                     break;
1492                 }
1493             }
1494 
1495             if (!found) {
pkg.addUsesPermission(new ParsedUsesPermissionImpl(name, usesPermissionFlags))1496                 pkg.addUsesPermission(new ParsedUsesPermissionImpl(name, usesPermissionFlags));
1497             }
1498             return success;
1499         } finally {
sa.recycle()1500             sa.recycle();
1501         }
1502     }
1503 
1504     private ParseResult<String> parseRequiredFeature(ParseInput input, Resources res,
1505             AttributeSet attrs) {
1506         final TypedArray sa = res.obtainAttributes(attrs,
1507                 com.android.internal.R.styleable.AndroidManifestRequiredFeature);
1508         try {
1509             final String featureName = sa.getString(
1510                     R.styleable.AndroidManifestRequiredFeature_name);
1511             return TextUtils.isEmpty(featureName)
1512                     ? input.error("Feature name is missing from <required-feature> tag.")
1513                     : input.success(featureName);
1514         } finally {
1515             sa.recycle();
1516         }
1517     }
1518 
1519     private ParseResult<String> parseRequiredNotFeature(ParseInput input, Resources res,
1520             AttributeSet attrs) {
1521         final TypedArray sa = res.obtainAttributes(attrs,
1522                 com.android.internal.R.styleable.AndroidManifestRequiredNotFeature);
1523         try {
1524             final String featureName = sa.getString(
1525                     R.styleable.AndroidManifestRequiredNotFeature_name);
1526             return TextUtils.isEmpty(featureName)
1527                     ? input.error("Feature name is missing from <required-not-feature> tag.")
1528                     : input.success(featureName);
1529         } finally {
1530             sa.recycle();
1531         }
1532     }
1533 
1534     private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input,
1535             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
1536         ConfigurationInfo cPref = new ConfigurationInfo();
1537         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesConfiguration);
1538         try {
1539             cPref.reqTouchScreen = sa.getInt(
1540                     R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
1541                     Configuration.TOUCHSCREEN_UNDEFINED);
1542             cPref.reqKeyboardType = sa.getInt(
1543                     R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
1544                     Configuration.KEYBOARD_UNDEFINED);
1545             if (sa.getBoolean(
1546                     R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
1547                     false)) {
1548                 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
1549             }
1550             cPref.reqNavigation = sa.getInt(
1551                     R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
1552                     Configuration.NAVIGATION_UNDEFINED);
1553             if (sa.getBoolean(
1554                     R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
1555                     false)) {
1556                 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
1557             }
1558             pkg.addConfigPreference(cPref);
1559             return input.success(pkg);
1560         } finally {
1561             sa.recycle();
1562         }
1563     }
1564 
1565     private static ParseResult<ParsingPackage> parseUsesFeature(ParseInput input,
1566             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
1567         FeatureInfo fi = parseFeatureInfo(res, parser);
1568         pkg.addReqFeature(fi);
1569 
1570         if (fi.name == null) {
1571             ConfigurationInfo cPref = new ConfigurationInfo();
1572             cPref.reqGlEsVersion = fi.reqGlEsVersion;
1573             pkg.addConfigPreference(cPref);
1574         }
1575 
1576         return input.success(pkg);
1577     }
1578 
1579     private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) {
1580         FeatureInfo fi = new FeatureInfo();
1581         TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestUsesFeature);
1582         try {
1583             // Note: don't allow this value to be a reference to a resource
1584             // that may change.
1585             fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name);
1586             fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0);
1587             if (fi.name == null) {
1588                 fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion,
1589                         FeatureInfo.GL_ES_VERSION_UNDEFINED);
1590             }
1591             if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) {
1592                 fi.flags |= FeatureInfo.FLAG_REQUIRED;
1593             }
1594             return fi;
1595         } finally {
1596             sa.recycle();
1597         }
1598     }
1599 
1600     private static ParseResult<ParsingPackage> parseFeatureGroup(ParseInput input,
1601             ParsingPackage pkg, Resources res, XmlResourceParser parser)
1602             throws IOException, XmlPullParserException {
1603         FeatureGroupInfo group = new FeatureGroupInfo();
1604         ArrayList<FeatureInfo> features = null;
1605         final int depth = parser.getDepth();
1606         int type;
1607         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1608                 && (type != XmlPullParser.END_TAG
1609                 || parser.getDepth() > depth)) {
1610             if (type != XmlPullParser.START_TAG) {
1611                 continue;
1612             }
1613             if (sAconfigFlags.skipCurrentElement(parser)) {
1614                 continue;
1615             }
1616 
1617             final String innerTagName = parser.getName();
1618             if (innerTagName.equals("uses-feature")) {
1619                 FeatureInfo featureInfo = parseFeatureInfo(res, parser);
1620                 // FeatureGroups are stricter and mandate that
1621                 // any <uses-feature> declared are mandatory.
1622                 featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
1623                 features = ArrayUtils.add(features, featureInfo);
1624             } else {
1625                 Slog.w(TAG,
1626                         "Unknown element under <feature-group>: " + innerTagName
1627                                 + " at " + pkg.getBaseApkPath() + " "
1628                                 + parser.getPositionDescription());
1629             }
1630         }
1631 
1632         if (features != null) {
1633             group.features = new FeatureInfo[features.size()];
1634             group.features = features.toArray(group.features);
1635         }
1636 
1637         pkg.addFeatureGroup(group);
1638         return input.success(pkg);
1639     }
1640 
1641     private static ParseResult<ParsingPackage> parseUsesSdk(ParseInput input,
1642             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
1643             throws IOException, XmlPullParserException {
1644         if (SDK_VERSION > 0) {
1645             final boolean isApkInApex = (flags & PARSE_APK_IN_APEX) != 0;
1646             TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
1647             try {
1648                 int minVers = ParsingUtils.DEFAULT_MIN_SDK_VERSION;
1649                 String minCode = null;
1650                 boolean minAssigned = false;
1651                 int targetVers = ParsingUtils.DEFAULT_TARGET_SDK_VERSION;
1652                 String targetCode = null;
1653                 int maxVers = Integer.MAX_VALUE;
1654 
1655                 TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion);
1656                 if (val != null) {
1657                     if (val.type == TypedValue.TYPE_STRING && val.string != null) {
1658                         minCode = val.string.toString();
1659                         minAssigned = !TextUtils.isEmpty(minCode);
1660                     } else {
1661                         // If it's not a string, it's an integer.
1662                         minVers = val.data;
1663                         minAssigned = true;
1664                     }
1665                 }
1666 
1667                 val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
1668                 if (val != null) {
1669                     if (val.type == TypedValue.TYPE_STRING && val.string != null) {
1670                         targetCode = val.string.toString();
1671                         if (!minAssigned) {
1672                             minCode = targetCode;
1673                         }
1674                     } else {
1675                         // If it's not a string, it's an integer.
1676                         targetVers = val.data;
1677                     }
1678                 } else {
1679                     targetVers = minVers;
1680                     targetCode = minCode;
1681                 }
1682 
1683                 if (isApkInApex) {
1684                     val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_maxSdkVersion);
1685                     if (val != null) {
1686                         // maxSdkVersion only supports integer
1687                         maxVers = val.data;
1688                     }
1689                 }
1690 
1691                 ParseResult<Integer> targetSdkVersionResult = FrameworkParsingPackageUtils
1692                         .computeTargetSdkVersion(targetVers, targetCode, SDK_CODENAMES, input,
1693                                 isApkInApex);
1694                 if (targetSdkVersionResult.isError()) {
1695                     return input.error(targetSdkVersionResult);
1696                 }
1697 
1698                 int targetSdkVersion = targetSdkVersionResult.getResult();
1699 
1700                 ParseResult<?> deferResult =
1701                         input.enableDeferredError(pkg.getPackageName(), targetSdkVersion);
1702                 if (deferResult.isError()) {
1703                     return input.error(deferResult);
1704                 }
1705 
1706                 ParseResult<Integer> minSdkVersionResult = FrameworkParsingPackageUtils
1707                         .computeMinSdkVersion(minVers, minCode, SDK_VERSION, SDK_CODENAMES, input);
1708                 if (minSdkVersionResult.isError()) {
1709                     return input.error(minSdkVersionResult);
1710                 }
1711 
1712                 int minSdkVersion = minSdkVersionResult.getResult();
1713 
1714                 pkg.setMinSdkVersion(minSdkVersion)
1715                         .setTargetSdkVersion(targetSdkVersion);
1716                 if (isApkInApex) {
1717                     ParseResult<Integer> maxSdkVersionResult = FrameworkParsingPackageUtils
1718                             .computeMaxSdkVersion(maxVers, SDK_VERSION, input);
1719                     if (maxSdkVersionResult.isError()) {
1720                         return input.error(maxSdkVersionResult);
1721                     }
1722                     int maxSdkVersion = maxSdkVersionResult.getResult();
1723                     pkg.setMaxSdkVersion(maxSdkVersion);
1724                 }
1725 
1726                 int type;
1727                 final int innerDepth = parser.getDepth();
1728                 SparseIntArray minExtensionVersions = null;
1729                 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1730                         && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1731                     if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1732                         continue;
1733                     }
1734 
1735                     final ParseResult result;
1736                     if (parser.getName().equals("extension-sdk")) {
1737                         if (minExtensionVersions == null) {
1738                             minExtensionVersions = new SparseIntArray();
1739                         }
1740                         result = parseExtensionSdk(input, res, parser, minExtensionVersions);
1741                         XmlUtils.skipCurrentTag(parser);
1742                     } else {
1743                         result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input);
1744                     }
1745 
1746                     if (result.isError()) {
1747                         return input.error(result);
1748                     }
1749                 }
1750                 pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions));
1751             } finally {
1752                 sa.recycle();
1753             }
1754         }
1755         return input.success(pkg);
1756     }
1757 
1758     @Nullable
1759     private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) {
1760         if (input == null) {
1761             return null;
1762         }
1763         SparseIntArray output = new SparseIntArray(input.size());
1764         for (int i = 0; i < input.size(); i++) {
1765             output.put(input.keyAt(i), input.valueAt(i));
1766         }
1767         return output;
1768     }
1769 
1770     private static ParseResult<SparseIntArray> parseExtensionSdk(
1771             ParseInput input, Resources res, XmlResourceParser parser,
1772             SparseIntArray minExtensionVersions) {
1773         int sdkVersion;
1774         int minVersion;
1775         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk);
1776         try {
1777             sdkVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1);
1778             minVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_minExtensionVersion, -1);
1779         } finally {
1780             sa.recycle();
1781         }
1782 
1783         if (sdkVersion < 0) {
1784             return input.error(
1785                     PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1786                     "<extension-sdk> must specify an sdkVersion >= 0");
1787         }
1788         if (minVersion < 0) {
1789             return input.error(
1790                     PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1791                     "<extension-sdk> must specify minExtensionVersion >= 0");
1792         }
1793 
1794         try {
1795             int version = SdkExtensions.getExtensionVersion(sdkVersion);
1796             if (version < minVersion) {
1797                 return input.error(
1798                         PackageManager.INSTALL_FAILED_OLDER_SDK,
1799                         "Package requires " + sdkVersion + " extension version " + minVersion
1800                                 + " which exceeds device version " + version);
1801             }
1802         } catch (RuntimeException e) {
1803             return input.error(
1804                     PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1805                     "Specified sdkVersion " + sdkVersion + " is not valid");
1806         }
1807         minExtensionVersions.put(sdkVersion, minVersion);
1808         return input.success(minExtensionVersions);
1809     }
1810 
1811     private static ParseResult<ParsingPackage> parseRestrictUpdateHash(int flags, ParseInput input,
1812             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
1813         if ((flags & PARSE_IS_SYSTEM_DIR) != 0) {
1814             TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestRestrictUpdate);
1815             try {
1816                 final String hash = sa.getNonConfigurationString(
1817                         R.styleable.AndroidManifestRestrictUpdate_hash,
1818                         0);
1819 
1820                 if (hash != null) {
1821                     final int hashLength = hash.length();
1822                     final byte[] hashBytes = new byte[hashLength / 2];
1823                     for (int i = 0; i < hashLength; i += 2) {
1824                         hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16)
1825                                 << 4)
1826                                 + Character.digit(hash.charAt(i + 1), 16));
1827                     }
1828                     pkg.setRestrictUpdateHash(hashBytes);
1829                 } else {
1830                     pkg.setRestrictUpdateHash(null);
1831                 }
1832             } finally {
1833                 sa.recycle();
1834             }
1835         }
1836         return input.success(pkg);
1837     }
1838 
1839     private static ParseResult<ParsingPackage> parseInstallConstraints(ParseInput input,
1840             ParsingPackage pkg, Resources res, XmlResourceParser parser, Set<String> allowlist)
1841             throws IOException, XmlPullParserException {
1842         return InstallConstraintsTagParser.parseInstallConstraints(
1843                 input, pkg, res, parser, allowlist);
1844     }
1845 
1846     private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
1847             Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
1848         final int depth = parser.getDepth();
1849         int type;
1850         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1851                 && (type != XmlPullParser.END_TAG
1852                 || parser.getDepth() > depth)) {
1853             if (type != XmlPullParser.START_TAG) {
1854                 continue;
1855             }
1856             if (sAconfigFlags.skipCurrentElement(parser)) {
1857                 continue;
1858             }
1859             if (parser.getName().equals("intent")) {
1860                 ParseResult<ParsedIntentInfoImpl> result = ParsedIntentInfoUtils.parseIntentInfo(
1861                         null /*className*/, pkg, res, parser, true /*allowGlobs*/,
1862                         true /*allowAutoVerify*/, input);
1863                 if (result.isError()) {
1864                     return input.error(result);
1865                 }
1866 
1867                 IntentFilter intentInfo = result.getResult().getIntentFilter();
1868 
1869                 Uri data = null;
1870                 String dataType = null;
1871                 String host = null;
1872                 final int numActions = intentInfo.countActions();
1873                 final int numSchemes = intentInfo.countDataSchemes();
1874                 final int numTypes = intentInfo.countDataTypes();
1875                 final int numHosts = intentInfo.getHosts().length;
1876                 if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {
1877                     return input.error("intent tags must contain either an action or data.");
1878                 }
1879                 if (numActions > 1) {
1880                     return input.error("intent tag may have at most one action.");
1881                 }
1882                 if (numTypes > 1) {
1883                     return input.error("intent tag may have at most one data type.");
1884                 }
1885                 if (numSchemes > 1) {
1886                     return input.error("intent tag may have at most one data scheme.");
1887                 }
1888                 if (numHosts > 1) {
1889                     return input.error("intent tag may have at most one data host.");
1890                 }
1891                 Intent intent = new Intent();
1892                 for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
1893                     intent.addCategory(intentInfo.getCategory(i));
1894                 }
1895                 if (numHosts == 1) {
1896                     host = intentInfo.getHosts()[0];
1897                 }
1898                 if (numSchemes == 1) {
1899                     data = new Uri.Builder()
1900                             .scheme(intentInfo.getDataScheme(0))
1901                             .authority(host)
1902                             .path(IntentFilter.WILDCARD_PATH)
1903                             .build();
1904                 }
1905                 if (numTypes == 1) {
1906                     dataType = intentInfo.getDataType(0);
1907                     // The dataType may have had the '/' removed for the dynamic mimeType feature.
1908                     // If we detect that case, we add the * back.
1909                     if (!dataType.contains("/")) {
1910                         dataType = dataType + "/*";
1911                     }
1912                     if (data == null) {
1913                         data = new Uri.Builder()
1914                                 .scheme("content")
1915                                 .authority(IntentFilter.WILDCARD)
1916                                 .path(IntentFilter.WILDCARD_PATH)
1917                                 .build();
1918                     }
1919                 }
1920                 intent.setDataAndType(data, dataType);
1921                 if (numActions == 1) {
1922                     intent.setAction(intentInfo.getAction(0));
1923                 }
1924                 pkg.addQueriesIntent(intent);
1925             } else if (parser.getName().equals("package")) {
1926                 final TypedArray sa = res.obtainAttributes(parser,
1927                         R.styleable.AndroidManifestQueriesPackage);
1928                 try {
1929                     final String packageName = sa.getNonConfigurationString(
1930                             R.styleable.AndroidManifestQueriesPackage_name, 0);
1931                     if (TextUtils.isEmpty(packageName)) {
1932                         return input.error("Package name is missing from package tag.");
1933                     }
1934                     pkg.addQueriesPackage(packageName.intern());
1935                 } finally {
1936                     sa.recycle();
1937                 }
1938             } else if (parser.getName().equals("provider")) {
1939                 final TypedArray sa = res.obtainAttributes(parser,
1940                         R.styleable.AndroidManifestQueriesProvider);
1941                 try {
1942                     final String authorities = sa.getNonConfigurationString(
1943                             R.styleable.AndroidManifestQueriesProvider_authorities, 0);
1944                     if (TextUtils.isEmpty(authorities)) {
1945                         return input.error(
1946                                 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1947                                 "Authority missing from provider tag."
1948                         );
1949                     }
1950                     StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";");
1951                     while (authoritiesTokenizer.hasMoreElements()) {
1952                         pkg.addQueriesProvider(authoritiesTokenizer.nextToken());
1953                     }
1954                 } finally {
1955                     sa.recycle();
1956                 }
1957             }
1958         }
1959         return input.success(pkg);
1960     }
1961 
1962     /**
1963      * Parse the {@code application} XML tree at the current parse location in a
1964      * <em>base APK</em> manifest.
1965      * <p>
1966      * When adding new features, carefully consider if they should also be supported by split APKs.
1967      * <p>
1968      * This method should avoid using a getter for fields set by this method. Prefer assigning a
1969      * local variable and using it. Otherwise there's an ordering problem which can be broken if any
1970      * code moves around.
1971      */
1972     private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
1973             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
1974             boolean shouldSkipComponents) throws XmlPullParserException, IOException {
1975         final String pkgName = pkg.getPackageName();
1976         int targetSdk = pkg.getTargetSdkVersion();
1977 
1978         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
1979         try {
1980             // TODO(b/135203078): Remove this and force unit tests to mock an empty manifest
1981             // This case can only happen in unit tests where we sometimes need to create fakes
1982             // of various package parser data structures.
1983             if (sa == null) {
1984                 return input.error("<application> does not contain any attributes");
1985             }
1986 
1987             String name = sa.getNonConfigurationString(R.styleable.AndroidManifestApplication_name,
1988                     0);
1989             if (name != null) {
1990                 String packageName = pkg.getPackageName();
1991                 String outInfoName = ParsingUtils.buildClassName(packageName, name);
1992                 if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
1993                     return input.error("<application> invalid android:name");
1994                 } else if (outInfoName == null) {
1995                     return input.error("Empty class name in package " + packageName);
1996                 }
1997 
1998                 pkg.setApplicationClassName(outInfoName);
1999             }
2000 
2001             TypedValue labelValue = sa.peekValue(R.styleable.AndroidManifestApplication_label);
2002             if (labelValue != null) {
2003                 pkg.setLabelResourceId(labelValue.resourceId);
2004                 if (labelValue.resourceId == 0) {
2005                     pkg.setNonLocalizedLabel(labelValue.coerceToString());
2006                 }
2007             }
2008 
2009             parseBaseAppBasicFlags(pkg, sa);
2010 
2011             String manageSpaceActivity = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
2012                     R.styleable.AndroidManifestApplication_manageSpaceActivity, sa);
2013             if (manageSpaceActivity != null) {
2014                 String manageSpaceActivityName = ParsingUtils.buildClassName(pkgName,
2015                         manageSpaceActivity);
2016 
2017                 if (manageSpaceActivityName == null) {
2018                     return input.error("Empty class name in package " + pkgName);
2019                 }
2020 
2021                 pkg.setManageSpaceActivityName(manageSpaceActivityName);
2022             }
2023 
2024             if (pkg.isBackupAllowed()) {
2025                 // backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
2026                 // and restoreAnyVersion are only relevant if backup is possible for the
2027                 // given application.
2028                 String backupAgent = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
2029                         R.styleable.AndroidManifestApplication_backupAgent, sa);
2030                 if (backupAgent != null) {
2031                     String backupAgentName = ParsingUtils.buildClassName(pkgName, backupAgent);
2032                     if (backupAgentName == null) {
2033                         return input.error("Empty class name in package " + pkgName);
2034                     }
2035 
2036                     if (DEBUG_BACKUP) {
2037                         Slog.v(TAG, "android:backupAgent = " + backupAgentName
2038                                 + " from " + pkgName + "+" + backupAgent);
2039                     }
2040 
2041                     pkg.setBackupAgentName(backupAgentName)
2042                             .setKillAfterRestoreAllowed(bool(true,
2043                                     R.styleable.AndroidManifestApplication_killAfterRestore, sa))
2044                             .setRestoreAnyVersion(bool(false,
2045                                     R.styleable.AndroidManifestApplication_restoreAnyVersion, sa))
2046                             .setFullBackupOnly(bool(false,
2047                                     R.styleable.AndroidManifestApplication_fullBackupOnly, sa))
2048                             .setBackupInForeground(bool(false,
2049                                     R.styleable.AndroidManifestApplication_backupInForeground, sa));
2050                 }
2051 
2052                 TypedValue v = sa.peekValue(
2053                         R.styleable.AndroidManifestApplication_fullBackupContent);
2054                 int fullBackupContent = 0;
2055 
2056                 if (v != null) {
2057                     fullBackupContent = v.resourceId;
2058 
2059                     if (v.resourceId == 0) {
2060                         if (DEBUG_BACKUP) {
2061                             Slog.v(TAG, "fullBackupContent specified as boolean=" +
2062                                     (v.data == 0 ? "false" : "true"));
2063                         }
2064                         // "false" => -1, "true" => 0
2065                         fullBackupContent = v.data == 0 ? -1 : 0;
2066                     }
2067 
2068                     pkg.setFullBackupContentResourceId(fullBackupContent);
2069                 }
2070                 if (DEBUG_BACKUP) {
2071                     Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
2072                 }
2073             }
2074 
2075             if (sa.getBoolean(R.styleable.AndroidManifestApplication_persistent, false)) {
2076                 // Check if persistence is based on a feature being present
2077                 final String requiredFeature = sa.getNonResourceString(R.styleable
2078                         .AndroidManifestApplication_persistentWhenFeatureAvailable);
2079                 pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature));
2080             }
2081 
2082             if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
2083                 pkg.setResizeableActivity(sa.getBoolean(
2084                         R.styleable.AndroidManifestApplication_resizeableActivity, true));
2085             } else {
2086                 pkg.setResizeableActivityViaSdkVersion(
2087                         targetSdk >= Build.VERSION_CODES.N);
2088             }
2089 
2090             String taskAffinity;
2091             if (targetSdk >= Build.VERSION_CODES.FROYO) {
2092                 taskAffinity = sa.getNonConfigurationString(
2093                         R.styleable.AndroidManifestApplication_taskAffinity,
2094                         Configuration.NATIVE_CONFIG_VERSION);
2095             } else {
2096                 // Some older apps have been seen to use a resource reference
2097                 // here that on older builds was ignored (with a warning).  We
2098                 // need to continue to do this for them so they don't break.
2099                 taskAffinity = sa.getNonResourceString(
2100                         R.styleable.AndroidManifestApplication_taskAffinity);
2101             }
2102 
2103             ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
2104                     pkgName, pkgName, taskAffinity, input);
2105             if (taskAffinityResult.isError()) {
2106                 return input.error(taskAffinityResult);
2107             }
2108 
2109             pkg.setTaskAffinity(taskAffinityResult.getResult());
2110             String factory = sa.getNonResourceString(
2111                     R.styleable.AndroidManifestApplication_appComponentFactory);
2112             if (factory != null) {
2113                 String appComponentFactory = ParsingUtils.buildClassName(pkgName, factory);
2114                 if (appComponentFactory == null) {
2115                     return input.error("Empty class name in package " + pkgName);
2116                 }
2117 
2118                 pkg.setAppComponentFactory(appComponentFactory);
2119             }
2120 
2121             CharSequence pname;
2122             if (targetSdk >= Build.VERSION_CODES.FROYO) {
2123                 pname = sa.getNonConfigurationString(
2124                         R.styleable.AndroidManifestApplication_process,
2125                         Configuration.NATIVE_CONFIG_VERSION);
2126             } else {
2127                 // Some older apps have been seen to use a resource reference
2128                 // here that on older builds was ignored (with a warning).  We
2129                 // need to continue to do this for them so they don't break.
2130                 pname = sa.getNonResourceString(
2131                         R.styleable.AndroidManifestApplication_process);
2132             }
2133             ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
2134                     pkgName, null /*defProc*/, pname, flags, mSeparateProcesses, input);
2135             if (processNameResult.isError()) {
2136                 return input.error(processNameResult);
2137             }
2138 
2139             String processName = processNameResult.getResult();
2140             pkg.setProcessName(processName);
2141 
2142             if (pkg.isSaveStateDisallowed()) {
2143                 // A heavy-weight application can not be in a custom process.
2144                 // We can do direct compare because we intern all strings.
2145                 if (processName != null && !processName.equals(pkgName)) {
2146                     return input.error(
2147                             "cantSaveState applications can not use custom processes");
2148                 }
2149             }
2150 
2151             String classLoaderName = pkg.getClassLoaderName();
2152             if (classLoaderName != null
2153                     && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
2154                 return input.error("Invalid class loader name: " + classLoaderName);
2155             }
2156 
2157             pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
2158             pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1));
2159             if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized)) {
2160                 final boolean v = sa.getBoolean(
2161                         R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized, false);
2162                 pkg.setNativeHeapZeroInitialized(
2163                         v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
2164             }
2165             if (sa.hasValue(
2166                     R.styleable.AndroidManifestApplication_requestRawExternalStorageAccess)) {
2167                 pkg.setRequestRawExternalStorageAccess(sa.getBoolean(R.styleable
2168                                 .AndroidManifestApplication_requestRawExternalStorageAccess,
2169                         false));
2170             }
2171             if (sa.hasValue(
2172                     R.styleable.AndroidManifestApplication_requestForegroundServiceExemption)) {
2173                 pkg.setRequestForegroundServiceExemption(sa.getBoolean(R.styleable
2174                                 .AndroidManifestApplication_requestForegroundServiceExemption,
2175                         false));
2176             }
2177             final ParseResult<Set<String>> knownActivityEmbeddingCertsResult =
2178                     parseKnownActivityEmbeddingCerts(sa, res,
2179                             R.styleable.AndroidManifestApplication_knownActivityEmbeddingCerts,
2180                             input);
2181             if (knownActivityEmbeddingCertsResult.isError()) {
2182                 return input.error(knownActivityEmbeddingCertsResult);
2183             } else {
2184                 final Set<String> knownActivityEmbeddingCerts = knownActivityEmbeddingCertsResult
2185                         .getResult();
2186                 if (knownActivityEmbeddingCerts != null) {
2187                     pkg.setKnownActivityEmbeddingCerts(knownActivityEmbeddingCerts);
2188                 }
2189             }
2190         } finally {
2191             sa.recycle();
2192         }
2193 
2194         boolean hasActivityOrder = false;
2195         boolean hasReceiverOrder = false;
2196         boolean hasServiceOrder = false;
2197         final int depth = parser.getDepth();
2198         int type;
2199         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
2200                 && (type != XmlPullParser.END_TAG
2201                 || parser.getDepth() > depth)) {
2202             if (type != XmlPullParser.START_TAG) {
2203                 continue;
2204             }
2205             if (sAconfigFlags.skipCurrentElement(parser)) {
2206                 continue;
2207             }
2208 
2209             final ParseResult result;
2210             String tagName = parser.getName();
2211             boolean isActivity = false;
2212             switch (tagName) {
2213                 case "activity":
2214                     isActivity = true;
2215                     // fall-through
2216                 case "receiver":
2217                     if (shouldSkipComponents) {
2218                         continue;
2219                     }
2220                     ParseResult<ParsedActivity> activityResult =
2221                             ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
2222                                     res, parser, flags, sUseRoundIcon, null /*defaultSplitName*/,
2223                                     input);
2224 
2225                     if (activityResult.isSuccess()) {
2226                         ParsedActivity activity = activityResult.getResult();
2227                         if (isActivity) {
2228                             hasActivityOrder |= (activity.getOrder() != 0);
2229                             pkg.addActivity(activity);
2230                         } else {
2231                             hasReceiverOrder |= (activity.getOrder() != 0);
2232                             pkg.addReceiver(activity);
2233                         }
2234                     }
2235 
2236                     result = activityResult;
2237                     break;
2238                 case "service":
2239                     if (shouldSkipComponents) {
2240                         continue;
2241                     }
2242                     ParseResult<ParsedService> serviceResult =
2243                             ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
2244                                     flags, sUseRoundIcon, null /*defaultSplitName*/,
2245                                     input);
2246                     if (serviceResult.isSuccess()) {
2247                         ParsedService service = serviceResult.getResult();
2248                         hasServiceOrder |= (service.getOrder() != 0);
2249                         pkg.addService(service);
2250                     }
2251 
2252                     result = serviceResult;
2253                     break;
2254                 case "provider":
2255                     if (shouldSkipComponents) {
2256                         continue;
2257                     }
2258                     ParseResult<ParsedProvider> providerResult =
2259                             ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
2260                                     flags, sUseRoundIcon, null /*defaultSplitName*/,
2261                                     input);
2262                     if (providerResult.isSuccess()) {
2263                         pkg.addProvider(providerResult.getResult());
2264                     }
2265 
2266                     result = providerResult;
2267                     break;
2268                 case "activity-alias":
2269                     if (shouldSkipComponents) {
2270                         continue;
2271                     }
2272                     activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
2273                             parser, sUseRoundIcon, null /*defaultSplitName*/,
2274                             input);
2275                     if (activityResult.isSuccess()) {
2276                         ParsedActivity activity = activityResult.getResult();
2277                         hasActivityOrder |= (activity.getOrder() != 0);
2278                         pkg.addActivity(activity);
2279                     }
2280 
2281                     result = activityResult;
2282                     break;
2283                 case "apex-system-service":
2284                     ParseResult<ParsedApexSystemService> systemServiceResult =
2285                             ParsedApexSystemServiceUtils.parseApexSystemService(res,
2286                                     parser, input);
2287                     if (systemServiceResult.isSuccess()) {
2288                         ParsedApexSystemService systemService =
2289                                 systemServiceResult.getResult();
2290                         pkg.addApexSystemService(systemService);
2291                     }
2292 
2293                     result = systemServiceResult;
2294                     break;
2295                 default:
2296                     result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
2297                     break;
2298             }
2299 
2300             if (result.isError()) {
2301                 return input.error(result);
2302             }
2303         }
2304 
2305         if (TextUtils.isEmpty(pkg.getStaticSharedLibraryName()) && TextUtils.isEmpty(
2306                 pkg.getSdkLibraryName())) {
2307             // Add a hidden app detail activity to normal apps which forwards user to App Details
2308             // page.
2309             ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
2310             if (a.isError()) {
2311                 // Error should be impossible here, as the only failure case as of SDK R is a
2312                 // string validation error on a constant ":app_details" string passed in by the
2313                 // parsing code itself. For this reason, this is just a hard failure instead of
2314                 // deferred.
2315                 return input.error(a);
2316             }
2317 
2318             pkg.addActivity(a.getResult());
2319         }
2320 
2321         if (hasActivityOrder) {
2322             pkg.sortActivities();
2323         }
2324         if (hasReceiverOrder) {
2325             pkg.sortReceivers();
2326         }
2327         if (hasServiceOrder) {
2328             pkg.sortServices();
2329         }
2330 
2331         afterParseBaseApplication(pkg);
2332 
2333         return input.success(pkg);
2334     }
2335 
2336     // Must be run after the entire {@link ApplicationInfo} has been fully processed and after
2337     // every activity info has had a chance to set it from its attributes.
2338     private void afterParseBaseApplication(ParsingPackage pkg) {
2339         setMaxAspectRatio(pkg);
2340         setMinAspectRatio(pkg);
2341         setSupportsSizeChanges(pkg);
2342 
2343         pkg.setHasDomainUrls(hasDomainURLs(pkg));
2344     }
2345 
2346     /**
2347      * Collection of single-line, no (or little) logic assignments. Separated for readability.
2348      * <p>
2349      * Flags are separated by type and by default value. They are sorted alphabetically within each
2350      * section.
2351      */
2352     private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) {
2353         int targetSdk = pkg.getTargetSdkVersion();
2354         //@formatter:off
2355         // CHECKSTYLE:off
2356         pkg
2357                 // Default true
2358                 .setBackupAllowed(bool(true, R.styleable.AndroidManifestApplication_allowBackup, sa))
2359                 .setClearUserDataAllowed(bool(true, R.styleable.AndroidManifestApplication_allowClearUserData, sa))
2360                 .setClearUserDataOnFailedRestoreAllowed(bool(true, R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore, sa))
2361                 .setAllowNativeHeapPointerTagging(bool(true, R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, sa))
2362                 .setEnabled(bool(true, R.styleable.AndroidManifestApplication_enabled, sa))
2363                 .setExtractNativeLibrariesRequested(bool(true, R.styleable.AndroidManifestApplication_extractNativeLibs, sa))
2364                 .setDeclaredHavingCode(bool(true, R.styleable.AndroidManifestApplication_hasCode, sa))
2365                 // Default false
2366                 .setTaskReparentingAllowed(bool(false, R.styleable.AndroidManifestApplication_allowTaskReparenting, sa))
2367                 .setSaveStateDisallowed(bool(false, R.styleable.AndroidManifestApplication_cantSaveState, sa))
2368                 .setCrossProfile(bool(false, R.styleable.AndroidManifestApplication_crossProfile, sa))
2369                 .setDebuggable(bool(false, R.styleable.AndroidManifestApplication_debuggable, sa))
2370                 .setDefaultToDeviceProtectedStorage(bool(false, R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage, sa))
2371                 .setDirectBootAware(bool(false, R.styleable.AndroidManifestApplication_directBootAware, sa))
2372                 .setForceQueryable(bool(false, R.styleable.AndroidManifestApplication_forceQueryable, sa))
2373                 .setGame(bool(false, R.styleable.AndroidManifestApplication_isGame, sa))
2374                 .setUserDataFragile(bool(false, R.styleable.AndroidManifestApplication_hasFragileUserData, sa))
2375                 .setLargeHeap(bool(false, R.styleable.AndroidManifestApplication_largeHeap, sa))
2376                 .setMultiArch(bool(false, R.styleable.AndroidManifestApplication_multiArch, sa))
2377                 .setPreserveLegacyExternalStorage(bool(false, R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage, sa))
2378                 .setRequiredForAllUsers(bool(false, R.styleable.AndroidManifestApplication_requiredForAllUsers, sa))
2379                 .setRtlSupported(bool(false, R.styleable.AndroidManifestApplication_supportsRtl, sa))
2380                 .setTestOnly(bool(false, R.styleable.AndroidManifestApplication_testOnly, sa))
2381                 .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
2382                 .setNonSdkApiRequested(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
2383                 .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
2384                 .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa))
2385                 .setAttributionsAreUserVisible(bool(false, R.styleable.AndroidManifestApplication_attributionsAreUserVisible, sa))
2386                 .setResetEnabledSettingsOnAppDataCleared(bool(false,
2387                         R.styleable.AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared,
2388                         sa))
2389                 .setOnBackInvokedCallbackEnabled(bool(false, R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback, sa))
2390                 // targetSdkVersion gated
2391                 .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
2392                 .setHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
2393                 .setRequestLegacyExternalStorage(bool(targetSdk < Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, sa))
2394                 .setCleartextTrafficAllowed(bool(targetSdk < Build.VERSION_CODES.P, R.styleable.AndroidManifestApplication_usesCleartextTraffic, sa))
2395                 // Ints Default 0
2396                 .setUiOptions(anInt(R.styleable.AndroidManifestApplication_uiOptions, sa))
2397                 // Ints
2398                 .setCategory(anInt(ApplicationInfo.CATEGORY_UNDEFINED, R.styleable.AndroidManifestApplication_appCategory, sa))
2399                 // Floats Default 0f
2400                 .setMaxAspectRatio(aFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, sa))
2401                 .setMinAspectRatio(aFloat(R.styleable.AndroidManifestApplication_minAspectRatio, sa))
2402                 // Resource ID
2403                 .setBannerResourceId(resId(R.styleable.AndroidManifestApplication_banner, sa))
2404                 .setDescriptionResourceId(resId(R.styleable.AndroidManifestApplication_description, sa))
2405                 .setIconResourceId(resId(R.styleable.AndroidManifestApplication_icon, sa))
2406                 .setLogoResourceId(resId(R.styleable.AndroidManifestApplication_logo, sa))
2407                 .setNetworkSecurityConfigResourceId(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa))
2408                 .setRoundIconResourceId(resId(R.styleable.AndroidManifestApplication_roundIcon, sa))
2409                 .setThemeResourceId(resId(R.styleable.AndroidManifestApplication_theme, sa))
2410                 .setDataExtractionRulesResourceId(
2411                         resId(R.styleable.AndroidManifestApplication_dataExtractionRules, sa))
2412                 .setLocaleConfigResourceId(resId(R.styleable.AndroidManifestApplication_localeConfig, sa))
2413                 // Strings
2414                 .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa))
2415                 .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa))
2416                 .setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa))
2417                 .setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa))
2418                 // Non-Config String
2419                 .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa))
2420                 .setAllowCrossUidActivitySwitchFromBelow(bool(true, R.styleable.AndroidManifestApplication_allowCrossUidActivitySwitchFromBelow, sa));
2421 
2422        // CHECKSTYLE:on
2423         //@formatter:on
2424     }
2425 
2426     /**
2427      * For parsing non-MainComponents. Main ones have an order and some special handling which is
2428      * done directly in {@link #parseBaseApplication(ParseInput, ParsingPackage, Resources,
2429      * XmlResourceParser, int, boolean)}.
2430      */
2431     private ParseResult parseBaseAppChildTag(ParseInput input, String tag, ParsingPackage pkg,
2432             Resources res, XmlResourceParser parser, int flags)
2433             throws IOException, XmlPullParserException {
2434         switch (tag) {
2435             case "meta-data":
2436                 // TODO(b/135203078): I have no idea what this comment means
2437                 // note: application meta-data is stored off to the side, so it can
2438                 // remain null in the primary copy (we like to avoid extra copies because
2439                 // it can be large)
2440                 final ParseResult<Property> metaDataResult = parseMetaData(pkg, null /*component*/,
2441                         res, parser, "<meta-data>", input);
2442                 if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
2443                     pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
2444                 }
2445                 return metaDataResult;
2446             case "property":
2447                 final ParseResult<Property> propertyResult = parseMetaData(pkg, null /*component*/,
2448                         res, parser, "<property>", input);
2449                 if (propertyResult.isSuccess()) {
2450                     pkg.addProperty(propertyResult.getResult());
2451                 }
2452                 return propertyResult;
2453             case "sdk-library":
2454                 return parseSdkLibrary(pkg, res, parser, input);
2455             case "static-library":
2456                 return parseStaticLibrary(pkg, res, parser, input);
2457             case "library":
2458                 return parseLibrary(pkg, res, parser, input);
2459             case "uses-sdk-library":
2460                 return parseUsesSdkLibrary(input, pkg, res, parser);
2461             case "uses-static-library":
2462                 return parseUsesStaticLibrary(input, pkg, res, parser);
2463             case "uses-library":
2464                 return parseUsesLibrary(input, pkg, res, parser);
2465             case "uses-native-library":
2466                 return parseUsesNativeLibrary(input, pkg, res, parser);
2467             case "processes":
2468                 return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
2469             case "uses-package":
2470                 // Dependencies for app installers; we don't currently try to
2471                 // enforce this.
2472                 return input.success(null);
2473             case "profileable":
2474                 return parseProfileable(input, pkg, res, parser);
2475             default:
2476                 return ParsingUtils.unknownTag("<application>", pkg, parser, input);
2477         }
2478     }
2479 
2480     @NonNull
2481     private static ParseResult<ParsingPackage> parseSdkLibrary(
2482             ParsingPackage pkg, Resources res,
2483             XmlResourceParser parser, ParseInput input) {
2484         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSdkLibrary);
2485         try {
2486             // Note: don't allow this value to be a reference to a resource that may change.
2487             String lname = sa.getNonResourceString(
2488                     R.styleable.AndroidManifestSdkLibrary_name);
2489             final int versionMajor = sa.getInt(
2490                     R.styleable.AndroidManifestSdkLibrary_versionMajor,
2491                     -1);
2492 
2493             // Fail if malformed.
2494             if (lname == null || versionMajor < 0) {
2495                 return input.error("Bad sdk-library declaration name: " + lname
2496                         + " version: " + versionMajor);
2497             } else if (pkg.getSharedUserId() != null) {
2498                 return input.error(
2499                         PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
2500                         "sharedUserId not allowed in SDK library"
2501                 );
2502             } else if (pkg.getSdkLibraryName() != null) {
2503                 return input.error("Multiple SDKs for package "
2504                         + pkg.getPackageName());
2505             }
2506 
2507             return input.success(pkg.setSdkLibraryName(lname.intern())
2508                     .setSdkLibVersionMajor(versionMajor)
2509                     .setSdkLibrary(true));
2510         } finally {
2511             sa.recycle();
2512         }
2513     }
2514 
2515     @NonNull
2516     private static ParseResult<ParsingPackage> parseStaticLibrary(
2517             ParsingPackage pkg, Resources res,
2518             XmlResourceParser parser, ParseInput input) {
2519         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestStaticLibrary);
2520         try {
2521             // Note: don't allow this value to be a reference to a resource
2522             // that may change.
2523             String lname = sa.getNonResourceString(
2524                     R.styleable.AndroidManifestStaticLibrary_name);
2525             final int version = sa.getInt(
2526                     R.styleable.AndroidManifestStaticLibrary_version, -1);
2527             final int versionMajor = sa.getInt(
2528                     R.styleable.AndroidManifestStaticLibrary_versionMajor,
2529                     0);
2530 
2531             // Since the app canot run without a static lib - fail if malformed
2532             if (lname == null || version < 0) {
2533                 return input.error("Bad static-library declaration name: " + lname
2534                         + " version: " + version);
2535             } else if (pkg.getSharedUserId() != null) {
2536                 return input.error(
2537                         PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
2538                         "sharedUserId not allowed in static shared library"
2539                 );
2540             } else if (pkg.getStaticSharedLibraryName() != null) {
2541                 return input.error("Multiple static-shared libs for package "
2542                         + pkg.getPackageName());
2543             }
2544 
2545             return input.success(pkg.setStaticSharedLibraryName(lname.intern())
2546                     .setStaticSharedLibraryVersion(
2547                             PackageInfo.composeLongVersionCode(versionMajor, version))
2548                     .setStaticSharedLibrary(true));
2549         } finally {
2550             sa.recycle();
2551         }
2552     }
2553 
2554     @NonNull
2555     private static ParseResult<ParsingPackage> parseLibrary(
2556             ParsingPackage pkg, Resources res,
2557             XmlResourceParser parser, ParseInput input) {
2558         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestLibrary);
2559         try {
2560             // Note: don't allow this value to be a reference to a resource
2561             // that may change.
2562             String lname = sa.getNonResourceString(R.styleable.AndroidManifestLibrary_name);
2563 
2564             if (lname != null) {
2565                 lname = lname.intern();
2566                 if (!ArrayUtils.contains(pkg.getLibraryNames(), lname)) {
2567                     pkg.addLibraryName(lname);
2568                 }
2569             }
2570             return input.success(pkg);
2571         } finally {
2572             sa.recycle();
2573         }
2574     }
2575 
2576     @NonNull
2577     private static ParseResult<ParsingPackage> parseUsesSdkLibrary(ParseInput input,
2578             ParsingPackage pkg, Resources res, XmlResourceParser parser)
2579             throws XmlPullParserException, IOException {
2580         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdkLibrary);
2581         try {
2582             // Note: don't allow this value to be a reference to a resource that may change.
2583             String lname = sa.getNonResourceString(
2584                     R.styleable.AndroidManifestUsesSdkLibrary_name);
2585             final int versionMajor = sa.getInt(
2586                     R.styleable.AndroidManifestUsesSdkLibrary_versionMajor, -1);
2587             String certSha256Digest = sa.getNonResourceString(R.styleable
2588                     .AndroidManifestUsesSdkLibrary_certDigest);
2589             boolean optional =
2590                     sa.getBoolean(R.styleable.AndroidManifestUsesSdkLibrary_optional, false);
2591 
2592             // Since an APK providing a static shared lib can only provide the lib - fail if
2593             // malformed
2594             if (lname == null || versionMajor < 0 || certSha256Digest == null) {
2595                 return input.error("Bad uses-sdk-library declaration name: " + lname
2596                         + " version: " + versionMajor + " certDigest" + certSha256Digest);
2597             }
2598 
2599             // Can depend only on one version of the same library
2600             List<String> usesSdkLibraries = pkg.getUsesSdkLibraries();
2601             if (usesSdkLibraries.contains(lname)) {
2602                 return input.error(
2603                         "Depending on multiple versions of SDK library " + lname);
2604             }
2605 
2606             lname = lname.intern();
2607             // We allow ":" delimiters in the SHA declaration as this is the format
2608             // emitted by the certtool making it easy for developers to copy/paste.
2609             certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
2610 
2611             if ("".equals(certSha256Digest)) {
2612                 // Test-only uses-sdk-library empty certificate digest override.
2613                 certSha256Digest = SystemProperties.get(
2614                         "debug.pm.uses_sdk_library_default_cert_digest", "");
2615                 // Validate the overridden digest.
2616                 try {
2617                     HexEncoding.decode(certSha256Digest, false);
2618                 } catch (IllegalArgumentException e) {
2619                     certSha256Digest = "";
2620                 }
2621             }
2622 
2623             ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
2624             if (certResult.isError()) {
2625                 return input.error(certResult);
2626             }
2627             String[] additionalCertSha256Digests = certResult.getResult();
2628 
2629             final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
2630             certSha256Digests[0] = certSha256Digest;
2631             System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
2632                     1, additionalCertSha256Digests.length);
2633 
2634             return input.success(
2635                     pkg.addUsesSdkLibrary(lname, versionMajor, certSha256Digests, optional));
2636         } finally {
2637             sa.recycle();
2638         }
2639     }
2640 
2641     @NonNull
2642     private static ParseResult<ParsingPackage> parseUsesStaticLibrary(ParseInput input,
2643             ParsingPackage pkg, Resources res, XmlResourceParser parser)
2644             throws XmlPullParserException, IOException {
2645         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesStaticLibrary);
2646         try {
2647             // Note: don't allow this value to be a reference to a resource that may change.
2648             String lname = sa.getNonResourceString(
2649                     R.styleable.AndroidManifestUsesLibrary_name);
2650             final int version = sa.getInt(
2651                     R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
2652             String certSha256Digest = sa.getNonResourceString(R.styleable
2653                     .AndroidManifestUsesStaticLibrary_certDigest);
2654 
2655             // Since an APK providing a static shared lib can only provide the lib - fail if
2656             // malformed
2657             if (lname == null || version < 0 || certSha256Digest == null) {
2658                 return input.error("Bad uses-static-library declaration name: " + lname
2659                         + " version: " + version + " certDigest" + certSha256Digest);
2660             }
2661 
2662             // Can depend only on one version of the same library
2663             List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
2664             if (usesStaticLibraries.contains(lname)) {
2665                 return input.error(
2666                         "Depending on multiple versions of static library " + lname);
2667             }
2668 
2669             lname = lname.intern();
2670             // We allow ":" delimiters in the SHA declaration as this is the format
2671             // emitted by the certtool making it easy for developers to copy/paste.
2672             certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
2673 
2674             // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
2675             String[] additionalCertSha256Digests = EmptyArray.STRING;
2676             if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
2677                 ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
2678                 if (certResult.isError()) {
2679                     return input.error(certResult);
2680                 }
2681                 additionalCertSha256Digests = certResult.getResult();
2682             }
2683 
2684             final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
2685             certSha256Digests[0] = certSha256Digest;
2686             System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
2687                     1, additionalCertSha256Digests.length);
2688 
2689             return input.success(pkg.addUsesStaticLibrary(lname, version, certSha256Digests));
2690         } finally {
2691             sa.recycle();
2692         }
2693     }
2694 
2695     @NonNull
2696     private static ParseResult<ParsingPackage> parseUsesLibrary(ParseInput input,
2697             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2698         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary);
2699         try {
2700             // Note: don't allow this value to be a reference to a resource
2701             // that may change.
2702             String lname = sa.getNonResourceString(R.styleable.AndroidManifestUsesLibrary_name);
2703             boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesLibrary_required, true);
2704 
2705             if (lname != null) {
2706                 lname = lname.intern();
2707                 if (req) {
2708                     // Upgrade to treat as stronger constraint
2709                     pkg.addUsesLibrary(lname)
2710                             .removeUsesOptionalLibrary(lname);
2711                 } else {
2712                     // Ignore if someone already defined as required
2713                     if (!ArrayUtils.contains(pkg.getUsesLibraries(), lname)) {
2714                         pkg.addUsesOptionalLibrary(lname);
2715                     }
2716                 }
2717             }
2718 
2719             return input.success(pkg);
2720         } finally {
2721             sa.recycle();
2722         }
2723     }
2724 
2725     @NonNull
2726     private static ParseResult<ParsingPackage> parseUsesNativeLibrary(ParseInput input,
2727             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2728         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesNativeLibrary);
2729         try {
2730             // Note: don't allow this value to be a reference to a resource
2731             // that may change.
2732             String lname = sa.getNonResourceString(
2733                     R.styleable.AndroidManifestUsesNativeLibrary_name);
2734             boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesNativeLibrary_required,
2735                     true);
2736 
2737             if (lname != null) {
2738                 if (req) {
2739                     // Upgrade to treat as stronger constraint
2740                     pkg.addUsesNativeLibrary(lname)
2741                             .removeUsesOptionalNativeLibrary(lname);
2742                 } else {
2743                     // Ignore if someone already defined as required
2744                     if (!ArrayUtils.contains(pkg.getUsesNativeLibraries(), lname)) {
2745                         pkg.addUsesOptionalNativeLibrary(lname);
2746                     }
2747                 }
2748             }
2749 
2750             return input.success(pkg);
2751         } finally {
2752             sa.recycle();
2753         }
2754     }
2755 
2756     @NonNull
2757     private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
2758             Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
2759             throws IOException, XmlPullParserException {
2760         ParseResult<ArrayMap<String, ParsedProcess>> result =
2761                 ParsedProcessUtils.parseProcesses(separateProcesses, pkg, res, parser, flags,
2762                         input);
2763         if (result.isError()) {
2764             return input.error(result);
2765         }
2766 
2767         return input.success(pkg.setProcesses(result.getResult()));
2768     }
2769 
2770     @NonNull
2771     private static ParseResult<ParsingPackage> parseProfileable(ParseInput input,
2772             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2773         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProfileable);
2774         try {
2775             ParsingPackage newPkg = pkg.setProfileableByShell(pkg.isProfileableByShell()
2776                     || bool(false, R.styleable.AndroidManifestProfileable_shell, sa));
2777             return input.success(newPkg.setProfileable(newPkg.isProfileable()
2778                     && bool(true, R.styleable.AndroidManifestProfileable_enabled, sa)));
2779         } finally {
2780             sa.recycle();
2781         }
2782     }
2783 
2784     private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input,
2785             Resources resources, XmlResourceParser parser)
2786             throws XmlPullParserException, IOException {
2787         String[] certSha256Digests = EmptyArray.STRING;
2788         final int depth = parser.getDepth();
2789         int type;
2790         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
2791                 && (type != XmlPullParser.END_TAG
2792                 || parser.getDepth() > depth)) {
2793             if (type != XmlPullParser.START_TAG) {
2794                 continue;
2795             }
2796             if (sAconfigFlags.skipCurrentElement(parser)) {
2797                 continue;
2798             }
2799 
2800             final String nodeName = parser.getName();
2801             if (nodeName.equals("additional-certificate")) {
2802                 TypedArray sa = resources.obtainAttributes(parser,
2803                         R.styleable.AndroidManifestAdditionalCertificate);
2804                 try {
2805                     String certSha256Digest = sa.getNonResourceString(
2806                             R.styleable.AndroidManifestAdditionalCertificate_certDigest);
2807 
2808                     if (TextUtils.isEmpty(certSha256Digest)) {
2809                         return input.error("Bad additional-certificate declaration with empty"
2810                                 + " certDigest:" + certSha256Digest);
2811                     }
2812 
2813 
2814                     // We allow ":" delimiters in the SHA declaration as this is the format
2815                     // emitted by the certtool making it easy for developers to copy/paste.
2816                     certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
2817                     certSha256Digests = ArrayUtils.appendElement(String.class,
2818                             certSha256Digests, certSha256Digest);
2819                 } finally {
2820                     sa.recycle();
2821                 }
2822             }
2823         }
2824 
2825         return input.success(certSha256Digests);
2826     }
2827 
2828     /**
2829      * Generate activity object that forwards user to App Details page automatically.
2830      * This activity should be invisible to user and user should not know or see it.
2831      */
2832     @NonNull
2833     private static ParseResult<ParsedActivity> generateAppDetailsHiddenActivity(ParseInput input,
2834             ParsingPackage pkg) {
2835         String packageName = pkg.getPackageName();
2836         ParseResult<String> result = ComponentParseUtils.buildTaskAffinityName(
2837                 packageName, packageName, ":app_details", input);
2838         if (result.isError()) {
2839             return input.error(result);
2840         }
2841 
2842         String taskAffinity = result.getResult();
2843 
2844         // Build custom App Details activity info instead of parsing it from xml
2845         return input.success(ParsedActivityImpl.makeAppDetailsActivity(packageName,
2846                 pkg.getProcessName(), pkg.getUiOptions(), taskAffinity,
2847                 pkg.isHardwareAccelerated()));
2848     }
2849 
2850     /**
2851      * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
2852      *
2853      * This is distinct from any of the functionality of app links domain verification, and cannot
2854      * be converted to remain backwards compatible. It's possible the presence of this flag does
2855      * not indicate a valid package for domain verification.
2856      */
2857     private static boolean hasDomainURLs(ParsingPackage pkg) {
2858         final List<ParsedActivity> activities = pkg.getActivities();
2859         final int activitiesSize = activities.size();
2860         for (int index = 0; index < activitiesSize; index++) {
2861             ParsedActivity activity = activities.get(index);
2862             List<ParsedIntentInfo> filters = activity.getIntents();
2863             final int filtersSize = filters.size();
2864             for (int filtersIndex = 0; filtersIndex < filtersSize; filtersIndex++) {
2865                 IntentFilter aii = filters.get(filtersIndex).getIntentFilter();
2866                 if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
2867                 if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
2868                 if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
2869                         aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
2870                     return true;
2871                 }
2872             }
2873         }
2874         return false;
2875     }
2876 
2877     /**
2878      * Sets the max aspect ratio of every child activity that doesn't already have an aspect
2879      * ratio set.
2880      */
2881     private static void setMaxAspectRatio(ParsingPackage pkg) {
2882         // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
2883         // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
2884         float maxAspectRatio = pkg.getTargetSdkVersion() < O ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
2885 
2886         float packageMaxAspectRatio = pkg.getMaxAspectRatio();
2887         if (packageMaxAspectRatio != 0) {
2888             // Use the application max aspect ration as default if set.
2889             maxAspectRatio = packageMaxAspectRatio;
2890         } else {
2891             Bundle appMetaData = pkg.getMetaData();
2892             if (appMetaData != null && appMetaData.containsKey(METADATA_MAX_ASPECT_RATIO)) {
2893                 maxAspectRatio = appMetaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio);
2894             }
2895         }
2896 
2897         List<ParsedActivity> activities = pkg.getActivities();
2898         int activitiesSize = activities.size();
2899         for (int index = 0; index < activitiesSize; index++) {
2900             ParsedActivity activity = activities.get(index);
2901             // If the max aspect ratio for the activity has already been set, skip.
2902             if (activity.getMaxAspectRatio() != ASPECT_RATIO_NOT_SET) {
2903                 continue;
2904             }
2905 
2906             // By default we prefer to use a values defined on the activity directly than values
2907             // defined on the application. We do not check the styled attributes on the activity
2908             // as it would have already been set when we processed the activity. We wait to
2909             // process the meta data here since this method is called at the end of processing
2910             // the application and all meta data is guaranteed.
2911             final float activityAspectRatio = activity.getMetaData()
2912                     .getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio);
2913 
2914             ComponentMutateUtils.setMaxAspectRatio(activity, activity.getResizeMode(),
2915                     activityAspectRatio);
2916         }
2917     }
2918 
2919     /**
2920      * Sets the min aspect ratio of every child activity that doesn't already have an aspect
2921      * ratio set.
2922      */
2923     private void setMinAspectRatio(ParsingPackage pkg) {
2924         // Use the application max aspect ration as default if set.
2925         final float minAspectRatio = pkg.getMinAspectRatio();
2926 
2927         List<ParsedActivity> activities = pkg.getActivities();
2928         int activitiesSize = activities.size();
2929         for (int index = 0; index < activitiesSize; index++) {
2930             ParsedActivity activity = activities.get(index);
2931             if (activity.getMinAspectRatio() == ASPECT_RATIO_NOT_SET) {
2932                 ComponentMutateUtils.setMinAspectRatio(activity, activity.getResizeMode(),
2933                         minAspectRatio);
2934             }
2935         }
2936     }
2937 
2938     private void setSupportsSizeChanges(ParsingPackage pkg) {
2939         final Bundle appMetaData = pkg.getMetaData();
2940         final boolean supportsSizeChanges = appMetaData != null
2941                 && appMetaData.getBoolean(METADATA_SUPPORTS_SIZE_CHANGES, false);
2942 
2943         List<ParsedActivity> activities = pkg.getActivities();
2944         int activitiesSize = activities.size();
2945         for (int index = 0; index < activitiesSize; index++) {
2946             ParsedActivity activity = activities.get(index);
2947             if (supportsSizeChanges || activity.getMetaData()
2948                     .getBoolean(METADATA_SUPPORTS_SIZE_CHANGES, false)) {
2949                 ComponentMutateUtils.setSupportsSizeChanges(activity, true);
2950             }
2951         }
2952     }
2953 
2954     private static ParseResult<ParsingPackage> parseOverlay(ParseInput input, ParsingPackage pkg,
2955             Resources res, XmlResourceParser parser) {
2956         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay);
2957         try {
2958             String target = sa.getString(R.styleable.AndroidManifestResourceOverlay_targetPackage);
2959             int priority = anInt(0, R.styleable.AndroidManifestResourceOverlay_priority, sa);
2960 
2961             if (target == null) {
2962                 return input.error("<overlay> does not specify a target package");
2963             } else if (priority < 0 || priority > 9999) {
2964                 return input.error("<overlay> priority must be between 0 and 9999");
2965             }
2966 
2967             // check to see if overlay should be excluded based on system property condition
2968             String propName = sa.getString(
2969                     R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName);
2970             String propValue = sa.getString(
2971                     R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
2972             if (!FrameworkParsingPackageUtils.checkRequiredSystemProperties(propName, propValue)) {
2973                 String message = "Skipping target and overlay pair " + target + " and "
2974                         + pkg.getBaseApkPath()
2975                         + ": overlay ignored due to required system property: "
2976                         + propName + " with value: " + propValue;
2977                 Slog.i(TAG, message);
2978                 return input.skip(message);
2979             }
2980 
2981             return input.success(pkg.setResourceOverlay(true)
2982                     .setOverlayTarget(target)
2983                     .setOverlayPriority(priority)
2984                     .setOverlayTargetOverlayableName(
2985                             sa.getString(R.styleable.AndroidManifestResourceOverlay_targetName))
2986                     .setOverlayCategory(
2987                             sa.getString(R.styleable.AndroidManifestResourceOverlay_category))
2988                     .setOverlayIsStatic(
2989                             bool(false, R.styleable.AndroidManifestResourceOverlay_isStatic, sa)));
2990         } finally {
2991             sa.recycle();
2992         }
2993     }
2994 
2995     private static ParseResult<ParsingPackage> parseProtectedBroadcast(ParseInput input,
2996             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2997         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProtectedBroadcast);
2998         try {
2999             // Note: don't allow this value to be a reference to a resource
3000             // that may change.
3001             String name = nonResString(R.styleable.AndroidManifestProtectedBroadcast_name, sa);
3002             if (name != null) {
3003                 pkg.addProtectedBroadcast(name);
3004             }
3005             return input.success(pkg);
3006         } finally {
3007             sa.recycle();
3008         }
3009     }
3010 
3011     private static ParseResult<ParsingPackage> parseSupportScreens(ParseInput input,
3012             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
3013         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSupportsScreens);
3014         try {
3015             int requiresSmallestWidthDp = anInt(0,
3016                     R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, sa);
3017             int compatibleWidthLimitDp = anInt(0,
3018                     R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, sa);
3019             int largestWidthLimitDp = anInt(0,
3020                     R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, sa);
3021 
3022             // This is a trick to get a boolean and still able to detect
3023             // if a value was actually set.
3024             return input.success(pkg
3025                     .setSmallScreensSupported(
3026                             anInt(1, R.styleable.AndroidManifestSupportsScreens_smallScreens, sa))
3027                     .setNormalScreensSupported(
3028                             anInt(1, R.styleable.AndroidManifestSupportsScreens_normalScreens, sa))
3029                     .setLargeScreensSupported(
3030                             anInt(1, R.styleable.AndroidManifestSupportsScreens_largeScreens, sa))
3031                     .setExtraLargeScreensSupported(
3032                             anInt(1, R.styleable.AndroidManifestSupportsScreens_xlargeScreens, sa))
3033                     .setResizeable(
3034                             anInt(1, R.styleable.AndroidManifestSupportsScreens_resizeable, sa))
3035                     .setAnyDensity(
3036                             anInt(1, R.styleable.AndroidManifestSupportsScreens_anyDensity, sa))
3037                     .setRequiresSmallestWidthDp(requiresSmallestWidthDp)
3038                     .setCompatibleWidthLimitDp(compatibleWidthLimitDp)
3039                     .setLargestWidthLimitDp(largestWidthLimitDp));
3040         } finally {
3041             sa.recycle();
3042         }
3043     }
3044 
3045     private static ParseResult<ParsingPackage> parseInstrumentation(ParseInput input,
3046             ParsingPackage pkg, Resources res, XmlResourceParser parser)
3047             throws XmlPullParserException, IOException {
3048         ParseResult<ParsedInstrumentation> result = ParsedInstrumentationUtils.parseInstrumentation(
3049                 pkg, res, parser, sUseRoundIcon, input);
3050         if (result.isError()) {
3051             return input.error(result);
3052         }
3053         return input.success(pkg.addInstrumentation(result.getResult()));
3054     }
3055 
3056     private static ParseResult<ParsingPackage> parseOriginalPackage(ParseInput input,
3057             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
3058         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
3059         try {
3060             String orig = sa.getNonConfigurationString(
3061                     R.styleable.AndroidManifestOriginalPackage_name,
3062                     0);
3063             if (!pkg.getPackageName().equals(orig)) {
3064                 pkg.addOriginalPackage(orig);
3065             }
3066             return input.success(pkg);
3067         } finally {
3068             sa.recycle();
3069         }
3070     }
3071 
3072     private static ParseResult<ParsingPackage> parseAdoptPermissions(ParseInput input,
3073             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
3074         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
3075         try {
3076             String name = nonConfigString(0, R.styleable.AndroidManifestOriginalPackage_name, sa);
3077             if (name != null) {
3078                 pkg.addAdoptPermission(name);
3079             }
3080             return input.success(pkg);
3081         } finally {
3082             sa.recycle();
3083         }
3084     }
3085 
3086     private static void convertCompatPermissions(ParsingPackage pkg) {
3087         for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
3088             final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
3089             if (pkg.getTargetSdkVersion() >= info.getSdkVersion()) {
3090                 break;
3091             }
3092             if (!pkg.getRequestedPermissions().contains(info.getName())) {
3093                 pkg.addImplicitPermission(info.getName());
3094             }
3095         }
3096     }
3097 
3098     private void convertSplitPermissions(ParsingPackage pkg) {
3099         final int listSize = mSplitPermissionInfos.size();
3100         for (int is = 0; is < listSize; is++) {
3101             final PermissionManager.SplitPermissionInfo spi = mSplitPermissionInfos.get(is);
3102             Set<String> requestedPermissions = pkg.getRequestedPermissions();
3103             if (pkg.getTargetSdkVersion() >= spi.getTargetSdk()
3104                     || !requestedPermissions.contains(spi.getSplitPermission())) {
3105                 continue;
3106             }
3107             final List<String> newPerms = spi.getNewPermissions();
3108             for (int in = 0; in < newPerms.size(); in++) {
3109                 final String perm = newPerms.get(in);
3110                 if (!requestedPermissions.contains(perm)) {
3111                     pkg.addImplicitPermission(perm);
3112                 }
3113             }
3114         }
3115     }
3116 
3117     /**
3118      * This is a pre-density application which will get scaled - instead of being pixel perfect.
3119      * This type of application is not resizable.
3120      *
3121      * @param pkg The package which needs to be marked as unresizable.
3122      */
3123     private static void adjustPackageToBeUnresizeableAndUnpipable(ParsingPackage pkg) {
3124         List<ParsedActivity> activities = pkg.getActivities();
3125         int activitiesSize = activities.size();
3126         for (int index = 0; index < activitiesSize; index++) {
3127             ParsedActivity activity = activities.get(index);
3128             ComponentMutateUtils.setResizeMode(activity, RESIZE_MODE_UNRESIZEABLE);
3129             ComponentMutateUtils.setExactFlags(activity,
3130                     activity.getFlags() & ~FLAG_SUPPORTS_PICTURE_IN_PICTURE);
3131         }
3132     }
3133 
3134     /**
3135      * Parse a meta data defined on the enclosing tag.
3136      * <p>Meta data can be defined by either &lt;meta-data&gt; or &lt;property&gt; elements.
3137      */
3138     public static ParseResult<Property> parseMetaData(ParsingPackage pkg, ParsedComponent component,
3139             Resources res, XmlResourceParser parser, String tagName, ParseInput input) {
3140         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData);
3141         try {
3142             final Property property;
3143             final String name = TextUtils.safeIntern(
3144                     nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa));
3145             if (name == null) {
3146                 return input.error(tagName + " requires an android:name attribute");
3147             }
3148 
3149             final String packageName = pkg.getPackageName();
3150             final String className = component != null ? component.getName() : null;
3151             TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource);
3152             if (v != null && v.resourceId != 0) {
3153                 property = new Property(name, v.resourceId, true, packageName, className);
3154             } else {
3155                 v = sa.peekValue(R.styleable.AndroidManifestMetaData_value);
3156                 if (v != null) {
3157                     if (v.type == TypedValue.TYPE_STRING) {
3158                         final CharSequence cs = v.coerceToString();
3159                         final String stringValue = cs != null ? cs.toString() : null;
3160                         property = new Property(name, stringValue, packageName, className);
3161                     } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
3162                         property = new Property(name, v.data != 0, packageName, className);
3163                     } else if (v.type >= TypedValue.TYPE_FIRST_INT
3164                             && v.type <= TypedValue.TYPE_LAST_INT) {
3165                         property = new Property(name, v.data, false, packageName, className);
3166                     } else if (v.type == TypedValue.TYPE_FLOAT) {
3167                         property = new Property(name, v.getFloat(), packageName, className);
3168                     } else {
3169                         if (!RIGID_PARSER) {
3170                             Slog.w(TAG,
3171                                     tagName + " only supports string, integer, float, color, "
3172                                             + "boolean, and resource reference types: "
3173                                             + parser.getName() + " at "
3174                                             + pkg.getBaseApkPath() + " "
3175                                             + parser.getPositionDescription());
3176                             property = null;
3177                         } else {
3178                             return input.error(tagName + " only supports string, integer, float, "
3179                                     + "color, boolean, and resource reference types");
3180                         }
3181                     }
3182                 } else {
3183                     return input.error(tagName + " requires an android:value "
3184                             + "or android:resource attribute");
3185                 }
3186             }
3187             return input.success(property);
3188         } finally {
3189             sa.recycle();
3190         }
3191     }
3192 
3193     /**
3194      * Collect certificates from all the APKs described in the given package. Also asserts that
3195      * all APK contents are signed correctly and consistently.
3196      *
3197      * TODO(b/155513789): Remove this in favor of collecting certificates during the original parse
3198      *  call if requested. Leaving this as an optional method for the caller means we have to
3199      *  construct a placeholder ParseInput.
3200      */
3201     @CheckResult
3202     public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
3203             ParsedPackage pkg, boolean skipVerify) {
3204         return getSigningDetails(input, pkg.getBaseApkPath(), pkg.isStaticSharedLibrary(),
3205                 pkg.getTargetSdkVersion(), pkg.getSplitCodePaths(), skipVerify);
3206     }
3207 
3208     @CheckResult
3209     private static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
3210             ParsingPackage pkg, boolean skipVerify) {
3211         return getSigningDetails(input, pkg.getBaseApkPath(), pkg.isStaticSharedLibrary(),
3212                 pkg.getTargetSdkVersion(), pkg.getSplitCodePaths(), skipVerify);
3213     }
3214 
3215     /**
3216      * Collect certificates from all the APKs described in the given package. Also asserts that
3217      * all APK contents are signed correctly and consistently.
3218      *
3219      * TODO(b/155513789): Remove this in favor of collecting certificates during the original parse
3220      *  call if requested. Leaving this as an optional method for the caller means we have to
3221      *  construct a dummy ParseInput.
3222      */
3223     @CheckResult
3224     public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
3225             String baseApkPath, boolean isStaticSharedLibrary, int targetSdkVersion,
3226             String[] splitCodePaths, boolean skipVerify) {
3227         SigningDetails signingDetails = SigningDetails.UNKNOWN;
3228 
3229         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
3230         try {
3231             ParseResult<SigningDetails> result = getSigningDetails(
3232                     input,
3233                     baseApkPath,
3234                     skipVerify,
3235                     isStaticSharedLibrary,
3236                     signingDetails,
3237                     targetSdkVersion
3238             );
3239             if (result.isError()) {
3240                 return input.error(result);
3241             }
3242 
3243             signingDetails = result.getResult();
3244             final File frameworkRes = new File(Environment.getRootDirectory(),
3245                     "framework/framework-res.apk");
3246             boolean isFrameworkResSplit = frameworkRes.getAbsolutePath()
3247                     .equals(baseApkPath);
3248             if (!ArrayUtils.isEmpty(splitCodePaths) && !isFrameworkResSplit) {
3249                 for (int i = 0; i < splitCodePaths.length; i++) {
3250                     result = getSigningDetails(
3251                             input,
3252                             splitCodePaths[i],
3253                             skipVerify,
3254                             isStaticSharedLibrary,
3255                             signingDetails,
3256                             targetSdkVersion
3257                     );
3258                     if (result.isError()) {
3259                         return input.error(result);
3260                     }
3261                 }
3262             }
3263             return result;
3264         } finally {
3265             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
3266         }
3267     }
3268 
3269     @CheckResult
3270     public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
3271             String baseCodePath, boolean skipVerify, boolean isStaticSharedLibrary,
3272             @NonNull SigningDetails existingSigningDetails, int targetSdk) {
3273         int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
3274                 targetSdk);
3275         if (isStaticSharedLibrary) {
3276             // must use v2 signing scheme
3277             minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
3278         }
3279         final ParseResult<SigningDetails> verified;
3280         if (skipVerify) {
3281             // systemDir APKs are already trusted, save time by not verifying
3282             verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(input, baseCodePath,
3283                     minSignatureScheme);
3284         } else {
3285             verified = ApkSignatureVerifier.verify(input, baseCodePath, minSignatureScheme);
3286         }
3287 
3288         if (verified.isError()) {
3289             return input.error(verified);
3290         }
3291 
3292         // Verify that entries are signed consistently with the first pkg
3293         // we encountered. Note that for splits, certificates may have
3294         // already been populated during an earlier parse of a base APK.
3295         if (existingSigningDetails == SigningDetails.UNKNOWN) {
3296             return verified;
3297         } else {
3298             if (!Signature.areExactMatch(existingSigningDetails, verified.getResult())) {
3299                 return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
3300                         baseCodePath + " has mismatched certificates");
3301             }
3302 
3303             return input.success(existingSigningDetails);
3304         }
3305     }
3306 
3307     /**
3308      * @hide
3309      */
3310     public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) {
3311         sCompatibilityModeEnabled = compatibilityModeEnabled;
3312     }
3313 
3314     /**
3315      * @hide
3316      */
3317     public static void readConfigUseRoundIcon(Resources r) {
3318         if (r != null) {
3319             sUseRoundIcon = r.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
3320             return;
3321         }
3322 
3323         final ApplicationInfo androidAppInfo;
3324         try {
3325             androidAppInfo = ActivityThread.getPackageManager().getApplicationInfo(
3326                     "android", 0 /* flags */,
3327                     UserHandle.myUserId());
3328         } catch (RemoteException e) {
3329             throw e.rethrowFromSystemServer();
3330         }
3331         final Resources systemResources = Resources.getSystem();
3332 
3333         // Create in-flight as this overlayable resource is only used when config changes
3334         final Resources overlayableRes = ResourcesManager.getInstance().getResources(
3335                 null /* activityToken */,
3336                 null /* resDir */,
3337                 null /* splitResDirs */,
3338                 androidAppInfo.resourceDirs,
3339                 androidAppInfo.overlayPaths,
3340                 androidAppInfo.sharedLibraryFiles,
3341                 null /* overrideDisplayId */,
3342                 null /* overrideConfig */,
3343                 systemResources.getCompatibilityInfo(),
3344                 systemResources.getClassLoader(),
3345                 null /* loaders */);
3346 
3347         sUseRoundIcon = overlayableRes.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
3348     }
3349 
3350     /*
3351      The following set of methods makes code easier to read by re-ordering the TypedArray methods.
3352 
3353      The first parameter is the default, which is the most important to understand for someone
3354      reading through the parsing code.
3355 
3356      That's followed by the attribute name, which is usually irrelevant during reading because
3357      it'll look like setSomeValue(true, R.styleable.ReallyLongParentName_SomeValueAttr... and
3358      the "setSomeValue" part is enough to communicate what the line does.
3359 
3360      Last comes the TypedArray, which is by far the least important since each try-with-resources
3361      should only have 1.
3362     */
3363 
3364     // Note there is no variant of bool without a defaultValue parameter, since explicit true/false
3365     // is important to specify when adding an attribute.
3366     private static boolean bool(boolean defaultValue, @StyleableRes int attribute, TypedArray sa) {
3367         return sa.getBoolean(attribute, defaultValue);
3368     }
3369 
3370     private static float aFloat(float defaultValue, @StyleableRes int attribute, TypedArray sa) {
3371         return sa.getFloat(attribute, defaultValue);
3372     }
3373 
3374     private static float aFloat(@StyleableRes int attribute, TypedArray sa) {
3375         return sa.getFloat(attribute, 0f);
3376     }
3377 
3378     private static int anInt(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
3379         return sa.getInt(attribute, defaultValue);
3380     }
3381 
3382     private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
3383         return sa.getInteger(attribute, defaultValue);
3384     }
3385 
3386     private static int anInt(@StyleableRes int attribute, TypedArray sa) {
3387         return sa.getInt(attribute, 0);
3388     }
3389 
3390     @AnyRes
3391     private static int resId(@StyleableRes int attribute, TypedArray sa) {
3392         return sa.getResourceId(attribute, 0);
3393     }
3394 
3395     private static String string(@StyleableRes int attribute, TypedArray sa) {
3396         return sa.getString(attribute);
3397     }
3398 
3399     private static String nonConfigString(int allowedChangingConfigs, @StyleableRes int attribute,
3400             TypedArray sa) {
3401         return sa.getNonConfigurationString(attribute, allowedChangingConfigs);
3402     }
3403 
3404     private static String nonResString(@StyleableRes int index, TypedArray sa) {
3405         return sa.getNonResourceString(index);
3406     }
3407 
3408     /**
3409      * Writes the keyset mapping to the provided package. {@code null} mappings are permitted.
3410      */
3411     public static void writeKeySetMapping(@NonNull Parcel dest,
3412             @NonNull Map<String, ArraySet<PublicKey>> keySetMapping) {
3413         if (keySetMapping == null) {
3414             dest.writeInt(-1);
3415             return;
3416         }
3417 
3418         final int N = keySetMapping.size();
3419         dest.writeInt(N);
3420 
3421         for (String key : keySetMapping.keySet()) {
3422             dest.writeString(key);
3423             ArraySet<PublicKey> keys = keySetMapping.get(key);
3424             if (keys == null) {
3425                 dest.writeInt(-1);
3426                 continue;
3427             }
3428 
3429             final int M = keys.size();
3430             dest.writeInt(M);
3431             for (int j = 0; j < M; j++) {
3432                 dest.writeSerializable(keys.valueAt(j));
3433             }
3434         }
3435     }
3436 
3437     /**
3438      * Reads a keyset mapping from the given parcel at the given data position. May return
3439      * {@code null} if the serialized mapping was {@code null}.
3440      */
3441     @NonNull
3442     public static ArrayMap<String, ArraySet<PublicKey>> readKeySetMapping(@NonNull Parcel in) {
3443         final int N = in.readInt();
3444         if (N == -1) {
3445             return null;
3446         }
3447 
3448         ArrayMap<String, ArraySet<PublicKey>> keySetMapping = new ArrayMap<>();
3449         for (int i = 0; i < N; ++i) {
3450             String key = in.readString();
3451             final int M = in.readInt();
3452             if (M == -1) {
3453                 keySetMapping.put(key, null);
3454                 continue;
3455             }
3456 
3457             ArraySet<PublicKey> keys = new ArraySet<>(M);
3458             for (int j = 0; j < M; ++j) {
3459                 PublicKey pk = (PublicKey) in.readSerializable(java.security.PublicKey.class.getClassLoader(), java.security.PublicKey.class);
3460                 keys.add(pk);
3461             }
3462 
3463             keySetMapping.put(key, keys);
3464         }
3465 
3466         return keySetMapping;
3467     }
3468 
3469     /**
3470      * Callback interface for retrieving information that may be needed while parsing
3471      * a package.
3472      */
3473     public interface Callback {
3474         boolean hasFeature(String feature);
3475 
3476         ParsingPackage startParsingPackage(@NonNull String packageName,
3477                 @NonNull String baseApkPath, @NonNull String path,
3478                 @NonNull TypedArray manifestArray, boolean isCoreApp);
3479 
3480         @NonNull Set<String> getHiddenApiWhitelistedApps();
3481 
3482         @NonNull Set<String> getInstallConstraintsAllowlist();
3483     }
3484 
3485     /**
3486      * Getter for the flags object
3487      */
3488     public static AconfigFlags getAconfigFlags() {
3489         return sAconfigFlags;
3490     }
3491 }
3492