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 
getPackageNamesForIntent(Intent intent, int userId)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).
sortPackagesByUsageDate(List<PackageParser.Package> pkgs, PackageManagerService packageManagerService)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).
applyPackageFilter(Predicate<PackageParser.Package> filter, Collection<PackageParser.Package> result, Collection<PackageParser.Package> packages, @NonNull List<PackageParser.Package> sortTemp, PackageManagerService packageManagerService)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.
getPackagesForDexopt( Collection<PackageParser.Package> packages, PackageManagerService packageManagerService)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      */
isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo, long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis)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 
getSettingsProblemFile()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 
dumpCriticalInfo(ProtoOutputStream proto)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 
dumpCriticalInfo(PrintWriter pw, String msg)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 
logCriticalInfo(int priority, String msg)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 
enforceShellRestriction(String restriction, int callingUid, int userHandle)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      */
deriveAbiOverride(String abiOverride, PackageSetting settings)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      */
compareSignatures(Signature[] s1, Signature[] s2)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      */
matchSignaturesCompat(String packageName, PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures)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 
matchSignaturesRecover( String packageName, PackageParser.SigningDetails existingSignatures, PackageParser.SigningDetails parsedSignatures, @PackageParser.SigningDetails.CertCapabilities int flags)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      */
matchSignatureInSystem(PackageSetting pkgSetting, PackageSetting disabledPkgSetting)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. */
isApkVerityEnabled()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. */
isApkVerificationForced(@ullable PackageSetting disabledPs)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      */
verifySignatures(PackageSetting pkgSetting, PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures, boolean compareCompat, boolean compareRecover)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 
decompressFile(File srcFile, File dstFile)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 
getCompressedFiles(String codePath)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 
compressedFileExists(String codePath)702     public static boolean compressedFileExists(String codePath) {
703         final File[] compressedFiles = getCompressedFiles(codePath);
704         return compressedFiles != null && compressedFiles.length > 0;
705     }
706 }
707