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