1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.pm;
18 
19 import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
20 import static com.android.server.pm.PackageManagerService.TAG;
21 
22 import android.app.AppGlobals;
23 import android.content.Intent;
24 import android.content.pm.PackageParser;
25 import android.content.pm.ResolveInfo;
26 import android.os.RemoteException;
27 import android.os.UserHandle;
28 import android.system.ErrnoException;
29 import android.util.ArraySet;
30 import android.util.Log;
31 import libcore.io.Libcore;
32 
33 import java.io.File;
34 import java.io.IOException;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Comparator;
39 import java.util.Date;
40 import java.util.HashSet;
41 import java.util.Iterator;
42 import java.util.LinkedList;
43 import java.util.List;
44 import java.util.Set;
45 
46 /**
47  * Class containing helper methods for the PackageManagerService.
48  *
49  * {@hide}
50  */
51 public class PackageManagerServiceUtils {
52     private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
53 
getPackageNamesForIntent(Intent intent, int userId)54     private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
55         List<ResolveInfo> ris = null;
56         try {
57             ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
58                     .getList();
59         } catch (RemoteException e) {
60         }
61         ArraySet<String> pkgNames = new ArraySet<String>();
62         if (ris != null) {
63             for (ResolveInfo ri : ris) {
64                 pkgNames.add(ri.activityInfo.packageName);
65             }
66         }
67         return pkgNames;
68     }
69 
filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs, long estimatedPreviousSystemUseTime, long dexOptLRUThresholdInMills)70     private static void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs,
71             long estimatedPreviousSystemUseTime,
72             long dexOptLRUThresholdInMills) {
73         // Filter out packages that aren't recently used.
74         int total = pkgs.size();
75         int skipped = 0;
76         for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
77             PackageParser.Package pkg = i.next();
78             long then = pkg.getLatestForegroundPackageUseTimeInMills();
79             if (then < estimatedPreviousSystemUseTime - dexOptLRUThresholdInMills) {
80                 if (DEBUG_DEXOPT) {
81                     Log.i(TAG, "Skipping dexopt of " + pkg.packageName +
82                             " last used in foreground: " +
83                             ((then == 0) ? "never" : new Date(then)));
84                 }
85                 i.remove();
86                 skipped++;
87             } else {
88                 if (DEBUG_DEXOPT) {
89                     Log.i(TAG, "Will dexopt " + pkg.packageName +
90                             " last used in foreground: " +
91                             ((then == 0) ? "never" : new Date(then)));
92                 }
93             }
94         }
95         if (DEBUG_DEXOPT) {
96             Log.i(TAG, "Skipped dexopt " + skipped + " of " + total);
97         }
98     }
99 
100     // Sort apps by importance for dexopt ordering. Important apps are given
101     // more priority in case the device runs out of space.
getPackagesForDexopt( Collection<PackageParser.Package> packages, PackageManagerService packageManagerService)102     public static List<PackageParser.Package> getPackagesForDexopt(
103             Collection<PackageParser.Package> packages,
104             PackageManagerService packageManagerService) {
105         ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
106         LinkedList<PackageParser.Package> result = new LinkedList<>();
107 
108         // Give priority to core apps.
109         for (PackageParser.Package pkg : remainingPkgs) {
110             if (pkg.coreApp) {
111                 if (DEBUG_DEXOPT) {
112                     Log.i(TAG, "Adding core app " + result.size() + ": " + pkg.packageName);
113                 }
114                 result.add(pkg);
115             }
116         }
117         remainingPkgs.removeAll(result);
118 
119         // Give priority to system apps that listen for pre boot complete.
120         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
121         ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
122         for (PackageParser.Package pkg : remainingPkgs) {
123             if (pkgNames.contains(pkg.packageName)) {
124                 if (DEBUG_DEXOPT) {
125                     Log.i(TAG, "Adding pre boot system app " + result.size() + ": " +
126                             pkg.packageName);
127                 }
128                 result.add(pkg);
129             }
130         }
131         remainingPkgs.removeAll(result);
132 
133         // Give priority to apps used by other apps.
134         for (PackageParser.Package pkg : remainingPkgs) {
135             if (PackageDexOptimizer.isUsedByOtherApps(pkg)) {
136                 if (DEBUG_DEXOPT) {
137                     Log.i(TAG, "Adding app used by other apps " + result.size() + ": " +
138                             pkg.packageName);
139                 }
140                 result.add(pkg);
141             }
142         }
143         remainingPkgs.removeAll(result);
144 
145         // Filter out packages that aren't recently used, add all remaining apps.
146         // TODO: add a property to control this?
147         if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
148             if (DEBUG_DEXOPT) {
149                 Log.i(TAG, "Looking at historical package use");
150             }
151             // Get the package that was used last.
152             PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
153                     Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
154                             pkg2.getLatestForegroundPackageUseTimeInMills()));
155             if (DEBUG_DEXOPT) {
156                 Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use");
157             }
158             long estimatedPreviousSystemUseTime =
159                     lastUsed.getLatestForegroundPackageUseTimeInMills();
160             // Be defensive if for some reason package usage has bogus data.
161             if (estimatedPreviousSystemUseTime != 0) {
162                 filterRecentlyUsedApps(remainingPkgs, estimatedPreviousSystemUseTime,
163                         SEVEN_DAYS_IN_MILLISECONDS);
164             }
165         }
166         result.addAll(remainingPkgs);
167 
168         // Now go ahead and also add the libraries required for these packages.
169         // TODO: Think about interleaving things.
170         Set<PackageParser.Package> dependencies = new HashSet<>();
171         for (PackageParser.Package p : result) {
172             dependencies.addAll(packageManagerService.findSharedNonSystemLibraries(p));
173         }
174         if (!dependencies.isEmpty()) {
175             // We might have packages already in `result` that are dependencies
176             // of other packages. Make sure we don't add those to the list twice.
177             dependencies.removeAll(result);
178         }
179         result.addAll(dependencies);
180 
181         if (DEBUG_DEXOPT) {
182             StringBuilder sb = new StringBuilder();
183             for (PackageParser.Package pkg : result) {
184                 if (sb.length() > 0) {
185                     sb.append(", ");
186                 }
187                 sb.append(pkg.packageName);
188             }
189             Log.i(TAG, "Packages to be dexopted: " + sb.toString());
190         }
191 
192         return result;
193     }
194 
195     /**
196      * Returns the canonicalized path of {@code path} as per {@code realpath(3)}
197      * semantics.
198      */
realpath(File path)199     public static String realpath(File path) throws IOException {
200         try {
201             return Libcore.os.realpath(path.getAbsolutePath());
202         } catch (ErrnoException ee) {
203             throw ee.rethrowAsIOException();
204         }
205     }
206 }
207