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