1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.content.pm.parsing; 18 19 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; 20 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 21 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; 22 23 import android.annotation.NonNull; 24 import android.app.admin.DeviceAdminReceiver; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.PackageInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.SharedLibraryInfo; 29 import android.content.pm.SigningDetails; 30 import android.content.pm.VerifierInfo; 31 import android.content.pm.parsing.result.ParseInput; 32 import android.content.pm.parsing.result.ParseResult; 33 import android.content.res.ApkAssets; 34 import android.content.res.XmlResourceParser; 35 import android.os.Build; 36 import android.os.SystemProperties; 37 import android.os.Trace; 38 import android.text.TextUtils; 39 import android.util.ArrayMap; 40 import android.util.ArraySet; 41 import android.util.AttributeSet; 42 import android.util.EmptyArray; 43 import android.util.Pair; 44 import android.util.Slog; 45 46 import com.android.internal.pm.pkg.component.flags.Flags; 47 import com.android.internal.util.ArrayUtils; 48 import com.android.internal.util.XmlUtils; 49 50 import libcore.io.IoUtils; 51 import libcore.util.HexEncoding; 52 53 import org.xmlpull.v1.XmlPullParser; 54 import org.xmlpull.v1.XmlPullParserException; 55 56 import java.io.File; 57 import java.io.FileDescriptor; 58 import java.io.IOException; 59 import java.security.PublicKey; 60 import java.util.ArrayList; 61 import java.util.Arrays; 62 import java.util.Comparator; 63 import java.util.List; 64 import java.util.Objects; 65 import java.util.Set; 66 67 /** @hide */ 68 public class ApkLiteParseUtils { 69 70 private static final String TAG = "ApkLiteParseUtils"; 71 72 private static final int PARSE_DEFAULT_INSTALL_LOCATION = 73 PackageInfo.INSTALL_LOCATION_UNSPECIFIED; 74 75 private static final Comparator<String> sSplitNameComparator = new SplitNameComparator(); 76 77 public static final String APK_FILE_EXTENSION = ".apk"; 78 79 80 // Constants copied from services.jar side since they're not accessible 81 private static final String ANDROID_RES_NAMESPACE = 82 "http://schemas.android.com/apk/res/android"; 83 public static final int DEFAULT_MIN_SDK_VERSION = 1; 84 private static final int DEFAULT_TARGET_SDK_VERSION = 0; 85 public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml"; 86 private static final int PARSE_IS_SYSTEM_DIR = 1 << 4; 87 private static final int PARSE_COLLECT_CERTIFICATES = 1 << 5; 88 private static final String TAG_APPLICATION = "application"; 89 private static final String TAG_PACKAGE_VERIFIER = "package-verifier"; 90 private static final String TAG_PROFILEABLE = "profileable"; 91 private static final String TAG_RECEIVER = "receiver"; 92 private static final String TAG_OVERLAY = "overlay"; 93 private static final String TAG_USES_SDK = "uses-sdk"; 94 private static final String TAG_USES_SPLIT = "uses-split"; 95 private static final String TAG_MANIFEST = "manifest"; 96 private static final String TAG_USES_SDK_LIBRARY = "uses-sdk-library"; 97 private static final String TAG_USES_STATIC_LIBRARY = "uses-static-library"; 98 private static final String TAG_SDK_LIBRARY = "sdk-library"; 99 private static final int SDK_VERSION = Build.VERSION.SDK_INT; 100 private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES; 101 private static final String TAG_PROCESSES = "processes"; 102 private static final String TAG_PROCESS = "process"; 103 private static final String TAG_STATIC_LIBRARY = "static-library"; 104 private static final String TAG_LIBRARY = "library"; 105 106 /** 107 * Parse only lightweight details about the package at the given location. 108 * Automatically detects if the package is a monolithic style (single APK 109 * file) or cluster style (directory of APKs). 110 * <p> 111 * This performs validity checking on cluster style packages, such as 112 * requiring identical package name and version codes, a single base APK, 113 * and unique split names. 114 */ parsePackageLite(ParseInput input, File packageFile, int flags)115 public static ParseResult<PackageLite> parsePackageLite(ParseInput input, 116 File packageFile, int flags) { 117 if (packageFile.isDirectory()) { 118 return parseClusterPackageLite(input, packageFile, flags); 119 } else { 120 return parseMonolithicPackageLite(input, packageFile, flags); 121 } 122 } 123 124 /** 125 * Parse lightweight details about a single APK file. 126 */ parseMonolithicPackageLite(ParseInput input, File packageFile, int flags)127 public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input, 128 File packageFile, int flags) { 129 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); 130 try { 131 final ParseResult<ApkLite> result = parseApkLite(input, packageFile, flags); 132 if (result.isError()) { 133 return input.error(result); 134 } 135 136 final ApkLite baseApk = result.getResult(); 137 final String packagePath = packageFile.getAbsolutePath(); 138 return input.success( 139 new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */, 140 null /* isFeatureSplits */, null /* usesSplitNames */, 141 null /* configForSplit */, null /* splitApkPaths */, 142 null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(), 143 null /* requiredSplitTypes */, null /* splitTypes */)); 144 } finally { 145 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 146 } 147 } 148 149 /** 150 * Parse lightweight details about a single APK file passed as an FD. 151 */ parseMonolithicPackageLite(ParseInput input, FileDescriptor packageFd, String debugPathName, int flags)152 public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input, 153 FileDescriptor packageFd, String debugPathName, int flags) { 154 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); 155 try { 156 final ParseResult<ApkLite> result = parseApkLite(input, packageFd, debugPathName, 157 flags); 158 if (result.isError()) { 159 return input.error(result); 160 } 161 162 final ApkLite baseApk = result.getResult(); 163 final String packagePath = debugPathName; 164 return input.success( 165 new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */, 166 null /* isFeatureSplits */, null /* usesSplitNames */, 167 null /* configForSplit */, null /* splitApkPaths */, 168 null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(), 169 null /* requiredSplitTypes */, null /* splitTypes */)); 170 } finally { 171 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 172 } 173 } 174 175 /** 176 * Parse lightweight details about a directory of APKs. 177 * 178 * @param packageDir is the folder that contains split apks for a regular app 179 */ parseClusterPackageLite(ParseInput input, File packageDir, int flags)180 public static ParseResult<PackageLite> parseClusterPackageLite(ParseInput input, 181 File packageDir, int flags) { 182 final File[] files; 183 files = packageDir.listFiles(); 184 if (ArrayUtils.isEmpty(files)) { 185 return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK, 186 "No packages found in split"); 187 } 188 // Apk directory is directly nested under the current directory 189 if (files.length == 1 && files[0].isDirectory()) { 190 return parseClusterPackageLite(input, files[0], flags); 191 } 192 193 String packageName = null; 194 int versionCode = 0; 195 ApkLite baseApk = null; 196 197 final ArrayMap<String, ApkLite> apks = new ArrayMap<>(); 198 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); 199 try { 200 for (File file : files) { 201 if (!isApkFile(file)) { 202 continue; 203 } 204 205 final ParseResult<ApkLite> result = parseApkLite(input, file, flags); 206 if (result.isError()) { 207 return input.error(result); 208 } 209 210 final ApkLite lite = result.getResult(); 211 // Assert that all package names and version codes are 212 // consistent with the first one we encounter. 213 if (packageName == null) { 214 packageName = lite.getPackageName(); 215 versionCode = lite.getVersionCode(); 216 } else { 217 if (!packageName.equals(lite.getPackageName())) { 218 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, 219 "Inconsistent package " + lite.getPackageName() + " in " + file 220 + "; expected " + packageName); 221 } 222 if (versionCode != lite.getVersionCode()) { 223 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, 224 "Inconsistent version " + lite.getVersionCode() + " in " + file 225 + "; expected " + versionCode); 226 } 227 } 228 229 // Assert that each split is defined only once 230 ApkLite prev = apks.put(lite.getSplitName(), lite); 231 if (prev != null) { 232 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, 233 "Split name " + lite.getSplitName() 234 + " defined more than once; most recent was " + file 235 + ", previous was " + prev.getPath()); 236 } 237 } 238 baseApk = apks.remove(null); 239 } finally { 240 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 241 } 242 return composePackageLiteFromApks(input, packageDir, baseApk, apks); 243 } 244 245 /** 246 * Utility method that retrieves lightweight details about the package by given location, 247 * base APK, and split APKs. 248 * 249 * @param packageDir Path to the package 250 * @param baseApk Parsed base APK 251 * @param splitApks Parsed split APKs 252 * @return PackageLite 253 */ composePackageLiteFromApks(ParseInput input, File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks)254 public static ParseResult<PackageLite> composePackageLiteFromApks(ParseInput input, 255 File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks) { 256 return composePackageLiteFromApks(input, packageDir, baseApk, splitApks, false); 257 } 258 259 /** 260 * Utility method that retrieves lightweight details about the package by given location, 261 * base APK, and split APKs. 262 * 263 * @param packageDir Path to the package 264 * @param baseApk Parsed base APK 265 * @param splitApks Parsed split APKs 266 * @param apkRenamed Indicate whether the APKs are renamed after parsed. 267 * @return PackageLite 268 */ composePackageLiteFromApks( ParseInput input, File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks, boolean apkRenamed)269 public static ParseResult<PackageLite> composePackageLiteFromApks( 270 ParseInput input, File packageDir, ApkLite baseApk, 271 ArrayMap<String, ApkLite> splitApks, boolean apkRenamed) { 272 if (baseApk == null) { 273 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, 274 "Missing base APK in " + packageDir); 275 } 276 // Always apply deterministic ordering based on splitName 277 final int size = ArrayUtils.size(splitApks); 278 279 String[] splitNames = null; 280 Set<String>[] requiredSplitTypes = null; 281 Set<String>[] splitTypes = null; 282 boolean[] isFeatureSplits = null; 283 String[] usesSplitNames = null; 284 String[] configForSplits = null; 285 String[] splitCodePaths = null; 286 int[] splitRevisionCodes = null; 287 if (size > 0) { 288 splitNames = new String[size]; 289 requiredSplitTypes = new Set[size]; 290 splitTypes = new Set[size]; 291 isFeatureSplits = new boolean[size]; 292 usesSplitNames = new String[size]; 293 configForSplits = new String[size]; 294 splitCodePaths = new String[size]; 295 splitRevisionCodes = new int[size]; 296 297 splitNames = splitApks.keySet().toArray(splitNames); 298 Arrays.sort(splitNames, sSplitNameComparator); 299 300 for (int i = 0; i < size; i++) { 301 final ApkLite apk = splitApks.get(splitNames[i]); 302 requiredSplitTypes[i] = apk.getRequiredSplitTypes(); 303 splitTypes[i] = apk.getSplitTypes(); 304 usesSplitNames[i] = apk.getUsesSplitName(); 305 isFeatureSplits[i] = apk.isFeatureSplit(); 306 configForSplits[i] = apk.getConfigForSplit(); 307 splitCodePaths[i] = apkRenamed ? new File(packageDir, 308 splitNameToFileName(apk)).getAbsolutePath() : apk.getPath(); 309 splitRevisionCodes[i] = apk.getRevisionCode(); 310 } 311 } 312 313 final String codePath = packageDir.getAbsolutePath(); 314 final String baseCodePath = apkRenamed ? new File(packageDir, 315 splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.getPath(); 316 return input.success( 317 new PackageLite(codePath, baseCodePath, baseApk, splitNames, isFeatureSplits, 318 usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes, 319 baseApk.getTargetSdkVersion(), requiredSplitTypes, splitTypes)); 320 } 321 322 /** 323 * Utility method that retrieves canonical file name by given split name from parsed APK. 324 * 325 * @param apk Parsed APK 326 * @return The canonical file name 327 */ splitNameToFileName(@onNull ApkLite apk)328 public static String splitNameToFileName(@NonNull ApkLite apk) { 329 Objects.requireNonNull(apk); 330 final String fileName = apk.getSplitName() == null ? "base" : "split_" + apk.getSplitName(); 331 return fileName + APK_FILE_EXTENSION; 332 } 333 334 /** 335 * Utility method that retrieves lightweight details about a single APK 336 * file, including package name, split name, and install location. 337 * 338 * @param apkFile path to a single APK 339 * @param flags optional parse flags, such as 340 * {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES} 341 */ parseApkLite(ParseInput input, File apkFile, int flags)342 public static ParseResult<ApkLite> parseApkLite(ParseInput input, File apkFile, int flags) { 343 return parseApkLiteInner(input, apkFile, null, null, flags); 344 } 345 346 /** 347 * Utility method that retrieves lightweight details about a single APK 348 * file, including package name, split name, and install location. 349 * 350 * @param fd already open file descriptor of an apk file 351 * @param debugPathName arbitrary text name for this file, for debug output 352 * @param flags optional parse flags, such as 353 * {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES} 354 */ parseApkLite(ParseInput input, FileDescriptor fd, String debugPathName, int flags)355 public static ParseResult<ApkLite> parseApkLite(ParseInput input, 356 FileDescriptor fd, String debugPathName, int flags) { 357 return parseApkLiteInner(input, null, fd, debugPathName, flags); 358 } 359 parseApkLiteInner(ParseInput input, File apkFile, FileDescriptor fd, String debugPathName, int flags)360 private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input, 361 File apkFile, FileDescriptor fd, String debugPathName, int flags) { 362 final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); 363 364 XmlResourceParser parser = null; 365 ApkAssets apkAssets = null; 366 try { 367 try { 368 apkAssets = fd != null 369 ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */) 370 : ApkAssets.loadFromPath(apkPath); 371 } catch (IOException e) { 372 return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK, 373 "Failed to parse " + apkPath, e); 374 } 375 376 parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); 377 378 final SigningDetails signingDetails; 379 if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { 380 final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0; 381 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); 382 try { 383 final ParseResult<SigningDetails> result = 384 FrameworkParsingPackageUtils.getSigningDetails(input, 385 apkFile.getAbsolutePath(), 386 skipVerify, /* isStaticSharedLibrary */ false, 387 SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION); 388 if (result.isError()) { 389 return input.error(result); 390 } 391 signingDetails = result.getResult(); 392 } finally { 393 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 394 } 395 } else { 396 signingDetails = SigningDetails.UNKNOWN; 397 } 398 399 return parseApkLite(input, apkPath, parser, signingDetails, flags); 400 } catch (XmlPullParserException | IOException | RuntimeException e) { 401 Slog.w(TAG, "Failed to parse " + apkPath, e); 402 return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, 403 "Failed to parse " + apkPath, e); 404 } finally { 405 IoUtils.closeQuietly(parser); 406 if (apkAssets != null) { 407 try { 408 apkAssets.close(); 409 } catch (Throwable ignored) { 410 } 411 } 412 // TODO(b/72056911): Implement AutoCloseable on ApkAssets. 413 } 414 } 415 parseApkLite(ParseInput input, String codePath, XmlResourceParser parser, SigningDetails signingDetails, int flags)416 private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath, 417 XmlResourceParser parser, SigningDetails signingDetails, int flags) 418 throws IOException, XmlPullParserException { 419 ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser); 420 if (result.isError()) { 421 return input.error(result); 422 } 423 Pair<String, String> packageSplit = result.getResult(); 424 425 final ParseResult<Pair<Set<String>, Set<String>>> requiredSplitTypesResult = 426 parseRequiredSplitTypes(input, parser); 427 if (requiredSplitTypesResult.isError()) { 428 return input.error(result); 429 } 430 Pair<Set<String>, Set<String>> requiredSplitTypes = requiredSplitTypesResult.getResult(); 431 432 int installLocation = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, 433 "installLocation", PARSE_DEFAULT_INSTALL_LOCATION); 434 int versionCode = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "versionCode", 0); 435 int versionCodeMajor = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, 436 "versionCodeMajor", 437 0); 438 int revisionCode = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "revisionCode", 0); 439 boolean coreApp = parser.getAttributeBooleanValue(null, "coreApp", false); 440 boolean updatableSystem = parser.getAttributeBooleanValue(null, "updatableSystem", true); 441 boolean isolatedSplits = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, 442 "isolatedSplits", false); 443 boolean isFeatureSplit = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, 444 "isFeatureSplit", false); 445 boolean isSplitRequired = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, 446 "isSplitRequired", false); 447 String configForSplit = parser.getAttributeValue(null, "configForSplit"); 448 String emergencyInstaller = parser.getAttributeValue(null, "emergencyInstaller"); 449 450 int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION; 451 int minSdkVersion = DEFAULT_MIN_SDK_VERSION; 452 boolean debuggable = false; 453 boolean profilableByShell = false; 454 boolean multiArch = false; 455 boolean use32bitAbi = false; 456 boolean extractNativeLibs = true; 457 boolean useEmbeddedDex = false; 458 String usesSplitName = null; 459 String targetPackage = null; 460 boolean overlayIsStatic = false; 461 int overlayPriority = 0; 462 int rollbackDataPolicy = 0; 463 int pageSizeCompat = ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED; 464 465 String requiredSystemPropertyName = null; 466 String requiredSystemPropertyValue = null; 467 468 boolean hasDeviceAdminReceiver = false; 469 470 boolean isSdkLibrary = false; 471 boolean isStaticLibrary = false; 472 List<String> usesSdkLibraries = new ArrayList<>(); 473 long[] usesSdkLibrariesVersionsMajor = new long[0]; 474 String[][] usesSdkLibrariesCertDigests = new String[0][0]; 475 476 List<String> usesStaticLibraries = new ArrayList<>(); 477 long[] usesStaticLibrariesVersions = new long[0]; 478 String[][] usesStaticLibrariesCertDigests = new String[0][0]; 479 480 List<SharedLibraryInfo> declaredLibraries = new ArrayList<>(); 481 482 // Only search the tree when the tag is the direct child of <manifest> tag 483 int type; 484 final int searchDepth = parser.getDepth() + 1; 485 486 final List<VerifierInfo> verifiers = new ArrayList<>(); 487 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 488 && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) { 489 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 490 continue; 491 } 492 493 if (parser.getDepth() != searchDepth) { 494 continue; 495 } 496 497 if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) { 498 final VerifierInfo verifier = parseVerifier(parser); 499 if (verifier != null) { 500 verifiers.add(verifier); 501 } 502 } else if (TAG_APPLICATION.equals(parser.getName())) { 503 debuggable = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "debuggable", 504 false); 505 multiArch = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "multiArch", 506 false); 507 use32bitAbi = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "use32bitAbi", 508 false); 509 extractNativeLibs = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, 510 "extractNativeLibs", true); 511 useEmbeddedDex = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, 512 "useEmbeddedDex", false); 513 514 rollbackDataPolicy = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, 515 "rollbackDataPolicy", 0); 516 String permission = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 517 "permission"); 518 boolean hasBindDeviceAdminPermission = 519 android.Manifest.permission.BIND_DEVICE_ADMIN.equals(permission); 520 521 pageSizeCompat = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, 522 "pageSizeCompat", 523 ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED); 524 525 final int innerDepth = parser.getDepth(); 526 int innerType; 527 while ((innerType = parser.next()) != XmlPullParser.END_DOCUMENT 528 && (innerType != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { 529 if (innerType == XmlPullParser.END_TAG || innerType == XmlPullParser.TEXT) { 530 continue; 531 } 532 533 if (parser.getDepth() != innerDepth + 1) { 534 // Search only under <application>. 535 continue; 536 } 537 538 switch (parser.getName()) { 539 case TAG_PROFILEABLE: 540 profilableByShell = parser.getAttributeBooleanValue( 541 ANDROID_RES_NAMESPACE, "shell", profilableByShell); 542 break; 543 case TAG_RECEIVER: 544 hasDeviceAdminReceiver |= isDeviceAdminReceiver(parser, 545 hasBindDeviceAdminPermission); 546 break; 547 case TAG_USES_SDK_LIBRARY: 548 String usesSdkLibName = parser.getAttributeValue( 549 ANDROID_RES_NAMESPACE, "name"); 550 // TODO(b/391604666): Due to a bug in bundletool, old apps could be 551 // using versionMajor as string. Do not remove this workaround until 552 // b/391604666 is resolved. 553 String usesSdkLibVersionMajorString = parser.getAttributeValue( 554 ANDROID_RES_NAMESPACE, "versionMajor"); 555 long usesSdkLibVersionMajor = XmlUtils.convertValueToInt( 556 usesSdkLibVersionMajorString, -1); 557 String usesSdkCertDigest = parser.getAttributeValue( 558 ANDROID_RES_NAMESPACE, "certDigest"); 559 560 if (usesSdkLibName == null || usesSdkLibName.isBlank() 561 || usesSdkLibVersionMajor < 0) { 562 return input.error( 563 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 564 "Bad uses-sdk-library declaration name: " 565 + usesSdkLibName 566 + " version: " + usesSdkLibVersionMajor); 567 } 568 569 if (usesSdkLibraries.contains(usesSdkLibName)) { 570 return input.error( 571 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 572 "Bad uses-sdk-library declaration. Depending on" 573 + " multiple versions of SDK library: " 574 + usesSdkLibName); 575 } 576 577 usesSdkLibraries.add(usesSdkLibName); 578 usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong( 579 usesSdkLibrariesVersionsMajor, usesSdkLibVersionMajor, 580 /*allowDuplicates=*/ true); 581 582 usesSdkCertDigest = normalizeCertDigest(usesSdkCertDigest); 583 584 if ("".equals(usesSdkCertDigest)) { 585 // Test-only uses-sdk-library empty certificate digest override. 586 usesSdkCertDigest = SystemProperties.get( 587 "debug.pm.uses_sdk_library_default_cert_digest", ""); 588 // Validate the overridden digest. 589 try { 590 HexEncoding.decode(usesSdkCertDigest, false); 591 } catch (IllegalArgumentException e) { 592 usesSdkCertDigest = ""; 593 } 594 } 595 // TODO(372862145): Add support for multiple signer 596 usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class, 597 usesSdkLibrariesCertDigests, new String[]{usesSdkCertDigest}, 598 /*allowDuplicates=*/ true); 599 break; 600 case TAG_USES_STATIC_LIBRARY: 601 String usesStaticLibName = parser.getAttributeValue( 602 ANDROID_RES_NAMESPACE, "name"); 603 long usesStaticLibVersion = parser.getAttributeIntValue( 604 ANDROID_RES_NAMESPACE, "version", -1); 605 String usesStaticLibCertDigest = parser.getAttributeValue( 606 ANDROID_RES_NAMESPACE, "certDigest"); 607 608 if (usesStaticLibName == null || usesStaticLibName.isBlank() 609 || usesStaticLibVersion < 0 610 || usesStaticLibCertDigest == null) { 611 return input.error( 612 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 613 "Bad uses-static-library declaration name: " 614 + usesStaticLibName 615 + " version: " + usesStaticLibVersion 616 + " certDigest: " + usesStaticLibCertDigest); 617 } 618 619 if (usesStaticLibraries.contains(usesStaticLibName)) { 620 return input.error( 621 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 622 "Bad uses-sdk-library declaration. Depending on" 623 + " multiple versions of static library: " 624 + usesStaticLibName); 625 } 626 627 usesStaticLibraries.add(usesStaticLibName); 628 usesStaticLibrariesVersions = ArrayUtils.appendLong( 629 usesStaticLibrariesVersions, usesStaticLibVersion, 630 /*allowDuplicates=*/ true); 631 632 usesStaticLibCertDigest = normalizeCertDigest(usesStaticLibCertDigest); 633 634 ParseResult<String[]> certResult = 635 parseAdditionalCertificates(input, parser); 636 if (certResult.isError()) { 637 return input.error(certResult); 638 } 639 String[] additionalCertSha256Digests = certResult.getResult(); 640 String[] certSha256Digests = 641 new String[additionalCertSha256Digests.length + 1]; 642 certSha256Digests[0] = usesStaticLibCertDigest; 643 System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests, 644 1, additionalCertSha256Digests.length); 645 646 usesStaticLibrariesCertDigests = ArrayUtils.appendElement( 647 String[].class, usesStaticLibrariesCertDigests, 648 certSha256Digests, /*allowDuplicates=*/ true); 649 break; 650 case TAG_SDK_LIBRARY: 651 isSdkLibrary = true; 652 // Mirrors ParsingPackageUtils#parseSdkLibrary until lite and full 653 // parsing are combined 654 String sdkLibName = parser.getAttributeValue( 655 ANDROID_RES_NAMESPACE, "name"); 656 int sdkLibVersionMajor = parser.getAttributeIntValue( 657 ANDROID_RES_NAMESPACE, "versionMajor", -1); 658 if (sdkLibName == null || sdkLibVersionMajor < 0) { 659 return input.error( 660 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 661 "Bad sdk-library declaration name: " + sdkLibName 662 + " version: " + sdkLibVersionMajor); 663 } 664 declaredLibraries.add(new SharedLibraryInfo( 665 sdkLibName, sdkLibVersionMajor, 666 SharedLibraryInfo.TYPE_SDK_PACKAGE)); 667 break; 668 case TAG_STATIC_LIBRARY: 669 isStaticLibrary = true; 670 // Mirrors ParsingPackageUtils#parseStaticLibrary until lite and full 671 // parsing are combined 672 String staticLibName = parser.getAttributeValue( 673 ANDROID_RES_NAMESPACE, "name"); 674 int staticLibVersion = parser.getAttributeIntValue( 675 ANDROID_RES_NAMESPACE, "version", -1); 676 int staticLibVersionMajor = parser.getAttributeIntValue( 677 ANDROID_RES_NAMESPACE, "versionMajor", 0); 678 if (staticLibName == null || staticLibVersion < 0) { 679 return input.error("Bad static-library declaration name: " 680 + staticLibName + " version: " + staticLibVersion); 681 } 682 declaredLibraries.add(new SharedLibraryInfo(staticLibName, 683 PackageInfo.composeLongVersionCode(staticLibVersionMajor, 684 staticLibVersion), SharedLibraryInfo.TYPE_STATIC)); 685 break; 686 case TAG_LIBRARY: 687 // Mirrors ParsingPackageUtils#parseLibrary until lite and full parsing 688 // are combined 689 String libName = parser.getAttributeValue( 690 ANDROID_RES_NAMESPACE, "name"); 691 if (libName == null) { 692 return input.error("Bad library declaration name: null"); 693 } 694 libName = libName.intern(); 695 declaredLibraries.add(new SharedLibraryInfo(libName, 696 SharedLibraryInfo.VERSION_UNDEFINED, 697 SharedLibraryInfo.TYPE_DYNAMIC)); 698 break; 699 case TAG_PROCESSES: 700 final int processesDepth = parser.getDepth(); 701 int processesType; 702 while ((processesType = parser.next()) != XmlPullParser.END_DOCUMENT 703 && (processesType != XmlPullParser.END_TAG 704 || parser.getDepth() > processesDepth)) { 705 if (processesType == XmlPullParser.END_TAG 706 || processesType == XmlPullParser.TEXT) { 707 continue; 708 } 709 710 if (parser.getDepth() != processesDepth + 1) { 711 // Search only under <processes>. 712 continue; 713 } 714 715 if (parser.getName().equals(TAG_PROCESS) 716 && Flags.enablePerProcessUseEmbeddedDexAttr()) { 717 useEmbeddedDex |= parser.getAttributeBooleanValue( 718 ANDROID_RES_NAMESPACE, "useEmbeddedDex", false); 719 } 720 } 721 } 722 } 723 } else if (TAG_OVERLAY.equals(parser.getName())) { 724 requiredSystemPropertyName = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 725 "requiredSystemPropertyName"); 726 requiredSystemPropertyValue = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 727 "requiredSystemPropertyValue"); 728 targetPackage = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "targetPackage"); 729 overlayIsStatic = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "isStatic", 730 false); 731 overlayPriority = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "priority", 0); 732 } else if (TAG_USES_SPLIT.equals(parser.getName())) { 733 if (usesSplitName != null) { 734 Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others."); 735 continue; 736 } 737 738 usesSplitName = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "name"); 739 if (usesSplitName == null) { 740 return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 741 "<uses-split> tag requires 'android:name' attribute"); 742 } 743 } else if (TAG_USES_SDK.equals(parser.getName())) { 744 // Mirrors FrameworkParsingPackageUtils#parseUsesSdk until lite and full parsing is combined 745 String minSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 746 "minSdkVersion"); 747 String targetSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 748 "targetSdkVersion"); 749 750 int minVer = DEFAULT_MIN_SDK_VERSION; 751 String minCode = null; 752 boolean minAssigned = false; 753 int targetVer = DEFAULT_TARGET_SDK_VERSION; 754 String targetCode = null; 755 756 if (!TextUtils.isEmpty(minSdkVersionString)) { 757 try { 758 minVer = Integer.parseInt(minSdkVersionString); 759 minAssigned = true; 760 } catch (NumberFormatException ignored) { 761 minCode = minSdkVersionString; 762 minAssigned = !TextUtils.isEmpty(minCode); 763 } 764 } 765 766 if (!TextUtils.isEmpty(targetSdkVersionString)) { 767 try { 768 targetVer = Integer.parseInt(targetSdkVersionString); 769 } catch (NumberFormatException ignored) { 770 targetCode = targetSdkVersionString; 771 if (!minAssigned) { 772 minCode = targetCode; 773 } 774 } 775 } else { 776 targetVer = minVer; 777 targetCode = minCode; 778 } 779 780 boolean allowUnknownCodenames = false; 781 if ((flags & FrameworkParsingPackageUtils.PARSE_APK_IN_APEX) != 0) { 782 allowUnknownCodenames = true; 783 } 784 785 ParseResult<Integer> targetResult = FrameworkParsingPackageUtils.computeTargetSdkVersion( 786 targetVer, targetCode, SDK_CODENAMES, input, 787 allowUnknownCodenames); 788 if (targetResult.isError()) { 789 return input.error(targetResult); 790 } 791 targetSdkVersion = targetResult.getResult(); 792 793 ParseResult<Integer> minResult = FrameworkParsingPackageUtils.computeMinSdkVersion( 794 minVer, minCode, SDK_VERSION, SDK_CODENAMES, input); 795 if (minResult.isError()) { 796 return input.error(minResult); 797 } 798 minSdkVersion = minResult.getResult(); 799 } 800 } 801 802 // Check to see if overlay should be excluded based on system property condition 803 if ((flags & FrameworkParsingPackageUtils.PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY) 804 == 0 && !FrameworkParsingPackageUtils.checkRequiredSystemProperties( 805 requiredSystemPropertyName, requiredSystemPropertyValue)) { 806 String message = "Skipping target and overlay pair " + targetPackage + " and " 807 + codePath + ": overlay ignored due to required system property: " 808 + requiredSystemPropertyName + " with value: " + requiredSystemPropertyValue; 809 Slog.i(TAG, message); 810 return input.skip(message); 811 } 812 813 return input.success( 814 new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit, 815 configForSplit, usesSplitName, isSplitRequired, versionCode, 816 versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails, 817 coreApp, debuggable, profilableByShell, multiArch, use32bitAbi, 818 useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage, 819 overlayIsStatic, overlayPriority, requiredSystemPropertyName, 820 requiredSystemPropertyValue, minSdkVersion, targetSdkVersion, 821 rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second, 822 hasDeviceAdminReceiver, isSdkLibrary, usesSdkLibraries, 823 usesSdkLibrariesVersionsMajor, usesSdkLibrariesCertDigests, isStaticLibrary, 824 usesStaticLibraries, usesStaticLibrariesVersions, 825 usesStaticLibrariesCertDigests, updatableSystem, emergencyInstaller, 826 declaredLibraries, pageSizeCompat)); 827 } 828 parseAdditionalCertificates(ParseInput input, XmlResourceParser parser)829 private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input, 830 XmlResourceParser parser) throws XmlPullParserException, IOException { 831 String[] certSha256Digests = EmptyArray.STRING; 832 final int depth = parser.getDepth(); 833 int type; 834 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 835 && (type != XmlPullParser.END_TAG 836 || parser.getDepth() > depth)) { 837 if (type != XmlPullParser.START_TAG) { 838 continue; 839 } 840 final String nodeName = parser.getName(); 841 if (nodeName.equals("additional-certificate")) { 842 String certSha256Digest = parser.getAttributeValue( 843 ANDROID_RES_NAMESPACE, "certDigest"); 844 if (TextUtils.isEmpty(certSha256Digest)) { 845 return input.error("Bad additional-certificate declaration with empty" 846 + " certDigest:" + certSha256Digest); 847 } 848 849 certSha256Digest = normalizeCertDigest(certSha256Digest); 850 certSha256Digests = ArrayUtils.appendElement(String.class, 851 certSha256Digests, certSha256Digest); 852 } 853 } 854 855 return input.success(certSha256Digests); 856 } 857 858 /** 859 * We allow ":" delimiters in the SHA declaration as this is the format emitted by the 860 * certtool making it easy for developers to copy/paste. 861 */ normalizeCertDigest(String certDigest)862 private static String normalizeCertDigest(String certDigest) { 863 return certDigest.replace(":", "").toLowerCase(); 864 } 865 isDeviceAdminReceiver( XmlResourceParser parser, boolean applicationHasBindDeviceAdminPermission)866 private static boolean isDeviceAdminReceiver( 867 XmlResourceParser parser, boolean applicationHasBindDeviceAdminPermission) 868 throws XmlPullParserException, IOException { 869 String permission = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 870 "permission"); 871 if (!applicationHasBindDeviceAdminPermission 872 && !android.Manifest.permission.BIND_DEVICE_ADMIN.equals(permission)) { 873 return false; 874 } 875 876 boolean hasDeviceAdminReceiver = false; 877 final int depth = parser.getDepth(); 878 int type; 879 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 880 && (type != XmlPullParser.END_TAG || parser.getDepth() > depth)) { 881 if (type == XmlPullParser.END_TAG 882 || type == XmlPullParser.TEXT) { 883 continue; 884 } 885 if (parser.getDepth() != depth + 1) { 886 // Search only under <receiver>. 887 continue; 888 } 889 if (!hasDeviceAdminReceiver && "meta-data".equals(parser.getName())) { 890 String name = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 891 "name"); 892 if (DeviceAdminReceiver.DEVICE_ADMIN_META_DATA.equals(name)) { 893 hasDeviceAdminReceiver = true; 894 } 895 } 896 } 897 return hasDeviceAdminReceiver; 898 } 899 parsePackageSplitNames(ParseInput input, XmlResourceParser parser)900 public static ParseResult<Pair<String, String>> parsePackageSplitNames(ParseInput input, 901 XmlResourceParser parser) throws IOException, XmlPullParserException { 902 int type; 903 while ((type = parser.next()) != XmlPullParser.START_TAG 904 && type != XmlPullParser.END_DOCUMENT) { 905 } 906 907 if (type != XmlPullParser.START_TAG) { 908 return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 909 "No start tag found"); 910 } 911 if (!parser.getName().equals(TAG_MANIFEST)) { 912 return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 913 "No <manifest> tag"); 914 } 915 916 final String packageName = parser.getAttributeValue(null, "package"); 917 if (!"android".equals(packageName)) { 918 final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, 919 packageName, true, true); 920 if (nameResult.isError()) { 921 return input.error(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, 922 "Invalid manifest package: " + nameResult.getErrorMessage()); 923 } 924 } 925 926 String splitName = parser.getAttributeValue(null, "split"); 927 if (splitName != null) { 928 if (splitName.length() == 0) { 929 splitName = null; 930 } else { 931 final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, 932 splitName, false, false); 933 if (nameResult.isError()) { 934 return input.error(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, 935 "Invalid manifest split: " + nameResult.getErrorMessage()); 936 } 937 } 938 } 939 940 return input.success(Pair.create(packageName.intern(), 941 (splitName != null) ? splitName.intern() : splitName)); 942 } 943 944 /** 945 * Utility method that parses attributes android:requiredSplitTypes and android:splitTypes. 946 */ parseRequiredSplitTypes( ParseInput input, XmlResourceParser parser)947 public static ParseResult<Pair<Set<String>, Set<String>>> parseRequiredSplitTypes( 948 ParseInput input, XmlResourceParser parser) { 949 Set<String> requiredSplitTypes = null; 950 Set<String> splitTypes = null; 951 String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "requiredSplitTypes"); 952 if (!TextUtils.isEmpty(value)) { 953 final ParseResult<Set<String>> result = separateAndValidateSplitTypes(input, value); 954 if (result.isError()) { 955 return input.error(result); 956 } 957 requiredSplitTypes = result.getResult(); 958 } 959 960 value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "splitTypes"); 961 if (!TextUtils.isEmpty(value)) { 962 final ParseResult<Set<String>> result = separateAndValidateSplitTypes(input, value); 963 if (result.isError()) { 964 return input.error(result); 965 } 966 splitTypes = result.getResult(); 967 } 968 969 return input.success(Pair.create(requiredSplitTypes, splitTypes)); 970 } 971 separateAndValidateSplitTypes(ParseInput input, String values)972 private static ParseResult<Set<String>> separateAndValidateSplitTypes(ParseInput input, 973 String values) { 974 final Set<String> ret = new ArraySet<>(); 975 for (String value : values.trim().split(",")) { 976 final String type = value.trim(); 977 // Using requireFilename as true because it limits length of the name to the 978 // {@link #MAX_FILE_NAME_SIZE}. 979 final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, type, 980 false /* requireSeparator */, true /* requireFilename */); 981 if (nameResult.isError()) { 982 return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 983 "Invalid manifest split types: " + nameResult.getErrorMessage()); 984 } 985 if (!ret.add(type)) { 986 Slog.w(TAG, type + " was defined multiple times"); 987 } 988 } 989 return input.success(ret); 990 } 991 parseVerifier(AttributeSet attrs)992 public static VerifierInfo parseVerifier(AttributeSet attrs) { 993 String packageName = attrs.getAttributeValue(ANDROID_RES_NAMESPACE, "name"); 994 String encodedPublicKey = attrs.getAttributeValue(ANDROID_RES_NAMESPACE, "publicKey"); 995 996 if (packageName == null || packageName.length() == 0) { 997 Slog.i(TAG, "verifier package name was null; skipping"); 998 return null; 999 } 1000 1001 final PublicKey publicKey = FrameworkParsingPackageUtils.parsePublicKey(encodedPublicKey); 1002 if (publicKey == null) { 1003 Slog.i(TAG, "Unable to parse verifier public key for " + packageName); 1004 return null; 1005 } 1006 1007 return new VerifierInfo(packageName, publicKey); 1008 } 1009 1010 /** 1011 * Used to sort a set of APKs based on their split names, always placing the 1012 * base APK (with {@code null} split name) first. 1013 */ 1014 private static class SplitNameComparator implements Comparator<String> { 1015 @Override compare(String lhs, String rhs)1016 public int compare(String lhs, String rhs) { 1017 if (lhs == null) { 1018 return -1; 1019 } else if (rhs == null) { 1020 return 1; 1021 } else { 1022 return lhs.compareTo(rhs); 1023 } 1024 } 1025 } 1026 1027 /** 1028 * Check if the given file is an APK file. 1029 * 1030 * @param file the file to check. 1031 * @return {@code true} if the given file is an APK file. 1032 */ isApkFile(File file)1033 public static boolean isApkFile(File file) { 1034 return isApkPath(file.getName()); 1035 } 1036 1037 /** 1038 * Check if the given path ends with APK file extension. 1039 * 1040 * @param path the path to check. 1041 * @return {@code true} if the given path ends with APK file extension. 1042 */ isApkPath(String path)1043 public static boolean isApkPath(String path) { 1044 return path.endsWith(APK_FILE_EXTENSION); 1045 } 1046 } 1047