• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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