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