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