PackageManagerServiceUtils.java revision 6d99f796711882ba60977c211d0f92252fe7ad4a
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.Date;
38import java.util.HashSet;
39import java.util.Iterator;
40import java.util.LinkedList;
41import java.util.List;
42import java.util.Set;
43
44/**
45 * Class containing helper methods for the PackageManagerService.
46 *
47 * {@hide}
48 */
49public class PackageManagerServiceUtils {
50    private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
51
52    private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
53        List<ResolveInfo> ris = null;
54        try {
55            ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
56                    .getList();
57        } catch (RemoteException e) {
58        }
59        ArraySet<String> pkgNames = new ArraySet<String>();
60        if (ris != null) {
61            for (ResolveInfo ri : ris) {
62                pkgNames.add(ri.activityInfo.packageName);
63            }
64        }
65        return pkgNames;
66    }
67
68    private static void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs,
69            long dexOptLRUThresholdInMills) {
70        // Filter out packages that aren't recently used.
71        int total = pkgs.size();
72        int skipped = 0;
73        long now = System.currentTimeMillis();
74        for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
75            PackageParser.Package pkg = i.next();
76            long then = pkg.getLatestForegroundPackageUseTimeInMills();
77            if (then + dexOptLRUThresholdInMills < now) {
78                if (DEBUG_DEXOPT) {
79                    Log.i(TAG, "Skipping dexopt of " + pkg.packageName +
80                            " last used in foreground: " +
81                            ((then == 0) ? "never" : new Date(then)));
82                }
83                i.remove();
84                skipped++;
85            }
86        }
87        if (DEBUG_DEXOPT) {
88            Log.i(TAG, "Skipped dexopt " + skipped + " of " + total);
89        }
90    }
91
92    // Sort apps by importance for dexopt ordering. Important apps are given
93    // more priority in case the device runs out of space.
94    public static List<PackageParser.Package> getPackagesForDexopt(
95            Collection<PackageParser.Package> packages,
96            PackageManagerService packageManagerService) {
97        ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
98        LinkedList<PackageParser.Package> result = new LinkedList<>();
99
100        // Give priority to core apps.
101        for (PackageParser.Package pkg : remainingPkgs) {
102            if (pkg.coreApp) {
103                if (DEBUG_DEXOPT) {
104                    Log.i(TAG, "Adding core app " + result.size() + ": " + pkg.packageName);
105                }
106                result.add(pkg);
107            }
108        }
109        remainingPkgs.removeAll(result);
110
111        // Give priority to system apps that listen for pre boot complete.
112        Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
113        ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
114        for (PackageParser.Package pkg : remainingPkgs) {
115            if (pkgNames.contains(pkg.packageName)) {
116                if (DEBUG_DEXOPT) {
117                    Log.i(TAG, "Adding pre boot system app " + result.size() + ": " +
118                            pkg.packageName);
119                }
120                result.add(pkg);
121            }
122        }
123        remainingPkgs.removeAll(result);
124
125        // Give priority to apps used by other apps.
126        for (PackageParser.Package pkg : remainingPkgs) {
127            if (PackageDexOptimizer.isUsedByOtherApps(pkg)) {
128                if (DEBUG_DEXOPT) {
129                    Log.i(TAG, "Adding app used by other apps " + result.size() + ": " +
130                            pkg.packageName);
131                }
132                result.add(pkg);
133            }
134        }
135        remainingPkgs.removeAll(result);
136
137        // Filter out packages that aren't recently used, add all remaining apps.
138        // TODO: add a property to control this?
139        if (packageManagerService.isHistoricalPackageUsageAvailable()) {
140            filterRecentlyUsedApps(remainingPkgs, SEVEN_DAYS_IN_MILLISECONDS);
141        }
142        result.addAll(remainingPkgs);
143
144        // Now go ahead and also add the libraries required for these packages.
145        // TODO: Think about interleaving things.
146        Set<PackageParser.Package> dependencies = new HashSet<>();
147        for (PackageParser.Package p : result) {
148            dependencies.addAll(packageManagerService.findSharedNonSystemLibraries(p));
149        }
150        if (!dependencies.isEmpty()) {
151            // We might have packages already in `result` that are dependencies
152            // of other packages. Make sure we don't add those to the list twice.
153            dependencies.removeAll(result);
154        }
155        result.addAll(dependencies);
156
157        if (DEBUG_DEXOPT) {
158            StringBuilder sb = new StringBuilder();
159            for (PackageParser.Package pkg : result) {
160                if (sb.length() > 0) {
161                    sb.append(", ");
162                }
163                sb.append(pkg.packageName);
164            }
165            Log.i(TAG, "Packages to be dexopted: " + sb.toString());
166        }
167
168        return result;
169    }
170
171    /**
172     * Returns the canonicalized path of {@code path} as per {@code realpath(3)}
173     * semantics.
174     */
175    public static String realpath(File path) throws IOException {
176        try {
177            return Libcore.os.realpath(path.getAbsolutePath());
178        } catch (ErrnoException ee) {
179            throw ee.rethrowAsIOException();
180        }
181    }
182}
183