PackageManagerServiceUtils.java revision 6b4736d604fd91aaedc6f3fe9be5a1e757aab86c
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 android.app.AppGlobals;
20import android.content.Intent;
21import android.content.pm.PackageParser;
22import android.content.pm.PackageParser.Package;
23import android.content.pm.ResolveInfo;
24import android.os.UserHandle;
25import android.os.RemoteException;
26import android.util.ArraySet;
27import android.util.Log;
28
29import java.util.ArrayList;
30import java.util.Collection;
31import java.util.Date;
32import java.util.HashSet;
33import java.util.Iterator;
34import java.util.LinkedList;
35import java.util.List;
36import java.util.Set;
37
38import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
39import static com.android.server.pm.PackageManagerService.TAG;
40
41/**
42 * Class containing helper methods for the PackageManagerService.
43 *
44 * {@hide}
45 */
46public class PackageManagerServiceUtils {
47    // Apps used in the last 7 days.
48    private final static long DEXOPT_LRU_THRESHOLD_IN_MINUTES = 7 * 24 * 60;
49
50    private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
51        List<ResolveInfo> ris = null;
52        try {
53            ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId);
54        } catch (RemoteException e) {
55        }
56        ArraySet<String> pkgNames = new ArraySet<String>();
57        if (ris != null) {
58            for (ResolveInfo ri : ris) {
59                pkgNames.add(ri.activityInfo.packageName);
60            }
61        }
62        return pkgNames;
63    }
64
65    private static void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs,
66            long dexOptLRUThresholdInMills) {
67        // Filter out packages that aren't recently used.
68        int total = pkgs.size();
69        int skipped = 0;
70        long now = System.currentTimeMillis();
71        for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
72            PackageParser.Package pkg = i.next();
73            long then = pkg.mLastPackageUsageTimeInMills;
74            if (then + dexOptLRUThresholdInMills < now) {
75                if (DEBUG_DEXOPT) {
76                    Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
77                          ((then == 0) ? "never" : new Date(then)));
78                }
79                i.remove();
80                skipped++;
81            }
82        }
83        if (DEBUG_DEXOPT) {
84            Log.i(TAG, "Skipped optimizing " + skipped + " of " + total);
85        }
86    }
87
88    // Sort apps by importance for dexopt ordering. Important apps are given
89    // more priority in case the device runs out of space.
90    public static List<PackageParser.Package> getPackagesForDexopt(
91            Collection<PackageParser.Package> packages,
92            PackageManagerService packageManagerService) {
93        ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
94        LinkedList<PackageParser.Package> result = new LinkedList<>();
95
96        // Give priority to core apps.
97        for (PackageParser.Package pkg : remainingPkgs) {
98            if (pkg.coreApp) {
99                if (DEBUG_DEXOPT) {
100                    Log.i(TAG, "Adding core app " + result.size() + ": " + pkg.packageName);
101                }
102                result.add(pkg);
103            }
104        }
105        remainingPkgs.removeAll(result);
106
107        // Give priority to system apps that listen for pre boot complete.
108        Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
109        ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
110        for (PackageParser.Package pkg : remainingPkgs) {
111            if (pkgNames.contains(pkg.packageName)) {
112                if (DEBUG_DEXOPT) {
113                    Log.i(TAG, "Adding pre boot system app " + result.size() + ": " +
114                            pkg.packageName);
115                }
116                result.add(pkg);
117            }
118        }
119        remainingPkgs.removeAll(result);
120
121        // Filter out packages that aren't recently used, add all remaining apps.
122        // TODO: add a property to control this?
123        if (packageManagerService.isHistoricalPackageUsageAvailable()) {
124            filterRecentlyUsedApps(remainingPkgs, DEXOPT_LRU_THRESHOLD_IN_MINUTES * 60 * 1000);
125        }
126        result.addAll(remainingPkgs);
127
128        // Now go ahead and also add the libraries required for these packages.
129        // TODO: Think about interleaving things.
130        Set<PackageParser.Package> dependencies = new HashSet<>();
131        for (PackageParser.Package p : result) {
132            dependencies.addAll(packageManagerService.findSharedNonSystemLibraries(p));
133        }
134        if (!dependencies.isEmpty()) {
135            dependencies.removeAll(result);
136        }
137        result.addAll(dependencies);
138
139        if (DEBUG_DEXOPT) {
140            StringBuilder sb = new StringBuilder();
141            for (PackageParser.Package pkg : result) {
142                if (sb.length() > 0) {
143                    sb.append(", ");
144                }
145                sb.append(pkg.packageName);
146            }
147            Log.i(TAG, "Packages to be optimized: " + sb.toString());
148        }
149
150        return result;
151    }
152}