PackageManagerServiceUtils.java revision 2e565e6d6ec1a8be52f9d2b5a67597cf4791434d
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 com.android.server.pm.dex.DexManager; 20import com.android.server.pm.dex.PackageDexUsage; 21 22import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; 23import static com.android.server.pm.PackageManagerService.TAG; 24 25import android.annotation.NonNull; 26import android.app.AppGlobals; 27import android.content.Intent; 28import android.content.pm.PackageInfo; 29import android.content.pm.PackageParser; 30import android.content.pm.ResolveInfo; 31import android.os.Build; 32import android.os.RemoteException; 33import android.os.UserHandle; 34import android.system.ErrnoException; 35import android.util.ArraySet; 36import android.util.Log; 37import dalvik.system.VMRuntime; 38import libcore.io.Libcore; 39 40import java.io.File; 41import java.io.IOException; 42import java.util.ArrayList; 43import java.util.Collection; 44import java.util.Collections; 45import java.util.LinkedList; 46import java.util.List; 47import java.util.function.Predicate; 48 49/** 50 * Class containing helper methods for the PackageManagerService. 51 * 52 * {@hide} 53 */ 54public class PackageManagerServiceUtils { 55 private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000; 56 57 private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) { 58 List<ResolveInfo> ris = null; 59 try { 60 ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId) 61 .getList(); 62 } catch (RemoteException e) { 63 } 64 ArraySet<String> pkgNames = new ArraySet<String>(); 65 if (ris != null) { 66 for (ResolveInfo ri : ris) { 67 pkgNames.add(ri.activityInfo.packageName); 68 } 69 } 70 return pkgNames; 71 } 72 73 // Sort a list of apps by their last usage, most recently used apps first. The order of 74 // packages without usage data is undefined (but they will be sorted after the packages 75 // that do have usage data). 76 public static void sortPackagesByUsageDate(List<PackageParser.Package> pkgs, 77 PackageManagerService packageManagerService) { 78 if (!packageManagerService.isHistoricalPackageUsageAvailable()) { 79 return; 80 } 81 82 Collections.sort(pkgs, (pkg1, pkg2) -> 83 Long.compare(pkg2.getLatestForegroundPackageUseTimeInMills(), 84 pkg1.getLatestForegroundPackageUseTimeInMills())); 85 } 86 87 // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the 88 // package will be removed from {@code packages} and added to {@code result} with its 89 // dependencies. If usage data is available, the positive packages will be sorted by usage 90 // data (with {@code sortTemp} as temporary storage). 91 private static void applyPackageFilter(Predicate<PackageParser.Package> filter, 92 Collection<PackageParser.Package> result, 93 Collection<PackageParser.Package> packages, 94 @NonNull List<PackageParser.Package> sortTemp, 95 PackageManagerService packageManagerService) { 96 for (PackageParser.Package pkg : packages) { 97 if (filter.test(pkg)) { 98 sortTemp.add(pkg); 99 } 100 } 101 102 sortPackagesByUsageDate(sortTemp, packageManagerService); 103 packages.removeAll(sortTemp); 104 105 for (PackageParser.Package pkg : sortTemp) { 106 result.add(pkg); 107 108 Collection<PackageParser.Package> deps = 109 packageManagerService.findSharedNonSystemLibraries(pkg); 110 if (!deps.isEmpty()) { 111 deps.removeAll(result); 112 result.addAll(deps); 113 packages.removeAll(deps); 114 } 115 } 116 117 sortTemp.clear(); 118 } 119 120 // Sort apps by importance for dexopt ordering. Important apps are given 121 // more priority in case the device runs out of space. 122 public static List<PackageParser.Package> getPackagesForDexopt( 123 Collection<PackageParser.Package> packages, 124 PackageManagerService packageManagerService) { 125 ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages); 126 LinkedList<PackageParser.Package> result = new LinkedList<>(); 127 ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size()); 128 129 // Give priority to core apps. 130 applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp, 131 packageManagerService); 132 133 // Give priority to system apps that listen for pre boot complete. 134 Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); 135 final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM); 136 applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs, 137 sortTemp, packageManagerService); 138 139 // Give priority to apps used by other apps. 140 applyPackageFilter((pkg) -> 141 packageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName), result, 142 remainingPkgs, sortTemp, packageManagerService); 143 144 // Filter out packages that aren't recently used, add all remaining apps. 145 // TODO: add a property to control this? 146 Predicate<PackageParser.Package> remainingPredicate; 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 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS; 163 remainingPredicate = 164 (pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime; 165 } else { 166 // No meaningful historical info. Take all. 167 remainingPredicate = (pkg) -> true; 168 } 169 sortPackagesByUsageDate(remainingPkgs, packageManagerService); 170 } else { 171 // No historical info. Take all. 172 remainingPredicate = (pkg) -> true; 173 } 174 applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp, 175 packageManagerService); 176 177 if (DEBUG_DEXOPT) { 178 Log.i(TAG, "Packages to be dexopted: " + packagesToString(result)); 179 Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs)); 180 } 181 182 return result; 183 } 184 185 /** 186 * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>. 187 * Package is considered active, if: 188 * 1) It was active in foreground. 189 * 2) It was active in background and also used by other apps. 190 * 191 * If it doesn't have sufficient information about the package, it return <code>false</code>. 192 */ 193 static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, 194 long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo, 195 long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) { 196 197 if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) { 198 return false; 199 } 200 201 // If the app was active in foreground during the threshold period. 202 boolean isActiveInForeground = (currentTimeInMillis 203 - latestForegroundPackageUseTimeInMillis) 204 < thresholdTimeinMillis; 205 206 if (isActiveInForeground) { 207 return false; 208 } 209 210 // If the app was active in background during the threshold period and was used 211 // by other packages. 212 // If packageUseInfo is null, it can be said that the package was not used by other 213 // packages. 214 boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis 215 - latestPackageUseTimeInMillis) 216 < thresholdTimeinMillis) 217 && (packageUseInfo != null && packageUseInfo.isUsedByOtherApps()); 218 219 return !isActiveInBackgroundAndUsedByOtherPackages; 220 } 221 222 /** 223 * Returns the canonicalized path of {@code path} as per {@code realpath(3)} 224 * semantics. 225 */ 226 public static String realpath(File path) throws IOException { 227 try { 228 return Libcore.os.realpath(path.getAbsolutePath()); 229 } catch (ErrnoException ee) { 230 throw ee.rethrowAsIOException(); 231 } 232 } 233 234 public static String packagesToString(Collection<PackageParser.Package> c) { 235 StringBuilder sb = new StringBuilder(); 236 for (PackageParser.Package pkg : c) { 237 if (sb.length() > 0) { 238 sb.append(", "); 239 } 240 sb.append(pkg.packageName); 241 } 242 return sb.toString(); 243 } 244 245 /** 246 * Verifies that the given string {@code isa} is a valid supported isa on 247 * the running device. 248 */ 249 public static boolean checkISA(String isa) { 250 for (String abi : Build.SUPPORTED_ABIS) { 251 if (VMRuntime.getInstructionSet(abi).equals(isa)) { 252 return true; 253 } 254 } 255 return false; 256 } 257} 258