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