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