PackageManagerServiceUtils.java revision 20a894ed549a66786c3ca999f1a0083970b7e59c
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 com.android.internal.util.ArrayUtils; 23 24import android.annotation.NonNull; 25import android.app.AppGlobals; 26import android.content.Intent; 27import android.content.pm.PackageParser; 28import android.content.pm.ResolveInfo; 29import android.os.Build; 30import android.os.RemoteException; 31import android.os.UserHandle; 32import android.system.ErrnoException; 33import android.util.ArraySet; 34import android.util.Log; 35import android.util.Slog; 36import android.util.jar.StrictJarFile; 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.Iterator; 46import java.util.LinkedList; 47import java.util.List; 48import java.util.function.Predicate; 49import java.util.zip.ZipEntry; 50 51/** 52 * Class containing helper methods for the PackageManagerService. 53 * 54 * {@hide} 55 */ 56public class PackageManagerServiceUtils { 57 private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000; 58 59 private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) { 60 List<ResolveInfo> ris = null; 61 try { 62 ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId) 63 .getList(); 64 } catch (RemoteException e) { 65 } 66 ArraySet<String> pkgNames = new ArraySet<String>(); 67 if (ris != null) { 68 for (ResolveInfo ri : ris) { 69 pkgNames.add(ri.activityInfo.packageName); 70 } 71 } 72 return pkgNames; 73 } 74 75 // Sort a list of apps by their last usage, most recently used apps first. The order of 76 // packages without usage data is undefined (but they will be sorted after the packages 77 // that do have usage data). 78 public static void sortPackagesByUsageDate(List<PackageParser.Package> pkgs, 79 PackageManagerService packageManagerService) { 80 if (!packageManagerService.isHistoricalPackageUsageAvailable()) { 81 return; 82 } 83 84 Collections.sort(pkgs, (pkg1, pkg2) -> 85 Long.compare(pkg2.getLatestForegroundPackageUseTimeInMills(), 86 pkg1.getLatestForegroundPackageUseTimeInMills())); 87 } 88 89 // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the 90 // package will be removed from {@code packages} and added to {@code result} with its 91 // dependencies. If usage data is available, the positive packages will be sorted by usage 92 // data (with {@code sortTemp} as temporary storage). 93 private static void applyPackageFilter(Predicate<PackageParser.Package> filter, 94 Collection<PackageParser.Package> result, 95 Collection<PackageParser.Package> packages, 96 @NonNull List<PackageParser.Package> sortTemp, 97 PackageManagerService packageManagerService) { 98 for (PackageParser.Package pkg : packages) { 99 if (filter.test(pkg)) { 100 sortTemp.add(pkg); 101 } 102 } 103 104 sortPackagesByUsageDate(sortTemp, packageManagerService); 105 packages.removeAll(sortTemp); 106 107 for (PackageParser.Package pkg : sortTemp) { 108 result.add(pkg); 109 110 Collection<PackageParser.Package> deps = 111 packageManagerService.findSharedNonSystemLibraries(pkg); 112 if (!deps.isEmpty()) { 113 deps.removeAll(result); 114 result.addAll(deps); 115 packages.removeAll(deps); 116 } 117 } 118 119 sortTemp.clear(); 120 } 121 122 // Sort apps by importance for dexopt ordering. Important apps are given 123 // more priority in case the device runs out of space. 124 public static List<PackageParser.Package> getPackagesForDexopt( 125 Collection<PackageParser.Package> packages, 126 PackageManagerService packageManagerService) { 127 ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages); 128 LinkedList<PackageParser.Package> result = new LinkedList<>(); 129 ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size()); 130 131 // Give priority to core apps. 132 applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp, 133 packageManagerService); 134 135 // Give priority to system apps that listen for pre boot complete. 136 Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); 137 final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM); 138 applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs, 139 sortTemp, packageManagerService); 140 141 // Give priority to apps used by other apps. 142 applyPackageFilter((pkg) -> 143 packageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName), result, 144 remainingPkgs, sortTemp, packageManagerService); 145 146 // Filter out packages that aren't recently used, add all remaining apps. 147 // TODO: add a property to control this? 148 Predicate<PackageParser.Package> remainingPredicate; 149 if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) { 150 if (DEBUG_DEXOPT) { 151 Log.i(TAG, "Looking at historical package use"); 152 } 153 // Get the package that was used last. 154 PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) -> 155 Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(), 156 pkg2.getLatestForegroundPackageUseTimeInMills())); 157 if (DEBUG_DEXOPT) { 158 Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use"); 159 } 160 long estimatedPreviousSystemUseTime = 161 lastUsed.getLatestForegroundPackageUseTimeInMills(); 162 // Be defensive if for some reason package usage has bogus data. 163 if (estimatedPreviousSystemUseTime != 0) { 164 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS; 165 remainingPredicate = 166 (pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime; 167 } else { 168 // No meaningful historical info. Take all. 169 remainingPredicate = (pkg) -> true; 170 } 171 sortPackagesByUsageDate(remainingPkgs, packageManagerService); 172 } else { 173 // No historical info. Take all. 174 remainingPredicate = (pkg) -> true; 175 } 176 applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp, 177 packageManagerService); 178 179 if (DEBUG_DEXOPT) { 180 Log.i(TAG, "Packages to be dexopted: " + packagesToString(result)); 181 Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs)); 182 } 183 184 return result; 185 } 186 187 /** 188 * Returns the canonicalized path of {@code path} as per {@code realpath(3)} 189 * semantics. 190 */ 191 public static String realpath(File path) throws IOException { 192 try { 193 return Libcore.os.realpath(path.getAbsolutePath()); 194 } catch (ErrnoException ee) { 195 throw ee.rethrowAsIOException(); 196 } 197 } 198 199 public static String packagesToString(Collection<PackageParser.Package> c) { 200 StringBuilder sb = new StringBuilder(); 201 for (PackageParser.Package pkg : c) { 202 if (sb.length() > 0) { 203 sb.append(", "); 204 } 205 sb.append(pkg.packageName); 206 } 207 return sb.toString(); 208 } 209 210 /** 211 * Verifies that the given string {@code isa} is a valid supported isa on 212 * the running device. 213 */ 214 public static boolean checkISA(String isa) { 215 for (String abi : Build.SUPPORTED_ABIS) { 216 if (VMRuntime.getInstructionSet(abi).equals(isa)) { 217 return true; 218 } 219 } 220 return false; 221 } 222 223 /** 224 * Checks that the archive located at {@code fileName} has uncompressed dex file and so 225 * files that can be direclty mapped. 226 */ 227 public static void logApkHasUncompressedCode(String fileName) { 228 StrictJarFile jarFile = null; 229 try { 230 jarFile = new StrictJarFile(fileName, 231 false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/); 232 Iterator<ZipEntry> it = jarFile.iterator(); 233 while (it.hasNext()) { 234 ZipEntry entry = it.next(); 235 if (entry.getName().endsWith(".dex")) { 236 if (entry.getMethod() != ZipEntry.STORED) { 237 Slog.wtf(TAG, "APK " + fileName + " has compressed dex code " + 238 entry.getName()); 239 } else if ((entry.getDataOffset() & 0x3) != 0) { 240 Slog.wtf(TAG, "APK " + fileName + " has unaligned dex code " + 241 entry.getName()); 242 } 243 } else if (entry.getName().endsWith(".so")) { 244 if (entry.getMethod() != ZipEntry.STORED) { 245 Slog.wtf(TAG, "APK " + fileName + " has compressed native code " + 246 entry.getName()); 247 } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) { 248 Slog.wtf(TAG, "APK " + fileName + " has unaligned native code " + 249 entry.getName()); 250 } 251 } 252 } 253 } catch (IOException ignore) { 254 Slog.wtf(TAG, "Error when parsing APK " + fileName); 255 } finally { 256 try { 257 if (jarFile != null) { 258 jarFile.close(); 259 } 260 } catch (IOException ignore) {} 261 } 262 return; 263 } 264 265 /** 266 * Checks that the APKs in the given package have uncompressed dex file and so 267 * files that can be direclty mapped. 268 */ 269 public static void logPackageHasUncompressedCode(PackageParser.Package pkg) { 270 logApkHasUncompressedCode(pkg.baseCodePath); 271 if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { 272 for (int i = 0; i < pkg.splitCodePaths.length; i++) { 273 logApkHasUncompressedCode(pkg.splitCodePaths[i]); 274 } 275 } 276 } 277} 278