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