Home | History | Annotate | Download | only in pm
      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.annotation.NonNull;
     23 import android.app.AppGlobals;
     24 import android.content.Intent;
     25 import android.content.pm.PackageParser;
     26 import android.content.pm.ResolveInfo;
     27 import android.os.RemoteException;
     28 import android.os.UserHandle;
     29 import android.system.ErrnoException;
     30 import android.util.ArraySet;
     31 import android.util.Log;
     32 import libcore.io.Libcore;
     33 
     34 import java.io.File;
     35 import java.io.IOException;
     36 import java.util.ArrayList;
     37 import java.util.Collection;
     38 import java.util.Collections;
     39 import java.util.LinkedList;
     40 import java.util.List;
     41 import java.util.function.Predicate;
     42 
     43 /**
     44  * Class containing helper methods for the PackageManagerService.
     45  *
     46  * {@hide}
     47  */
     48 public 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