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