1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.pm; 18 19 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; 20 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; 21 import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; 22 import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID; 23 import static android.system.OsConstants.O_CREAT; 24 import static android.system.OsConstants.O_RDWR; 25 26 import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; 27 import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION; 28 import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; 29 import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING; 30 import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED; 31 import static com.android.server.pm.PackageManagerService.RANDOM_CODEPATH_PREFIX; 32 import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX; 33 import static com.android.server.pm.PackageManagerService.STUB_SUFFIX; 34 import static com.android.server.pm.PackageManagerService.TAG; 35 36 import android.annotation.NonNull; 37 import android.annotation.Nullable; 38 import android.annotation.UserIdInt; 39 import android.app.ActivityManager; 40 import android.compat.annotation.ChangeId; 41 import android.compat.annotation.Disabled; 42 import android.content.Context; 43 import android.content.Intent; 44 import android.content.IntentFilter; 45 import android.content.pm.ActivityInfo; 46 import android.content.pm.ApplicationInfo; 47 import android.content.pm.ComponentInfo; 48 import android.content.pm.PackageInfoLite; 49 import android.content.pm.PackageInstaller; 50 import android.content.pm.PackageManager; 51 import android.content.pm.PackagePartitions; 52 import android.content.pm.ResolveInfo; 53 import android.content.pm.ServiceInfo; 54 import android.content.pm.Signature; 55 import android.content.pm.SigningDetails; 56 import android.content.pm.parsing.ApkLiteParseUtils; 57 import android.content.pm.parsing.PackageLite; 58 import android.content.pm.parsing.result.ParseResult; 59 import android.content.pm.parsing.result.ParseTypeImpl; 60 import android.os.Binder; 61 import android.os.Build; 62 import android.os.Debug; 63 import android.os.Environment; 64 import android.os.FileUtils; 65 import android.os.Process; 66 import android.os.SystemProperties; 67 import android.os.incremental.IncrementalManager; 68 import android.os.incremental.IncrementalStorage; 69 import android.os.incremental.V4Signature; 70 import android.os.incremental.V4Signature.HashingInfo; 71 import android.os.storage.DiskInfo; 72 import android.os.storage.VolumeInfo; 73 import android.service.pm.PackageServiceDumpProto; 74 import android.stats.storage.StorageEnums; 75 import android.system.ErrnoException; 76 import android.system.Os; 77 import android.util.ArraySet; 78 import android.util.AtomicFile; 79 import android.util.Base64; 80 import android.util.Log; 81 import android.util.LogPrinter; 82 import android.util.Printer; 83 import android.util.Slog; 84 import android.util.proto.ProtoOutputStream; 85 86 import com.android.internal.content.InstallLocationUtils; 87 import com.android.internal.content.NativeLibraryHelper; 88 import com.android.internal.util.ArrayUtils; 89 import com.android.internal.util.FastPrintWriter; 90 import com.android.internal.util.HexDump; 91 import com.android.server.EventLogTags; 92 import com.android.server.IntentResolver; 93 import com.android.server.Watchdog; 94 import com.android.server.compat.PlatformCompat; 95 import com.android.server.pm.dex.PackageDexUsage; 96 import com.android.server.pm.parsing.pkg.AndroidPackage; 97 import com.android.server.pm.pkg.PackageStateInternal; 98 import com.android.server.pm.pkg.component.ParsedMainComponent; 99 import com.android.server.pm.resolution.ComponentResolverApi; 100 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; 101 102 import dalvik.system.VMRuntime; 103 104 import libcore.io.IoUtils; 105 106 import java.io.BufferedReader; 107 import java.io.File; 108 import java.io.FileDescriptor; 109 import java.io.FileInputStream; 110 import java.io.FileOutputStream; 111 import java.io.FileReader; 112 import java.io.FilenameFilter; 113 import java.io.IOException; 114 import java.io.InputStream; 115 import java.io.PrintWriter; 116 import java.nio.file.Path; 117 import java.security.SecureRandom; 118 import java.security.cert.CertificateEncodingException; 119 import java.security.cert.CertificateException; 120 import java.text.SimpleDateFormat; 121 import java.util.Arrays; 122 import java.util.Date; 123 import java.util.List; 124 import java.util.Objects; 125 import java.util.function.Function; 126 import java.util.function.Predicate; 127 import java.util.zip.GZIPInputStream; 128 129 /** 130 * Class containing helper methods for the PackageManagerService. 131 * 132 * {@hide} 133 */ 134 public class PackageManagerServiceUtils { 135 private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 3 * 1000 * 1000; // 3MB 136 137 private static final boolean DEBUG = Build.IS_DEBUGGABLE; 138 139 public final static Predicate<PackageStateInternal> REMOVE_IF_NULL_PKG = 140 pkgSetting -> pkgSetting.getPkg() == null; 141 142 // This is a horrible hack to workaround b/240373119, specifically for fixing the T branch. 143 // A proper fix should be implemented in master instead. 144 public static final ThreadLocal<Boolean> DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 145 ThreadLocal.withInitial(() -> false); 146 147 /** 148 * Components of apps targeting Android T and above will stop receiving intents from 149 * external callers that do not match its declared intent filters. 150 * 151 * When an app registers an exported component in its manifest and adds an <intent-filter>, 152 * the component can be started by any intent - even those that do not match the intent filter. 153 * This has proven to be something that many developers find counterintuitive. 154 * Without checking the intent when the component is started, in some circumstances this can 155 * allow 3P apps to trigger internal-only functionality. 156 */ 157 @ChangeId 158 @Disabled /* Revert enforcement: b/274147456 */ 159 private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188; 160 161 /** 162 * The initial enabled state of the cache before other checks are done. 163 */ 164 private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true; 165 166 /** 167 * Whether to skip all other checks and force the cache to be enabled. 168 * 169 * Setting this to true will cause the cache to be named "debug" to avoid eviction from 170 * build fingerprint changes. 171 */ 172 private static final boolean FORCE_PACKAGE_PARSED_CACHE_ENABLED = false; 173 174 /** 175 * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>. 176 * Package is considered active, if: 177 * 1) It was active in foreground. 178 * 2) It was active in background and also used by other apps. 179 * 180 * If it doesn't have sufficient information about the package, it return <code>false</code>. 181 */ isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo, long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis)182 public static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, 183 long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo, 184 long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) { 185 186 if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) { 187 return false; 188 } 189 190 // If the app was active in foreground during the threshold period. 191 boolean isActiveInForeground = (currentTimeInMillis 192 - latestForegroundPackageUseTimeInMillis) 193 < thresholdTimeinMillis; 194 195 if (isActiveInForeground) { 196 return false; 197 } 198 199 // If the app was active in background during the threshold period and was used 200 // by other packages. 201 boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis 202 - latestPackageUseTimeInMillis) 203 < thresholdTimeinMillis) 204 && packageUseInfo.isAnyCodePathUsedByOtherApps(); 205 206 return !isActiveInBackgroundAndUsedByOtherPackages; 207 } 208 209 /** 210 * Returns the canonicalized path of {@code path} as per {@code realpath(3)} 211 * semantics. 212 */ 213 public static String realpath(File path) throws IOException { 214 try { 215 return Os.realpath(path.getAbsolutePath()); 216 } catch (ErrnoException ee) { 217 throw ee.rethrowAsIOException(); 218 } 219 } 220 221 /** 222 * Verifies that the given string {@code isa} is a valid supported isa on 223 * the running device. 224 */ 225 public static boolean checkISA(String isa) { 226 for (String abi : Build.SUPPORTED_ABIS) { 227 if (VMRuntime.getInstructionSet(abi).equals(isa)) { 228 return true; 229 } 230 } 231 return false; 232 } 233 234 public static long getLastModifiedTime(AndroidPackage pkg) { 235 final File srcFile = new File(pkg.getPath()); 236 if (!srcFile.isDirectory()) { 237 return srcFile.lastModified(); 238 } 239 final File baseFile = new File(pkg.getBaseApkPath()); 240 long maxModifiedTime = baseFile.lastModified(); 241 for (int i = pkg.getSplitCodePaths().length - 1; i >=0; --i) { 242 final File splitFile = new File(pkg.getSplitCodePaths()[i]); 243 maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified()); 244 } 245 return maxModifiedTime; 246 } 247 248 private static File getSettingsProblemFile() { 249 File dataDir = Environment.getDataDirectory(); 250 File systemDir = new File(dataDir, "system"); 251 File fname = new File(systemDir, "uiderrors.txt"); 252 return fname; 253 } 254 255 public static void dumpCriticalInfo(ProtoOutputStream proto) { 256 final File file = getSettingsProblemFile(); 257 final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE; 258 try (BufferedReader in = new BufferedReader(new FileReader(file))) { 259 if (skipSize > 0) { 260 in.skip(skipSize); 261 } 262 String line = null; 263 while ((line = in.readLine()) != null) { 264 if (line.contains("ignored: updated version")) continue; 265 proto.write(PackageServiceDumpProto.MESSAGES, line); 266 } 267 } catch (IOException ignored) { 268 } 269 } 270 dumpCriticalInfo(PrintWriter pw, String msg)271 public static void dumpCriticalInfo(PrintWriter pw, String msg) { 272 final File file = getSettingsProblemFile(); 273 final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE; 274 try (BufferedReader in = new BufferedReader(new FileReader(file))) { 275 if (skipSize > 0) { 276 in.skip(skipSize); 277 } 278 String line = null; 279 while ((line = in.readLine()) != null) { 280 if (line.contains("ignored: updated version")) continue; 281 if (msg != null) { 282 pw.print(msg); 283 } 284 pw.println(line); 285 } 286 } catch (IOException ignored) { 287 } 288 } 289 logCriticalInfo(int priority, String msg)290 public static void logCriticalInfo(int priority, String msg) { 291 Slog.println(priority, TAG, msg); 292 EventLogTags.writePmCriticalInfo(msg); 293 try { 294 File fname = getSettingsProblemFile(); 295 FileOutputStream out = new FileOutputStream(fname, true); 296 PrintWriter pw = new FastPrintWriter(out); 297 SimpleDateFormat formatter = new SimpleDateFormat(); 298 String dateString = formatter.format(new Date(System.currentTimeMillis())); 299 pw.println(dateString + ": " + msg); 300 pw.close(); 301 FileUtils.setPermissions( 302 fname.toString(), 303 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH, 304 -1, -1); 305 } catch (java.io.IOException e) { 306 } 307 } 308 309 /** Enforces that if the caller is shell, it does not have the provided user restriction. */ enforceShellRestriction( UserManagerInternal userManager, String restriction, int callingUid, int userHandle)310 public static void enforceShellRestriction( 311 UserManagerInternal userManager, String restriction, int callingUid, int userHandle) { 312 if (callingUid == Process.SHELL_UID) { 313 if (userHandle >= 0 314 && userManager.hasUserRestriction( 315 restriction, userHandle)) { 316 throw new SecurityException("Shell does not have permission to access user " 317 + userHandle); 318 } else if (userHandle < 0) { 319 Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user " 320 + userHandle + "\n\t" + Debug.getCallers(3)); 321 } 322 } 323 } 324 325 /** 326 * Enforces that the caller must be either the system process or the phone process. 327 * If not, throws a {@link SecurityException}. 328 */ enforceSystemOrPhoneCaller(String methodName, int callingUid)329 public static void enforceSystemOrPhoneCaller(String methodName, int callingUid) { 330 if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) { 331 throw new SecurityException( 332 "Cannot call " + methodName + " from UID " + callingUid); 333 } 334 } 335 336 /** 337 * Derive the value of the {@code cpuAbiOverride} based on the provided 338 * value. 339 */ deriveAbiOverride(String abiOverride)340 public static String deriveAbiOverride(String abiOverride) { 341 if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) { 342 return null; 343 } 344 return abiOverride; 345 } 346 347 /** 348 * Compares two sets of signatures. Returns: 349 * <br /> 350 * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null, 351 * <br /> 352 * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null, 353 * <br /> 354 * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null, 355 * <br /> 356 * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical, 357 * <br /> 358 * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ. 359 */ compareSignatures(Signature[] s1, Signature[] s2)360 public static int compareSignatures(Signature[] s1, Signature[] s2) { 361 if (s1 == null) { 362 return s2 == null 363 ? PackageManager.SIGNATURE_NEITHER_SIGNED 364 : PackageManager.SIGNATURE_FIRST_NOT_SIGNED; 365 } 366 367 if (s2 == null) { 368 return PackageManager.SIGNATURE_SECOND_NOT_SIGNED; 369 } 370 371 if (s1.length != s2.length) { 372 return PackageManager.SIGNATURE_NO_MATCH; 373 } 374 375 // Since both signature sets are of size 1, we can compare without HashSets. 376 if (s1.length == 1) { 377 return s1[0].equals(s2[0]) ? 378 PackageManager.SIGNATURE_MATCH : 379 PackageManager.SIGNATURE_NO_MATCH; 380 } 381 382 ArraySet<Signature> set1 = new ArraySet<Signature>(); 383 for (Signature sig : s1) { 384 set1.add(sig); 385 } 386 ArraySet<Signature> set2 = new ArraySet<Signature>(); 387 for (Signature sig : s2) { 388 set2.add(sig); 389 } 390 // Make sure s2 contains all signatures in s1. 391 if (set1.equals(set2)) { 392 return PackageManager.SIGNATURE_MATCH; 393 } 394 return PackageManager.SIGNATURE_NO_MATCH; 395 } 396 397 /** 398 * Returns true if the signature set of the package is identical to the specified signature 399 * set or if the signing details of the package are unknown. 400 */ comparePackageSignatures(PackageSetting pkgSetting, Signature[] signatures)401 public static boolean comparePackageSignatures(PackageSetting pkgSetting, 402 Signature[] signatures) { 403 final SigningDetails signingDetails = pkgSetting.getSigningDetails(); 404 return signingDetails == SigningDetails.UNKNOWN 405 || compareSignatures(signingDetails.getSignatures(), signatures) 406 == PackageManager.SIGNATURE_MATCH; 407 } 408 409 /** 410 * Used for backward compatibility to make sure any packages with 411 * certificate chains get upgraded to the new style. {@code existingSigs} 412 * will be in the old format (since they were stored on disk from before the 413 * system upgrade) and {@code scannedSigs} will be in the newer format. 414 */ matchSignaturesCompat(String packageName, PackageSignatures packageSignatures, SigningDetails parsedSignatures)415 private static boolean matchSignaturesCompat(String packageName, 416 PackageSignatures packageSignatures, SigningDetails parsedSignatures) { 417 ArraySet<Signature> existingSet = new ArraySet<Signature>(); 418 for (Signature sig : packageSignatures.mSigningDetails.getSignatures()) { 419 existingSet.add(sig); 420 } 421 ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>(); 422 for (Signature sig : parsedSignatures.getSignatures()) { 423 try { 424 Signature[] chainSignatures = sig.getChainSignatures(); 425 for (Signature chainSig : chainSignatures) { 426 scannedCompatSet.add(chainSig); 427 } 428 } catch (CertificateEncodingException e) { 429 scannedCompatSet.add(sig); 430 } 431 } 432 // make sure the expanded scanned set contains all signatures in the existing one 433 if (scannedCompatSet.equals(existingSet)) { 434 // migrate the old signatures to the new scheme 435 packageSignatures.mSigningDetails = parsedSignatures; 436 return true; 437 } else if (parsedSignatures.hasPastSigningCertificates()) { 438 439 // well this sucks: the parsed package has probably rotated signing certificates, but 440 // we don't have enough information to determine if the new signing certificate was 441 // blessed by the old one 442 logCriticalInfo(Log.INFO, "Existing package " + packageName + " has flattened signing " 443 + "certificate chain. Unable to install newer version with rotated signing " 444 + "certificate."); 445 } 446 return false; 447 } 448 matchSignaturesRecover( String packageName, SigningDetails existingSignatures, SigningDetails parsedSignatures, @SigningDetails.CertCapabilities int flags)449 private static boolean matchSignaturesRecover( 450 String packageName, 451 SigningDetails existingSignatures, 452 SigningDetails parsedSignatures, 453 @SigningDetails.CertCapabilities int flags) { 454 String msg = null; 455 try { 456 if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) { 457 logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for " 458 + packageName); 459 return true; 460 } 461 } catch (CertificateException e) { 462 msg = e.getMessage(); 463 } 464 logCriticalInfo(Log.INFO, 465 "Failed to recover certificates for " + packageName + ": " + msg); 466 return false; 467 } 468 469 /** 470 * Make sure the updated priv app is signed with the same key as the original APK file on the 471 * /system partition. 472 * 473 * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data 474 * and is not tamperproof. 475 */ matchSignatureInSystem(@onNull String packageName, @NonNull SigningDetails signingDetails, PackageSetting disabledPkgSetting)476 private static boolean matchSignatureInSystem(@NonNull String packageName, 477 @NonNull SigningDetails signingDetails, PackageSetting disabledPkgSetting) { 478 if (signingDetails.checkCapability( 479 disabledPkgSetting.getSigningDetails(), 480 SigningDetails.CertCapabilities.INSTALLED_DATA) 481 || disabledPkgSetting.getSigningDetails().checkCapability( 482 signingDetails, 483 SigningDetails.CertCapabilities.ROLLBACK)) { 484 return true; 485 } else { 486 logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " + 487 packageName); 488 return false; 489 } 490 } 491 492 /** Default is to not use fs-verity since it depends on kernel support. */ 493 private static final int FSVERITY_DISABLED = 0; 494 495 /** Standard fs-verity. */ 496 private static final int FSVERITY_ENABLED = 2; 497 498 /** Returns true if standard APK Verity is enabled. */ isApkVerityEnabled()499 static boolean isApkVerityEnabled() { 500 return Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.R 501 || SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) 502 == FSVERITY_ENABLED; 503 } 504 505 /** Returns true to force apk verification if the package is considered privileged. */ isApkVerificationForced(@ullable PackageSetting ps)506 static boolean isApkVerificationForced(@Nullable PackageSetting ps) { 507 // TODO(b/154310064): re-enable. 508 return false; 509 } 510 511 /** 512 * Verifies that signatures match. 513 * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}. 514 * @throws PackageManagerException if the signatures did not match. 515 */ verifySignatures(PackageSetting pkgSetting, @Nullable SharedUserSetting sharedUserSetting, PackageSetting disabledPkgSetting, SigningDetails parsedSignatures, boolean compareCompat, boolean compareRecover, boolean isRollback)516 public static boolean verifySignatures(PackageSetting pkgSetting, 517 @Nullable SharedUserSetting sharedUserSetting, 518 PackageSetting disabledPkgSetting, SigningDetails parsedSignatures, 519 boolean compareCompat, boolean compareRecover, boolean isRollback) 520 throws PackageManagerException { 521 final String packageName = pkgSetting.getPackageName(); 522 boolean compatMatch = false; 523 if (pkgSetting.getSigningDetails().getSignatures() != null) { 524 // Already existing package. Make sure signatures match 525 boolean match = parsedSignatures.checkCapability( 526 pkgSetting.getSigningDetails(), 527 SigningDetails.CertCapabilities.INSTALLED_DATA) 528 || pkgSetting.getSigningDetails().checkCapability( 529 parsedSignatures, 530 SigningDetails.CertCapabilities.ROLLBACK); 531 if (!match && compareCompat) { 532 match = matchSignaturesCompat(packageName, pkgSetting.getSignatures(), 533 parsedSignatures); 534 compatMatch = match; 535 } 536 if (!match && compareRecover) { 537 match = matchSignaturesRecover( 538 packageName, 539 pkgSetting.getSigningDetails(), 540 parsedSignatures, 541 SigningDetails.CertCapabilities.INSTALLED_DATA) 542 || matchSignaturesRecover( 543 packageName, 544 parsedSignatures, 545 pkgSetting.getSigningDetails(), 546 SigningDetails.CertCapabilities.ROLLBACK); 547 } 548 549 if (!match && isApkVerificationForced(disabledPkgSetting)) { 550 match = matchSignatureInSystem(packageName, pkgSetting.getSigningDetails(), 551 disabledPkgSetting); 552 } 553 554 if (!match && isRollback) { 555 // Since a rollback can only be initiated for an APK previously installed on the 556 // device allow rolling back to a previous signing key even if the rollback 557 // capability has not been granted. 558 match = pkgSetting.getSigningDetails().hasAncestorOrSelf(parsedSignatures); 559 } 560 561 if (!match) { 562 throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, 563 "Existing package " + packageName 564 + " signatures do not match newer version; ignoring!"); 565 } 566 } 567 // Check for shared user signatures 568 if (sharedUserSetting != null 569 && sharedUserSetting.getSigningDetails() != SigningDetails.UNKNOWN) { 570 // Already existing package. Make sure signatures match. In case of signing certificate 571 // rotation, the packages with newer certs need to be ok with being sharedUserId with 572 // the older ones. We check to see if either the new package is signed by an older cert 573 // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok 574 // with being sharedUser with the existing signing cert. 575 boolean match = canJoinSharedUserId(parsedSignatures, 576 sharedUserSetting.getSigningDetails()); 577 // Special case: if the sharedUserId capability check failed it could be due to this 578 // being the only package in the sharedUserId so far and the lineage being updated to 579 // deny the sharedUserId capability of the previous key in the lineage. 580 final ArraySet<PackageStateInternal> susPackageStates = 581 (ArraySet<PackageStateInternal>) sharedUserSetting.getPackageStates(); 582 if (!match && susPackageStates.size() == 1 583 && susPackageStates.valueAt(0).getPackageName().equals(packageName)) { 584 match = true; 585 } 586 if (!match && compareCompat) { 587 match = matchSignaturesCompat( 588 packageName, sharedUserSetting.signatures, parsedSignatures); 589 } 590 if (!match && compareRecover) { 591 match = 592 matchSignaturesRecover(packageName, 593 sharedUserSetting.signatures.mSigningDetails, 594 parsedSignatures, 595 SigningDetails.CertCapabilities.SHARED_USER_ID) 596 || matchSignaturesRecover(packageName, 597 parsedSignatures, 598 sharedUserSetting.signatures.mSigningDetails, 599 SigningDetails.CertCapabilities.SHARED_USER_ID); 600 compatMatch |= match; 601 } 602 if (!match) { 603 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, 604 "Package " + packageName 605 + " has no signatures that match those in shared user " 606 + sharedUserSetting.name + "; ignoring!"); 607 } 608 // It is possible that this package contains a lineage that blocks sharedUserId access 609 // to an already installed package in the sharedUserId signed with a previous key. 610 // Iterate over all of the packages in the sharedUserId and ensure any that are signed 611 // with a key in this package's lineage have the SHARED_USER_ID capability granted. 612 if (parsedSignatures.hasPastSigningCertificates()) { 613 for (int i = 0; i < susPackageStates.size(); i++) { 614 PackageStateInternal shUidPkgSetting = susPackageStates.valueAt(i); 615 // if the current package in the sharedUserId is the package being updated then 616 // skip this check as the update may revoke the sharedUserId capability from 617 // the key with which this app was previously signed. 618 if (packageName.equals(shUidPkgSetting.getPackageName())) { 619 continue; 620 } 621 SigningDetails shUidSigningDetails = 622 shUidPkgSetting.getSigningDetails(); 623 // The capability check only needs to be performed against the package if it is 624 // signed with a key that is in the lineage of the package being installed. 625 if (parsedSignatures.hasAncestor(shUidSigningDetails)) { 626 if (!parsedSignatures.checkCapability(shUidSigningDetails, 627 SigningDetails.CertCapabilities.SHARED_USER_ID)) { 628 throw new PackageManagerException( 629 INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, 630 "Package " + packageName 631 + " revoked the sharedUserId capability from the" 632 + " signing key used to sign " 633 + shUidPkgSetting.getPackageName()); 634 } 635 } 636 } 637 } 638 // If the lineage of this package diverges from the lineage of the sharedUserId then 639 // do not allow the installation to proceed. 640 if (!parsedSignatures.hasCommonAncestor( 641 sharedUserSetting.signatures.mSigningDetails)) { 642 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, 643 "Package " + packageName + " has a signing lineage " 644 + "that diverges from the lineage of the sharedUserId"); 645 } 646 } 647 return compatMatch; 648 } 649 650 /** 651 * Returns whether the package with {@code packageSigningDetails} can join the sharedUserId 652 * with {@code sharedUserSigningDetails}. 653 * <p> 654 * A sharedUserId maintains a shared {@link SigningDetails} containing the full lineage and 655 * capabilities for each package in the sharedUserId. A package can join the sharedUserId if 656 * its current signer is the same as the shared signer, or if the current signer of either 657 * is in the signing lineage of the other with the {@link 658 * SigningDetails.CertCapabilities#SHARED_USER_ID} capability granted to that previous signer 659 * in the lineage. 660 * 661 * @param packageSigningDetails the {@code SigningDetails} of the package seeking to join the 662 * sharedUserId 663 * @param sharedUserSigningDetails the {@code SigningDetails} of the sharedUserId 664 * @return true if the package seeking to join the sharedUserId meets the requirements 665 */ canJoinSharedUserId(@onNull SigningDetails packageSigningDetails, @NonNull SigningDetails sharedUserSigningDetails)666 public static boolean canJoinSharedUserId(@NonNull SigningDetails packageSigningDetails, 667 @NonNull SigningDetails sharedUserSigningDetails) { 668 return packageSigningDetails.checkCapability(sharedUserSigningDetails, SHARED_USER_ID) 669 || sharedUserSigningDetails.checkCapability(packageSigningDetails, SHARED_USER_ID); 670 } 671 672 /** 673 * Extract native libraries to a target path 674 */ extractNativeBinaries(File dstCodePath, String packageName)675 public static int extractNativeBinaries(File dstCodePath, String packageName) { 676 final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME); 677 NativeLibraryHelper.Handle handle = null; 678 try { 679 handle = NativeLibraryHelper.Handle.create(dstCodePath); 680 return NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot, 681 null /*abiOverride*/, false /*isIncremental*/); 682 } catch (IOException e) { 683 logCriticalInfo(Log.ERROR, "Failed to extract native libraries" 684 + "; pkg: " + packageName); 685 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; 686 } finally { 687 IoUtils.closeQuietly(handle); 688 } 689 } 690 691 /** 692 * Remove native libraries of a given package 693 */ removeNativeBinariesLI(PackageSetting ps)694 public static void removeNativeBinariesLI(PackageSetting ps) { 695 if (ps != null) { 696 NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath()); 697 } 698 } 699 700 /** 701 * Wait for native library extraction to be done in IncrementalService 702 */ waitForNativeBinariesExtractionForIncremental( ArraySet<IncrementalStorage> incrementalStorages)703 public static void waitForNativeBinariesExtractionForIncremental( 704 ArraySet<IncrementalStorage> incrementalStorages) { 705 if (incrementalStorages.isEmpty()) { 706 return; 707 } 708 try { 709 // Native library extraction may take very long time: each page could potentially 710 // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds 711 // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't 712 // make much sense as blocking here doesn't lock up the framework, but only blocks 713 // the installation session and the following ones. 714 Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract"); 715 for (int i = 0; i < incrementalStorages.size(); ++i) { 716 IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i); 717 storage.waitForNativeBinariesExtraction(); 718 } 719 } finally { 720 Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract"); 721 } 722 } 723 724 /** 725 * Decompress files stored in codePath to dstCodePath for a certain package. 726 */ decompressFiles(String codePath, File dstCodePath, String packageName)727 public static int decompressFiles(String codePath, File dstCodePath, String packageName) { 728 final File[] compressedFiles = getCompressedFiles(codePath); 729 int ret = PackageManager.INSTALL_SUCCEEDED; 730 try { 731 makeDirRecursive(dstCodePath, 0755); 732 for (File srcFile : compressedFiles) { 733 final String srcFileName = srcFile.getName(); 734 final String dstFileName = srcFileName.substring( 735 0, srcFileName.length() - COMPRESSED_EXTENSION.length()); 736 final File dstFile = new File(dstCodePath, dstFileName); 737 ret = decompressFile(srcFile, dstFile); 738 if (ret != PackageManager.INSTALL_SUCCEEDED) { 739 logCriticalInfo(Log.ERROR, "Failed to decompress" 740 + "; pkg: " + packageName 741 + ", file: " + dstFileName); 742 break; 743 } 744 } 745 } catch (ErrnoException e) { 746 logCriticalInfo(Log.ERROR, "Failed to decompress" 747 + "; pkg: " + packageName 748 + ", err: " + e.errno); 749 } 750 return ret; 751 } 752 decompressFile(File srcFile, File dstFile)753 public static int decompressFile(File srcFile, File dstFile) throws ErrnoException { 754 if (DEBUG_COMPRESSION) { 755 Slog.i(TAG, "Decompress file" 756 + "; src: " + srcFile.getAbsolutePath() 757 + ", dst: " + dstFile.getAbsolutePath()); 758 } 759 final AtomicFile atomicFile = new AtomicFile(dstFile); 760 FileOutputStream outputStream = null; 761 try ( 762 InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile)) 763 ) { 764 outputStream = atomicFile.startWrite(); 765 FileUtils.copy(fileIn, outputStream); 766 // Flush anything in buffer before chmod, because any writes after chmod will fail. 767 outputStream.flush(); 768 Os.fchmod(outputStream.getFD(), 0644); 769 atomicFile.finishWrite(outputStream); 770 return PackageManager.INSTALL_SUCCEEDED; 771 } catch (IOException e) { 772 logCriticalInfo(Log.ERROR, "Failed to decompress file" 773 + "; src: " + srcFile.getAbsolutePath() 774 + ", dst: " + dstFile.getAbsolutePath()); 775 atomicFile.failWrite(outputStream); 776 } 777 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; 778 } 779 getCompressedFiles(String codePath)780 public static File[] getCompressedFiles(String codePath) { 781 final File stubCodePath = new File(codePath); 782 final String stubName = stubCodePath.getName(); 783 784 // The layout of a compressed package on a given partition is as follows : 785 // 786 // Compressed artifacts: 787 // 788 // /partition/ModuleName/foo.gz 789 // /partation/ModuleName/bar.gz 790 // 791 // Stub artifact: 792 // 793 // /partition/ModuleName-Stub/ModuleName-Stub.apk 794 // 795 // In other words, stub is on the same partition as the compressed artifacts 796 // and in a directory that's suffixed with "-Stub". 797 int idx = stubName.lastIndexOf(STUB_SUFFIX); 798 if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) { 799 return null; 800 } 801 802 final File stubParentDir = stubCodePath.getParentFile(); 803 if (stubParentDir == null) { 804 Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath); 805 return null; 806 } 807 808 final File compressedPath = new File(stubParentDir, stubName.substring(0, idx)); 809 final File[] files = compressedPath.listFiles(new FilenameFilter() { 810 @Override 811 public boolean accept(File dir, String name) { 812 return name.toLowerCase().endsWith(COMPRESSED_EXTENSION); 813 } 814 }); 815 816 if (DEBUG_COMPRESSION && files != null && files.length > 0) { 817 Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files)); 818 } 819 820 return files; 821 } 822 compressedFileExists(String codePath)823 public static boolean compressedFileExists(String codePath) { 824 final File[] compressedFiles = getCompressedFiles(codePath); 825 return compressedFiles != null && compressedFiles.length > 0; 826 } 827 828 /** 829 * Parse given package and return minimal details. 830 */ getMinimalPackageInfo(Context context, PackageLite pkg, String packagePath, int flags, String abiOverride)831 public static PackageInfoLite getMinimalPackageInfo(Context context, PackageLite pkg, 832 String packagePath, int flags, String abiOverride) { 833 final PackageInfoLite ret = new PackageInfoLite(); 834 if (packagePath == null || pkg == null) { 835 Slog.i(TAG, "Invalid package file " + packagePath); 836 ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK; 837 return ret; 838 } 839 840 final File packageFile = new File(packagePath); 841 final long sizeBytes; 842 try { 843 sizeBytes = InstallLocationUtils.calculateInstalledSize(pkg, abiOverride); 844 } catch (IOException e) { 845 if (!packageFile.exists()) { 846 ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI; 847 } else { 848 ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK; 849 } 850 851 return ret; 852 } 853 854 final PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams( 855 PackageInstaller.SessionParams.MODE_INVALID); 856 sessionParams.appPackageName = pkg.getPackageName(); 857 sessionParams.installLocation = pkg.getInstallLocation(); 858 sessionParams.sizeBytes = sizeBytes; 859 sessionParams.installFlags = flags; 860 final int recommendedInstallLocation; 861 try { 862 recommendedInstallLocation = InstallLocationUtils.resolveInstallLocation(context, 863 sessionParams); 864 } catch (IOException e) { 865 throw new IllegalStateException(e); 866 } 867 ret.packageName = pkg.getPackageName(); 868 ret.splitNames = pkg.getSplitNames(); 869 ret.versionCode = pkg.getVersionCode(); 870 ret.versionCodeMajor = pkg.getVersionCodeMajor(); 871 ret.baseRevisionCode = pkg.getBaseRevisionCode(); 872 ret.splitRevisionCodes = pkg.getSplitRevisionCodes(); 873 ret.installLocation = pkg.getInstallLocation(); 874 ret.verifiers = pkg.getVerifiers(); 875 ret.recommendedInstallLocation = recommendedInstallLocation; 876 ret.multiArch = pkg.isMultiArch(); 877 ret.debuggable = pkg.isDebuggable(); 878 ret.isSdkLibrary = pkg.isIsSdkLibrary(); 879 880 return ret; 881 } 882 883 /** 884 * Calculate estimated footprint of given package post-installation. 885 * 886 * @return -1 if there's some error calculating the size, otherwise installed size of the 887 * package. 888 */ calculateInstalledSize(String packagePath, String abiOverride)889 public static long calculateInstalledSize(String packagePath, String abiOverride) { 890 final File packageFile = new File(packagePath); 891 try { 892 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 893 final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite( 894 input.reset(), packageFile, /* flags */ 0); 895 if (result.isError()) { 896 throw new PackageManagerException(result.getErrorCode(), 897 result.getErrorMessage(), result.getException()); 898 } 899 return InstallLocationUtils.calculateInstalledSize(result.getResult(), abiOverride); 900 } catch (PackageManagerException | IOException e) { 901 Slog.w(TAG, "Failed to calculate installed size: " + e); 902 return -1; 903 } 904 } 905 906 /** 907 * Checks whenever downgrade of an app is permitted. 908 * 909 * @param installFlags flags of the current install. 910 * @param isAppDebuggable if the currently installed version of the app is debuggable. 911 * @return {@code true} if downgrade is permitted according to the {@code installFlags} and 912 * {@code applicationFlags}. 913 */ isDowngradePermitted(int installFlags, boolean isAppDebuggable)914 public static boolean isDowngradePermitted(int installFlags, boolean isAppDebuggable) { 915 // If installed, the package will get access to data left on the device by its 916 // predecessor. As a security measure, this is permitted only if this is not a 917 // version downgrade or if the predecessor package is marked as debuggable and 918 // a downgrade is explicitly requested. 919 // 920 // On debuggable platform builds, downgrades are permitted even for 921 // non-debuggable packages to make testing easier. Debuggable platform builds do 922 // not offer security guarantees and thus it's OK to disable some security 923 // mechanisms to make debugging/testing easier on those builds. However, even on 924 // debuggable builds downgrades of packages are permitted only if requested via 925 // installFlags. This is because we aim to keep the behavior of debuggable 926 // platform builds as close as possible to the behavior of non-debuggable 927 // platform builds. 928 // 929 // In case of user builds, downgrade is permitted only for the system server initiated 930 // sessions. This is enforced by INSTALL_ALLOW_DOWNGRADE flag parameter. 931 final boolean downgradeRequested = 932 (installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0; 933 if (!downgradeRequested) { 934 return false; 935 } 936 final boolean isDebuggable = Build.IS_DEBUGGABLE || isAppDebuggable; 937 if (isDebuggable) { 938 return true; 939 } 940 return (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0; 941 } 942 943 /** 944 * Copy package to the target location. 945 * 946 * @param packagePath absolute path to the package to be copied. Can be 947 * a single monolithic APK file or a cluster directory 948 * containing one or more APKs. 949 * @return returns status code according to those in 950 * {@link PackageManager} 951 */ copyPackage(String packagePath, File targetDir)952 public static int copyPackage(String packagePath, File targetDir) { 953 if (packagePath == null) { 954 return PackageManager.INSTALL_FAILED_INVALID_URI; 955 } 956 957 try { 958 final File packageFile = new File(packagePath); 959 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 960 final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite( 961 input.reset(), packageFile, /* flags */ 0); 962 if (result.isError()) { 963 Slog.w(TAG, "Failed to parse package at " + packagePath); 964 return result.getErrorCode(); 965 } 966 final PackageLite pkg = result.getResult(); 967 copyFile(pkg.getBaseApkPath(), targetDir, "base.apk"); 968 if (!ArrayUtils.isEmpty(pkg.getSplitNames())) { 969 for (int i = 0; i < pkg.getSplitNames().length; i++) { 970 copyFile(pkg.getSplitApkPaths()[i], targetDir, 971 "split_" + pkg.getSplitNames()[i] + ".apk"); 972 } 973 } 974 return PackageManager.INSTALL_SUCCEEDED; 975 } catch (IOException | ErrnoException e) { 976 Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e); 977 return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; 978 } 979 } 980 copyFile(String sourcePath, File targetDir, String targetName)981 private static void copyFile(String sourcePath, File targetDir, String targetName) 982 throws ErrnoException, IOException { 983 if (!FileUtils.isValidExtFilename(targetName)) { 984 throw new IllegalArgumentException("Invalid filename: " + targetName); 985 } 986 Slog.d(TAG, "Copying " + sourcePath + " to " + targetName); 987 988 final File targetFile = new File(targetDir, targetName); 989 final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(), 990 O_RDWR | O_CREAT, 0644); 991 Os.chmod(targetFile.getAbsolutePath(), 0644); 992 FileInputStream source = null; 993 try { 994 source = new FileInputStream(sourcePath); 995 FileUtils.copy(source.getFD(), targetFd); 996 } finally { 997 IoUtils.closeQuietly(source); 998 } 999 } 1000 1001 /** 1002 * Recursively create target directory 1003 */ makeDirRecursive(File targetDir, int mode)1004 public static void makeDirRecursive(File targetDir, int mode) throws ErrnoException { 1005 final Path targetDirPath = targetDir.toPath(); 1006 final int directoriesCount = targetDirPath.getNameCount(); 1007 File currentDir; 1008 for (int i = 1; i <= directoriesCount; i++) { 1009 currentDir = targetDirPath.subpath(0, i).toFile(); 1010 if (currentDir.exists()) { 1011 continue; 1012 } 1013 Os.mkdir(currentDir.getAbsolutePath(), mode); 1014 Os.chmod(currentDir.getAbsolutePath(), mode); 1015 } 1016 } 1017 1018 /** 1019 * Returns a string that's compatible with the verification root hash extra. 1020 * @see PackageManager#EXTRA_VERIFICATION_ROOT_HASH 1021 */ 1022 @NonNull buildVerificationRootHashString(@onNull String baseFilename, @Nullable String[] splitFilenameArray)1023 public static String buildVerificationRootHashString(@NonNull String baseFilename, 1024 @Nullable String[] splitFilenameArray) { 1025 final StringBuilder sb = new StringBuilder(); 1026 final String baseFilePath = 1027 baseFilename.substring(baseFilename.lastIndexOf(File.separator) + 1); 1028 sb.append(baseFilePath).append(":"); 1029 final byte[] baseRootHash = getRootHash(baseFilename); 1030 if (baseRootHash == null) { 1031 sb.append("0"); 1032 } else { 1033 sb.append(HexDump.toHexString(baseRootHash)); 1034 } 1035 if (splitFilenameArray == null || splitFilenameArray.length == 0) { 1036 return sb.toString(); 1037 } 1038 1039 for (int i = splitFilenameArray.length - 1; i >= 0; i--) { 1040 final String splitFilename = splitFilenameArray[i]; 1041 final String splitFilePath = 1042 splitFilename.substring(splitFilename.lastIndexOf(File.separator) + 1); 1043 final byte[] splitRootHash = getRootHash(splitFilename); 1044 sb.append(";").append(splitFilePath).append(":"); 1045 if (splitRootHash == null) { 1046 sb.append("0"); 1047 } else { 1048 sb.append(HexDump.toHexString(splitRootHash)); 1049 } 1050 } 1051 return sb.toString(); 1052 } 1053 1054 /** 1055 * Returns the root has for the given file. 1056 * <p>Otherwise, returns {@code null} if the root hash could not be found or calculated. 1057 * <p>NOTE: This currently only works on files stored on the incremental file system. The 1058 * eventual goal is that this hash [among others] can be retrieved for any file. 1059 */ 1060 @Nullable getRootHash(String filename)1061 private static byte[] getRootHash(String filename) { 1062 try { 1063 final byte[] baseFileSignature = 1064 IncrementalManager.unsafeGetFileSignature(filename); 1065 if (baseFileSignature == null) { 1066 throw new IOException("File signature not present"); 1067 } 1068 final V4Signature signature = 1069 V4Signature.readFrom(baseFileSignature); 1070 if (signature.hashingInfo == null) { 1071 throw new IOException("Hashing info not present"); 1072 } 1073 final HashingInfo hashInfo = 1074 HashingInfo.fromByteArray(signature.hashingInfo); 1075 if (ArrayUtils.isEmpty(hashInfo.rawRootHash)) { 1076 throw new IOException("Root has not present"); 1077 } 1078 return ApkChecksums.verityHashForFile(new File(filename), hashInfo.rawRootHash); 1079 } catch (IOException ignore) { 1080 Slog.e(TAG, "ERROR: could not load root hash from incremental install"); 1081 } 1082 return null; 1083 } 1084 isSystemApp(PackageSetting ps)1085 public static boolean isSystemApp(PackageSetting ps) { 1086 return (ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0; 1087 } 1088 isUpdatedSystemApp(PackageSetting ps)1089 public static boolean isUpdatedSystemApp(PackageSetting ps) { 1090 return (ps.getFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 1091 } 1092 1093 // Static to give access to ComputeEngine applyEnforceIntentFilterMatching( PlatformCompat compat, ComponentResolverApi resolver, List<ResolveInfo> resolveInfos, boolean isReceiver, Intent intent, String resolvedType, int filterCallingUid)1094 public static void applyEnforceIntentFilterMatching( 1095 PlatformCompat compat, ComponentResolverApi resolver, 1096 List<ResolveInfo> resolveInfos, boolean isReceiver, 1097 Intent intent, String resolvedType, int filterCallingUid) { 1098 if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return; 1099 1100 final Printer logPrinter = DEBUG_INTENT_MATCHING 1101 ? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM) 1102 : null; 1103 1104 for (int i = resolveInfos.size() - 1; i >= 0; --i) { 1105 final ComponentInfo info = resolveInfos.get(i).getComponentInfo(); 1106 1107 // Do not enforce filter matching when the caller is system, root, or the same app 1108 if (ActivityManager.checkComponentPermission(null, filterCallingUid, 1109 info.applicationInfo.uid, false) == PackageManager.PERMISSION_GRANTED) { 1110 continue; 1111 } 1112 1113 // Only enforce filter matching if target app's target SDK >= T 1114 if (!compat.isChangeEnabledInternal( 1115 ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, info.applicationInfo)) { 1116 continue; 1117 } 1118 1119 final ParsedMainComponent comp; 1120 if (info instanceof ActivityInfo) { 1121 if (isReceiver) { 1122 comp = resolver.getReceiver(info.getComponentName()); 1123 } else { 1124 comp = resolver.getActivity(info.getComponentName()); 1125 } 1126 } else if (info instanceof ServiceInfo) { 1127 comp = resolver.getService(info.getComponentName()); 1128 } else { 1129 // This shall never happen 1130 throw new IllegalArgumentException("Unsupported component type"); 1131 } 1132 1133 if (comp == null || comp.getIntents().isEmpty()) { 1134 continue; 1135 } 1136 1137 boolean match = false; 1138 for (int j = 0, size = comp.getIntents().size(); j < size; ++j) { 1139 IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter(); 1140 if (IntentResolver.intentMatchesFilter(intentFilter, intent, resolvedType)) { 1141 match = true; 1142 break; 1143 } 1144 } 1145 if (!match) { 1146 Slog.w(TAG, "Intent does not match component's intent filter: " + intent); 1147 Slog.w(TAG, "Access blocked: " + comp.getComponentName()); 1148 if (DEBUG_INTENT_MATCHING) { 1149 Slog.v(TAG, "Component intent filters:"); 1150 comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, " ")); 1151 Slog.v(TAG, "-----------------------------"); 1152 } 1153 resolveInfos.remove(i); 1154 } 1155 } 1156 } 1157 1158 1159 /** 1160 * Do NOT use for intent resolution filtering. That should be done with 1161 * {@link DomainVerificationManagerInternal#filterToApprovedApp(Intent, List, int, Function)}. 1162 * 1163 * @return if the package is approved at any non-zero level for the domain in the intent 1164 */ hasAnyDomainApproval( @onNull DomainVerificationManagerInternal manager, @NonNull PackageStateInternal pkgSetting, @NonNull Intent intent, @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags, @UserIdInt int userId)1165 public static boolean hasAnyDomainApproval( 1166 @NonNull DomainVerificationManagerInternal manager, 1167 @NonNull PackageStateInternal pkgSetting, @NonNull Intent intent, 1168 @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags, @UserIdInt int userId) { 1169 return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId) 1170 > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE; 1171 } 1172 1173 /** 1174 * Update given intent when being used to request {@link ResolveInfo}. 1175 */ updateIntentForResolve(Intent intent)1176 public static Intent updateIntentForResolve(Intent intent) { 1177 if (intent.getSelector() != null) { 1178 intent = intent.getSelector(); 1179 } 1180 if (DEBUG_PREFERRED) { 1181 intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); 1182 } 1183 return intent; 1184 } 1185 arrayToString(int[] array)1186 public static String arrayToString(int[] array) { 1187 StringBuilder stringBuilder = new StringBuilder(128); 1188 stringBuilder.append('['); 1189 if (array != null) { 1190 for (int i = 0; i < array.length; i++) { 1191 if (i > 0) stringBuilder.append(", "); 1192 stringBuilder.append(array[i]); 1193 } 1194 } 1195 stringBuilder.append(']'); 1196 return stringBuilder.toString(); 1197 } 1198 1199 /** 1200 * Given {@code targetDir}, returns {@code targetDir/~~[randomStrA]/[packageName]-[randomStrB].} 1201 * Makes sure that {@code targetDir/~~[randomStrA]} directory doesn't exist. 1202 * Notice that this method doesn't actually create any directory. 1203 * 1204 * @param targetDir Directory that is two-levels up from the result directory. 1205 * @param packageName Name of the package whose code files are to be installed under the result 1206 * directory. 1207 * @return File object for the directory that should hold the code files of {@code packageName}. 1208 */ getNextCodePath(File targetDir, String packageName)1209 public static File getNextCodePath(File targetDir, String packageName) { 1210 SecureRandom random = new SecureRandom(); 1211 byte[] bytes = new byte[16]; 1212 File firstLevelDir; 1213 do { 1214 random.nextBytes(bytes); 1215 String firstLevelDirName = RANDOM_DIR_PREFIX 1216 + Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP); 1217 firstLevelDir = new File(targetDir, firstLevelDirName); 1218 } while (firstLevelDir.exists()); 1219 1220 random.nextBytes(bytes); 1221 String dirName = packageName + RANDOM_CODEPATH_PREFIX + Base64.encodeToString(bytes, 1222 Base64.URL_SAFE | Base64.NO_WRAP); 1223 final File result = new File(firstLevelDir, dirName); 1224 if (DEBUG && !Objects.equals(tryParsePackageName(result.getName()), packageName)) { 1225 throw new RuntimeException( 1226 "codepath is off: " + result.getName() + " (" + packageName + ")"); 1227 } 1228 return result; 1229 } 1230 tryParsePackageName(@onNull String codePath)1231 static String tryParsePackageName(@NonNull String codePath) throws IllegalArgumentException { 1232 int packageNameEnds = codePath.indexOf(RANDOM_CODEPATH_PREFIX); 1233 if (packageNameEnds == -1) { 1234 throw new IllegalArgumentException("Not a valid package folder name"); 1235 } 1236 return codePath.substring(0, packageNameEnds); 1237 } 1238 1239 /** 1240 * Gets the type of the external storage a package is installed on. 1241 * @param packageVolume The storage volume of the package. 1242 * @param packageIsExternal true if the package is currently installed on 1243 * external/removable/unprotected storage. 1244 * @return {@link StorageEnums#UNKNOWN} if the package is not stored externally or the 1245 * corresponding {@link StorageEnums} storage type value if it is. 1246 * corresponding {@link StorageEnums} storage type value if it is. 1247 */ getPackageExternalStorageType(VolumeInfo packageVolume, boolean packageIsExternal)1248 public static int getPackageExternalStorageType(VolumeInfo packageVolume, 1249 boolean packageIsExternal) { 1250 if (packageVolume != null) { 1251 DiskInfo disk = packageVolume.getDisk(); 1252 if (disk != null) { 1253 if (disk.isSd()) { 1254 return StorageEnums.SD_CARD; 1255 } 1256 if (disk.isUsb()) { 1257 return StorageEnums.USB; 1258 } 1259 if (packageIsExternal) { 1260 return StorageEnums.OTHER; 1261 } 1262 } 1263 } 1264 return StorageEnums.UNKNOWN; 1265 } 1266 1267 /** 1268 * Enforces that only the system UID or root's UID or shell's UID can call 1269 * a method exposed via Binder. 1270 * 1271 * @param message used as message if SecurityException is thrown 1272 * @throws SecurityException if the caller is not system or shell 1273 */ enforceSystemOrRootOrShell(String message)1274 public static void enforceSystemOrRootOrShell(String message) { 1275 if (!isSystemOrRootOrShell()) { 1276 throw new SecurityException(message); 1277 } 1278 } 1279 1280 /** 1281 * Check if the Binder caller is system UID, root's UID, or shell's UID. 1282 */ isSystemOrRootOrShell()1283 public static boolean isSystemOrRootOrShell() { 1284 final int uid = Binder.getCallingUid(); 1285 return uid == Process.SYSTEM_UID || uid == Process.ROOT_UID || uid == Process.SHELL_UID; 1286 } 1287 1288 /** 1289 * Check if the Binder caller is system UID or root's UID. 1290 */ isSystemOrRoot()1291 public static boolean isSystemOrRoot() { 1292 final int uid = Binder.getCallingUid(); 1293 return uid == Process.SYSTEM_UID || uid == Process.ROOT_UID; 1294 } 1295 1296 /** 1297 * Enforces that only the system UID or root's UID can call a method exposed 1298 * via Binder. 1299 * 1300 * @param message used as message if SecurityException is thrown 1301 * @throws SecurityException if the caller is not system or root 1302 */ enforceSystemOrRoot(String message)1303 public static void enforceSystemOrRoot(String message) { 1304 if (!isSystemOrRoot()) { 1305 throw new SecurityException(message); 1306 } 1307 } 1308 preparePackageParserCache(boolean forEngBuild, boolean isUserDebugBuild, String incrementalVersion)1309 public static @Nullable File preparePackageParserCache(boolean forEngBuild, 1310 boolean isUserDebugBuild, String incrementalVersion) { 1311 if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) { 1312 if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) { 1313 return null; 1314 } 1315 1316 // Disable package parsing on eng builds to allow for faster incremental development. 1317 if (forEngBuild) { 1318 return null; 1319 } 1320 1321 if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) { 1322 Slog.i(TAG, "Disabling package parser cache due to system property."); 1323 return null; 1324 } 1325 } 1326 1327 // The base directory for the package parser cache lives under /data/system/. 1328 final File cacheBaseDir = Environment.getPackageCacheDirectory(); 1329 if (!FileUtils.createDir(cacheBaseDir)) { 1330 return null; 1331 } 1332 1333 // There are several items that need to be combined together to safely 1334 // identify cached items. In particular, changing the value of certain 1335 // feature flags should cause us to invalidate any caches. 1336 final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug" 1337 : PackagePartitions.FINGERPRINT; 1338 1339 // Reconcile cache directories, keeping only what we'd actually use. 1340 for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { 1341 if (Objects.equals(cacheName, cacheDir.getName())) { 1342 Slog.d(TAG, "Keeping known cache " + cacheDir.getName()); 1343 } else { 1344 Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName()); 1345 FileUtils.deleteContentsAndDir(cacheDir); 1346 } 1347 } 1348 1349 // Return the versioned package cache directory. 1350 File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); 1351 1352 if (cacheDir == null) { 1353 // Something went wrong. Attempt to delete everything and return. 1354 Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir); 1355 FileUtils.deleteContentsAndDir(cacheBaseDir); 1356 return null; 1357 } 1358 1359 // The following is a workaround to aid development on non-numbered userdebug 1360 // builds or cases where "adb sync" is used on userdebug builds. If we detect that 1361 // the system partition is newer. 1362 // 1363 // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build 1364 // that starts with "eng." to signify that this is an engineering build and not 1365 // destined for release. 1366 if (isUserDebugBuild && incrementalVersion.startsWith("eng.")) { 1367 Slog.w(TAG, "Wiping cache directory because the system partition changed."); 1368 1369 // Heuristic: If the /system directory has been modified recently due to an "adb sync" 1370 // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable 1371 // in general and should not be used for production changes. In this specific case, 1372 // we know that they will work. 1373 File frameworkDir = 1374 new File(Environment.getRootDirectory(), "framework"); 1375 if (cacheDir.lastModified() < frameworkDir.lastModified()) { 1376 FileUtils.deleteContents(cacheBaseDir); 1377 cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); 1378 } 1379 } 1380 1381 return cacheDir; 1382 } 1383 1384 /** 1385 * Check and throw if the given before/after packages would be considered a 1386 * downgrade. 1387 */ checkDowngrade(AndroidPackage before, PackageInfoLite after)1388 public static void checkDowngrade(AndroidPackage before, PackageInfoLite after) 1389 throws PackageManagerException { 1390 if (after.getLongVersionCode() < before.getLongVersionCode()) { 1391 throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, 1392 "Update version code " + after.versionCode + " is older than current " 1393 + before.getLongVersionCode()); 1394 } else if (after.getLongVersionCode() == before.getLongVersionCode()) { 1395 if (after.baseRevisionCode < before.getBaseRevisionCode()) { 1396 throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, 1397 "Update base revision code " + after.baseRevisionCode 1398 + " is older than current " + before.getBaseRevisionCode()); 1399 } 1400 1401 if (!ArrayUtils.isEmpty(after.splitNames)) { 1402 for (int i = 0; i < after.splitNames.length; i++) { 1403 final String splitName = after.splitNames[i]; 1404 final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName); 1405 if (j != -1) { 1406 if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) { 1407 throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, 1408 "Update split " + splitName + " revision code " 1409 + after.splitRevisionCodes[i] 1410 + " is older than current " 1411 + before.getSplitRevisionCodes()[j]); 1412 } 1413 } 1414 } 1415 } 1416 } 1417 } 1418 } 1419