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