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