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.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.SigningDetails; 28 import android.content.pm.VerifierInfo; 29 import android.content.pm.parsing.result.ParseInput; 30 import android.content.pm.parsing.result.ParseResult; 31 import android.content.res.ApkAssets; 32 import android.content.res.XmlResourceParser; 33 import android.os.Build; 34 import android.os.Trace; 35 import android.text.TextUtils; 36 import android.util.ArrayMap; 37 import android.util.ArraySet; 38 import android.util.AttributeSet; 39 import android.util.Pair; 40 import android.util.Slog; 41 42 import com.android.internal.util.ArrayUtils; 43 44 import libcore.io.IoUtils; 45 46 import org.xmlpull.v1.XmlPullParser; 47 import org.xmlpull.v1.XmlPullParserException; 48 49 import java.io.File; 50 import java.io.FileDescriptor; 51 import java.io.IOException; 52 import java.security.PublicKey; 53 import java.util.ArrayList; 54 import java.util.Arrays; 55 import java.util.Comparator; 56 import java.util.List; 57 import java.util.Objects; 58 import java.util.Set; 59 60 /** @hide */ 61 public class ApkLiteParseUtils { 62 63 private static final String TAG = "ApkLiteParseUtils"; 64 65 private static final int PARSE_DEFAULT_INSTALL_LOCATION = 66 PackageInfo.INSTALL_LOCATION_UNSPECIFIED; 67 68 private static final Comparator<String> sSplitNameComparator = new SplitNameComparator(); 69 70 public static final String APK_FILE_EXTENSION = ".apk"; 71 72 73 // Constants copied from services.jar side since they're not accessible 74 private static final String ANDROID_RES_NAMESPACE = 75 "http://schemas.android.com/apk/res/android"; 76 private static final int DEFAULT_MIN_SDK_VERSION = 1; 77 private static final int DEFAULT_TARGET_SDK_VERSION = 0; 78 public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml"; 79 private static final int PARSE_IS_SYSTEM_DIR = 1 << 4; 80 private static final int PARSE_COLLECT_CERTIFICATES = 1 << 5; 81 private static final String TAG_APPLICATION = "application"; 82 private static final String TAG_PACKAGE_VERIFIER = "package-verifier"; 83 private static final String TAG_PROFILEABLE = "profileable"; 84 private static final String TAG_RECEIVER = "receiver"; 85 private static final String TAG_OVERLAY = "overlay"; 86 private static final String TAG_USES_SDK = "uses-sdk"; 87 private static final String TAG_USES_SPLIT = "uses-split"; 88 private static final String TAG_MANIFEST = "manifest"; 89 private static final String TAG_SDK_LIBRARY = "sdk-library"; 90 private static final int SDK_VERSION = Build.VERSION.SDK_INT; 91 private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES; 92 93 /** 94 * Parse only lightweight details about the package at the given location. 95 * Automatically detects if the package is a monolithic style (single APK 96 * file) or cluster style (directory of APKs). 97 * <p> 98 * This performs validity checking on cluster style packages, such as 99 * requiring identical package name and version codes, a single base APK, 100 * and unique split names. 101 */ parsePackageLite(ParseInput input, File packageFile, int flags)102 public static ParseResult<PackageLite> parsePackageLite(ParseInput input, 103 File packageFile, int flags) { 104 if (packageFile.isDirectory()) { 105 return parseClusterPackageLite(input, packageFile, flags); 106 } else { 107 return parseMonolithicPackageLite(input, packageFile, flags); 108 } 109 } 110 111 /** 112 * Parse lightweight details about a single APK file. 113 */ parseMonolithicPackageLite(ParseInput input, File packageFile, int flags)114 public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input, 115 File packageFile, int flags) { 116 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); 117 try { 118 final ParseResult<ApkLite> result = parseApkLite(input, packageFile, flags); 119 if (result.isError()) { 120 return input.error(result); 121 } 122 123 final ApkLite baseApk = result.getResult(); 124 final String packagePath = packageFile.getAbsolutePath(); 125 return input.success( 126 new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */, 127 null /* isFeatureSplits */, null /* usesSplitNames */, 128 null /* configForSplit */, null /* splitApkPaths */, 129 null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(), 130 null /* requiredSplitTypes */, null /* splitTypes */)); 131 } finally { 132 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 133 } 134 } 135 136 /** 137 * Parse lightweight details about a single APK file passed as an FD. 138 */ parseMonolithicPackageLite(ParseInput input, FileDescriptor packageFd, String debugPathName, int flags)139 public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input, 140 FileDescriptor packageFd, String debugPathName, int flags) { 141 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); 142 try { 143 final ParseResult<ApkLite> result = parseApkLite(input, packageFd, debugPathName, 144 flags); 145 if (result.isError()) { 146 return input.error(result); 147 } 148 149 final ApkLite baseApk = result.getResult(); 150 final String packagePath = debugPathName; 151 return input.success( 152 new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */, 153 null /* isFeatureSplits */, null /* usesSplitNames */, 154 null /* configForSplit */, null /* splitApkPaths */, 155 null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(), 156 null /* requiredSplitTypes */, null /* splitTypes */)); 157 } finally { 158 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 159 } 160 } 161 162 /** 163 * Parse lightweight details about a directory of APKs. 164 * 165 * @param packageDir is the folder that contains split apks for a regular app 166 */ parseClusterPackageLite(ParseInput input, File packageDir, int flags)167 public static ParseResult<PackageLite> parseClusterPackageLite(ParseInput input, 168 File packageDir, int flags) { 169 final File[] files; 170 files = packageDir.listFiles(); 171 if (ArrayUtils.isEmpty(files)) { 172 return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK, 173 "No packages found in split"); 174 } 175 // Apk directory is directly nested under the current directory 176 if (files.length == 1 && files[0].isDirectory()) { 177 return parseClusterPackageLite(input, files[0], flags); 178 } 179 180 String packageName = null; 181 int versionCode = 0; 182 ApkLite baseApk = null; 183 184 final ArrayMap<String, ApkLite> apks = new ArrayMap<>(); 185 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); 186 try { 187 for (File file : files) { 188 if (!isApkFile(file)) { 189 continue; 190 } 191 192 final ParseResult<ApkLite> result = parseApkLite(input, file, flags); 193 if (result.isError()) { 194 return input.error(result); 195 } 196 197 final ApkLite lite = result.getResult(); 198 // Assert that all package names and version codes are 199 // consistent with the first one we encounter. 200 if (packageName == null) { 201 packageName = lite.getPackageName(); 202 versionCode = lite.getVersionCode(); 203 } else { 204 if (!packageName.equals(lite.getPackageName())) { 205 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, 206 "Inconsistent package " + lite.getPackageName() + " in " + file 207 + "; expected " + packageName); 208 } 209 if (versionCode != lite.getVersionCode()) { 210 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, 211 "Inconsistent version " + lite.getVersionCode() + " in " + file 212 + "; expected " + versionCode); 213 } 214 } 215 216 // Assert that each split is defined only once 217 ApkLite prev = apks.put(lite.getSplitName(), lite); 218 if (prev != null) { 219 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, 220 "Split name " + lite.getSplitName() 221 + " defined more than once; most recent was " + file 222 + ", previous was " + prev.getPath()); 223 } 224 } 225 baseApk = apks.remove(null); 226 } finally { 227 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 228 } 229 return composePackageLiteFromApks(input, packageDir, baseApk, apks); 230 } 231 232 /** 233 * Utility method that retrieves lightweight details about the package by given location, 234 * base APK, and split APKs. 235 * 236 * @param packageDir Path to the package 237 * @param baseApk Parsed base APK 238 * @param splitApks Parsed split APKs 239 * @return PackageLite 240 */ composePackageLiteFromApks(ParseInput input, File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks)241 public static ParseResult<PackageLite> composePackageLiteFromApks(ParseInput input, 242 File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks) { 243 return composePackageLiteFromApks(input, packageDir, baseApk, splitApks, false); 244 } 245 246 /** 247 * Utility method that retrieves lightweight details about the package by given location, 248 * base APK, and split APKs. 249 * 250 * @param packageDir Path to the package 251 * @param baseApk Parsed base APK 252 * @param splitApks Parsed split APKs 253 * @param apkRenamed Indicate whether the APKs are renamed after parsed. 254 * @return PackageLite 255 */ composePackageLiteFromApks( ParseInput input, File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks, boolean apkRenamed)256 public static ParseResult<PackageLite> composePackageLiteFromApks( 257 ParseInput input, File packageDir, ApkLite baseApk, 258 ArrayMap<String, ApkLite> splitApks, boolean apkRenamed) { 259 if (baseApk == null) { 260 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST, 261 "Missing base APK in " + packageDir); 262 } 263 // Always apply deterministic ordering based on splitName 264 final int size = ArrayUtils.size(splitApks); 265 266 String[] splitNames = null; 267 Set<String>[] requiredSplitTypes = null; 268 Set<String>[] splitTypes = null; 269 boolean[] isFeatureSplits = null; 270 String[] usesSplitNames = null; 271 String[] configForSplits = null; 272 String[] splitCodePaths = null; 273 int[] splitRevisionCodes = null; 274 if (size > 0) { 275 splitNames = new String[size]; 276 requiredSplitTypes = new Set[size]; 277 splitTypes = new Set[size]; 278 isFeatureSplits = new boolean[size]; 279 usesSplitNames = new String[size]; 280 configForSplits = new String[size]; 281 splitCodePaths = new String[size]; 282 splitRevisionCodes = new int[size]; 283 284 splitNames = splitApks.keySet().toArray(splitNames); 285 Arrays.sort(splitNames, sSplitNameComparator); 286 287 for (int i = 0; i < size; i++) { 288 final ApkLite apk = splitApks.get(splitNames[i]); 289 requiredSplitTypes[i] = apk.getRequiredSplitTypes(); 290 splitTypes[i] = apk.getSplitTypes(); 291 usesSplitNames[i] = apk.getUsesSplitName(); 292 isFeatureSplits[i] = apk.isFeatureSplit(); 293 configForSplits[i] = apk.getConfigForSplit(); 294 splitCodePaths[i] = apkRenamed ? new File(packageDir, 295 splitNameToFileName(apk)).getAbsolutePath() : apk.getPath(); 296 splitRevisionCodes[i] = apk.getRevisionCode(); 297 } 298 } 299 300 final String codePath = packageDir.getAbsolutePath(); 301 final String baseCodePath = apkRenamed ? new File(packageDir, 302 splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.getPath(); 303 return input.success( 304 new PackageLite(codePath, baseCodePath, baseApk, splitNames, isFeatureSplits, 305 usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes, 306 baseApk.getTargetSdkVersion(), requiredSplitTypes, splitTypes)); 307 } 308 309 /** 310 * Utility method that retrieves canonical file name by given split name from parsed APK. 311 * 312 * @param apk Parsed APK 313 * @return The canonical file name 314 */ splitNameToFileName(@onNull ApkLite apk)315 public static String splitNameToFileName(@NonNull ApkLite apk) { 316 Objects.requireNonNull(apk); 317 final String fileName = apk.getSplitName() == null ? "base" : "split_" + apk.getSplitName(); 318 return fileName + APK_FILE_EXTENSION; 319 } 320 321 /** 322 * Utility method that retrieves lightweight details about a single APK 323 * file, including package name, split name, and install location. 324 * 325 * @param apkFile path to a single APK 326 * @param flags optional parse flags, such as 327 * {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES} 328 */ parseApkLite(ParseInput input, File apkFile, int flags)329 public static ParseResult<ApkLite> parseApkLite(ParseInput input, File apkFile, int flags) { 330 return parseApkLiteInner(input, apkFile, null, null, flags); 331 } 332 333 /** 334 * Utility method that retrieves lightweight details about a single APK 335 * file, including package name, split name, and install location. 336 * 337 * @param fd already open file descriptor of an apk file 338 * @param debugPathName arbitrary text name for this file, for debug output 339 * @param flags optional parse flags, such as 340 * {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES} 341 */ parseApkLite(ParseInput input, FileDescriptor fd, String debugPathName, int flags)342 public static ParseResult<ApkLite> parseApkLite(ParseInput input, 343 FileDescriptor fd, String debugPathName, int flags) { 344 return parseApkLiteInner(input, null, fd, debugPathName, flags); 345 } 346 parseApkLiteInner(ParseInput input, File apkFile, FileDescriptor fd, String debugPathName, int flags)347 private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input, 348 File apkFile, FileDescriptor fd, String debugPathName, int flags) { 349 final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); 350 351 XmlResourceParser parser = null; 352 ApkAssets apkAssets = null; 353 try { 354 try { 355 apkAssets = fd != null 356 ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */) 357 : ApkAssets.loadFromPath(apkPath); 358 } catch (IOException e) { 359 return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK, 360 "Failed to parse " + apkPath, e); 361 } 362 363 parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); 364 365 final SigningDetails signingDetails; 366 if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { 367 final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0; 368 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); 369 try { 370 final ParseResult<SigningDetails> result = 371 FrameworkParsingPackageUtils.getSigningDetails(input, 372 apkFile.getAbsolutePath(), 373 skipVerify, /* isStaticSharedLibrary */ false, 374 SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION); 375 if (result.isError()) { 376 return input.error(result); 377 } 378 signingDetails = result.getResult(); 379 } finally { 380 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 381 } 382 } else { 383 signingDetails = SigningDetails.UNKNOWN; 384 } 385 386 return parseApkLite(input, apkPath, parser, signingDetails, flags); 387 } catch (XmlPullParserException | IOException | RuntimeException e) { 388 Slog.w(TAG, "Failed to parse " + apkPath, e); 389 return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, 390 "Failed to parse " + apkPath, e); 391 } finally { 392 IoUtils.closeQuietly(parser); 393 if (apkAssets != null) { 394 try { 395 apkAssets.close(); 396 } catch (Throwable ignored) { 397 } 398 } 399 // TODO(b/72056911): Implement AutoCloseable on ApkAssets. 400 } 401 } 402 parseApkLite(ParseInput input, String codePath, XmlResourceParser parser, SigningDetails signingDetails, int flags)403 private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath, 404 XmlResourceParser parser, SigningDetails signingDetails, int flags) 405 throws IOException, XmlPullParserException { 406 ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser); 407 if (result.isError()) { 408 return input.error(result); 409 } 410 Pair<String, String> packageSplit = result.getResult(); 411 412 final ParseResult<Pair<Set<String>, Set<String>>> requiredSplitTypesResult = 413 parseRequiredSplitTypes(input, parser); 414 if (requiredSplitTypesResult.isError()) { 415 return input.error(result); 416 } 417 Pair<Set<String>, Set<String>> requiredSplitTypes = requiredSplitTypesResult.getResult(); 418 419 int installLocation = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, 420 "installLocation", PARSE_DEFAULT_INSTALL_LOCATION); 421 int versionCode = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "versionCode", 0); 422 int versionCodeMajor = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, 423 "versionCodeMajor", 424 0); 425 int revisionCode = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "revisionCode", 0); 426 boolean coreApp = parser.getAttributeBooleanValue(null, "coreApp", false); 427 boolean isolatedSplits = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, 428 "isolatedSplits", false); 429 boolean isFeatureSplit = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, 430 "isFeatureSplit", false); 431 boolean isSplitRequired = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, 432 "isSplitRequired", false); 433 String configForSplit = parser.getAttributeValue(null, "configForSplit"); 434 435 int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION; 436 int minSdkVersion = DEFAULT_MIN_SDK_VERSION; 437 boolean debuggable = false; 438 boolean profilableByShell = false; 439 boolean multiArch = false; 440 boolean use32bitAbi = false; 441 boolean extractNativeLibs = true; 442 boolean useEmbeddedDex = false; 443 String usesSplitName = null; 444 String targetPackage = null; 445 boolean overlayIsStatic = false; 446 int overlayPriority = 0; 447 int rollbackDataPolicy = 0; 448 449 String requiredSystemPropertyName = null; 450 String requiredSystemPropertyValue = null; 451 452 boolean hasDeviceAdminReceiver = false; 453 454 boolean isSdkLibrary = false; 455 456 // Only search the tree when the tag is the direct child of <manifest> tag 457 int type; 458 final int searchDepth = parser.getDepth() + 1; 459 460 final List<VerifierInfo> verifiers = new ArrayList<>(); 461 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 462 && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) { 463 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 464 continue; 465 } 466 467 if (parser.getDepth() != searchDepth) { 468 continue; 469 } 470 471 if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) { 472 final VerifierInfo verifier = parseVerifier(parser); 473 if (verifier != null) { 474 verifiers.add(verifier); 475 } 476 } else if (TAG_APPLICATION.equals(parser.getName())) { 477 debuggable = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "debuggable", 478 false); 479 multiArch = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "multiArch", 480 false); 481 use32bitAbi = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "use32bitAbi", 482 false); 483 extractNativeLibs = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, 484 "extractNativeLibs", true); 485 useEmbeddedDex = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, 486 "useEmbeddedDex", false); 487 rollbackDataPolicy = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, 488 "rollbackDataPolicy", 0); 489 String permission = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 490 "permission"); 491 boolean hasBindDeviceAdminPermission = 492 android.Manifest.permission.BIND_DEVICE_ADMIN.equals(permission); 493 494 final int innerDepth = parser.getDepth(); 495 int innerType; 496 while ((innerType = parser.next()) != XmlPullParser.END_DOCUMENT 497 && (innerType != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { 498 if (innerType == XmlPullParser.END_TAG || innerType == XmlPullParser.TEXT) { 499 continue; 500 } 501 502 if (parser.getDepth() != innerDepth + 1) { 503 // Search only under <application>. 504 continue; 505 } 506 507 if (TAG_PROFILEABLE.equals(parser.getName())) { 508 profilableByShell = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, 509 "shell", profilableByShell); 510 } else if (TAG_RECEIVER.equals(parser.getName())) { 511 hasDeviceAdminReceiver |= isDeviceAdminReceiver( 512 parser, hasBindDeviceAdminPermission); 513 } else if (TAG_SDK_LIBRARY.equals(parser.getName())) { 514 isSdkLibrary = true; 515 } 516 } 517 } else if (TAG_OVERLAY.equals(parser.getName())) { 518 requiredSystemPropertyName = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 519 "requiredSystemPropertyName"); 520 requiredSystemPropertyValue = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 521 "requiredSystemPropertyValue"); 522 targetPackage = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "targetPackage"); 523 overlayIsStatic = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE, "isStatic", 524 false); 525 overlayPriority = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "priority", 0); 526 } else if (TAG_USES_SPLIT.equals(parser.getName())) { 527 if (usesSplitName != null) { 528 Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others."); 529 continue; 530 } 531 532 usesSplitName = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "name"); 533 if (usesSplitName == null) { 534 return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 535 "<uses-split> tag requires 'android:name' attribute"); 536 } 537 } else if (TAG_USES_SDK.equals(parser.getName())) { 538 // Mirrors FrameworkParsingPackageUtils#parseUsesSdk until lite and full parsing is combined 539 String minSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 540 "minSdkVersion"); 541 String targetSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 542 "targetSdkVersion"); 543 544 int minVer = DEFAULT_MIN_SDK_VERSION; 545 String minCode = null; 546 boolean minAssigned = false; 547 int targetVer = DEFAULT_TARGET_SDK_VERSION; 548 String targetCode = null; 549 550 if (!TextUtils.isEmpty(minSdkVersionString)) { 551 try { 552 minVer = Integer.parseInt(minSdkVersionString); 553 minAssigned = true; 554 } catch (NumberFormatException ignored) { 555 minCode = minSdkVersionString; 556 minAssigned = !TextUtils.isEmpty(minCode); 557 } 558 } 559 560 if (!TextUtils.isEmpty(targetSdkVersionString)) { 561 try { 562 targetVer = Integer.parseInt(targetSdkVersionString); 563 } catch (NumberFormatException ignored) { 564 targetCode = targetSdkVersionString; 565 if (!minAssigned) { 566 minCode = targetCode; 567 } 568 } 569 } else { 570 targetVer = minVer; 571 targetCode = minCode; 572 } 573 574 boolean allowUnknownCodenames = false; 575 if ((flags & FrameworkParsingPackageUtils.PARSE_APK_IN_APEX) != 0) { 576 allowUnknownCodenames = true; 577 } 578 579 ParseResult<Integer> targetResult = FrameworkParsingPackageUtils.computeTargetSdkVersion( 580 targetVer, targetCode, SDK_CODENAMES, input, 581 allowUnknownCodenames); 582 if (targetResult.isError()) { 583 return input.error(targetResult); 584 } 585 targetSdkVersion = targetResult.getResult(); 586 587 ParseResult<Integer> minResult = FrameworkParsingPackageUtils.computeMinSdkVersion( 588 minVer, minCode, SDK_VERSION, SDK_CODENAMES, input); 589 if (minResult.isError()) { 590 return input.error(minResult); 591 } 592 minSdkVersion = minResult.getResult(); 593 } 594 } 595 596 // Check to see if overlay should be excluded based on system property condition 597 if ((flags & FrameworkParsingPackageUtils.PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY) 598 == 0 && !FrameworkParsingPackageUtils.checkRequiredSystemProperties( 599 requiredSystemPropertyName, requiredSystemPropertyValue)) { 600 String message = "Skipping target and overlay pair " + targetPackage + " and " 601 + codePath + ": overlay ignored due to required system property: " 602 + requiredSystemPropertyName + " with value: " + requiredSystemPropertyValue; 603 Slog.i(TAG, message); 604 return input.skip(message); 605 } 606 607 return input.success( 608 new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit, 609 configForSplit, usesSplitName, isSplitRequired, versionCode, 610 versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails, 611 coreApp, debuggable, profilableByShell, multiArch, use32bitAbi, 612 useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage, 613 overlayIsStatic, overlayPriority, requiredSystemPropertyName, 614 requiredSystemPropertyValue, minSdkVersion, targetSdkVersion, 615 rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second, 616 hasDeviceAdminReceiver, isSdkLibrary)); 617 } 618 isDeviceAdminReceiver( XmlResourceParser parser, boolean applicationHasBindDeviceAdminPermission)619 private static boolean isDeviceAdminReceiver( 620 XmlResourceParser parser, boolean applicationHasBindDeviceAdminPermission) 621 throws XmlPullParserException, IOException { 622 String permission = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 623 "permission"); 624 if (!applicationHasBindDeviceAdminPermission 625 && !android.Manifest.permission.BIND_DEVICE_ADMIN.equals(permission)) { 626 return false; 627 } 628 629 boolean hasDeviceAdminReceiver = false; 630 final int depth = parser.getDepth(); 631 int type; 632 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 633 && (type != XmlPullParser.END_TAG || parser.getDepth() > depth)) { 634 if (type == XmlPullParser.END_TAG 635 || type == XmlPullParser.TEXT) { 636 continue; 637 } 638 if (parser.getDepth() != depth + 1) { 639 // Search only under <receiver>. 640 continue; 641 } 642 if (!hasDeviceAdminReceiver && "meta-data".equals(parser.getName())) { 643 String name = parser.getAttributeValue(ANDROID_RES_NAMESPACE, 644 "name"); 645 if (DeviceAdminReceiver.DEVICE_ADMIN_META_DATA.equals(name)) { 646 hasDeviceAdminReceiver = true; 647 } 648 } 649 } 650 return hasDeviceAdminReceiver; 651 } 652 parsePackageSplitNames(ParseInput input, XmlResourceParser parser)653 public static ParseResult<Pair<String, String>> parsePackageSplitNames(ParseInput input, 654 XmlResourceParser parser) throws IOException, XmlPullParserException { 655 int type; 656 while ((type = parser.next()) != XmlPullParser.START_TAG 657 && type != XmlPullParser.END_DOCUMENT) { 658 } 659 660 if (type != XmlPullParser.START_TAG) { 661 return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 662 "No start tag found"); 663 } 664 if (!parser.getName().equals(TAG_MANIFEST)) { 665 return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 666 "No <manifest> tag"); 667 } 668 669 final String packageName = parser.getAttributeValue(null, "package"); 670 if (!"android".equals(packageName)) { 671 final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, 672 packageName, true, true); 673 if (nameResult.isError()) { 674 return input.error(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, 675 "Invalid manifest package: " + nameResult.getErrorMessage()); 676 } 677 } 678 679 String splitName = parser.getAttributeValue(null, "split"); 680 if (splitName != null) { 681 if (splitName.length() == 0) { 682 splitName = null; 683 } else { 684 final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, 685 splitName, false, false); 686 if (nameResult.isError()) { 687 return input.error(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, 688 "Invalid manifest split: " + nameResult.getErrorMessage()); 689 } 690 } 691 } 692 693 return input.success(Pair.create(packageName.intern(), 694 (splitName != null) ? splitName.intern() : splitName)); 695 } 696 697 /** 698 * Utility method that parses attributes android:requiredSplitTypes and android:splitTypes. 699 */ parseRequiredSplitTypes( ParseInput input, XmlResourceParser parser)700 public static ParseResult<Pair<Set<String>, Set<String>>> parseRequiredSplitTypes( 701 ParseInput input, XmlResourceParser parser) { 702 Set<String> requiredSplitTypes = null; 703 Set<String> splitTypes = null; 704 String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "requiredSplitTypes"); 705 if (!TextUtils.isEmpty(value)) { 706 final ParseResult<Set<String>> result = separateAndValidateSplitTypes(input, value); 707 if (result.isError()) { 708 return input.error(result); 709 } 710 requiredSplitTypes = result.getResult(); 711 } 712 713 value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "splitTypes"); 714 if (!TextUtils.isEmpty(value)) { 715 final ParseResult<Set<String>> result = separateAndValidateSplitTypes(input, value); 716 if (result.isError()) { 717 return input.error(result); 718 } 719 splitTypes = result.getResult(); 720 } 721 722 return input.success(Pair.create(requiredSplitTypes, splitTypes)); 723 } 724 separateAndValidateSplitTypes(ParseInput input, String values)725 private static ParseResult<Set<String>> separateAndValidateSplitTypes(ParseInput input, 726 String values) { 727 final Set<String> ret = new ArraySet<>(); 728 for (String value : values.trim().split(",")) { 729 final String type = value.trim(); 730 // Using requireFilename as true because it limits length of the name to the 731 // {@link #MAX_FILE_NAME_SIZE}. 732 final ParseResult<?> nameResult = FrameworkParsingPackageUtils.validateName(input, type, 733 false /* requireSeparator */, true /* requireFilename */); 734 if (nameResult.isError()) { 735 return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 736 "Invalid manifest split types: " + nameResult.getErrorMessage()); 737 } 738 if (!ret.add(type)) { 739 Slog.w(TAG, type + " was defined multiple times"); 740 } 741 } 742 return input.success(ret); 743 } 744 parseVerifier(AttributeSet attrs)745 public static VerifierInfo parseVerifier(AttributeSet attrs) { 746 String packageName = attrs.getAttributeValue(ANDROID_RES_NAMESPACE, "name"); 747 String encodedPublicKey = attrs.getAttributeValue(ANDROID_RES_NAMESPACE, "publicKey"); 748 749 if (packageName == null || packageName.length() == 0) { 750 Slog.i(TAG, "verifier package name was null; skipping"); 751 return null; 752 } 753 754 final PublicKey publicKey = FrameworkParsingPackageUtils.parsePublicKey(encodedPublicKey); 755 if (publicKey == null) { 756 Slog.i(TAG, "Unable to parse verifier public key for " + packageName); 757 return null; 758 } 759 760 return new VerifierInfo(packageName, publicKey); 761 } 762 763 /** 764 * Used to sort a set of APKs based on their split names, always placing the 765 * base APK (with {@code null} split name) first. 766 */ 767 private static class SplitNameComparator implements Comparator<String> { 768 @Override compare(String lhs, String rhs)769 public int compare(String lhs, String rhs) { 770 if (lhs == null) { 771 return -1; 772 } else if (rhs == null) { 773 return 1; 774 } else { 775 return lhs.compareTo(rhs); 776 } 777 } 778 } 779 780 /** 781 * Check if the given file is an APK file. 782 * 783 * @param file the file to check. 784 * @return {@code true} if the given file is an APK file. 785 */ isApkFile(File file)786 public static boolean isApkFile(File file) { 787 return isApkPath(file.getName()); 788 } 789 790 /** 791 * Check if the given path ends with APK file extension. 792 * 793 * @param path the path to check. 794 * @return {@code true} if the given path ends with APK file extension. 795 */ isApkPath(String path)796 public static boolean isApkPath(String path) { 797 return path.endsWith(APK_FILE_EXTENSION); 798 } 799 } 800