PackageManagerServiceUtils.java revision b18f899241231991e314c40990eb8e9f703732d4
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 17package com.android.server.pm; 18 19import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; 20import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; 21import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION; 22import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; 23import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; 24import static com.android.server.pm.PackageManagerService.STUB_SUFFIX; 25import static com.android.server.pm.PackageManagerService.TAG; 26import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; 27 28import com.android.internal.content.NativeLibraryHelper; 29import com.android.internal.util.ArrayUtils; 30import com.android.internal.util.FastPrintWriter; 31import com.android.server.EventLogTags; 32import com.android.server.pm.dex.DexManager; 33import com.android.server.pm.dex.PackageDexUsage; 34 35import android.annotation.NonNull; 36import android.annotation.Nullable; 37import android.app.AppGlobals; 38import android.content.Intent; 39import android.content.pm.PackageManager; 40import android.content.pm.PackageParser; 41import android.content.pm.PackageParser.PackageParserException; 42import android.content.pm.ResolveInfo; 43import android.content.pm.Signature; 44import android.os.Build; 45import android.os.Debug; 46import android.os.Environment; 47import android.os.FileUtils; 48import android.os.Process; 49import android.os.RemoteException; 50import android.os.SystemProperties; 51import android.os.UserHandle; 52import android.service.pm.PackageServiceDumpProto; 53import android.system.ErrnoException; 54import android.system.Os; 55import android.util.ArraySet; 56import android.util.Log; 57import android.util.PackageUtils; 58import android.util.Slog; 59import android.util.jar.StrictJarFile; 60import android.util.proto.ProtoOutputStream; 61 62import dalvik.system.VMRuntime; 63 64import libcore.io.IoUtils; 65import libcore.io.Libcore; 66import libcore.io.Streams; 67 68import java.io.BufferedReader; 69import java.io.File; 70import java.io.FileInputStream; 71import java.io.FileOutputStream; 72import java.io.FileReader; 73import java.io.FilenameFilter; 74import java.io.IOException; 75import java.io.InputStream; 76import java.io.OutputStream; 77import java.io.PrintWriter; 78import java.security.MessageDigest; 79import java.security.NoSuchAlgorithmException; 80import java.security.cert.CertificateEncodingException; 81import java.security.cert.CertificateException; 82import java.text.SimpleDateFormat; 83import java.util.ArrayList; 84import java.util.Arrays; 85import java.util.Collection; 86import java.util.Collections; 87import java.util.Date; 88import java.util.Iterator; 89import java.util.LinkedList; 90import java.util.List; 91import java.util.function.Predicate; 92import java.util.zip.GZIPInputStream; 93import java.util.zip.ZipEntry; 94 95/** 96 * Class containing helper methods for the PackageManagerService. 97 * 98 * {@hide} 99 */ 100public class PackageManagerServiceUtils { 101 private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000; 102 103 private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) { 104 List<ResolveInfo> ris = null; 105 try { 106 ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId) 107 .getList(); 108 } catch (RemoteException e) { 109 } 110 ArraySet<String> pkgNames = new ArraySet<String>(); 111 if (ris != null) { 112 for (ResolveInfo ri : ris) { 113 pkgNames.add(ri.activityInfo.packageName); 114 } 115 } 116 return pkgNames; 117 } 118 119 // Sort a list of apps by their last usage, most recently used apps first. The order of 120 // packages without usage data is undefined (but they will be sorted after the packages 121 // that do have usage data). 122 public static void sortPackagesByUsageDate(List<PackageParser.Package> pkgs, 123 PackageManagerService packageManagerService) { 124 if (!packageManagerService.isHistoricalPackageUsageAvailable()) { 125 return; 126 } 127 128 Collections.sort(pkgs, (pkg1, pkg2) -> 129 Long.compare(pkg2.getLatestForegroundPackageUseTimeInMills(), 130 pkg1.getLatestForegroundPackageUseTimeInMills())); 131 } 132 133 // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the 134 // package will be removed from {@code packages} and added to {@code result} with its 135 // dependencies. If usage data is available, the positive packages will be sorted by usage 136 // data (with {@code sortTemp} as temporary storage). 137 private static void applyPackageFilter(Predicate<PackageParser.Package> filter, 138 Collection<PackageParser.Package> result, 139 Collection<PackageParser.Package> packages, 140 @NonNull List<PackageParser.Package> sortTemp, 141 PackageManagerService packageManagerService) { 142 for (PackageParser.Package pkg : packages) { 143 if (filter.test(pkg)) { 144 sortTemp.add(pkg); 145 } 146 } 147 148 sortPackagesByUsageDate(sortTemp, packageManagerService); 149 packages.removeAll(sortTemp); 150 151 for (PackageParser.Package pkg : sortTemp) { 152 result.add(pkg); 153 154 Collection<PackageParser.Package> deps = 155 packageManagerService.findSharedNonSystemLibraries(pkg); 156 if (!deps.isEmpty()) { 157 deps.removeAll(result); 158 result.addAll(deps); 159 packages.removeAll(deps); 160 } 161 } 162 163 sortTemp.clear(); 164 } 165 166 // Sort apps by importance for dexopt ordering. Important apps are given 167 // more priority in case the device runs out of space. 168 public static List<PackageParser.Package> getPackagesForDexopt( 169 Collection<PackageParser.Package> packages, 170 PackageManagerService packageManagerService) { 171 ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages); 172 LinkedList<PackageParser.Package> result = new LinkedList<>(); 173 ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size()); 174 175 // Give priority to core apps. 176 applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp, 177 packageManagerService); 178 179 // Give priority to system apps that listen for pre boot complete. 180 Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); 181 final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM); 182 applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs, 183 sortTemp, packageManagerService); 184 185 // Give priority to apps used by other apps. 186 DexManager dexManager = packageManagerService.getDexManager(); 187 applyPackageFilter((pkg) -> 188 dexManager.getPackageUseInfoOrDefault(pkg.packageName) 189 .isAnyCodePathUsedByOtherApps(), 190 result, remainingPkgs, sortTemp, packageManagerService); 191 192 // Filter out packages that aren't recently used, add all remaining apps. 193 // TODO: add a property to control this? 194 Predicate<PackageParser.Package> remainingPredicate; 195 if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) { 196 if (DEBUG_DEXOPT) { 197 Log.i(TAG, "Looking at historical package use"); 198 } 199 // Get the package that was used last. 200 PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) -> 201 Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(), 202 pkg2.getLatestForegroundPackageUseTimeInMills())); 203 if (DEBUG_DEXOPT) { 204 Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use"); 205 } 206 long estimatedPreviousSystemUseTime = 207 lastUsed.getLatestForegroundPackageUseTimeInMills(); 208 // Be defensive if for some reason package usage has bogus data. 209 if (estimatedPreviousSystemUseTime != 0) { 210 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS; 211 remainingPredicate = 212 (pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime; 213 } else { 214 // No meaningful historical info. Take all. 215 remainingPredicate = (pkg) -> true; 216 } 217 sortPackagesByUsageDate(remainingPkgs, packageManagerService); 218 } else { 219 // No historical info. Take all. 220 remainingPredicate = (pkg) -> true; 221 } 222 applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp, 223 packageManagerService); 224 225 if (DEBUG_DEXOPT) { 226 Log.i(TAG, "Packages to be dexopted: " + packagesToString(result)); 227 Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs)); 228 } 229 230 return result; 231 } 232 233 /** 234 * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>. 235 * Package is considered active, if: 236 * 1) It was active in foreground. 237 * 2) It was active in background and also used by other apps. 238 * 239 * If it doesn't have sufficient information about the package, it return <code>false</code>. 240 */ 241 public static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, 242 long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo, 243 long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) { 244 245 if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) { 246 return false; 247 } 248 249 // If the app was active in foreground during the threshold period. 250 boolean isActiveInForeground = (currentTimeInMillis 251 - latestForegroundPackageUseTimeInMillis) 252 < thresholdTimeinMillis; 253 254 if (isActiveInForeground) { 255 return false; 256 } 257 258 // If the app was active in background during the threshold period and was used 259 // by other packages. 260 boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis 261 - latestPackageUseTimeInMillis) 262 < thresholdTimeinMillis) 263 && packageUseInfo.isAnyCodePathUsedByOtherApps(); 264 265 return !isActiveInBackgroundAndUsedByOtherPackages; 266 } 267 268 /** 269 * Returns the canonicalized path of {@code path} as per {@code realpath(3)} 270 * semantics. 271 */ 272 public static String realpath(File path) throws IOException { 273 try { 274 return Os.realpath(path.getAbsolutePath()); 275 } catch (ErrnoException ee) { 276 throw ee.rethrowAsIOException(); 277 } 278 } 279 280 public static String packagesToString(Collection<PackageParser.Package> c) { 281 StringBuilder sb = new StringBuilder(); 282 for (PackageParser.Package pkg : c) { 283 if (sb.length() > 0) { 284 sb.append(", "); 285 } 286 sb.append(pkg.packageName); 287 } 288 return sb.toString(); 289 } 290 291 /** 292 * Verifies that the given string {@code isa} is a valid supported isa on 293 * the running device. 294 */ 295 public static boolean checkISA(String isa) { 296 for (String abi : Build.SUPPORTED_ABIS) { 297 if (VMRuntime.getInstructionSet(abi).equals(isa)) { 298 return true; 299 } 300 } 301 return false; 302 } 303 304 public static long getLastModifiedTime(PackageParser.Package pkg) { 305 final File srcFile = new File(pkg.codePath); 306 if (!srcFile.isDirectory()) { 307 return srcFile.lastModified(); 308 } 309 final File baseFile = new File(pkg.baseCodePath); 310 long maxModifiedTime = baseFile.lastModified(); 311 if (pkg.splitCodePaths != null) { 312 for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) { 313 final File splitFile = new File(pkg.splitCodePaths[i]); 314 maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified()); 315 } 316 } 317 return maxModifiedTime; 318 } 319 320 /** 321 * Checks that the archive located at {@code fileName} has uncompressed dex file and so 322 * files that can be direclty mapped. 323 */ 324 public static void logApkHasUncompressedCode(String fileName) { 325 StrictJarFile jarFile = null; 326 try { 327 jarFile = new StrictJarFile(fileName, 328 false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/); 329 Iterator<ZipEntry> it = jarFile.iterator(); 330 while (it.hasNext()) { 331 ZipEntry entry = it.next(); 332 if (entry.getName().endsWith(".dex")) { 333 if (entry.getMethod() != ZipEntry.STORED) { 334 Slog.wtf(TAG, "APK " + fileName + " has compressed dex code " + 335 entry.getName()); 336 } else if ((entry.getDataOffset() & 0x3) != 0) { 337 Slog.wtf(TAG, "APK " + fileName + " has unaligned dex code " + 338 entry.getName()); 339 } 340 } else if (entry.getName().endsWith(".so")) { 341 if (entry.getMethod() != ZipEntry.STORED) { 342 Slog.wtf(TAG, "APK " + fileName + " has compressed native code " + 343 entry.getName()); 344 } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) { 345 Slog.wtf(TAG, "APK " + fileName + " has unaligned native code " + 346 entry.getName()); 347 } 348 } 349 } 350 } catch (IOException ignore) { 351 Slog.wtf(TAG, "Error when parsing APK " + fileName); 352 } finally { 353 try { 354 if (jarFile != null) { 355 jarFile.close(); 356 } 357 } catch (IOException ignore) {} 358 } 359 return; 360 } 361 362 /** 363 * Checks that the APKs in the given package have uncompressed dex file and so 364 * files that can be direclty mapped. 365 */ 366 public static void logPackageHasUncompressedCode(PackageParser.Package pkg) { 367 logApkHasUncompressedCode(pkg.baseCodePath); 368 if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { 369 for (int i = 0; i < pkg.splitCodePaths.length; i++) { 370 logApkHasUncompressedCode(pkg.splitCodePaths[i]); 371 } 372 } 373 } 374 375 private static File getSettingsProblemFile() { 376 File dataDir = Environment.getDataDirectory(); 377 File systemDir = new File(dataDir, "system"); 378 File fname = new File(systemDir, "uiderrors.txt"); 379 return fname; 380 } 381 382 public static void dumpCriticalInfo(ProtoOutputStream proto) { 383 try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) { 384 String line = null; 385 while ((line = in.readLine()) != null) { 386 if (line.contains("ignored: updated version")) continue; 387 proto.write(PackageServiceDumpProto.MESSAGES, line); 388 } 389 } catch (IOException ignored) { 390 } 391 } 392 393 public static void dumpCriticalInfo(PrintWriter pw, String msg) { 394 try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) { 395 String line = null; 396 while ((line = in.readLine()) != null) { 397 if (line.contains("ignored: updated version")) continue; 398 if (msg != null) { 399 pw.print(msg); 400 } 401 pw.println(line); 402 } 403 } catch (IOException ignored) { 404 } 405 } 406 407 public static void logCriticalInfo(int priority, String msg) { 408 Slog.println(priority, TAG, msg); 409 EventLogTags.writePmCriticalInfo(msg); 410 try { 411 File fname = getSettingsProblemFile(); 412 FileOutputStream out = new FileOutputStream(fname, true); 413 PrintWriter pw = new FastPrintWriter(out); 414 SimpleDateFormat formatter = new SimpleDateFormat(); 415 String dateString = formatter.format(new Date(System.currentTimeMillis())); 416 pw.println(dateString + ": " + msg); 417 pw.close(); 418 FileUtils.setPermissions( 419 fname.toString(), 420 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH, 421 -1, -1); 422 } catch (java.io.IOException e) { 423 } 424 } 425 426 public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) { 427 if (callingUid == Process.SHELL_UID) { 428 if (userHandle >= 0 429 && PackageManagerService.sUserManager.hasUserRestriction( 430 restriction, userHandle)) { 431 throw new SecurityException("Shell does not have permission to access user " 432 + userHandle); 433 } else if (userHandle < 0) { 434 Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user " 435 + userHandle + "\n\t" + Debug.getCallers(3)); 436 } 437 } 438 } 439 440 /** 441 * Derive the value of the {@code cpuAbiOverride} based on the provided 442 * value and an optional stored value from the package settings. 443 */ 444 public static String deriveAbiOverride(String abiOverride, PackageSetting settings) { 445 String cpuAbiOverride = null; 446 if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) { 447 cpuAbiOverride = null; 448 } else if (abiOverride != null) { 449 cpuAbiOverride = abiOverride; 450 } else if (settings != null) { 451 cpuAbiOverride = settings.cpuAbiOverrideString; 452 } 453 return cpuAbiOverride; 454 } 455 456 /** 457 * Compares two sets of signatures. Returns: 458 * <br /> 459 * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null, 460 * <br /> 461 * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null, 462 * <br /> 463 * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null, 464 * <br /> 465 * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical, 466 * <br /> 467 * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ. 468 */ 469 public static int compareSignatures(Signature[] s1, Signature[] s2) { 470 if (s1 == null) { 471 return s2 == null 472 ? PackageManager.SIGNATURE_NEITHER_SIGNED 473 : PackageManager.SIGNATURE_FIRST_NOT_SIGNED; 474 } 475 476 if (s2 == null) { 477 return PackageManager.SIGNATURE_SECOND_NOT_SIGNED; 478 } 479 480 if (s1.length != s2.length) { 481 return PackageManager.SIGNATURE_NO_MATCH; 482 } 483 484 // Since both signature sets are of size 1, we can compare without HashSets. 485 if (s1.length == 1) { 486 return s1[0].equals(s2[0]) ? 487 PackageManager.SIGNATURE_MATCH : 488 PackageManager.SIGNATURE_NO_MATCH; 489 } 490 491 ArraySet<Signature> set1 = new ArraySet<Signature>(); 492 for (Signature sig : s1) { 493 set1.add(sig); 494 } 495 ArraySet<Signature> set2 = new ArraySet<Signature>(); 496 for (Signature sig : s2) { 497 set2.add(sig); 498 } 499 // Make sure s2 contains all signatures in s1. 500 if (set1.equals(set2)) { 501 return PackageManager.SIGNATURE_MATCH; 502 } 503 return PackageManager.SIGNATURE_NO_MATCH; 504 } 505 506 /** 507 * Used for backward compatibility to make sure any packages with 508 * certificate chains get upgraded to the new style. {@code existingSigs} 509 * will be in the old format (since they were stored on disk from before the 510 * system upgrade) and {@code scannedSigs} will be in the newer format. 511 */ 512 private static boolean matchSignaturesCompat(String packageName, 513 PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) { 514 ArraySet<Signature> existingSet = new ArraySet<Signature>(); 515 for (Signature sig : packageSignatures.mSigningDetails.signatures) { 516 existingSet.add(sig); 517 } 518 ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>(); 519 for (Signature sig : parsedSignatures.signatures) { 520 try { 521 Signature[] chainSignatures = sig.getChainSignatures(); 522 for (Signature chainSig : chainSignatures) { 523 scannedCompatSet.add(chainSig); 524 } 525 } catch (CertificateEncodingException e) { 526 scannedCompatSet.add(sig); 527 } 528 } 529 // make sure the expanded scanned set contains all signatures in the existing one 530 if (scannedCompatSet.equals(existingSet)) { 531 // migrate the old signatures to the new scheme 532 packageSignatures.mSigningDetails = parsedSignatures; 533 return true; 534 } 535 return false; 536 } 537 538 private static boolean matchSignaturesRecover(String packageName, 539 Signature[] existingSignatures, Signature[] parsedSignatures) { 540 String msg = null; 541 try { 542 if (Signature.areEffectiveMatch(existingSignatures, parsedSignatures)) { 543 logCriticalInfo(Log.INFO, 544 "Recovered effectively matching certificates for " + packageName); 545 return true; 546 } 547 } catch (CertificateException e) { 548 msg = e.getMessage(); 549 } 550 logCriticalInfo(Log.INFO, 551 "Failed to recover certificates for " + packageName + ": " + msg); 552 return false; 553 } 554 555 /** 556 * Make sure the updated priv app is signed with the same key as the original APK file on the 557 * /system partition. 558 * 559 * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data 560 * and is not tamperproof. 561 */ 562 private static boolean matchSignatureInSystem(PackageSetting pkgSetting, 563 PackageSetting disabledPkgSetting) { 564 try { 565 PackageParser.collectCertificates(disabledPkgSetting.pkg, true /* skipVerify */); 566 if (compareSignatures(pkgSetting.signatures.mSigningDetails.signatures, 567 disabledPkgSetting.signatures.mSigningDetails.signatures) 568 != PackageManager.SIGNATURE_MATCH) { 569 logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " + 570 pkgSetting.name); 571 return false; 572 } 573 } catch (PackageParserException e) { 574 logCriticalInfo(Log.ERROR, "Failed to collect cert for " + pkgSetting.name + ": " + 575 e.getMessage()); 576 return false; 577 } 578 return true; 579 } 580 581 /** 582 * Checks the signing certificates to see if the provided certificate is a member. Invalid for 583 * {@code SigningDetails} with multiple signing certificates. 584 * @param certificate certificate to check for membership 585 * @param signingDetails signing certificates record whose members are to be searched 586 * @return true if {@code certificate} is in {@code signingDetails} 587 */ 588 public static boolean signingDetailsHasCertificate( 589 byte[] certificate, PackageParser.SigningDetails signingDetails) { 590 if (signingDetails == PackageParser.SigningDetails.UNKNOWN) { 591 return false; 592 } 593 Signature signature = new Signature(certificate); 594 if (signingDetails.hasPastSigningCertificates()) { 595 for (int i = 0; i < signingDetails.pastSigningCertificates.length; i++) { 596 if (signingDetails.pastSigningCertificates[i].equals(signature)) { 597 return true; 598 } 599 } 600 } else { 601 // no signing history, just check the current signer 602 if (signingDetails.signatures.length == 1 603 && signingDetails.signatures[0].equals(signature)) { 604 return true; 605 } 606 } 607 return false; 608 } 609 610 /** 611 * Checks the signing certificates to see if the provided certificate is a member. Invalid for 612 * {@code SigningDetails} with multiple signing certificaes. 613 * @param sha256Certificate certificate to check for membership 614 * @param signingDetails signing certificates record whose members are to be searched 615 * @return true if {@code certificate} is in {@code signingDetails} 616 */ 617 public static boolean signingDetailsHasSha256Certificate( 618 byte[] sha256Certificate, PackageParser.SigningDetails signingDetails ) { 619 if (signingDetails == PackageParser.SigningDetails.UNKNOWN) { 620 return false; 621 } 622 if (signingDetails.hasPastSigningCertificates()) { 623 for (int i = 0; i < signingDetails.pastSigningCertificates.length; i++) { 624 byte[] digest = PackageUtils.computeSha256DigestBytes( 625 signingDetails.pastSigningCertificates[i].toByteArray()); 626 if (Arrays.equals(sha256Certificate, digest)) { 627 return true; 628 } 629 } 630 } else { 631 // no signing history, just check the current signer 632 if (signingDetails.signatures.length == 1) { 633 byte[] digest = PackageUtils.computeSha256DigestBytes( 634 signingDetails.signatures[0].toByteArray()); 635 if (Arrays.equals(sha256Certificate, digest)) { 636 return true; 637 } 638 } 639 } 640 return false; 641 } 642 643 /** Returns true if APK Verity is enabled. */ 644 static boolean isApkVerityEnabled() { 645 return SystemProperties.getInt("ro.apk_verity.mode", 0) != 0; 646 } 647 648 /** Returns true to force apk verification if the updated package (in /data) is a priv app. */ 649 static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) { 650 return disabledPs != null && disabledPs.isPrivileged() && isApkVerityEnabled(); 651 } 652 653 /** 654 * Verifies that signatures match. 655 * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}. 656 * @throws PackageManagerException if the signatures did not match. 657 */ 658 public static boolean verifySignatures(PackageSetting pkgSetting, 659 PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures, 660 boolean compareCompat, boolean compareRecover) 661 throws PackageManagerException { 662 final String packageName = pkgSetting.name; 663 boolean compatMatch = false; 664 if (pkgSetting.signatures.mSigningDetails.signatures != null) { 665 // Already existing package. Make sure signatures match 666 boolean match = compareSignatures(pkgSetting.signatures.mSigningDetails.signatures, 667 parsedSignatures.signatures) 668 == PackageManager.SIGNATURE_MATCH; 669 if (!match && compareCompat) { 670 match = matchSignaturesCompat(packageName, pkgSetting.signatures, 671 parsedSignatures); 672 compatMatch = match; 673 } 674 if (!match && compareRecover) { 675 match = matchSignaturesRecover( 676 packageName, pkgSetting.signatures.mSigningDetails.signatures, 677 parsedSignatures.signatures); 678 } 679 680 if (!match && isApkVerificationForced(disabledPkgSetting)) { 681 match = matchSignatureInSystem(pkgSetting, disabledPkgSetting); 682 } 683 684 if (!match) { 685 throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, 686 "Package " + packageName + 687 " signatures do not match previously installed version; ignoring!"); 688 } 689 } 690 // Check for shared user signatures 691 if (pkgSetting.sharedUser != null 692 && pkgSetting.sharedUser.signatures.mSigningDetails.signatures != null) { 693 // Already existing package. Make sure signatures match 694 boolean match = 695 compareSignatures( 696 pkgSetting.sharedUser.signatures.mSigningDetails.signatures, 697 parsedSignatures.signatures) == PackageManager.SIGNATURE_MATCH; 698 if (!match && compareCompat) { 699 match = matchSignaturesCompat( 700 packageName, pkgSetting.sharedUser.signatures, parsedSignatures); 701 } 702 if (!match && compareRecover) { 703 match = matchSignaturesRecover(packageName, 704 pkgSetting.sharedUser.signatures.mSigningDetails.signatures, 705 parsedSignatures.signatures); 706 compatMatch |= match; 707 } 708 if (!match) { 709 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, 710 "Package " + packageName 711 + " has no signatures that match those in shared user " 712 + pkgSetting.sharedUser.name + "; ignoring!"); 713 } 714 } 715 return compatMatch; 716 } 717 718 public static int decompressFile(File srcFile, File dstFile) throws ErrnoException { 719 if (DEBUG_COMPRESSION) { 720 Slog.i(TAG, "Decompress file" 721 + "; src: " + srcFile.getAbsolutePath() 722 + ", dst: " + dstFile.getAbsolutePath()); 723 } 724 try ( 725 InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile)); 726 OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/); 727 ) { 728 FileUtils.copy(fileIn, fileOut); 729 Os.chmod(dstFile.getAbsolutePath(), 0644); 730 return PackageManager.INSTALL_SUCCEEDED; 731 } catch (IOException e) { 732 logCriticalInfo(Log.ERROR, "Failed to decompress file" 733 + "; src: " + srcFile.getAbsolutePath() 734 + ", dst: " + dstFile.getAbsolutePath()); 735 } 736 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; 737 } 738 739 public static File[] getCompressedFiles(String codePath) { 740 final File stubCodePath = new File(codePath); 741 final String stubName = stubCodePath.getName(); 742 743 // The layout of a compressed package on a given partition is as follows : 744 // 745 // Compressed artifacts: 746 // 747 // /partition/ModuleName/foo.gz 748 // /partation/ModuleName/bar.gz 749 // 750 // Stub artifact: 751 // 752 // /partition/ModuleName-Stub/ModuleName-Stub.apk 753 // 754 // In other words, stub is on the same partition as the compressed artifacts 755 // and in a directory that's suffixed with "-Stub". 756 int idx = stubName.lastIndexOf(STUB_SUFFIX); 757 if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) { 758 return null; 759 } 760 761 final File stubParentDir = stubCodePath.getParentFile(); 762 if (stubParentDir == null) { 763 Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath); 764 return null; 765 } 766 767 final File compressedPath = new File(stubParentDir, stubName.substring(0, idx)); 768 final File[] files = compressedPath.listFiles(new FilenameFilter() { 769 @Override 770 public boolean accept(File dir, String name) { 771 return name.toLowerCase().endsWith(COMPRESSED_EXTENSION); 772 } 773 }); 774 775 if (DEBUG_COMPRESSION && files != null && files.length > 0) { 776 Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files)); 777 } 778 779 return files; 780 } 781 782 public static boolean compressedFileExists(String codePath) { 783 final File[] compressedFiles = getCompressedFiles(codePath); 784 return compressedFiles != null && compressedFiles.length > 0; 785 } 786} 787