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