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