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