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 android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
     20 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
     21 import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
     22 import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
     23 import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
     24 import static com.android.server.pm.PackageManagerService.STUB_SUFFIX;
     25 import static com.android.server.pm.PackageManagerService.TAG;
     26 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
     27 
     28 import com.android.internal.content.NativeLibraryHelper;
     29 import com.android.internal.util.FastPrintWriter;
     30 import com.android.server.EventLogTags;
     31 import com.android.server.pm.dex.DexManager;
     32 import com.android.server.pm.dex.PackageDexUsage;
     33 
     34 import android.annotation.NonNull;
     35 import android.annotation.Nullable;
     36 import android.app.AppGlobals;
     37 import android.content.Intent;
     38 import android.content.pm.PackageManager;
     39 import android.content.pm.PackageParser;
     40 import android.content.pm.PackageParser.PackageParserException;
     41 import android.content.pm.ResolveInfo;
     42 import android.content.pm.Signature;
     43 import android.os.Build;
     44 import android.os.Debug;
     45 import android.os.Environment;
     46 import android.os.FileUtils;
     47 import android.os.Process;
     48 import android.os.RemoteException;
     49 import android.os.SystemProperties;
     50 import android.os.UserHandle;
     51 import android.service.pm.PackageServiceDumpProto;
     52 import android.system.ErrnoException;
     53 import android.system.Os;
     54 import android.util.ArraySet;
     55 import android.util.Log;
     56 import android.util.PackageUtils;
     57 import android.util.Slog;
     58 import android.util.proto.ProtoOutputStream;
     59 
     60 import dalvik.system.VMRuntime;
     61 
     62 import libcore.io.IoUtils;
     63 import libcore.io.Libcore;
     64 import libcore.io.Streams;
     65 
     66 import java.io.BufferedReader;
     67 import java.io.File;
     68 import java.io.FileInputStream;
     69 import java.io.FileOutputStream;
     70 import java.io.FileReader;
     71 import java.io.FilenameFilter;
     72 import java.io.IOException;
     73 import java.io.InputStream;
     74 import java.io.OutputStream;
     75 import java.io.PrintWriter;
     76 import java.security.MessageDigest;
     77 import java.security.NoSuchAlgorithmException;
     78 import java.security.cert.CertificateEncodingException;
     79 import java.security.cert.CertificateException;
     80 import java.text.SimpleDateFormat;
     81 import java.util.ArrayList;
     82 import java.util.Arrays;
     83 import java.util.Collection;
     84 import java.util.Collections;
     85 import java.util.Date;
     86 import java.util.LinkedList;
     87 import java.util.List;
     88 import java.util.function.Predicate;
     89 import java.util.zip.GZIPInputStream;
     90 
     91 /**
     92  * Class containing helper methods for the PackageManagerService.
     93  *
     94  * {@hide}
     95  */
     96 public class PackageManagerServiceUtils {
     97     private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
     98 
     99     private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
    100         List<ResolveInfo> ris = null;
    101         try {
    102             ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
    103                     .getList();
    104         } catch (RemoteException e) {
    105         }
    106         ArraySet<String> pkgNames = new ArraySet<String>();
    107         if (ris != null) {
    108             for (ResolveInfo ri : ris) {
    109                 pkgNames.add(ri.activityInfo.packageName);
    110             }
    111         }
    112         return pkgNames;
    113     }
    114 
    115     // Sort a list of apps by their last usage, most recently used apps first. The order of
    116     // packages without usage data is undefined (but they will be sorted after the packages
    117     // that do have usage data).
    118     public static void sortPackagesByUsageDate(List<PackageParser.Package> pkgs,
    119             PackageManagerService packageManagerService) {
    120         if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
    121             return;
    122         }
    123 
    124         Collections.sort(pkgs, (pkg1, pkg2) ->
    125                 Long.compare(pkg2.getLatestForegroundPackageUseTimeInMills(),
    126                         pkg1.getLatestForegroundPackageUseTimeInMills()));
    127     }
    128 
    129     // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
    130     // package will be removed from {@code packages} and added to {@code result} with its
    131     // dependencies. If usage data is available, the positive packages will be sorted by usage
    132     // data (with {@code sortTemp} as temporary storage).
    133     private static void applyPackageFilter(Predicate<PackageParser.Package> filter,
    134             Collection<PackageParser.Package> result,
    135             Collection<PackageParser.Package> packages,
    136             @NonNull List<PackageParser.Package> sortTemp,
    137             PackageManagerService packageManagerService) {
    138         for (PackageParser.Package pkg : packages) {
    139             if (filter.test(pkg)) {
    140                 sortTemp.add(pkg);
    141             }
    142         }
    143 
    144         sortPackagesByUsageDate(sortTemp, packageManagerService);
    145         packages.removeAll(sortTemp);
    146 
    147         for (PackageParser.Package pkg : sortTemp) {
    148             result.add(pkg);
    149 
    150             Collection<PackageParser.Package> deps =
    151                     packageManagerService.findSharedNonSystemLibraries(pkg);
    152             if (!deps.isEmpty()) {
    153                 deps.removeAll(result);
    154                 result.addAll(deps);
    155                 packages.removeAll(deps);
    156             }
    157         }
    158 
    159         sortTemp.clear();
    160     }
    161 
    162     // Sort apps by importance for dexopt ordering. Important apps are given
    163     // more priority in case the device runs out of space.
    164     public static List<PackageParser.Package> getPackagesForDexopt(
    165             Collection<PackageParser.Package> packages,
    166             PackageManagerService packageManagerService) {
    167         ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
    168         LinkedList<PackageParser.Package> result = new LinkedList<>();
    169         ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size());
    170 
    171         // Give priority to core apps.
    172         applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp,
    173                 packageManagerService);
    174 
    175         // Give priority to system apps that listen for pre boot complete.
    176         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
    177         final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
    178         applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs,
    179                 sortTemp, packageManagerService);
    180 
    181         // Give priority to apps used by other apps.
    182         DexManager dexManager = packageManagerService.getDexManager();
    183         applyPackageFilter((pkg) ->
    184                 dexManager.getPackageUseInfoOrDefault(pkg.packageName)
    185                         .isAnyCodePathUsedByOtherApps(),
    186                 result, remainingPkgs, sortTemp, packageManagerService);
    187 
    188         // Filter out packages that aren't recently used, add all remaining apps.
    189         // TODO: add a property to control this?
    190         Predicate<PackageParser.Package> remainingPredicate;
    191         if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
    192             if (DEBUG_DEXOPT) {
    193                 Log.i(TAG, "Looking at historical package use");
    194             }
    195             // Get the package that was used last.
    196             PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
    197                     Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
    198                             pkg2.getLatestForegroundPackageUseTimeInMills()));
    199             if (DEBUG_DEXOPT) {
    200                 Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use");
    201             }
    202             long estimatedPreviousSystemUseTime =
    203                     lastUsed.getLatestForegroundPackageUseTimeInMills();
    204             // Be defensive if for some reason package usage has bogus data.
    205             if (estimatedPreviousSystemUseTime != 0) {
    206                 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
    207                 remainingPredicate =
    208                         (pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
    209             } else {
    210                 // No meaningful historical info. Take all.
    211                 remainingPredicate = (pkg) -> true;
    212             }
    213             sortPackagesByUsageDate(remainingPkgs, packageManagerService);
    214         } else {
    215             // No historical info. Take all.
    216             remainingPredicate = (pkg) -> true;
    217         }
    218         applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp,
    219                 packageManagerService);
    220 
    221         if (DEBUG_DEXOPT) {
    222             Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
    223             Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs));
    224         }
    225 
    226         return result;
    227     }
    228 
    229     /**
    230      * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
    231      * Package is considered active, if:
    232      * 1) It was active in foreground.
    233      * 2) It was active in background and also used by other apps.
    234      *
    235      * If it doesn't have sufficient information about the package, it return <code>false</code>.
    236      */
    237     public static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis,
    238             long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo,
    239             long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) {
    240 
    241         if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) {
    242             return false;
    243         }
    244 
    245         // If the app was active in foreground during the threshold period.
    246         boolean isActiveInForeground = (currentTimeInMillis
    247                 - latestForegroundPackageUseTimeInMillis)
    248                 < thresholdTimeinMillis;
    249 
    250         if (isActiveInForeground) {
    251             return false;
    252         }
    253 
    254         // If the app was active in background during the threshold period and was used
    255         // by other packages.
    256         boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
    257                 - latestPackageUseTimeInMillis)
    258                 < thresholdTimeinMillis)
    259                 && packageUseInfo.isAnyCodePathUsedByOtherApps();
    260 
    261         return !isActiveInBackgroundAndUsedByOtherPackages;
    262     }
    263 
    264     /**
    265      * Returns the canonicalized path of {@code path} as per {@code realpath(3)}
    266      * semantics.
    267      */
    268     public static String realpath(File path) throws IOException {
    269         try {
    270             return Os.realpath(path.getAbsolutePath());
    271         } catch (ErrnoException ee) {
    272             throw ee.rethrowAsIOException();
    273         }
    274     }
    275 
    276     public static String packagesToString(Collection<PackageParser.Package> c) {
    277         StringBuilder sb = new StringBuilder();
    278         for (PackageParser.Package pkg : c) {
    279             if (sb.length() > 0) {
    280                 sb.append(", ");
    281             }
    282             sb.append(pkg.packageName);
    283         }
    284         return sb.toString();
    285     }
    286 
    287     /**
    288      * Verifies that the given string {@code isa} is a valid supported isa on
    289      * the running device.
    290      */
    291     public static boolean checkISA(String isa) {
    292         for (String abi : Build.SUPPORTED_ABIS) {
    293             if (VMRuntime.getInstructionSet(abi).equals(isa)) {
    294                 return true;
    295             }
    296         }
    297         return false;
    298     }
    299 
    300     public static long getLastModifiedTime(PackageParser.Package pkg) {
    301         final File srcFile = new File(pkg.codePath);
    302         if (!srcFile.isDirectory()) {
    303             return srcFile.lastModified();
    304         }
    305         final File baseFile = new File(pkg.baseCodePath);
    306         long maxModifiedTime = baseFile.lastModified();
    307         if (pkg.splitCodePaths != null) {
    308             for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) {
    309                 final File splitFile = new File(pkg.splitCodePaths[i]);
    310                 maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
    311             }
    312         }
    313         return maxModifiedTime;
    314     }
    315 
    316     private static File getSettingsProblemFile() {
    317         File dataDir = Environment.getDataDirectory();
    318         File systemDir = new File(dataDir, "system");
    319         File fname = new File(systemDir, "uiderrors.txt");
    320         return fname;
    321     }
    322 
    323     public static void dumpCriticalInfo(ProtoOutputStream proto) {
    324         try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
    325             String line = null;
    326             while ((line = in.readLine()) != null) {
    327                 if (line.contains("ignored: updated version")) continue;
    328                 proto.write(PackageServiceDumpProto.MESSAGES, line);
    329             }
    330         } catch (IOException ignored) {
    331         }
    332     }
    333 
    334     public static void dumpCriticalInfo(PrintWriter pw, String msg) {
    335         try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
    336             String line = null;
    337             while ((line = in.readLine()) != null) {
    338                 if (line.contains("ignored: updated version")) continue;
    339                 if (msg != null) {
    340                     pw.print(msg);
    341                 }
    342                 pw.println(line);
    343             }
    344         } catch (IOException ignored) {
    345         }
    346     }
    347 
    348     public static void logCriticalInfo(int priority, String msg) {
    349         Slog.println(priority, TAG, msg);
    350         EventLogTags.writePmCriticalInfo(msg);
    351         try {
    352             File fname = getSettingsProblemFile();
    353             FileOutputStream out = new FileOutputStream(fname, true);
    354             PrintWriter pw = new FastPrintWriter(out);
    355             SimpleDateFormat formatter = new SimpleDateFormat();
    356             String dateString = formatter.format(new Date(System.currentTimeMillis()));
    357             pw.println(dateString + ": " + msg);
    358             pw.close();
    359             FileUtils.setPermissions(
    360                     fname.toString(),
    361                     FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH,
    362                     -1, -1);
    363         } catch (java.io.IOException e) {
    364         }
    365     }
    366 
    367     public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) {
    368         if (callingUid == Process.SHELL_UID) {
    369             if (userHandle >= 0
    370                     && PackageManagerService.sUserManager.hasUserRestriction(
    371                             restriction, userHandle)) {
    372                 throw new SecurityException("Shell does not have permission to access user "
    373                         + userHandle);
    374             } else if (userHandle < 0) {
    375                 Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user "
    376                         + userHandle + "\n\t" + Debug.getCallers(3));
    377             }
    378         }
    379     }
    380 
    381     /**
    382      * Derive the value of the {@code cpuAbiOverride} based on the provided
    383      * value and an optional stored value from the package settings.
    384      */
    385     public static String deriveAbiOverride(String abiOverride, PackageSetting settings) {
    386         String cpuAbiOverride = null;
    387         if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
    388             cpuAbiOverride = null;
    389         } else if (abiOverride != null) {
    390             cpuAbiOverride = abiOverride;
    391         } else if (settings != null) {
    392             cpuAbiOverride = settings.cpuAbiOverrideString;
    393         }
    394         return cpuAbiOverride;
    395     }
    396 
    397     /**
    398      * Compares two sets of signatures. Returns:
    399      * <br />
    400      * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null,
    401      * <br />
    402      * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null,
    403      * <br />
    404      * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null,
    405      * <br />
    406      * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical,
    407      * <br />
    408      * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
    409      */
    410     public static int compareSignatures(Signature[] s1, Signature[] s2) {
    411         if (s1 == null) {
    412             return s2 == null
    413                     ? PackageManager.SIGNATURE_NEITHER_SIGNED
    414                     : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
    415         }
    416 
    417         if (s2 == null) {
    418             return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
    419         }
    420 
    421         if (s1.length != s2.length) {
    422             return PackageManager.SIGNATURE_NO_MATCH;
    423         }
    424 
    425         // Since both signature sets are of size 1, we can compare without HashSets.
    426         if (s1.length == 1) {
    427             return s1[0].equals(s2[0]) ?
    428                     PackageManager.SIGNATURE_MATCH :
    429                     PackageManager.SIGNATURE_NO_MATCH;
    430         }
    431 
    432         ArraySet<Signature> set1 = new ArraySet<Signature>();
    433         for (Signature sig : s1) {
    434             set1.add(sig);
    435         }
    436         ArraySet<Signature> set2 = new ArraySet<Signature>();
    437         for (Signature sig : s2) {
    438             set2.add(sig);
    439         }
    440         // Make sure s2 contains all signatures in s1.
    441         if (set1.equals(set2)) {
    442             return PackageManager.SIGNATURE_MATCH;
    443         }
    444         return PackageManager.SIGNATURE_NO_MATCH;
    445     }
    446 
    447     /**
    448      * Used for backward compatibility to make sure any packages with
    449      * certificate chains get upgraded to the new style. {@code existingSigs}
    450      * will be in the old format (since they were stored on disk from before the
    451      * system upgrade) and {@code scannedSigs} will be in the newer format.
    452      */
    453     private static boolean matchSignaturesCompat(String packageName,
    454             PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
    455         ArraySet<Signature> existingSet = new ArraySet<Signature>();
    456         for (Signature sig : packageSignatures.mSigningDetails.signatures) {
    457             existingSet.add(sig);
    458         }
    459         ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
    460         for (Signature sig : parsedSignatures.signatures) {
    461             try {
    462                 Signature[] chainSignatures = sig.getChainSignatures();
    463                 for (Signature chainSig : chainSignatures) {
    464                     scannedCompatSet.add(chainSig);
    465                 }
    466             } catch (CertificateEncodingException e) {
    467                 scannedCompatSet.add(sig);
    468             }
    469         }
    470         // make sure the expanded scanned set contains all signatures in the existing one
    471         if (scannedCompatSet.equals(existingSet)) {
    472             // migrate the old signatures to the new scheme
    473             packageSignatures.mSigningDetails = parsedSignatures;
    474             return true;
    475         } else if (parsedSignatures.hasPastSigningCertificates()) {
    476 
    477             // well this sucks: the parsed package has probably rotated signing certificates, but
    478             // we don't have enough information to determine if the new signing certificate was
    479             // blessed by the old one
    480             logCriticalInfo(Log.INFO, "Existing package " + packageName + " has flattened signing "
    481                     + "certificate chain. Unable to install newer version with rotated signing "
    482                     + "certificate.");
    483         }
    484         return false;
    485     }
    486 
    487     private static boolean matchSignaturesRecover(
    488             String packageName,
    489             PackageParser.SigningDetails existingSignatures,
    490             PackageParser.SigningDetails parsedSignatures,
    491             @PackageParser.SigningDetails.CertCapabilities int flags) {
    492         String msg = null;
    493         try {
    494             if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) {
    495                 logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for "
    496                         + packageName);
    497                     return true;
    498             }
    499         } catch (CertificateException e) {
    500             msg = e.getMessage();
    501         }
    502         logCriticalInfo(Log.INFO,
    503                 "Failed to recover certificates for " + packageName + ": " + msg);
    504         return false;
    505     }
    506 
    507     /**
    508      * Make sure the updated priv app is signed with the same key as the original APK file on the
    509      * /system partition.
    510      *
    511      * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data
    512      * and is not tamperproof.
    513      */
    514     private static boolean matchSignatureInSystem(PackageSetting pkgSetting,
    515             PackageSetting disabledPkgSetting) {
    516         try {
    517             PackageParser.collectCertificates(disabledPkgSetting.pkg, true /* skipVerify */);
    518             if (pkgSetting.signatures.mSigningDetails.checkCapability(
    519                     disabledPkgSetting.signatures.mSigningDetails,
    520                     PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
    521                     || disabledPkgSetting.signatures.mSigningDetails.checkCapability(
    522                             pkgSetting.signatures.mSigningDetails,
    523                             PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
    524                 return true;
    525             } else {
    526                 logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
    527                         pkgSetting.name);
    528                 return false;
    529             }
    530         } catch (PackageParserException e) {
    531             logCriticalInfo(Log.ERROR, "Failed to collect cert for " + pkgSetting.name + ": " +
    532                     e.getMessage());
    533             return false;
    534         }
    535     }
    536 
    537     /** Returns true if APK Verity is enabled. */
    538     static boolean isApkVerityEnabled() {
    539         return SystemProperties.getInt("ro.apk_verity.mode", 0) != 0;
    540     }
    541 
    542     /** Returns true to force apk verification if the updated package (in /data) is a priv app. */
    543     static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
    544         return disabledPs != null && disabledPs.isPrivileged() && isApkVerityEnabled();
    545     }
    546 
    547     /**
    548      * Verifies that signatures match.
    549      * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}.
    550      * @throws PackageManagerException if the signatures did not match.
    551      */
    552     public static boolean verifySignatures(PackageSetting pkgSetting,
    553             PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
    554             boolean compareCompat, boolean compareRecover)
    555             throws PackageManagerException {
    556         final String packageName = pkgSetting.name;
    557         boolean compatMatch = false;
    558         if (pkgSetting.signatures.mSigningDetails.signatures != null) {
    559 
    560             // Already existing package. Make sure signatures match
    561             boolean match = parsedSignatures.checkCapability(
    562                     pkgSetting.signatures.mSigningDetails,
    563                     PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
    564                             || pkgSetting.signatures.mSigningDetails.checkCapability(
    565                                     parsedSignatures,
    566                                     PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
    567             if (!match && compareCompat) {
    568                 match = matchSignaturesCompat(packageName, pkgSetting.signatures,
    569                         parsedSignatures);
    570                 compatMatch = match;
    571             }
    572             if (!match && compareRecover) {
    573                 match = matchSignaturesRecover(
    574                         packageName,
    575                         pkgSetting.signatures.mSigningDetails,
    576                         parsedSignatures,
    577                         PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
    578                                 || matchSignaturesRecover(
    579                                         packageName,
    580                                         parsedSignatures,
    581                                         pkgSetting.signatures.mSigningDetails,
    582                                         PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
    583             }
    584 
    585             if (!match && isApkVerificationForced(disabledPkgSetting)) {
    586                 match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);
    587             }
    588 
    589             if (!match) {
    590                 throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
    591                         "Package " + packageName +
    592                         " signatures do not match previously installed version; ignoring!");
    593             }
    594         }
    595         // Check for shared user signatures
    596         if (pkgSetting.sharedUser != null
    597                 && pkgSetting.sharedUser.signatures.mSigningDetails
    598                         != PackageParser.SigningDetails.UNKNOWN) {
    599 
    600             // Already existing package. Make sure signatures match.  In case of signing certificate
    601             // rotation, the packages with newer certs need to be ok with being sharedUserId with
    602             // the older ones.  We check to see if either the new package is signed by an older cert
    603             // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok
    604             // with being sharedUser with the existing signing cert.
    605             boolean match =
    606                     parsedSignatures.checkCapability(
    607                             pkgSetting.sharedUser.signatures.mSigningDetails,
    608                             PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
    609                     || pkgSetting.sharedUser.signatures.mSigningDetails.checkCapability(
    610                             parsedSignatures,
    611                             PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
    612             if (!match && compareCompat) {
    613                 match = matchSignaturesCompat(
    614                         packageName, pkgSetting.sharedUser.signatures, parsedSignatures);
    615             }
    616             if (!match && compareRecover) {
    617                 match =
    618                         matchSignaturesRecover(packageName,
    619                                 pkgSetting.sharedUser.signatures.mSigningDetails,
    620                                 parsedSignatures,
    621                                 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
    622                         || matchSignaturesRecover(packageName,
    623                                 parsedSignatures,
    624                                 pkgSetting.sharedUser.signatures.mSigningDetails,
    625                                 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
    626                 compatMatch |= match;
    627             }
    628             if (!match) {
    629                 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
    630                         "Package " + packageName
    631                         + " has no signatures that match those in shared user "
    632                         + pkgSetting.sharedUser.name + "; ignoring!");
    633             }
    634         }
    635         return compatMatch;
    636     }
    637 
    638     public static int decompressFile(File srcFile, File dstFile) throws ErrnoException {
    639         if (DEBUG_COMPRESSION) {
    640             Slog.i(TAG, "Decompress file"
    641                     + "; src: " + srcFile.getAbsolutePath()
    642                     + ", dst: " + dstFile.getAbsolutePath());
    643         }
    644         try (
    645                 InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
    646                 OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
    647         ) {
    648             FileUtils.copy(fileIn, fileOut);
    649             Os.chmod(dstFile.getAbsolutePath(), 0644);
    650             return PackageManager.INSTALL_SUCCEEDED;
    651         } catch (IOException e) {
    652             logCriticalInfo(Log.ERROR, "Failed to decompress file"
    653                     + "; src: " + srcFile.getAbsolutePath()
    654                     + ", dst: " + dstFile.getAbsolutePath());
    655         }
    656         return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
    657     }
    658 
    659     public static File[] getCompressedFiles(String codePath) {
    660         final File stubCodePath = new File(codePath);
    661         final String stubName = stubCodePath.getName();
    662 
    663         // The layout of a compressed package on a given partition is as follows :
    664         //
    665         // Compressed artifacts:
    666         //
    667         // /partition/ModuleName/foo.gz
    668         // /partation/ModuleName/bar.gz
    669         //
    670         // Stub artifact:
    671         //
    672         // /partition/ModuleName-Stub/ModuleName-Stub.apk
    673         //
    674         // In other words, stub is on the same partition as the compressed artifacts
    675         // and in a directory that's suffixed with "-Stub".
    676         int idx = stubName.lastIndexOf(STUB_SUFFIX);
    677         if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) {
    678             return null;
    679         }
    680 
    681         final File stubParentDir = stubCodePath.getParentFile();
    682         if (stubParentDir == null) {
    683             Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath);
    684             return null;
    685         }
    686 
    687         final File compressedPath = new File(stubParentDir, stubName.substring(0, idx));
    688         final File[] files = compressedPath.listFiles(new FilenameFilter() {
    689             @Override
    690             public boolean accept(File dir, String name) {
    691                 return name.toLowerCase().endsWith(COMPRESSED_EXTENSION);
    692             }
    693         });
    694 
    695         if (DEBUG_COMPRESSION && files != null && files.length > 0) {
    696             Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files));
    697         }
    698 
    699         return files;
    700     }
    701 
    702     public static boolean compressedFileExists(String codePath) {
    703         final File[] compressedFiles = getCompressedFiles(codePath);
    704         return compressedFiles != null && compressedFiles.length > 0;
    705     }
    706 }
    707