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