PackageManagerServiceUtils.java revision fa78b219b7dc006a2863fc0a6d2ff68324797f8f
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
17package com.android.server.pm;
18
19import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
20import static com.android.server.pm.PackageManagerService.TAG;
21
22import android.app.AppGlobals;
23import android.content.Intent;
24import android.content.pm.PackageParser;
25import android.content.pm.ResolveInfo;
26import android.os.RemoteException;
27import android.os.UserHandle;
28import android.system.ErrnoException;
29import android.util.ArraySet;
30import android.util.Log;
31import libcore.io.Libcore;
32
33import java.io.File;
34import java.io.IOException;
35import java.util.ArrayList;
36import java.util.Collection;
37import java.util.Collections;
38import java.util.Comparator;
39import java.util.Date;
40import java.util.HashSet;
41import java.util.Iterator;
42import java.util.LinkedList;
43import java.util.List;
44import java.util.Set;
45
46/**
47 * Class containing helper methods for the PackageManagerService.
48 *
49 * {@hide}
50 */
51public class PackageManagerServiceUtils {
52    private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
53
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
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.
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     */
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