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