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