• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
22 import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID;
23 import static android.system.OsConstants.O_CREAT;
24 import static android.system.OsConstants.O_RDWR;
25 
26 import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
27 import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
28 import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
29 import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING;
30 import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED;
31 import static com.android.server.pm.PackageManagerService.RANDOM_CODEPATH_PREFIX;
32 import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX;
33 import static com.android.server.pm.PackageManagerService.STUB_SUFFIX;
34 import static com.android.server.pm.PackageManagerService.TAG;
35 
36 import android.annotation.NonNull;
37 import android.annotation.Nullable;
38 import android.annotation.UserIdInt;
39 import android.app.ActivityManager;
40 import android.compat.annotation.ChangeId;
41 import android.compat.annotation.Disabled;
42 import android.content.Context;
43 import android.content.Intent;
44 import android.content.IntentFilter;
45 import android.content.pm.ActivityInfo;
46 import android.content.pm.ApplicationInfo;
47 import android.content.pm.ComponentInfo;
48 import android.content.pm.PackageInfoLite;
49 import android.content.pm.PackageInstaller;
50 import android.content.pm.PackageManager;
51 import android.content.pm.PackagePartitions;
52 import android.content.pm.ResolveInfo;
53 import android.content.pm.ServiceInfo;
54 import android.content.pm.Signature;
55 import android.content.pm.SigningDetails;
56 import android.content.pm.parsing.ApkLiteParseUtils;
57 import android.content.pm.parsing.PackageLite;
58 import android.content.pm.parsing.result.ParseResult;
59 import android.content.pm.parsing.result.ParseTypeImpl;
60 import android.os.Binder;
61 import android.os.Build;
62 import android.os.Debug;
63 import android.os.Environment;
64 import android.os.FileUtils;
65 import android.os.Process;
66 import android.os.SystemProperties;
67 import android.os.incremental.IncrementalManager;
68 import android.os.incremental.IncrementalStorage;
69 import android.os.incremental.V4Signature;
70 import android.os.incremental.V4Signature.HashingInfo;
71 import android.os.storage.DiskInfo;
72 import android.os.storage.VolumeInfo;
73 import android.service.pm.PackageServiceDumpProto;
74 import android.stats.storage.StorageEnums;
75 import android.system.ErrnoException;
76 import android.system.Os;
77 import android.util.ArraySet;
78 import android.util.AtomicFile;
79 import android.util.Base64;
80 import android.util.Log;
81 import android.util.LogPrinter;
82 import android.util.Printer;
83 import android.util.Slog;
84 import android.util.proto.ProtoOutputStream;
85 
86 import com.android.internal.content.InstallLocationUtils;
87 import com.android.internal.content.NativeLibraryHelper;
88 import com.android.internal.util.ArrayUtils;
89 import com.android.internal.util.FastPrintWriter;
90 import com.android.internal.util.HexDump;
91 import com.android.server.EventLogTags;
92 import com.android.server.IntentResolver;
93 import com.android.server.Watchdog;
94 import com.android.server.compat.PlatformCompat;
95 import com.android.server.pm.dex.PackageDexUsage;
96 import com.android.server.pm.parsing.pkg.AndroidPackage;
97 import com.android.server.pm.pkg.PackageStateInternal;
98 import com.android.server.pm.pkg.component.ParsedMainComponent;
99 import com.android.server.pm.resolution.ComponentResolverApi;
100 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
101 
102 import dalvik.system.VMRuntime;
103 
104 import libcore.io.IoUtils;
105 
106 import java.io.BufferedReader;
107 import java.io.File;
108 import java.io.FileDescriptor;
109 import java.io.FileInputStream;
110 import java.io.FileOutputStream;
111 import java.io.FileReader;
112 import java.io.FilenameFilter;
113 import java.io.IOException;
114 import java.io.InputStream;
115 import java.io.PrintWriter;
116 import java.nio.file.Path;
117 import java.security.SecureRandom;
118 import java.security.cert.CertificateEncodingException;
119 import java.security.cert.CertificateException;
120 import java.text.SimpleDateFormat;
121 import java.util.Arrays;
122 import java.util.Date;
123 import java.util.List;
124 import java.util.Objects;
125 import java.util.function.Function;
126 import java.util.function.Predicate;
127 import java.util.zip.GZIPInputStream;
128 
129 /**
130  * Class containing helper methods for the PackageManagerService.
131  *
132  * {@hide}
133  */
134 public class PackageManagerServiceUtils {
135     private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 3 * 1000 * 1000; // 3MB
136 
137     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
138 
139     public final static Predicate<PackageStateInternal> REMOVE_IF_NULL_PKG =
140             pkgSetting -> pkgSetting.getPkg() == null;
141 
142     // This is a horrible hack to workaround b/240373119, specifically for fixing the T branch.
143     // A proper fix should be implemented in master instead.
144     public static final ThreadLocal<Boolean> DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS =
145             ThreadLocal.withInitial(() -> false);
146 
147     /**
148      * Components of apps targeting Android T and above will stop receiving intents from
149      * external callers that do not match its declared intent filters.
150      *
151      * When an app registers an exported component in its manifest and adds an <intent-filter>,
152      * the component can be started by any intent - even those that do not match the intent filter.
153      * This has proven to be something that many developers find counterintuitive.
154      * Without checking the intent when the component is started, in some circumstances this can
155      * allow 3P apps to trigger internal-only functionality.
156      */
157     @ChangeId
158     @Disabled  /* Revert enforcement: b/274147456 */
159     private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188;
160 
161     /**
162      * The initial enabled state of the cache before other checks are done.
163      */
164     private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
165 
166     /**
167      * Whether to skip all other checks and force the cache to be enabled.
168      *
169      * Setting this to true will cause the cache to be named "debug" to avoid eviction from
170      * build fingerprint changes.
171      */
172     private static final boolean FORCE_PACKAGE_PARSED_CACHE_ENABLED = false;
173 
174     /**
175      * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
176      * Package is considered active, if:
177      * 1) It was active in foreground.
178      * 2) It was active in background and also used by other apps.
179      *
180      * If it doesn't have sufficient information about the package, it return <code>false</code>.
181      */
isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo, long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis)182     public static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis,
183             long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo,
184             long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) {
185 
186         if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) {
187             return false;
188         }
189 
190         // If the app was active in foreground during the threshold period.
191         boolean isActiveInForeground = (currentTimeInMillis
192                 - latestForegroundPackageUseTimeInMillis)
193                 < thresholdTimeinMillis;
194 
195         if (isActiveInForeground) {
196             return false;
197         }
198 
199         // If the app was active in background during the threshold period and was used
200         // by other packages.
201         boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
202                 - latestPackageUseTimeInMillis)
203                 < thresholdTimeinMillis)
204                 && packageUseInfo.isAnyCodePathUsedByOtherApps();
205 
206         return !isActiveInBackgroundAndUsedByOtherPackages;
207     }
208 
209     /**
210      * Returns the canonicalized path of {@code path} as per {@code realpath(3)}
211      * semantics.
212      */
213     public static String realpath(File path) throws IOException {
214         try {
215             return Os.realpath(path.getAbsolutePath());
216         } catch (ErrnoException ee) {
217             throw ee.rethrowAsIOException();
218         }
219     }
220 
221     /**
222      * Verifies that the given string {@code isa} is a valid supported isa on
223      * the running device.
224      */
225     public static boolean checkISA(String isa) {
226         for (String abi : Build.SUPPORTED_ABIS) {
227             if (VMRuntime.getInstructionSet(abi).equals(isa)) {
228                 return true;
229             }
230         }
231         return false;
232     }
233 
234     public static long getLastModifiedTime(AndroidPackage pkg) {
235         final File srcFile = new File(pkg.getPath());
236         if (!srcFile.isDirectory()) {
237             return srcFile.lastModified();
238         }
239         final File baseFile = new File(pkg.getBaseApkPath());
240         long maxModifiedTime = baseFile.lastModified();
241         for (int i = pkg.getSplitCodePaths().length - 1; i >=0; --i) {
242             final File splitFile = new File(pkg.getSplitCodePaths()[i]);
243             maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
244         }
245         return maxModifiedTime;
246     }
247 
248     private static File getSettingsProblemFile() {
249         File dataDir = Environment.getDataDirectory();
250         File systemDir = new File(dataDir, "system");
251         File fname = new File(systemDir, "uiderrors.txt");
252         return fname;
253     }
254 
255     public static void dumpCriticalInfo(ProtoOutputStream proto) {
256         final File file = getSettingsProblemFile();
257         final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE;
258         try (BufferedReader in = new BufferedReader(new FileReader(file))) {
259             if (skipSize > 0) {
260                 in.skip(skipSize);
261             }
262             String line = null;
263             while ((line = in.readLine()) != null) {
264                 if (line.contains("ignored: updated version")) continue;
265                 proto.write(PackageServiceDumpProto.MESSAGES, line);
266             }
267         } catch (IOException ignored) {
268         }
269     }
270 
dumpCriticalInfo(PrintWriter pw, String msg)271     public static void dumpCriticalInfo(PrintWriter pw, String msg) {
272         final File file = getSettingsProblemFile();
273         final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE;
274         try (BufferedReader in = new BufferedReader(new FileReader(file))) {
275             if (skipSize > 0) {
276                 in.skip(skipSize);
277             }
278             String line = null;
279             while ((line = in.readLine()) != null) {
280                 if (line.contains("ignored: updated version")) continue;
281                 if (msg != null) {
282                     pw.print(msg);
283                 }
284                 pw.println(line);
285             }
286         } catch (IOException ignored) {
287         }
288     }
289 
logCriticalInfo(int priority, String msg)290     public static void logCriticalInfo(int priority, String msg) {
291         Slog.println(priority, TAG, msg);
292         EventLogTags.writePmCriticalInfo(msg);
293         try {
294             File fname = getSettingsProblemFile();
295             FileOutputStream out = new FileOutputStream(fname, true);
296             PrintWriter pw = new FastPrintWriter(out);
297             SimpleDateFormat formatter = new SimpleDateFormat();
298             String dateString = formatter.format(new Date(System.currentTimeMillis()));
299             pw.println(dateString + ": " + msg);
300             pw.close();
301             FileUtils.setPermissions(
302                     fname.toString(),
303                     FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH,
304                     -1, -1);
305         } catch (java.io.IOException e) {
306         }
307     }
308 
309     /** Enforces that if the caller is shell, it does not have the provided user restriction. */
enforceShellRestriction( UserManagerInternal userManager, String restriction, int callingUid, int userHandle)310     public static void enforceShellRestriction(
311             UserManagerInternal userManager, String restriction, int callingUid, int userHandle) {
312         if (callingUid == Process.SHELL_UID) {
313             if (userHandle >= 0
314                     && userManager.hasUserRestriction(
315                             restriction, userHandle)) {
316                 throw new SecurityException("Shell does not have permission to access user "
317                         + userHandle);
318             } else if (userHandle < 0) {
319                 Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user "
320                         + userHandle + "\n\t" + Debug.getCallers(3));
321             }
322         }
323     }
324 
325     /**
326      * Enforces that the caller must be either the system process or the phone process.
327      * If not, throws a {@link SecurityException}.
328      */
enforceSystemOrPhoneCaller(String methodName, int callingUid)329     public static void enforceSystemOrPhoneCaller(String methodName, int callingUid) {
330         if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) {
331             throw new SecurityException(
332                     "Cannot call " + methodName + " from UID " + callingUid);
333         }
334     }
335 
336     /**
337      * Derive the value of the {@code cpuAbiOverride} based on the provided
338      * value.
339      */
deriveAbiOverride(String abiOverride)340     public static String deriveAbiOverride(String abiOverride) {
341         if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
342             return null;
343         }
344         return abiOverride;
345     }
346 
347     /**
348      * Compares two sets of signatures. Returns:
349      * <br />
350      * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null,
351      * <br />
352      * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null,
353      * <br />
354      * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null,
355      * <br />
356      * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical,
357      * <br />
358      * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
359      */
compareSignatures(Signature[] s1, Signature[] s2)360     public static int compareSignatures(Signature[] s1, Signature[] s2) {
361         if (s1 == null) {
362             return s2 == null
363                     ? PackageManager.SIGNATURE_NEITHER_SIGNED
364                     : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
365         }
366 
367         if (s2 == null) {
368             return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
369         }
370 
371         if (s1.length != s2.length) {
372             return PackageManager.SIGNATURE_NO_MATCH;
373         }
374 
375         // Since both signature sets are of size 1, we can compare without HashSets.
376         if (s1.length == 1) {
377             return s1[0].equals(s2[0]) ?
378                     PackageManager.SIGNATURE_MATCH :
379                     PackageManager.SIGNATURE_NO_MATCH;
380         }
381 
382         ArraySet<Signature> set1 = new ArraySet<Signature>();
383         for (Signature sig : s1) {
384             set1.add(sig);
385         }
386         ArraySet<Signature> set2 = new ArraySet<Signature>();
387         for (Signature sig : s2) {
388             set2.add(sig);
389         }
390         // Make sure s2 contains all signatures in s1.
391         if (set1.equals(set2)) {
392             return PackageManager.SIGNATURE_MATCH;
393         }
394         return PackageManager.SIGNATURE_NO_MATCH;
395     }
396 
397     /**
398      * Returns true if the signature set of the package is identical to the specified signature
399      * set or if the signing details of the package are unknown.
400      */
comparePackageSignatures(PackageSetting pkgSetting, Signature[] signatures)401     public static boolean comparePackageSignatures(PackageSetting pkgSetting,
402             Signature[] signatures) {
403         final SigningDetails signingDetails = pkgSetting.getSigningDetails();
404         return signingDetails == SigningDetails.UNKNOWN
405                 || compareSignatures(signingDetails.getSignatures(), signatures)
406                 == PackageManager.SIGNATURE_MATCH;
407     }
408 
409     /**
410      * Used for backward compatibility to make sure any packages with
411      * certificate chains get upgraded to the new style. {@code existingSigs}
412      * will be in the old format (since they were stored on disk from before the
413      * system upgrade) and {@code scannedSigs} will be in the newer format.
414      */
matchSignaturesCompat(String packageName, PackageSignatures packageSignatures, SigningDetails parsedSignatures)415     private static boolean matchSignaturesCompat(String packageName,
416             PackageSignatures packageSignatures, SigningDetails parsedSignatures) {
417         ArraySet<Signature> existingSet = new ArraySet<Signature>();
418         for (Signature sig : packageSignatures.mSigningDetails.getSignatures()) {
419             existingSet.add(sig);
420         }
421         ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
422         for (Signature sig : parsedSignatures.getSignatures()) {
423             try {
424                 Signature[] chainSignatures = sig.getChainSignatures();
425                 for (Signature chainSig : chainSignatures) {
426                     scannedCompatSet.add(chainSig);
427                 }
428             } catch (CertificateEncodingException e) {
429                 scannedCompatSet.add(sig);
430             }
431         }
432         // make sure the expanded scanned set contains all signatures in the existing one
433         if (scannedCompatSet.equals(existingSet)) {
434             // migrate the old signatures to the new scheme
435             packageSignatures.mSigningDetails = parsedSignatures;
436             return true;
437         } else if (parsedSignatures.hasPastSigningCertificates()) {
438 
439             // well this sucks: the parsed package has probably rotated signing certificates, but
440             // we don't have enough information to determine if the new signing certificate was
441             // blessed by the old one
442             logCriticalInfo(Log.INFO, "Existing package " + packageName + " has flattened signing "
443                     + "certificate chain. Unable to install newer version with rotated signing "
444                     + "certificate.");
445         }
446         return false;
447     }
448 
matchSignaturesRecover( String packageName, SigningDetails existingSignatures, SigningDetails parsedSignatures, @SigningDetails.CertCapabilities int flags)449     private static boolean matchSignaturesRecover(
450             String packageName,
451             SigningDetails existingSignatures,
452             SigningDetails parsedSignatures,
453             @SigningDetails.CertCapabilities int flags) {
454         String msg = null;
455         try {
456             if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) {
457                 logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for "
458                         + packageName);
459                     return true;
460             }
461         } catch (CertificateException e) {
462             msg = e.getMessage();
463         }
464         logCriticalInfo(Log.INFO,
465                 "Failed to recover certificates for " + packageName + ": " + msg);
466         return false;
467     }
468 
469     /**
470      * Make sure the updated priv app is signed with the same key as the original APK file on the
471      * /system partition.
472      *
473      * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data
474      * and is not tamperproof.
475      */
matchSignatureInSystem(@onNull String packageName, @NonNull SigningDetails signingDetails, PackageSetting disabledPkgSetting)476     private static boolean matchSignatureInSystem(@NonNull String packageName,
477             @NonNull SigningDetails signingDetails, PackageSetting disabledPkgSetting) {
478         if (signingDetails.checkCapability(
479                 disabledPkgSetting.getSigningDetails(),
480                 SigningDetails.CertCapabilities.INSTALLED_DATA)
481                 || disabledPkgSetting.getSigningDetails().checkCapability(
482                 signingDetails,
483                 SigningDetails.CertCapabilities.ROLLBACK)) {
484             return true;
485         } else {
486             logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
487                     packageName);
488             return false;
489         }
490     }
491 
492     /** Default is to not use fs-verity since it depends on kernel support. */
493     private static final int FSVERITY_DISABLED = 0;
494 
495     /** Standard fs-verity. */
496     private static final int FSVERITY_ENABLED = 2;
497 
498     /** Returns true if standard APK Verity is enabled. */
isApkVerityEnabled()499     static boolean isApkVerityEnabled() {
500         return Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.R
501                 || SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED)
502                         == FSVERITY_ENABLED;
503     }
504 
505     /** Returns true to force apk verification if the package is considered privileged. */
isApkVerificationForced(@ullable PackageSetting ps)506     static boolean isApkVerificationForced(@Nullable PackageSetting ps) {
507         // TODO(b/154310064): re-enable.
508         return false;
509     }
510 
511     /**
512      * Verifies that signatures match.
513      * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}.
514      * @throws PackageManagerException if the signatures did not match.
515      */
verifySignatures(PackageSetting pkgSetting, @Nullable SharedUserSetting sharedUserSetting, PackageSetting disabledPkgSetting, SigningDetails parsedSignatures, boolean compareCompat, boolean compareRecover, boolean isRollback)516     public static boolean verifySignatures(PackageSetting pkgSetting,
517             @Nullable SharedUserSetting sharedUserSetting,
518             PackageSetting disabledPkgSetting, SigningDetails parsedSignatures,
519             boolean compareCompat, boolean compareRecover, boolean isRollback)
520             throws PackageManagerException {
521         final String packageName = pkgSetting.getPackageName();
522         boolean compatMatch = false;
523         if (pkgSetting.getSigningDetails().getSignatures() != null) {
524             // Already existing package. Make sure signatures match
525             boolean match = parsedSignatures.checkCapability(
526                     pkgSetting.getSigningDetails(),
527                     SigningDetails.CertCapabilities.INSTALLED_DATA)
528                             || pkgSetting.getSigningDetails().checkCapability(
529                                     parsedSignatures,
530                                     SigningDetails.CertCapabilities.ROLLBACK);
531             if (!match && compareCompat) {
532                 match = matchSignaturesCompat(packageName, pkgSetting.getSignatures(),
533                         parsedSignatures);
534                 compatMatch = match;
535             }
536             if (!match && compareRecover) {
537                 match = matchSignaturesRecover(
538                         packageName,
539                         pkgSetting.getSigningDetails(),
540                         parsedSignatures,
541                         SigningDetails.CertCapabilities.INSTALLED_DATA)
542                                 || matchSignaturesRecover(
543                                         packageName,
544                                         parsedSignatures,
545                                         pkgSetting.getSigningDetails(),
546                                         SigningDetails.CertCapabilities.ROLLBACK);
547             }
548 
549             if (!match && isApkVerificationForced(disabledPkgSetting)) {
550                 match = matchSignatureInSystem(packageName, pkgSetting.getSigningDetails(),
551                         disabledPkgSetting);
552             }
553 
554             if (!match && isRollback) {
555                 // Since a rollback can only be initiated for an APK previously installed on the
556                 // device allow rolling back to a previous signing key even if the rollback
557                 // capability has not been granted.
558                 match = pkgSetting.getSigningDetails().hasAncestorOrSelf(parsedSignatures);
559             }
560 
561             if (!match) {
562                 throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
563                         "Existing package " + packageName
564                                 + " signatures do not match newer version; ignoring!");
565             }
566         }
567         // Check for shared user signatures
568         if (sharedUserSetting != null
569                 && sharedUserSetting.getSigningDetails() != SigningDetails.UNKNOWN) {
570             // Already existing package. Make sure signatures match.  In case of signing certificate
571             // rotation, the packages with newer certs need to be ok with being sharedUserId with
572             // the older ones.  We check to see if either the new package is signed by an older cert
573             // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok
574             // with being sharedUser with the existing signing cert.
575             boolean match = canJoinSharedUserId(parsedSignatures,
576                     sharedUserSetting.getSigningDetails());
577             // Special case: if the sharedUserId capability check failed it could be due to this
578             // being the only package in the sharedUserId so far and the lineage being updated to
579             // deny the sharedUserId capability of the previous key in the lineage.
580             final ArraySet<PackageStateInternal> susPackageStates =
581                     (ArraySet<PackageStateInternal>) sharedUserSetting.getPackageStates();
582             if (!match && susPackageStates.size() == 1
583                     && susPackageStates.valueAt(0).getPackageName().equals(packageName)) {
584                 match = true;
585             }
586             if (!match && compareCompat) {
587                 match = matchSignaturesCompat(
588                         packageName, sharedUserSetting.signatures, parsedSignatures);
589             }
590             if (!match && compareRecover) {
591                 match =
592                         matchSignaturesRecover(packageName,
593                                 sharedUserSetting.signatures.mSigningDetails,
594                                 parsedSignatures,
595                                 SigningDetails.CertCapabilities.SHARED_USER_ID)
596                         || matchSignaturesRecover(packageName,
597                                 parsedSignatures,
598                                 sharedUserSetting.signatures.mSigningDetails,
599                                 SigningDetails.CertCapabilities.SHARED_USER_ID);
600                 compatMatch |= match;
601             }
602             if (!match) {
603                 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
604                         "Package " + packageName
605                         + " has no signatures that match those in shared user "
606                         + sharedUserSetting.name + "; ignoring!");
607             }
608             // It is possible that this package contains a lineage that blocks sharedUserId access
609             // to an already installed package in the sharedUserId signed with a previous key.
610             // Iterate over all of the packages in the sharedUserId and ensure any that are signed
611             // with a key in this package's lineage have the SHARED_USER_ID capability granted.
612             if (parsedSignatures.hasPastSigningCertificates()) {
613                 for (int i = 0; i < susPackageStates.size(); i++) {
614                     PackageStateInternal shUidPkgSetting = susPackageStates.valueAt(i);
615                     // if the current package in the sharedUserId is the package being updated then
616                     // skip this check as the update may revoke the sharedUserId capability from
617                     // the key with which this app was previously signed.
618                     if (packageName.equals(shUidPkgSetting.getPackageName())) {
619                         continue;
620                     }
621                     SigningDetails shUidSigningDetails =
622                             shUidPkgSetting.getSigningDetails();
623                     // The capability check only needs to be performed against the package if it is
624                     // signed with a key that is in the lineage of the package being installed.
625                     if (parsedSignatures.hasAncestor(shUidSigningDetails)) {
626                         if (!parsedSignatures.checkCapability(shUidSigningDetails,
627                                 SigningDetails.CertCapabilities.SHARED_USER_ID)) {
628                             throw new PackageManagerException(
629                                     INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
630                                     "Package " + packageName
631                                             + " revoked the sharedUserId capability from the"
632                                             + " signing key used to sign "
633                                             + shUidPkgSetting.getPackageName());
634                         }
635                     }
636                 }
637             }
638             // If the lineage of this package diverges from the lineage of the sharedUserId then
639             // do not allow the installation to proceed.
640             if (!parsedSignatures.hasCommonAncestor(
641                     sharedUserSetting.signatures.mSigningDetails)) {
642                 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
643                         "Package " + packageName + " has a signing lineage "
644                                 + "that diverges from the lineage of the sharedUserId");
645             }
646         }
647         return compatMatch;
648     }
649 
650     /**
651      * Returns whether the package with {@code packageSigningDetails} can join the sharedUserId
652      * with {@code sharedUserSigningDetails}.
653      * <p>
654      * A sharedUserId maintains a shared {@link SigningDetails} containing the full lineage and
655      * capabilities for each package in the sharedUserId. A package can join the sharedUserId if
656      * its current signer is the same as the shared signer, or if the current signer of either
657      * is in the signing lineage of the other with the {@link
658      * SigningDetails.CertCapabilities#SHARED_USER_ID} capability granted to that previous signer
659      * in the lineage.
660      *
661      * @param packageSigningDetails the {@code SigningDetails} of the package seeking to join the
662      *                             sharedUserId
663      * @param sharedUserSigningDetails the {@code SigningDetails} of the sharedUserId
664      * @return true if the package seeking to join the sharedUserId meets the requirements
665      */
canJoinSharedUserId(@onNull SigningDetails packageSigningDetails, @NonNull SigningDetails sharedUserSigningDetails)666     public static boolean canJoinSharedUserId(@NonNull SigningDetails packageSigningDetails,
667             @NonNull SigningDetails sharedUserSigningDetails) {
668         return packageSigningDetails.checkCapability(sharedUserSigningDetails, SHARED_USER_ID)
669                 || sharedUserSigningDetails.checkCapability(packageSigningDetails, SHARED_USER_ID);
670     }
671 
672     /**
673      * Extract native libraries to a target path
674      */
extractNativeBinaries(File dstCodePath, String packageName)675     public static int extractNativeBinaries(File dstCodePath, String packageName) {
676         final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
677         NativeLibraryHelper.Handle handle = null;
678         try {
679             handle = NativeLibraryHelper.Handle.create(dstCodePath);
680             return NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
681                     null /*abiOverride*/, false /*isIncremental*/);
682         } catch (IOException e) {
683             logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
684                     + "; pkg: " + packageName);
685             return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
686         } finally {
687             IoUtils.closeQuietly(handle);
688         }
689     }
690 
691     /**
692      * Remove native libraries of a given package
693      */
removeNativeBinariesLI(PackageSetting ps)694     public static void removeNativeBinariesLI(PackageSetting ps) {
695         if (ps != null) {
696             NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath());
697         }
698     }
699 
700     /**
701      * Wait for native library extraction to be done in IncrementalService
702      */
waitForNativeBinariesExtractionForIncremental( ArraySet<IncrementalStorage> incrementalStorages)703     public static void waitForNativeBinariesExtractionForIncremental(
704             ArraySet<IncrementalStorage> incrementalStorages) {
705         if (incrementalStorages.isEmpty()) {
706             return;
707         }
708         try {
709             // Native library extraction may take very long time: each page could potentially
710             // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds
711             // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't
712             // make much sense as blocking here doesn't lock up the framework, but only blocks
713             // the installation session and the following ones.
714             Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract");
715             for (int i = 0; i < incrementalStorages.size(); ++i) {
716                 IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i);
717                 storage.waitForNativeBinariesExtraction();
718             }
719         } finally {
720             Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract");
721         }
722     }
723 
724     /**
725      * Decompress files stored in codePath to dstCodePath for a certain package.
726      */
decompressFiles(String codePath, File dstCodePath, String packageName)727     public static int decompressFiles(String codePath, File dstCodePath, String packageName) {
728         final File[] compressedFiles = getCompressedFiles(codePath);
729         int ret = PackageManager.INSTALL_SUCCEEDED;
730         try {
731             makeDirRecursive(dstCodePath, 0755);
732             for (File srcFile : compressedFiles) {
733                 final String srcFileName = srcFile.getName();
734                 final String dstFileName = srcFileName.substring(
735                         0, srcFileName.length() - COMPRESSED_EXTENSION.length());
736                 final File dstFile = new File(dstCodePath, dstFileName);
737                 ret = decompressFile(srcFile, dstFile);
738                 if (ret != PackageManager.INSTALL_SUCCEEDED) {
739                     logCriticalInfo(Log.ERROR, "Failed to decompress"
740                             + "; pkg: " + packageName
741                             + ", file: " + dstFileName);
742                     break;
743                 }
744             }
745         } catch (ErrnoException e) {
746             logCriticalInfo(Log.ERROR, "Failed to decompress"
747                     + "; pkg: " + packageName
748                     + ", err: " + e.errno);
749         }
750         return ret;
751     }
752 
decompressFile(File srcFile, File dstFile)753     public static int decompressFile(File srcFile, File dstFile) throws ErrnoException {
754         if (DEBUG_COMPRESSION) {
755             Slog.i(TAG, "Decompress file"
756                     + "; src: " + srcFile.getAbsolutePath()
757                     + ", dst: " + dstFile.getAbsolutePath());
758         }
759         final AtomicFile atomicFile = new AtomicFile(dstFile);
760         FileOutputStream outputStream = null;
761         try (
762                 InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile))
763         ) {
764             outputStream = atomicFile.startWrite();
765             FileUtils.copy(fileIn, outputStream);
766             // Flush anything in buffer before chmod, because any writes after chmod will fail.
767             outputStream.flush();
768             Os.fchmod(outputStream.getFD(), 0644);
769             atomicFile.finishWrite(outputStream);
770             return PackageManager.INSTALL_SUCCEEDED;
771         } catch (IOException e) {
772             logCriticalInfo(Log.ERROR, "Failed to decompress file"
773                     + "; src: " + srcFile.getAbsolutePath()
774                     + ", dst: " + dstFile.getAbsolutePath());
775             atomicFile.failWrite(outputStream);
776         }
777         return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
778     }
779 
getCompressedFiles(String codePath)780     public static File[] getCompressedFiles(String codePath) {
781         final File stubCodePath = new File(codePath);
782         final String stubName = stubCodePath.getName();
783 
784         // The layout of a compressed package on a given partition is as follows :
785         //
786         // Compressed artifacts:
787         //
788         // /partition/ModuleName/foo.gz
789         // /partation/ModuleName/bar.gz
790         //
791         // Stub artifact:
792         //
793         // /partition/ModuleName-Stub/ModuleName-Stub.apk
794         //
795         // In other words, stub is on the same partition as the compressed artifacts
796         // and in a directory that's suffixed with "-Stub".
797         int idx = stubName.lastIndexOf(STUB_SUFFIX);
798         if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) {
799             return null;
800         }
801 
802         final File stubParentDir = stubCodePath.getParentFile();
803         if (stubParentDir == null) {
804             Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath);
805             return null;
806         }
807 
808         final File compressedPath = new File(stubParentDir, stubName.substring(0, idx));
809         final File[] files = compressedPath.listFiles(new FilenameFilter() {
810             @Override
811             public boolean accept(File dir, String name) {
812                 return name.toLowerCase().endsWith(COMPRESSED_EXTENSION);
813             }
814         });
815 
816         if (DEBUG_COMPRESSION && files != null && files.length > 0) {
817             Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files));
818         }
819 
820         return files;
821     }
822 
compressedFileExists(String codePath)823     public static boolean compressedFileExists(String codePath) {
824         final File[] compressedFiles = getCompressedFiles(codePath);
825         return compressedFiles != null && compressedFiles.length > 0;
826     }
827 
828     /**
829      * Parse given package and return minimal details.
830      */
getMinimalPackageInfo(Context context, PackageLite pkg, String packagePath, int flags, String abiOverride)831     public static PackageInfoLite getMinimalPackageInfo(Context context, PackageLite pkg,
832             String packagePath, int flags, String abiOverride) {
833         final PackageInfoLite ret = new PackageInfoLite();
834         if (packagePath == null || pkg == null) {
835             Slog.i(TAG, "Invalid package file " + packagePath);
836             ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK;
837             return ret;
838         }
839 
840         final File packageFile = new File(packagePath);
841         final long sizeBytes;
842         try {
843             sizeBytes = InstallLocationUtils.calculateInstalledSize(pkg, abiOverride);
844         } catch (IOException e) {
845             if (!packageFile.exists()) {
846                 ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI;
847             } else {
848                 ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK;
849             }
850 
851             return ret;
852         }
853 
854         final PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
855                 PackageInstaller.SessionParams.MODE_INVALID);
856         sessionParams.appPackageName = pkg.getPackageName();
857         sessionParams.installLocation = pkg.getInstallLocation();
858         sessionParams.sizeBytes = sizeBytes;
859         sessionParams.installFlags = flags;
860         final int recommendedInstallLocation;
861         try {
862             recommendedInstallLocation = InstallLocationUtils.resolveInstallLocation(context,
863                     sessionParams);
864         } catch (IOException e) {
865             throw new IllegalStateException(e);
866         }
867         ret.packageName = pkg.getPackageName();
868         ret.splitNames = pkg.getSplitNames();
869         ret.versionCode = pkg.getVersionCode();
870         ret.versionCodeMajor = pkg.getVersionCodeMajor();
871         ret.baseRevisionCode = pkg.getBaseRevisionCode();
872         ret.splitRevisionCodes = pkg.getSplitRevisionCodes();
873         ret.installLocation = pkg.getInstallLocation();
874         ret.verifiers = pkg.getVerifiers();
875         ret.recommendedInstallLocation = recommendedInstallLocation;
876         ret.multiArch = pkg.isMultiArch();
877         ret.debuggable = pkg.isDebuggable();
878         ret.isSdkLibrary = pkg.isIsSdkLibrary();
879 
880         return ret;
881     }
882 
883     /**
884      * Calculate estimated footprint of given package post-installation.
885      *
886      * @return -1 if there's some error calculating the size, otherwise installed size of the
887      *         package.
888      */
calculateInstalledSize(String packagePath, String abiOverride)889     public static long calculateInstalledSize(String packagePath, String abiOverride) {
890         final File packageFile = new File(packagePath);
891         try {
892             final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
893             final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
894                     input.reset(), packageFile, /* flags */ 0);
895             if (result.isError()) {
896                 throw new PackageManagerException(result.getErrorCode(),
897                         result.getErrorMessage(), result.getException());
898             }
899             return InstallLocationUtils.calculateInstalledSize(result.getResult(), abiOverride);
900         } catch (PackageManagerException | IOException e) {
901             Slog.w(TAG, "Failed to calculate installed size: " + e);
902             return -1;
903         }
904     }
905 
906     /**
907      * Checks whenever downgrade of an app is permitted.
908      *
909      * @param installFlags flags of the current install.
910      * @param isAppDebuggable if the currently installed version of the app is debuggable.
911      * @return {@code true} if downgrade is permitted according to the {@code installFlags} and
912      *         {@code applicationFlags}.
913      */
isDowngradePermitted(int installFlags, boolean isAppDebuggable)914     public static boolean isDowngradePermitted(int installFlags, boolean isAppDebuggable) {
915         // If installed, the package will get access to data left on the device by its
916         // predecessor. As a security measure, this is permitted only if this is not a
917         // version downgrade or if the predecessor package is marked as debuggable and
918         // a downgrade is explicitly requested.
919         //
920         // On debuggable platform builds, downgrades are permitted even for
921         // non-debuggable packages to make testing easier. Debuggable platform builds do
922         // not offer security guarantees and thus it's OK to disable some security
923         // mechanisms to make debugging/testing easier on those builds. However, even on
924         // debuggable builds downgrades of packages are permitted only if requested via
925         // installFlags. This is because we aim to keep the behavior of debuggable
926         // platform builds as close as possible to the behavior of non-debuggable
927         // platform builds.
928         //
929         // In case of user builds, downgrade is permitted only for the system server initiated
930         // sessions. This is enforced by INSTALL_ALLOW_DOWNGRADE flag parameter.
931         final boolean downgradeRequested =
932                 (installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0;
933         if (!downgradeRequested) {
934             return false;
935         }
936         final boolean isDebuggable = Build.IS_DEBUGGABLE || isAppDebuggable;
937         if (isDebuggable) {
938             return true;
939         }
940         return (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
941     }
942 
943     /**
944      * Copy package to the target location.
945      *
946      * @param packagePath absolute path to the package to be copied. Can be
947      *                    a single monolithic APK file or a cluster directory
948      *                    containing one or more APKs.
949      * @return returns status code according to those in
950      *         {@link PackageManager}
951      */
copyPackage(String packagePath, File targetDir)952     public static int copyPackage(String packagePath, File targetDir) {
953         if (packagePath == null) {
954             return PackageManager.INSTALL_FAILED_INVALID_URI;
955         }
956 
957         try {
958             final File packageFile = new File(packagePath);
959             final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
960             final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
961                     input.reset(), packageFile, /* flags */ 0);
962             if (result.isError()) {
963                 Slog.w(TAG, "Failed to parse package at " + packagePath);
964                 return result.getErrorCode();
965             }
966             final PackageLite pkg = result.getResult();
967             copyFile(pkg.getBaseApkPath(), targetDir, "base.apk");
968             if (!ArrayUtils.isEmpty(pkg.getSplitNames())) {
969                 for (int i = 0; i < pkg.getSplitNames().length; i++) {
970                     copyFile(pkg.getSplitApkPaths()[i], targetDir,
971                             "split_" + pkg.getSplitNames()[i] + ".apk");
972                 }
973             }
974             return PackageManager.INSTALL_SUCCEEDED;
975         } catch (IOException | ErrnoException e) {
976             Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
977             return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
978         }
979     }
980 
copyFile(String sourcePath, File targetDir, String targetName)981     private static void copyFile(String sourcePath, File targetDir, String targetName)
982             throws ErrnoException, IOException {
983         if (!FileUtils.isValidExtFilename(targetName)) {
984             throw new IllegalArgumentException("Invalid filename: " + targetName);
985         }
986         Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
987 
988         final File targetFile = new File(targetDir, targetName);
989         final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
990                 O_RDWR | O_CREAT, 0644);
991         Os.chmod(targetFile.getAbsolutePath(), 0644);
992         FileInputStream source = null;
993         try {
994             source = new FileInputStream(sourcePath);
995             FileUtils.copy(source.getFD(), targetFd);
996         } finally {
997             IoUtils.closeQuietly(source);
998         }
999     }
1000 
1001     /**
1002      * Recursively create target directory
1003      */
makeDirRecursive(File targetDir, int mode)1004     public static void makeDirRecursive(File targetDir, int mode) throws ErrnoException {
1005         final Path targetDirPath = targetDir.toPath();
1006         final int directoriesCount = targetDirPath.getNameCount();
1007         File currentDir;
1008         for (int i = 1; i <= directoriesCount; i++) {
1009             currentDir = targetDirPath.subpath(0, i).toFile();
1010             if (currentDir.exists()) {
1011                 continue;
1012             }
1013             Os.mkdir(currentDir.getAbsolutePath(), mode);
1014             Os.chmod(currentDir.getAbsolutePath(), mode);
1015         }
1016     }
1017 
1018     /**
1019      * Returns a string that's compatible with the verification root hash extra.
1020      * @see PackageManager#EXTRA_VERIFICATION_ROOT_HASH
1021      */
1022     @NonNull
buildVerificationRootHashString(@onNull String baseFilename, @Nullable String[] splitFilenameArray)1023     public static String buildVerificationRootHashString(@NonNull String baseFilename,
1024             @Nullable String[] splitFilenameArray) {
1025         final StringBuilder sb = new StringBuilder();
1026         final String baseFilePath =
1027                 baseFilename.substring(baseFilename.lastIndexOf(File.separator) + 1);
1028         sb.append(baseFilePath).append(":");
1029         final byte[] baseRootHash = getRootHash(baseFilename);
1030         if (baseRootHash == null) {
1031             sb.append("0");
1032         } else {
1033             sb.append(HexDump.toHexString(baseRootHash));
1034         }
1035         if (splitFilenameArray == null || splitFilenameArray.length == 0) {
1036             return sb.toString();
1037         }
1038 
1039         for (int i = splitFilenameArray.length - 1; i >= 0; i--) {
1040             final String splitFilename = splitFilenameArray[i];
1041             final String splitFilePath =
1042                     splitFilename.substring(splitFilename.lastIndexOf(File.separator) + 1);
1043             final byte[] splitRootHash = getRootHash(splitFilename);
1044             sb.append(";").append(splitFilePath).append(":");
1045             if (splitRootHash == null) {
1046                 sb.append("0");
1047             } else {
1048                 sb.append(HexDump.toHexString(splitRootHash));
1049             }
1050         }
1051         return sb.toString();
1052     }
1053 
1054     /**
1055      * Returns the root has for the given file.
1056      * <p>Otherwise, returns {@code null} if the root hash could not be found or calculated.
1057      * <p>NOTE: This currently only works on files stored on the incremental file system. The
1058      * eventual goal is that this hash [among others] can be retrieved for any file.
1059      */
1060     @Nullable
getRootHash(String filename)1061     private static byte[] getRootHash(String filename) {
1062         try {
1063             final byte[] baseFileSignature =
1064                     IncrementalManager.unsafeGetFileSignature(filename);
1065             if (baseFileSignature == null) {
1066                 throw new IOException("File signature not present");
1067             }
1068             final V4Signature signature =
1069                     V4Signature.readFrom(baseFileSignature);
1070             if (signature.hashingInfo == null) {
1071                 throw new IOException("Hashing info not present");
1072             }
1073             final HashingInfo hashInfo =
1074                     HashingInfo.fromByteArray(signature.hashingInfo);
1075             if (ArrayUtils.isEmpty(hashInfo.rawRootHash)) {
1076                 throw new IOException("Root has not present");
1077             }
1078             return ApkChecksums.verityHashForFile(new File(filename), hashInfo.rawRootHash);
1079         } catch (IOException ignore) {
1080             Slog.e(TAG, "ERROR: could not load root hash from incremental install");
1081         }
1082         return null;
1083     }
1084 
isSystemApp(PackageSetting ps)1085     public static boolean isSystemApp(PackageSetting ps) {
1086         return (ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0;
1087     }
1088 
isUpdatedSystemApp(PackageSetting ps)1089     public static boolean isUpdatedSystemApp(PackageSetting ps) {
1090         return (ps.getFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
1091     }
1092 
1093     // Static to give access to ComputeEngine
applyEnforceIntentFilterMatching( PlatformCompat compat, ComponentResolverApi resolver, List<ResolveInfo> resolveInfos, boolean isReceiver, Intent intent, String resolvedType, int filterCallingUid)1094     public static void applyEnforceIntentFilterMatching(
1095             PlatformCompat compat, ComponentResolverApi resolver,
1096             List<ResolveInfo> resolveInfos, boolean isReceiver,
1097             Intent intent, String resolvedType, int filterCallingUid) {
1098         if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return;
1099 
1100         final Printer logPrinter = DEBUG_INTENT_MATCHING
1101                 ? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM)
1102                 : null;
1103 
1104         for (int i = resolveInfos.size() - 1; i >= 0; --i) {
1105             final ComponentInfo info = resolveInfos.get(i).getComponentInfo();
1106 
1107             // Do not enforce filter matching when the caller is system, root, or the same app
1108             if (ActivityManager.checkComponentPermission(null, filterCallingUid,
1109                     info.applicationInfo.uid, false) == PackageManager.PERMISSION_GRANTED) {
1110                 continue;
1111             }
1112 
1113             // Only enforce filter matching if target app's target SDK >= T
1114             if (!compat.isChangeEnabledInternal(
1115                     ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, info.applicationInfo)) {
1116                 continue;
1117             }
1118 
1119             final ParsedMainComponent comp;
1120             if (info instanceof ActivityInfo) {
1121                 if (isReceiver) {
1122                     comp = resolver.getReceiver(info.getComponentName());
1123                 } else {
1124                     comp = resolver.getActivity(info.getComponentName());
1125                 }
1126             } else if (info instanceof ServiceInfo) {
1127                 comp = resolver.getService(info.getComponentName());
1128             } else {
1129                 // This shall never happen
1130                 throw new IllegalArgumentException("Unsupported component type");
1131             }
1132 
1133             if (comp == null || comp.getIntents().isEmpty()) {
1134                 continue;
1135             }
1136 
1137             boolean match = false;
1138             for (int j = 0, size = comp.getIntents().size(); j < size; ++j) {
1139                 IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter();
1140                 if (IntentResolver.intentMatchesFilter(intentFilter, intent, resolvedType)) {
1141                     match = true;
1142                     break;
1143                 }
1144             }
1145             if (!match) {
1146                 Slog.w(TAG, "Intent does not match component's intent filter: " + intent);
1147                 Slog.w(TAG, "Access blocked: " + comp.getComponentName());
1148                 if (DEBUG_INTENT_MATCHING) {
1149                     Slog.v(TAG, "Component intent filters:");
1150                     comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, "  "));
1151                     Slog.v(TAG, "-----------------------------");
1152                 }
1153                 resolveInfos.remove(i);
1154             }
1155         }
1156     }
1157 
1158 
1159     /**
1160      * Do NOT use for intent resolution filtering. That should be done with
1161      * {@link DomainVerificationManagerInternal#filterToApprovedApp(Intent, List, int, Function)}.
1162      *
1163      * @return if the package is approved at any non-zero level for the domain in the intent
1164      */
hasAnyDomainApproval( @onNull DomainVerificationManagerInternal manager, @NonNull PackageStateInternal pkgSetting, @NonNull Intent intent, @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags, @UserIdInt int userId)1165     public static boolean hasAnyDomainApproval(
1166             @NonNull DomainVerificationManagerInternal manager,
1167             @NonNull PackageStateInternal pkgSetting, @NonNull Intent intent,
1168             @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags, @UserIdInt int userId) {
1169         return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId)
1170                 > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
1171     }
1172 
1173     /**
1174      * Update given intent when being used to request {@link ResolveInfo}.
1175      */
updateIntentForResolve(Intent intent)1176     public static Intent updateIntentForResolve(Intent intent) {
1177         if (intent.getSelector() != null) {
1178             intent = intent.getSelector();
1179         }
1180         if (DEBUG_PREFERRED) {
1181             intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
1182         }
1183         return intent;
1184     }
1185 
arrayToString(int[] array)1186     public static String arrayToString(int[] array) {
1187         StringBuilder stringBuilder = new StringBuilder(128);
1188         stringBuilder.append('[');
1189         if (array != null) {
1190             for (int i = 0; i < array.length; i++) {
1191                 if (i > 0) stringBuilder.append(", ");
1192                 stringBuilder.append(array[i]);
1193             }
1194         }
1195         stringBuilder.append(']');
1196         return stringBuilder.toString();
1197     }
1198 
1199     /**
1200      * Given {@code targetDir}, returns {@code targetDir/~~[randomStrA]/[packageName]-[randomStrB].}
1201      * Makes sure that {@code targetDir/~~[randomStrA]} directory doesn't exist.
1202      * Notice that this method doesn't actually create any directory.
1203      *
1204      * @param targetDir Directory that is two-levels up from the result directory.
1205      * @param packageName Name of the package whose code files are to be installed under the result
1206      *                    directory.
1207      * @return File object for the directory that should hold the code files of {@code packageName}.
1208      */
getNextCodePath(File targetDir, String packageName)1209     public static File getNextCodePath(File targetDir, String packageName) {
1210         SecureRandom random = new SecureRandom();
1211         byte[] bytes = new byte[16];
1212         File firstLevelDir;
1213         do {
1214             random.nextBytes(bytes);
1215             String firstLevelDirName = RANDOM_DIR_PREFIX
1216                     + Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
1217             firstLevelDir = new File(targetDir, firstLevelDirName);
1218         } while (firstLevelDir.exists());
1219 
1220         random.nextBytes(bytes);
1221         String dirName = packageName + RANDOM_CODEPATH_PREFIX + Base64.encodeToString(bytes,
1222                 Base64.URL_SAFE | Base64.NO_WRAP);
1223         final File result = new File(firstLevelDir, dirName);
1224         if (DEBUG && !Objects.equals(tryParsePackageName(result.getName()), packageName)) {
1225             throw new RuntimeException(
1226                     "codepath is off: " + result.getName() + " (" + packageName + ")");
1227         }
1228         return result;
1229     }
1230 
tryParsePackageName(@onNull String codePath)1231     static String tryParsePackageName(@NonNull String codePath) throws IllegalArgumentException {
1232         int packageNameEnds = codePath.indexOf(RANDOM_CODEPATH_PREFIX);
1233         if (packageNameEnds == -1) {
1234             throw new IllegalArgumentException("Not a valid package folder name");
1235         }
1236         return codePath.substring(0, packageNameEnds);
1237     }
1238 
1239     /**
1240      * Gets the type of the external storage a package is installed on.
1241      * @param packageVolume The storage volume of the package.
1242      * @param packageIsExternal true if the package is currently installed on
1243      * external/removable/unprotected storage.
1244      * @return {@link StorageEnums#UNKNOWN} if the package is not stored externally or the
1245      * corresponding {@link StorageEnums} storage type value if it is.
1246      * corresponding {@link StorageEnums} storage type value if it is.
1247      */
getPackageExternalStorageType(VolumeInfo packageVolume, boolean packageIsExternal)1248     public static int getPackageExternalStorageType(VolumeInfo packageVolume,
1249             boolean packageIsExternal) {
1250         if (packageVolume != null) {
1251             DiskInfo disk = packageVolume.getDisk();
1252             if (disk != null) {
1253                 if (disk.isSd()) {
1254                     return StorageEnums.SD_CARD;
1255                 }
1256                 if (disk.isUsb()) {
1257                     return StorageEnums.USB;
1258                 }
1259                 if (packageIsExternal) {
1260                     return StorageEnums.OTHER;
1261                 }
1262             }
1263         }
1264         return StorageEnums.UNKNOWN;
1265     }
1266 
1267     /**
1268      * Enforces that only the system UID or root's UID or shell's UID can call
1269      * a method exposed via Binder.
1270      *
1271      * @param message used as message if SecurityException is thrown
1272      * @throws SecurityException if the caller is not system or shell
1273      */
enforceSystemOrRootOrShell(String message)1274     public static void enforceSystemOrRootOrShell(String message) {
1275         if (!isSystemOrRootOrShell()) {
1276             throw new SecurityException(message);
1277         }
1278     }
1279 
1280     /**
1281      * Check if the Binder caller is system UID, root's UID, or shell's UID.
1282      */
isSystemOrRootOrShell()1283     public static boolean isSystemOrRootOrShell() {
1284         final int uid = Binder.getCallingUid();
1285         return uid == Process.SYSTEM_UID || uid == Process.ROOT_UID || uid == Process.SHELL_UID;
1286     }
1287 
1288     /**
1289      * Check if the Binder caller is system UID or root's UID.
1290      */
isSystemOrRoot()1291     public static boolean isSystemOrRoot() {
1292         final int uid = Binder.getCallingUid();
1293         return uid == Process.SYSTEM_UID || uid == Process.ROOT_UID;
1294     }
1295 
1296     /**
1297      * Enforces that only the system UID or root's UID can call a method exposed
1298      * via Binder.
1299      *
1300      * @param message used as message if SecurityException is thrown
1301      * @throws SecurityException if the caller is not system or root
1302      */
enforceSystemOrRoot(String message)1303     public static void enforceSystemOrRoot(String message) {
1304         if (!isSystemOrRoot()) {
1305             throw new SecurityException(message);
1306         }
1307     }
1308 
preparePackageParserCache(boolean forEngBuild, boolean isUserDebugBuild, String incrementalVersion)1309     public static @Nullable File preparePackageParserCache(boolean forEngBuild,
1310             boolean isUserDebugBuild, String incrementalVersion) {
1311         if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) {
1312             if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
1313                 return null;
1314             }
1315 
1316             // Disable package parsing on eng builds to allow for faster incremental development.
1317             if (forEngBuild) {
1318                 return null;
1319             }
1320 
1321             if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
1322                 Slog.i(TAG, "Disabling package parser cache due to system property.");
1323                 return null;
1324             }
1325         }
1326 
1327         // The base directory for the package parser cache lives under /data/system/.
1328         final File cacheBaseDir = Environment.getPackageCacheDirectory();
1329         if (!FileUtils.createDir(cacheBaseDir)) {
1330             return null;
1331         }
1332 
1333         // There are several items that need to be combined together to safely
1334         // identify cached items. In particular, changing the value of certain
1335         // feature flags should cause us to invalidate any caches.
1336         final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
1337                 : PackagePartitions.FINGERPRINT;
1338 
1339         // Reconcile cache directories, keeping only what we'd actually use.
1340         for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
1341             if (Objects.equals(cacheName, cacheDir.getName())) {
1342                 Slog.d(TAG, "Keeping known cache " + cacheDir.getName());
1343             } else {
1344                 Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName());
1345                 FileUtils.deleteContentsAndDir(cacheDir);
1346             }
1347         }
1348 
1349         // Return the versioned package cache directory.
1350         File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
1351 
1352         if (cacheDir == null) {
1353             // Something went wrong. Attempt to delete everything and return.
1354             Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir);
1355             FileUtils.deleteContentsAndDir(cacheBaseDir);
1356             return null;
1357         }
1358 
1359         // The following is a workaround to aid development on non-numbered userdebug
1360         // builds or cases where "adb sync" is used on userdebug builds. If we detect that
1361         // the system partition is newer.
1362         //
1363         // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build
1364         // that starts with "eng." to signify that this is an engineering build and not
1365         // destined for release.
1366         if (isUserDebugBuild && incrementalVersion.startsWith("eng.")) {
1367             Slog.w(TAG, "Wiping cache directory because the system partition changed.");
1368 
1369             // Heuristic: If the /system directory has been modified recently due to an "adb sync"
1370             // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable
1371             // in general and should not be used for production changes. In this specific case,
1372             // we know that they will work.
1373             File frameworkDir =
1374                     new File(Environment.getRootDirectory(), "framework");
1375             if (cacheDir.lastModified() < frameworkDir.lastModified()) {
1376                 FileUtils.deleteContents(cacheBaseDir);
1377                 cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
1378             }
1379         }
1380 
1381         return cacheDir;
1382     }
1383 
1384     /**
1385      * Check and throw if the given before/after packages would be considered a
1386      * downgrade.
1387      */
checkDowngrade(AndroidPackage before, PackageInfoLite after)1388     public static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
1389             throws PackageManagerException {
1390         if (after.getLongVersionCode() < before.getLongVersionCode()) {
1391             throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
1392                     "Update version code " + after.versionCode + " is older than current "
1393                             + before.getLongVersionCode());
1394         } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
1395             if (after.baseRevisionCode < before.getBaseRevisionCode()) {
1396                 throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
1397                         "Update base revision code " + after.baseRevisionCode
1398                                 + " is older than current " + before.getBaseRevisionCode());
1399             }
1400 
1401             if (!ArrayUtils.isEmpty(after.splitNames)) {
1402                 for (int i = 0; i < after.splitNames.length; i++) {
1403                     final String splitName = after.splitNames[i];
1404                     final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
1405                     if (j != -1) {
1406                         if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
1407                             throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
1408                                     "Update split " + splitName + " revision code "
1409                                             + after.splitRevisionCodes[i]
1410                                             + " is older than current "
1411                                             + before.getSplitRevisionCodes()[j]);
1412                         }
1413                     }
1414                 }
1415             }
1416         }
1417     }
1418 }
1419