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.dex; 18 19 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; 20 import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; 21 import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo; 22 23 import static java.util.function.Function.identity; 24 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.content.Context; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.IPackageManager; 30 import android.content.pm.PackageInfo; 31 import android.content.pm.PackageManager; 32 import android.content.pm.PackagePartitions; 33 import android.os.BatteryManager; 34 import android.os.FileUtils; 35 import android.os.PowerManager; 36 import android.os.RemoteException; 37 import android.os.ServiceManager; 38 import android.os.UserHandle; 39 import android.os.storage.StorageManager; 40 import android.util.Log; 41 import android.util.Slog; 42 import android.util.jar.StrictJarFile; 43 44 import com.android.internal.annotations.GuardedBy; 45 import com.android.internal.annotations.VisibleForTesting; 46 import com.android.server.pm.Installer; 47 import com.android.server.pm.Installer.InstallerException; 48 import com.android.server.pm.Installer.LegacyDexoptDisabledException; 49 import com.android.server.pm.PackageDexOptimizer; 50 import com.android.server.pm.PackageManagerService; 51 import com.android.server.pm.PackageManagerServiceUtils; 52 53 import dalvik.system.VMRuntime; 54 55 import java.io.File; 56 import java.io.IOException; 57 import java.nio.file.Files; 58 import java.nio.file.Paths; 59 import java.util.ArrayList; 60 import java.util.Collections; 61 import java.util.HashMap; 62 import java.util.HashSet; 63 import java.util.Iterator; 64 import java.util.List; 65 import java.util.Map; 66 import java.util.Set; 67 import java.util.zip.ZipEntry; 68 69 /** 70 * This class keeps track of how dex files are used. 71 * Every time it gets a notification about a dex file being loaded it tracks 72 * its owning package and records it in PackageDexUsage (package-dex-usage.list). 73 * 74 * TODO(calin): Extract related dexopt functionality from PackageManagerService 75 * into this class. 76 */ 77 public class DexManager { 78 private static final String TAG = "DexManager"; 79 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 80 81 // System server cannot load executable code outside system partitions. 82 // However it can load verification data - thus we pick the "verify" compiler filter. 83 private static final String SYSTEM_SERVER_COMPILER_FILTER = "verify"; 84 85 // The suffix we add to the package name when the loading happens in an isolated process. 86 // Note that the double dot creates and "invalid" package name which makes it clear that this 87 // is an artificially constructed name. 88 private static final String ISOLATED_PROCESS_PACKAGE_SUFFIX = "..isolated"; 89 90 private final Context mContext; 91 92 // Maps package name to code locations. 93 // It caches the code locations for the installed packages. This allows for 94 // faster lookups (no locks) when finding what package owns the dex file. 95 @GuardedBy("mPackageCodeLocationsCache") 96 private final Map<String, PackageCodeLocations> mPackageCodeLocationsCache; 97 98 // PackageDexUsage handles the actual I/O operations. It is responsible to 99 // encode and save the dex usage data. 100 private final PackageDexUsage mPackageDexUsage; 101 102 // DynamicCodeLogger handles recording of dynamic code loading - which is similar to 103 // PackageDexUsage but records a different aspect of the data. 104 // (It additionally includes DEX files loaded with unsupported class loaders, and doesn't 105 // record class loaders or ISAs.) 106 private final DynamicCodeLogger mDynamicCodeLogger; 107 108 private IPackageManager mPackageManager; 109 private final PackageDexOptimizer mPackageDexOptimizer; 110 private final Object mInstallLock; 111 @GuardedBy("mInstallLock") 112 private final Installer mInstaller; 113 114 private BatteryManager mBatteryManager = null; 115 private PowerManager mPowerManager = null; 116 117 // An integer percentage value used to determine when the device is considered to be on low 118 // power for compilation purposes. 119 private final int mCriticalBatteryLevel; 120 121 // Possible outcomes of a dex search. 122 private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found 123 private static int DEX_SEARCH_FOUND_PRIMARY = 1; // dex file is the primary/base apk 124 private static int DEX_SEARCH_FOUND_SPLIT = 2; // dex file is a split apk 125 private static int DEX_SEARCH_FOUND_SECONDARY = 3; // dex file is a secondary dex 126 DexManager(Context context, PackageDexOptimizer pdo, Installer installer, Object installLock, DynamicCodeLogger dynamicCodeLogger)127 public DexManager(Context context, PackageDexOptimizer pdo, Installer installer, 128 Object installLock, DynamicCodeLogger dynamicCodeLogger) { 129 this(context, pdo, installer, installLock, dynamicCodeLogger, null); 130 } 131 132 @VisibleForTesting DexManager(Context context, PackageDexOptimizer pdo, Installer installer, Object installLock, DynamicCodeLogger dynamicCodeLogger, @Nullable IPackageManager packageManager)133 public DexManager(Context context, PackageDexOptimizer pdo, Installer installer, 134 Object installLock, DynamicCodeLogger dynamicCodeLogger, 135 @Nullable IPackageManager packageManager) { 136 mContext = context; 137 mPackageCodeLocationsCache = new HashMap<>(); 138 mPackageDexUsage = new PackageDexUsage(); 139 mPackageDexOptimizer = pdo; 140 mInstaller = installer; 141 mInstallLock = installLock; 142 mDynamicCodeLogger = dynamicCodeLogger; 143 mPackageManager = packageManager; 144 145 // This is currently checked to handle tests that pass in a null context. 146 // TODO(b/174783329): Modify the tests to pass in a mocked Context, PowerManager, 147 // and BatteryManager. 148 if (mContext != null) { 149 mPowerManager = mContext.getSystemService(PowerManager.class); 150 151 if (mPowerManager == null) { 152 Slog.wtf(TAG, "Power Manager is unavailable at time of Dex Manager start"); 153 } 154 155 mCriticalBatteryLevel = mContext.getResources().getInteger( 156 com.android.internal.R.integer.config_criticalBatteryWarningLevel); 157 } else { 158 // This value will never be used as the Battery Manager null check will fail first. 159 mCriticalBatteryLevel = 0; 160 } 161 } 162 163 @NonNull getPackageManager()164 private IPackageManager getPackageManager() { 165 if (mPackageManager == null) { 166 mPackageManager = IPackageManager.Stub.asInterface( 167 ServiceManager.getService("package")); 168 } 169 return mPackageManager; 170 } 171 172 /** 173 * Notify about dex files loads. 174 * Note that this method is invoked when apps load dex files and it should 175 * return as fast as possible. 176 * 177 * @param loadingAppInfo the package performing the load 178 * @param classLoaderContextMap a map from file paths to dex files that have been loaded to 179 * the class loader context that was used to load them. 180 * @param loaderIsa the ISA of the app loading the dex files 181 * @param loaderUserId the user id which runs the code loading the dex files 182 * @param loaderIsIsolatedProcess whether or not the loading process is isolated. 183 */ notifyDexLoad(ApplicationInfo loadingAppInfo, Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId, boolean loaderIsIsolatedProcess)184 public void notifyDexLoad(ApplicationInfo loadingAppInfo, 185 Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId, 186 boolean loaderIsIsolatedProcess) { 187 try { 188 notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa, 189 loaderUserId, loaderIsIsolatedProcess); 190 } catch (RuntimeException e) { 191 Slog.w(TAG, "Exception while notifying dex load for package " + 192 loadingAppInfo.packageName, e); 193 } 194 } 195 196 @VisibleForTesting notifyDexLoadInternal(ApplicationInfo loadingAppInfo, Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId, boolean loaderIsIsolatedProcess)197 /*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo, 198 Map<String, String> classLoaderContextMap, String loaderIsa, 199 int loaderUserId, boolean loaderIsIsolatedProcess) { 200 if (classLoaderContextMap == null) { 201 return; 202 } 203 if (classLoaderContextMap.isEmpty()) { 204 Slog.wtf(TAG, "Bad call to notifyDexLoad: class loaders list is empty"); 205 return; 206 } 207 if (!PackageManagerServiceUtils.checkISA(loaderIsa)) { 208 Slog.w(TAG, "Loading dex files " + classLoaderContextMap.keySet() 209 + " in unsupported ISA: " + loaderIsa + "?"); 210 return; 211 } 212 213 // If this load is coming from an isolated process we need to be able to prevent profile 214 // based optimizations. This is because isolated processes are sandboxed and can only read 215 // world readable files, so they need world readable optimization files. An 216 // example of such a package is webview. 217 // 218 // In order to prevent profile optimization we pretend that the load is coming from a 219 // different package, and so we assign a artificial name to the loading package making it 220 // clear that it comes from an isolated process. This blends well with the entire 221 // usedByOthers logic without needing to special handle isolated process in all dexopt 222 // layers. 223 String loadingPackageAmendedName = loadingAppInfo.packageName; 224 if (loaderIsIsolatedProcess) { 225 loadingPackageAmendedName += ISOLATED_PROCESS_PACKAGE_SUFFIX; 226 } 227 for (Map.Entry<String, String> mapping : classLoaderContextMap.entrySet()) { 228 String dexPath = mapping.getKey(); 229 // Find the owning package name. 230 DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId); 231 232 if (DEBUG) { 233 Slog.i(TAG, loadingPackageAmendedName 234 + " loads from " + searchResult + " : " + loaderUserId + " : " + dexPath); 235 } 236 237 if (searchResult.mOutcome != DEX_SEARCH_NOT_FOUND) { 238 // TODO(calin): extend isUsedByOtherApps check to detect the cases where 239 // different apps share the same runtime. In that case we should not mark the dex 240 // file as isUsedByOtherApps. Currently this is a safe approximation. 241 boolean isUsedByOtherApps = 242 !loadingPackageAmendedName.equals(searchResult.mOwningPackageName); 243 boolean primaryOrSplit = searchResult.mOutcome == DEX_SEARCH_FOUND_PRIMARY || 244 searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT; 245 246 if (primaryOrSplit && !isUsedByOtherApps 247 && !isPlatformPackage(searchResult.mOwningPackageName)) { 248 // If the dex file is the primary apk (or a split) and not isUsedByOtherApps 249 // do not record it. This case does not bring any new usable information 250 // and can be safely skipped. 251 // Note this is just an optimization that makes things easier to read in the 252 // package-dex-use file since we don't need to pollute it with redundant info. 253 // However, we always record system server packages. 254 continue; 255 } 256 257 if (!primaryOrSplit) { 258 // Record loading of a DEX file from an app data directory. 259 mDynamicCodeLogger.recordDex(loaderUserId, dexPath, 260 searchResult.mOwningPackageName, loadingAppInfo.packageName); 261 } 262 263 String classLoaderContext = mapping.getValue(); 264 265 // Overwrite the class loader context for system server (instead of merging it). 266 // We expect system server jars to only change contexts in between OTAs and to 267 // otherwise be stable. 268 // Instead of implementing a complex clear-context logic post OTA, it is much 269 // simpler to always override the context for system server. This way, the context 270 // will always be up to date and we will avoid merging which could lead to the 271 // the context being marked as variable and thus making dexopt non-optimal. 272 boolean overwriteCLC = isPlatformPackage(searchResult.mOwningPackageName); 273 274 if (classLoaderContext != null 275 && VMRuntime.isValidClassLoaderContext(classLoaderContext)) { 276 // Record dex file usage. If the current usage is a new pattern (e.g. new 277 // secondary, or UsedByOtherApps), record will return true and we trigger an 278 // async write to disk to make sure we don't loose the data in case of a reboot. 279 if (mPackageDexUsage.record(searchResult.mOwningPackageName, 280 dexPath, loaderUserId, loaderIsa, primaryOrSplit, 281 loadingPackageAmendedName, classLoaderContext, overwriteCLC)) { 282 mPackageDexUsage.maybeWriteAsync(); 283 } 284 } 285 } else { 286 // If we can't find the owner of the dex we simply do not track it. The impact is 287 // that the dex file will not be considered for offline optimizations. 288 if (DEBUG) { 289 Slog.i(TAG, "Could not find owning package for dex file: " + dexPath); 290 } 291 } 292 } 293 } 294 295 /** 296 * Check if the dexPath belongs to system server. 297 * System server can load code from different location, so we cast a wide-net here, and 298 * assume that if the paths is on any of the registered system partitions then it can be loaded 299 * by system server. 300 */ isSystemServerDexPathSupportedForOdex(String dexPath)301 private boolean isSystemServerDexPathSupportedForOdex(String dexPath) { 302 ArrayList<PackagePartitions.SystemPartition> partitions = 303 PackagePartitions.getOrderedPartitions(identity()); 304 // First check the apex partition as it's not part of the SystemPartitions. 305 if (dexPath.startsWith("/apex/")) { 306 return true; 307 } 308 for (int i = 0; i < partitions.size(); i++) { 309 if (partitions.get(i).containsPath(dexPath)) { 310 return true; 311 } 312 } 313 return false; 314 } 315 316 /** 317 * Read the dex usage from disk and populate the code cache locations. 318 * @param existingPackages a map containing information about what packages 319 * are available to what users. Only packages in this list will be 320 * recognized during notifyDexLoad(). 321 */ load(Map<Integer, List<PackageInfo>> existingPackages)322 public void load(Map<Integer, List<PackageInfo>> existingPackages) { 323 try { 324 loadInternal(existingPackages); 325 } catch (RuntimeException e) { 326 mPackageDexUsage.clear(); 327 Slog.w(TAG, "Exception while loading. Starting with a fresh state.", e); 328 } 329 } 330 331 /** 332 * Notifies that a new package was installed for {@code userId}. 333 * {@code userId} must not be {@code UserHandle.USER_ALL}. 334 * 335 * @throws IllegalArgumentException if {@code userId} is {@code UserHandle.USER_ALL}. 336 */ notifyPackageInstalled(PackageInfo pi, int userId)337 public void notifyPackageInstalled(PackageInfo pi, int userId) { 338 if (userId == UserHandle.USER_ALL) { 339 throw new IllegalArgumentException( 340 "notifyPackageInstalled called with USER_ALL"); 341 } 342 cachePackageInfo(pi, userId); 343 } 344 345 /** 346 * Notifies that package {@code packageName} was updated. 347 * This will clear the UsedByOtherApps mark if it exists. 348 */ notifyPackageUpdated(String packageName, String baseCodePath, String[] splitCodePaths)349 public void notifyPackageUpdated(String packageName, String baseCodePath, 350 String[] splitCodePaths) { 351 cachePackageCodeLocation(packageName, baseCodePath, splitCodePaths, null, /*userId*/ -1); 352 // In case there was an update, write the package use info to disk async. 353 // Note that we do the writing here and not in PackageDexUsage in order to be 354 // consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs 355 // multiple updates in PackageDexUsage before writing it). 356 if (mPackageDexUsage.clearUsedByOtherApps(packageName)) { 357 mPackageDexUsage.maybeWriteAsync(); 358 } 359 } 360 361 /** 362 * Notifies that the user {@code userId} data for package {@code packageName} 363 * was destroyed. This will remove all usage info associated with the package 364 * for the given user. 365 * {@code userId} is allowed to be {@code UserHandle.USER_ALL} in which case 366 * all usage information for the package will be removed. 367 */ notifyPackageDataDestroyed(String packageName, int userId)368 public void notifyPackageDataDestroyed(String packageName, int userId) { 369 // In case there was an update, write the package use info to disk async. 370 // Note that we do the writing here and not in the lower level classes in order to be 371 // consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs 372 // multiple updates in PackageDexUsage before writing it). 373 if (userId == UserHandle.USER_ALL) { 374 if (mPackageDexUsage.removePackage(packageName)) { 375 mPackageDexUsage.maybeWriteAsync(); 376 } 377 } else { 378 if (mPackageDexUsage.removeUserPackage(packageName, userId)) { 379 mPackageDexUsage.maybeWriteAsync(); 380 } 381 } 382 } 383 384 /** 385 * Caches the code location from the given package info. 386 */ cachePackageInfo(PackageInfo pi, int userId)387 private void cachePackageInfo(PackageInfo pi, int userId) { 388 ApplicationInfo ai = pi.applicationInfo; 389 String[] dataDirs = new String[] {ai.dataDir, ai.deviceProtectedDataDir, 390 ai.credentialProtectedDataDir}; 391 cachePackageCodeLocation(pi.packageName, ai.sourceDir, ai.splitSourceDirs, 392 dataDirs, userId); 393 } 394 cachePackageCodeLocation(String packageName, String baseCodePath, String[] splitCodePaths, String[] dataDirs, int userId)395 private void cachePackageCodeLocation(String packageName, String baseCodePath, 396 String[] splitCodePaths, String[] dataDirs, int userId) { 397 synchronized (mPackageCodeLocationsCache) { 398 PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName, 399 new PackageCodeLocations(packageName, baseCodePath, splitCodePaths)); 400 // TODO(calin): We are forced to extend the scope of this synchronization because 401 // the values of the cache (PackageCodeLocations) are updated in place. 402 // Make PackageCodeLocations immutable to simplify the synchronization reasoning. 403 pcl.updateCodeLocation(baseCodePath, splitCodePaths); 404 if (dataDirs != null) { 405 for (String dataDir : dataDirs) { 406 // The set of data dirs includes deviceProtectedDataDir and 407 // credentialProtectedDataDir which might be null for shared 408 // libraries. Currently we don't track these but be lenient 409 // and check in case we ever decide to store their usage data. 410 if (dataDir != null) { 411 pcl.mergeAppDataDirs(dataDir, userId); 412 } 413 } 414 } 415 } 416 } 417 loadInternal(Map<Integer, List<PackageInfo>> existingPackages)418 private void loadInternal(Map<Integer, List<PackageInfo>> existingPackages) { 419 Map<String, Set<Integer>> packageToUsersMap = new HashMap<>(); 420 Map<String, Set<String>> packageToCodePaths = new HashMap<>(); 421 422 // Cache the code locations for the installed packages. This allows for 423 // faster lookups (no locks) when finding what package owns the dex file. 424 for (Map.Entry<Integer, List<PackageInfo>> entry : existingPackages.entrySet()) { 425 List<PackageInfo> packageInfoList = entry.getValue(); 426 int userId = entry.getKey(); 427 for (PackageInfo pi : packageInfoList) { 428 // Cache the code locations. 429 cachePackageInfo(pi, userId); 430 431 // Cache two maps: 432 // - from package name to the set of user ids who installed the package. 433 // - from package name to the set of code paths. 434 // We will use it to sync the data and remove obsolete entries from 435 // mPackageDexUsage. 436 Set<Integer> users = putIfAbsent( 437 packageToUsersMap, pi.packageName, new HashSet<>()); 438 users.add(userId); 439 440 Set<String> codePaths = putIfAbsent( 441 packageToCodePaths, pi.packageName, new HashSet<>()); 442 codePaths.add(pi.applicationInfo.sourceDir); 443 if (pi.applicationInfo.splitSourceDirs != null) { 444 Collections.addAll(codePaths, pi.applicationInfo.splitSourceDirs); 445 } 446 } 447 } 448 449 try { 450 mPackageDexUsage.read(); 451 List<String> packagesToKeepDataAbout = new ArrayList<>(); 452 mPackageDexUsage.syncData( 453 packageToUsersMap, packageToCodePaths, packagesToKeepDataAbout); 454 } catch (RuntimeException e) { 455 mPackageDexUsage.clear(); 456 Slog.w(TAG, "Exception while loading package dex usage. " 457 + "Starting with a fresh state.", e); 458 } 459 } 460 461 /** 462 * Get the package dex usage for the given package name. 463 * If there is no usage info the method will return a default {@code PackageUseInfo} with 464 * no data about secondary dex files and marked as not being used by other apps. 465 * 466 * Note that no use info means the package was not used or it was used but not by other apps. 467 * Also, note that right now we might prune packages which are not used by other apps. 468 * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try 469 * to access the package use. 470 */ getPackageUseInfoOrDefault(String packageName)471 public PackageUseInfo getPackageUseInfoOrDefault(String packageName) { 472 // We do not record packages that have no secondary dex files or that are not used by other 473 // apps. This is an optimization to reduce the amount of data that needs to be written to 474 // disk (apps will not usually be shared so this trims quite a bit the number we record). 475 // 476 // To make this behaviour transparent to the callers which need use information on packages, 477 // DexManager will return this DEFAULT instance from 478 // {@link DexManager#getPackageUseInfoOrDefault}. It has no data about secondary dex files 479 // and is marked as not being used by other apps. This reflects the intended behaviour when 480 // we don't find the package in the underlying data file. 481 PackageUseInfo useInfo = mPackageDexUsage.getPackageUseInfo(packageName); 482 return useInfo == null ? new PackageUseInfo(packageName) : useInfo; 483 } 484 485 /** 486 * Return whether or not the manager has usage information on the give package. 487 * 488 * Note that no use info means the package was not used or it was used but not by other apps. 489 * Also, note that right now we might prune packages which are not used by other apps. 490 * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try 491 * to access the package use. 492 */ 493 @VisibleForTesting hasInfoOnPackage(String packageName)494 /*package*/ boolean hasInfoOnPackage(String packageName) { 495 return mPackageDexUsage.getPackageUseInfo(packageName) != null; 496 } 497 498 /** 499 * Perform dexopt on with the given {@code options} on the secondary dex files. 500 * @return true if all secondary dex files were processed successfully (compiled or skipped 501 * because they don't need to be compiled).. 502 */ dexoptSecondaryDex(DexoptOptions options)503 public boolean dexoptSecondaryDex(DexoptOptions options) throws LegacyDexoptDisabledException { 504 if (isPlatformPackage(options.getPackageName())) { 505 // We could easily redirect to #dexoptSystemServer in this case. But there should be 506 // no-one calling this method directly for system server. 507 // As such we prefer to abort in this case. 508 Slog.wtf(TAG, "System server jars should be optimized with dexoptSystemServer"); 509 return false; 510 } 511 512 PackageDexOptimizer pdo = getPackageDexOptimizer(options); 513 String packageName = options.getPackageName(); 514 PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName); 515 if (useInfo.getDexUseInfoMap().isEmpty()) { 516 if (DEBUG) { 517 Slog.d(TAG, "No secondary dex use for package:" + packageName); 518 } 519 // Nothing to compile, return true. 520 return true; 521 } 522 boolean success = true; 523 for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) { 524 String dexPath = entry.getKey(); 525 DexUseInfo dexUseInfo = entry.getValue(); 526 527 PackageInfo pkg; 528 try { 529 pkg = getPackageManager().getPackageInfo(packageName, /*flags*/0, 530 dexUseInfo.getOwnerUserId()); 531 } catch (RemoteException e) { 532 throw new AssertionError(e); 533 } 534 // It may be that the package gets uninstalled while we try to compile its 535 // secondary dex files. If that's the case, just ignore. 536 // Note that we don't break the entire loop because the package might still be 537 // installed for other users. 538 if (pkg == null) { 539 Slog.d(TAG, "Could not find package when compiling secondary dex " + packageName 540 + " for user " + dexUseInfo.getOwnerUserId()); 541 mPackageDexUsage.removeUserPackage(packageName, dexUseInfo.getOwnerUserId()); 542 continue; 543 } 544 545 int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath, 546 dexUseInfo, options); 547 success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED); 548 } 549 return success; 550 } 551 552 /** 553 * Select the dex optimizer based on the force parameter. 554 * Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust 555 * the necessary dexopt flags to make sure that compilation is not skipped. This avoid 556 * passing the force flag through the multitude of layers. 557 * Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to 558 * allocate an object here. 559 */ getPackageDexOptimizer(DexoptOptions options)560 private PackageDexOptimizer getPackageDexOptimizer(DexoptOptions options) { 561 return options.isForce() 562 ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer) 563 : mPackageDexOptimizer; 564 } 565 566 /** 567 * Reconcile the information we have about the secondary dex files belonging to 568 * {@code packagName} and the actual dex files. For all dex files that were 569 * deleted, update the internal records and delete any generated oat files. 570 */ reconcileSecondaryDexFiles(String packageName)571 public void reconcileSecondaryDexFiles(String packageName) 572 throws LegacyDexoptDisabledException { 573 PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName); 574 if (useInfo.getDexUseInfoMap().isEmpty()) { 575 if (DEBUG) { 576 Slog.d(TAG, "No secondary dex use for package:" + packageName); 577 } 578 // Nothing to reconcile. 579 return; 580 } 581 582 boolean updated = false; 583 for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) { 584 String dexPath = entry.getKey(); 585 DexUseInfo dexUseInfo = entry.getValue(); 586 PackageInfo pkg = null; 587 try { 588 // Note that we look for the package in the PackageManager just to be able 589 // to get back the real app uid and its storage kind. These are only used 590 // to perform extra validation in installd. 591 // TODO(calin): maybe a bit overkill. 592 pkg = getPackageManager().getPackageInfo(packageName, /*flags*/0, 593 dexUseInfo.getOwnerUserId()); 594 } catch (RemoteException ignore) { 595 // Can't happen, DexManager is local. 596 } 597 if (pkg == null) { 598 // It may be that the package was uninstalled while we process the secondary 599 // dex files. 600 Slog.d(TAG, "Could not find package when compiling secondary dex " + packageName 601 + " for user " + dexUseInfo.getOwnerUserId()); 602 // Update the usage and continue, another user might still have the package. 603 updated = mPackageDexUsage.removeUserPackage( 604 packageName, dexUseInfo.getOwnerUserId()) || updated; 605 continue; 606 } 607 608 // Special handle system server files. 609 // We don't need an installd call because we have permissions to check if the file 610 // exists. 611 if (isPlatformPackage(packageName)) { 612 if (!Files.exists(Paths.get(dexPath))) { 613 if (DEBUG) { 614 Slog.w(TAG, "A dex file previously loaded by System Server does not exist " 615 + " anymore: " + dexPath); 616 } 617 updated = mPackageDexUsage.removeUserPackage( 618 packageName, dexUseInfo.getOwnerUserId()) || updated; 619 } 620 continue; 621 } 622 623 // This is a regular application. 624 ApplicationInfo info = pkg.applicationInfo; 625 int flags = 0; 626 if (info.deviceProtectedDataDir != null && 627 FileUtils.contains(info.deviceProtectedDataDir, dexPath)) { 628 flags |= StorageManager.FLAG_STORAGE_DE; 629 } else if (info.credentialProtectedDataDir!= null && 630 FileUtils.contains(info.credentialProtectedDataDir, dexPath)) { 631 flags |= StorageManager.FLAG_STORAGE_CE; 632 } else { 633 Slog.e(TAG, "Could not infer CE/DE storage for path " + dexPath); 634 updated = mPackageDexUsage.removeDexFile( 635 packageName, dexPath, dexUseInfo.getOwnerUserId()) || updated; 636 continue; 637 } 638 639 boolean dexStillExists = true; 640 synchronized(mInstallLock) { 641 try { 642 String[] isas = dexUseInfo.getLoaderIsas().toArray(new String[0]); 643 dexStillExists = mInstaller.reconcileSecondaryDexFile(dexPath, packageName, 644 info.uid, isas, info.volumeUuid, flags); 645 } catch (InstallerException e) { 646 Slog.e(TAG, "Got InstallerException when reconciling dex " + dexPath + 647 " : " + e.getMessage()); 648 } 649 } 650 if (!dexStillExists) { 651 updated = mPackageDexUsage.removeDexFile( 652 packageName, dexPath, dexUseInfo.getOwnerUserId()) || updated; 653 } 654 655 } 656 if (updated) { 657 mPackageDexUsage.maybeWriteAsync(); 658 } 659 } 660 661 /** 662 * Return all packages that contain records of secondary dex files. 663 */ getAllPackagesWithSecondaryDexFiles()664 public Set<String> getAllPackagesWithSecondaryDexFiles() { 665 return mPackageDexUsage.getAllPackagesWithSecondaryDexFiles(); 666 } 667 668 /** 669 * Retrieves the package which owns the given dexPath. 670 */ getDexPackage( ApplicationInfo loadingAppInfo, String dexPath, int userId)671 private DexSearchResult getDexPackage( 672 ApplicationInfo loadingAppInfo, String dexPath, int userId) { 673 // First, check if the package which loads the dex file actually owns it. 674 // Most of the time this will be true and we can return early. 675 PackageCodeLocations loadingPackageCodeLocations = 676 new PackageCodeLocations(loadingAppInfo, userId); 677 int outcome = loadingPackageCodeLocations.searchDex(dexPath, userId); 678 if (outcome != DEX_SEARCH_NOT_FOUND) { 679 // TODO(calin): evaluate if we bother to detect symlinks at the dexPath level. 680 return new DexSearchResult(loadingPackageCodeLocations.mPackageName, outcome); 681 } 682 683 // The loadingPackage does not own the dex file. 684 // Perform a reverse look-up in the cache to detect if any package has ownership. 685 // Note that we can have false negatives if the cache falls out of date. 686 synchronized (mPackageCodeLocationsCache) { 687 for (PackageCodeLocations pcl : mPackageCodeLocationsCache.values()) { 688 outcome = pcl.searchDex(dexPath, userId); 689 if (outcome != DEX_SEARCH_NOT_FOUND) { 690 return new DexSearchResult(pcl.mPackageName, outcome); 691 } 692 } 693 } 694 695 // We could not find the owning package amongst regular apps. 696 // If the loading package is system server, see if the dex file resides 697 // on any of the potentially system server owning location and if so, 698 // assuming system server ownership. 699 // 700 // Note: We don't have any way to detect which code paths are actually 701 // owned by system server. We can only assume that such paths are on 702 // system partitions. 703 if (isPlatformPackage(loadingAppInfo.packageName)) { 704 if (isSystemServerDexPathSupportedForOdex(dexPath)) { 705 // We record system server dex files as secondary dex files. 706 // The reason is that we only record the class loader context for secondary dex 707 // files and we expect that all primary apks are loaded with an empty class loader. 708 // System server dex files may be loaded in non-empty class loader so we need to 709 // keep track of their context. 710 return new DexSearchResult(PLATFORM_PACKAGE_NAME, DEX_SEARCH_FOUND_SECONDARY); 711 } else { 712 Slog.wtf(TAG, "System server loads dex files outside paths supported for odex: " 713 + dexPath); 714 } 715 } 716 717 if (DEBUG) { 718 // TODO(calin): Consider checking for /data/data symlink. 719 // /data/data/ symlinks /data/user/0/ and there's nothing stopping apps 720 // to load dex files through it. 721 try { 722 String dexPathReal = PackageManagerServiceUtils.realpath(new File(dexPath)); 723 if (!dexPath.equals(dexPathReal)) { 724 Slog.d(TAG, "Dex loaded with symlink. dexPath=" + 725 dexPath + " dexPathReal=" + dexPathReal); 726 } 727 } catch (IOException e) { 728 // Ignore 729 } 730 } 731 // Cache miss. The cache is updated during installs and uninstalls, 732 // so if we get here we're pretty sure the dex path does not exist. 733 return new DexSearchResult(null, DEX_SEARCH_NOT_FOUND); 734 } 735 736 /** Returns true if this is the platform package .*/ isPlatformPackage(String packageName)737 private static boolean isPlatformPackage(String packageName) { 738 return PLATFORM_PACKAGE_NAME.equals(packageName); 739 } 740 putIfAbsent(Map<K,V> map, K key, V newValue)741 private static <K,V> V putIfAbsent(Map<K,V> map, K key, V newValue) { 742 V existingValue = map.putIfAbsent(key, newValue); 743 return existingValue == null ? newValue : existingValue; 744 } 745 746 /** 747 * Writes the in-memory package dex usage to disk right away. 748 */ writePackageDexUsageNow()749 public void writePackageDexUsageNow() { 750 mPackageDexUsage.writeNow(); 751 } 752 753 /** 754 * Generates log if the archive located at {@code fileName} has uncompressed dex file that can 755 * be direclty mapped. 756 */ auditUncompressedDexInApk(String fileName)757 public static boolean auditUncompressedDexInApk(String fileName) { 758 StrictJarFile jarFile = null; 759 try { 760 jarFile = new StrictJarFile(fileName, 761 false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/); 762 Iterator<ZipEntry> it = jarFile.iterator(); 763 boolean allCorrect = true; 764 while (it.hasNext()) { 765 ZipEntry entry = it.next(); 766 if (entry.getName().endsWith(".dex")) { 767 if (entry.getMethod() != ZipEntry.STORED) { 768 allCorrect = false; 769 Slog.w(TAG, "APK " + fileName + " has compressed dex code " + 770 entry.getName()); 771 } else if ((entry.getDataOffset() & 0x3) != 0) { 772 allCorrect = false; 773 Slog.w(TAG, "APK " + fileName + " has unaligned dex code " + 774 entry.getName()); 775 } 776 } 777 } 778 return allCorrect; 779 } catch (IOException ignore) { 780 Slog.wtf(TAG, "Error when parsing APK " + fileName); 781 return false; 782 } finally { 783 try { 784 if (jarFile != null) { 785 jarFile.close(); 786 } 787 } catch (IOException ignore) {} 788 } 789 } 790 791 /** 792 * Translates install scenarios into compilation reasons. This process can be influenced 793 * by the state of the device. 794 */ getCompilationReasonForInstallScenario(int installScenario)795 public int getCompilationReasonForInstallScenario(int installScenario) { 796 // Compute the compilation reason from the installation scenario. 797 798 boolean resourcesAreCritical = areBatteryThermalOrMemoryCritical(); 799 switch (installScenario) { 800 case PackageManager.INSTALL_SCENARIO_DEFAULT: { 801 return PackageManagerService.REASON_INSTALL; 802 } 803 case PackageManager.INSTALL_SCENARIO_FAST: { 804 return PackageManagerService.REASON_INSTALL_FAST; 805 } 806 case PackageManager.INSTALL_SCENARIO_BULK: { 807 if (resourcesAreCritical) { 808 return PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED; 809 } else { 810 return PackageManagerService.REASON_INSTALL_BULK; 811 } 812 } 813 case PackageManager.INSTALL_SCENARIO_BULK_SECONDARY: { 814 if (resourcesAreCritical) { 815 return PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED; 816 } else { 817 return PackageManagerService.REASON_INSTALL_BULK_SECONDARY; 818 } 819 } 820 default: { 821 throw new IllegalArgumentException("Invalid installation scenario"); 822 } 823 } 824 } 825 826 /** 827 * Fetches the battery manager object and caches it if it hasn't been fetched already. 828 */ getBatteryManager()829 private BatteryManager getBatteryManager() { 830 if (mBatteryManager == null && mContext != null) { 831 mBatteryManager = mContext.getSystemService(BatteryManager.class); 832 } 833 834 return mBatteryManager; 835 } 836 837 /** 838 * Returns true if the battery level, device temperature, or memory usage are considered to be 839 * in a critical state. 840 */ areBatteryThermalOrMemoryCritical()841 private boolean areBatteryThermalOrMemoryCritical() { 842 BatteryManager batteryManager = getBatteryManager(); 843 boolean isBtmCritical = (batteryManager != null 844 && batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS) 845 == BatteryManager.BATTERY_STATUS_DISCHARGING 846 && batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) 847 <= mCriticalBatteryLevel) 848 || (mPowerManager != null 849 && mPowerManager.getCurrentThermalStatus() 850 >= PowerManager.THERMAL_STATUS_SEVERE); 851 852 return isBtmCritical; 853 } 854 855 /** 856 * Deletes all the optimizations files generated by ART. 857 * This is best effort, and the method will log but not throw errors 858 * for individual deletes 859 * 860 * @param packageInfo the package information. 861 * @return the number of freed bytes or -1 if there was an error in the process. 862 */ deleteOptimizedFiles(ArtPackageInfo packageInfo)863 public long deleteOptimizedFiles(ArtPackageInfo packageInfo) 864 throws LegacyDexoptDisabledException { 865 long freedBytes = 0; 866 boolean hadErrors = false; 867 final String packageName = packageInfo.getPackageName(); 868 for (String codePath : packageInfo.getCodePaths()) { 869 for (String isa : packageInfo.getInstructionSets()) { 870 try { 871 freedBytes += mInstaller.deleteOdex(packageName, codePath, isa, 872 packageInfo.getOatDir()); 873 } catch (InstallerException e) { 874 Log.e(TAG, "Failed deleting oat files for " + codePath, e); 875 hadErrors = true; 876 } 877 } 878 } 879 return hadErrors ? -1 : freedBytes; 880 } 881 882 public static class RegisterDexModuleResult { RegisterDexModuleResult()883 public RegisterDexModuleResult() { 884 this(false, null); 885 } 886 RegisterDexModuleResult(boolean success, String message)887 public RegisterDexModuleResult(boolean success, String message) { 888 this.success = success; 889 this.message = message; 890 } 891 892 public final boolean success; 893 public final String message; 894 } 895 896 /** 897 * Convenience class to store the different locations where a package might 898 * own code. 899 */ 900 private static class PackageCodeLocations { 901 private final String mPackageName; 902 private String mBaseCodePath; 903 private final Set<String> mSplitCodePaths; 904 // Maps user id to the application private directory. 905 private final Map<Integer, Set<String>> mAppDataDirs; 906 PackageCodeLocations(ApplicationInfo ai, int userId)907 public PackageCodeLocations(ApplicationInfo ai, int userId) { 908 this(ai.packageName, ai.sourceDir, ai.splitSourceDirs); 909 mergeAppDataDirs(ai.dataDir, userId); 910 } PackageCodeLocations(String packageName, String baseCodePath, String[] splitCodePaths)911 public PackageCodeLocations(String packageName, String baseCodePath, 912 String[] splitCodePaths) { 913 mPackageName = packageName; 914 mSplitCodePaths = new HashSet<>(); 915 mAppDataDirs = new HashMap<>(); 916 updateCodeLocation(baseCodePath, splitCodePaths); 917 } 918 updateCodeLocation(String baseCodePath, String[] splitCodePaths)919 public void updateCodeLocation(String baseCodePath, String[] splitCodePaths) { 920 mBaseCodePath = baseCodePath; 921 mSplitCodePaths.clear(); 922 if (splitCodePaths != null) { 923 for (String split : splitCodePaths) { 924 mSplitCodePaths.add(split); 925 } 926 } 927 } 928 mergeAppDataDirs(String dataDir, int userId)929 public void mergeAppDataDirs(String dataDir, int userId) { 930 Set<String> dataDirs = putIfAbsent(mAppDataDirs, userId, new HashSet<>()); 931 dataDirs.add(dataDir); 932 } 933 searchDex(String dexPath, int userId)934 public int searchDex(String dexPath, int userId) { 935 // First check that this package is installed or active for the given user. 936 // A missing data dir means the package is not installed. 937 Set<String> userDataDirs = mAppDataDirs.get(userId); 938 if (userDataDirs == null) { 939 return DEX_SEARCH_NOT_FOUND; 940 } 941 942 if (mBaseCodePath.equals(dexPath)) { 943 return DEX_SEARCH_FOUND_PRIMARY; 944 } 945 if (mSplitCodePaths.contains(dexPath)) { 946 return DEX_SEARCH_FOUND_SPLIT; 947 } 948 for (String dataDir : userDataDirs) { 949 if (dexPath.startsWith(dataDir)) { 950 return DEX_SEARCH_FOUND_SECONDARY; 951 } 952 } 953 954 return DEX_SEARCH_NOT_FOUND; 955 } 956 } 957 958 /** 959 * Convenience class to store ownership search results. 960 */ 961 private class DexSearchResult { 962 private String mOwningPackageName; 963 private int mOutcome; 964 DexSearchResult(String owningPackageName, int outcome)965 public DexSearchResult(String owningPackageName, int outcome) { 966 this.mOwningPackageName = owningPackageName; 967 this.mOutcome = outcome; 968 } 969 970 @Override toString()971 public String toString() { 972 return mOwningPackageName + "-" + mOutcome; 973 } 974 } 975 } 976