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