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