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