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