PackageManagerServiceUtils.java revision dab38e000436bf8234955b0333eaecf389e65b6f
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.annotation.NonNull; 23import android.app.AppGlobals; 24import android.content.Intent; 25import android.content.pm.PackageParser; 26import android.content.pm.ResolveInfo; 27import android.os.RemoteException; 28import android.os.UserHandle; 29import android.system.ErrnoException; 30import android.util.ArraySet; 31import android.util.Log; 32import libcore.io.Libcore; 33 34import java.io.File; 35import java.io.IOException; 36import java.util.ArrayList; 37import java.util.Collection; 38import java.util.Collections; 39import java.util.LinkedList; 40import java.util.List; 41import java.util.function.Predicate; 42 43/** 44 * Class containing helper methods for the PackageManagerService. 45 * 46 * {@hide} 47 */ 48public class PackageManagerServiceUtils { 49 private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000; 50 51 private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) { 52 List<ResolveInfo> ris = null; 53 try { 54 ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId) 55 .getList(); 56 } catch (RemoteException e) { 57 } 58 ArraySet<String> pkgNames = new ArraySet<String>(); 59 if (ris != null) { 60 for (ResolveInfo ri : ris) { 61 pkgNames.add(ri.activityInfo.packageName); 62 } 63 } 64 return pkgNames; 65 } 66 67 // Sort a list of apps by their last usage, most recently used apps first. The order of 68 // packages without usage data is undefined (but they will be sorted after the packages 69 // that do have usage data). 70 public static void sortPackagesByUsageDate(List<PackageParser.Package> pkgs, 71 PackageManagerService packageManagerService) { 72 if (!packageManagerService.isHistoricalPackageUsageAvailable()) { 73 return; 74 } 75 76 Collections.sort(pkgs, (pkg1, pkg2) -> 77 Long.compare(pkg2.getLatestForegroundPackageUseTimeInMills(), 78 pkg1.getLatestForegroundPackageUseTimeInMills())); 79 } 80 81 // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the 82 // package will be removed from {@code packages} and added to {@code result} with its 83 // dependencies. If usage data is available, the positive packages will be sorted by usage 84 // data (with {@code sortTemp} as temporary storage). 85 private static void applyPackageFilter(Predicate<PackageParser.Package> filter, 86 Collection<PackageParser.Package> result, 87 Collection<PackageParser.Package> packages, 88 @NonNull List<PackageParser.Package> sortTemp, 89 PackageManagerService packageManagerService) { 90 for (PackageParser.Package pkg : packages) { 91 if (filter.test(pkg)) { 92 sortTemp.add(pkg); 93 } 94 } 95 96 sortPackagesByUsageDate(sortTemp, packageManagerService); 97 packages.removeAll(sortTemp); 98 99 for (PackageParser.Package pkg : sortTemp) { 100 result.add(pkg); 101 102 Collection<PackageParser.Package> deps = 103 packageManagerService.findSharedNonSystemLibraries(pkg); 104 if (!deps.isEmpty()) { 105 deps.removeAll(result); 106 result.addAll(deps); 107 packages.removeAll(deps); 108 } 109 } 110 111 sortTemp.clear(); 112 } 113 114 // Sort apps by importance for dexopt ordering. Important apps are given 115 // more priority in case the device runs out of space. 116 public static List<PackageParser.Package> getPackagesForDexopt( 117 Collection<PackageParser.Package> packages, 118 PackageManagerService packageManagerService) { 119 ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages); 120 LinkedList<PackageParser.Package> result = new LinkedList<>(); 121 ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size()); 122 123 // Give priority to core apps. 124 applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp, 125 packageManagerService); 126 127 // Give priority to system apps that listen for pre boot complete. 128 Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); 129 final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM); 130 applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs, 131 sortTemp, packageManagerService); 132 133 // Give priority to apps used by other apps. 134 applyPackageFilter((pkg) -> PackageDexOptimizer.isUsedByOtherApps(pkg), result, 135 remainingPkgs, sortTemp, packageManagerService); 136 137 // Filter out packages that aren't recently used, add all remaining apps. 138 // TODO: add a property to control this? 139 Predicate<PackageParser.Package> remainingPredicate; 140 if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) { 141 if (DEBUG_DEXOPT) { 142 Log.i(TAG, "Looking at historical package use"); 143 } 144 // Get the package that was used last. 145 PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) -> 146 Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(), 147 pkg2.getLatestForegroundPackageUseTimeInMills())); 148 if (DEBUG_DEXOPT) { 149 Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use"); 150 } 151 long estimatedPreviousSystemUseTime = 152 lastUsed.getLatestForegroundPackageUseTimeInMills(); 153 // Be defensive if for some reason package usage has bogus data. 154 if (estimatedPreviousSystemUseTime != 0) { 155 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS; 156 remainingPredicate = 157 (pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime; 158 } else { 159 // No meaningful historical info. Take all. 160 remainingPredicate = (pkg) -> true; 161 } 162 sortPackagesByUsageDate(remainingPkgs, packageManagerService); 163 } else { 164 // No historical info. Take all. 165 remainingPredicate = (pkg) -> true; 166 } 167 applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp, 168 packageManagerService); 169 170 if (DEBUG_DEXOPT) { 171 Log.i(TAG, "Packages to be dexopted: " + packagesToString(result)); 172 Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs)); 173 } 174 175 return result; 176 } 177 178 /** 179 * Returns the canonicalized path of {@code path} as per {@code realpath(3)} 180 * semantics. 181 */ 182 public static String realpath(File path) throws IOException { 183 try { 184 return Libcore.os.realpath(path.getAbsolutePath()); 185 } catch (ErrnoException ee) { 186 throw ee.rethrowAsIOException(); 187 } 188 } 189 190 public static String packagesToString(Collection<PackageParser.Package> c) { 191 StringBuilder sb = new StringBuilder(); 192 for (PackageParser.Package pkg : c) { 193 if (sb.length() > 0) { 194 sb.append(", "); 195 } 196 sb.append(pkg.packageName); 197 } 198 return sb.toString(); 199 } 200} 201