Loading services/core/java/com/android/server/RescueParty.java +5 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import android.content.ContentResolver; import android.content.Context; import android.os.Build; Loading Loading @@ -146,7 +148,7 @@ public class RescueParty { SystemProperties.set(PROP_RESCUE_LEVEL, Integer.toString(level)); EventLogTags.writeRescueLevel(level, triggerUid); PackageManagerService.logCriticalInfo(Log.WARN, "Incremented rescue level to " logCriticalInfo(Log.WARN, "Incremented rescue level to " + levelToString(level) + " triggered by UID " + triggerUid); } Loading @@ -166,12 +168,12 @@ public class RescueParty { try { executeRescueLevelInternal(context, level); EventLogTags.writeRescueSuccess(level); PackageManagerService.logCriticalInfo(Log.DEBUG, logCriticalInfo(Log.DEBUG, "Finished rescue level " + levelToString(level)); } catch (Throwable t) { final String msg = ExceptionUtils.getCompleteMessage(t); EventLogTags.writeRescueFailure(level, msg); PackageManagerService.logCriticalInfo(Log.ERROR, logCriticalInfo(Log.ERROR, "Failed rescue level " + levelToString(level) + ": " + msg); } } Loading services/core/java/com/android/server/pm/KeySetManagerService.java +37 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static com.android.server.pm.PackageManagerService.SCAN_INITIAL; import com.android.internal.util.Preconditions; import android.content.pm.PackageParser; import android.util.ArrayMap; Loading Loading @@ -341,6 +343,41 @@ public class KeySetManagerService { return mKeySets.get(id) != null; } public boolean shouldCheckUpgradeKeySetLocked(PackageSettingBase oldPs, int scanFlags) { // Can't rotate keys during boot or if sharedUser. if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.isSharedUser() || !oldPs.keySetData.isUsingUpgradeKeySets()) { return false; } // app is using upgradeKeySets; make sure all are valid long[] upgradeKeySets = oldPs.keySetData.getUpgradeKeySets(); for (int i = 0; i < upgradeKeySets.length; i++) { if (!isIdValidKeySetId(upgradeKeySets[i])) { Slog.wtf(TAG, "Package " + (oldPs.name != null ? oldPs.name : "<null>") + " contains upgrade-key-set reference to unknown key-set: " + upgradeKeySets[i] + " reverting to signatures check."); return false; } } return true; } public boolean checkUpgradeKeySetLocked(PackageSettingBase oldPS, PackageParser.Package newPkg) { // Upgrade keysets are being used. Determine if new package has a superset of the // required keys. long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets(); for (int i = 0; i < upgradeKeySets.length; i++) { Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]); if (upgradeSet != null && newPkg.mSigningKeys.containsAll(upgradeSet)) { return true; } } return false; } /** * Fetches the {@link PublicKey public keys} which belong to the specified * KeySet id. Loading services/core/java/com/android/server/pm/PackageManagerService.java +55 −385 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +338 −4 Original line number Diff line number Diff line Loading @@ -16,42 +16,74 @@ package com.android.server.pm; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION; import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; import static com.android.server.pm.PackageManagerService.STUB_SUFFIX; import static com.android.server.pm.PackageManagerService.TAG; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.server.EventLogTags; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; import android.annotation.NonNull; import android.app.AppGlobals; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.ResolveInfo; import android.content.pm.Signature; import android.os.Build; import android.os.Debug; import android.os.Environment; import android.os.FileUtils; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.service.pm.PackageServiceDumpProto; import android.system.ErrnoException; import android.system.Os; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.jar.StrictJarFile; import android.util.proto.ProtoOutputStream; import dalvik.system.VMRuntime; import libcore.io.IoUtils; import libcore.io.Libcore; import libcore.io.Streams; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.function.Predicate; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; /** Loading Loading @@ -200,7 +232,7 @@ public class PackageManagerServiceUtils { * * If it doesn't have sufficient information about the package, it return <code>false</code>. */ static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, public static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo, long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) { Loading Loading @@ -263,6 +295,21 @@ public class PackageManagerServiceUtils { return false; } public static long getLastModifiedTime(PackageParser.Package pkg, File srcFile) { if (srcFile.isDirectory()) { final File baseFile = new File(pkg.baseCodePath); long maxModifiedTime = baseFile.lastModified(); if (pkg.splitCodePaths != null) { for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) { final File splitFile = new File(pkg.splitCodePaths[i]); maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified()); } } return maxModifiedTime; } return srcFile.lastModified(); } /** * Checks that the archive located at {@code fileName} has uncompressed dex file and so * files that can be direclty mapped. Loading Loading @@ -318,6 +365,57 @@ public class PackageManagerServiceUtils { } } private static File getSettingsProblemFile() { File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); File fname = new File(systemDir, "uiderrors.txt"); return fname; } public static void dumpCriticalInfo(ProtoOutputStream proto) { try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) { String line = null; while ((line = in.readLine()) != null) { if (line.contains("ignored: updated version")) continue; proto.write(PackageServiceDumpProto.MESSAGES, line); } } catch (IOException ignored) { } } public static void dumpCriticalInfo(PrintWriter pw, String msg) { try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) { String line = null; while ((line = in.readLine()) != null) { if (line.contains("ignored: updated version")) continue; if (msg != null) { pw.print(msg); } pw.println(line); } } catch (IOException ignored) { } } public static void logCriticalInfo(int priority, String msg) { Slog.println(priority, TAG, msg); EventLogTags.writePmCriticalInfo(msg); try { File fname = getSettingsProblemFile(); FileOutputStream out = new FileOutputStream(fname, true); PrintWriter pw = new FastPrintWriter(out); SimpleDateFormat formatter = new SimpleDateFormat(); String dateString = formatter.format(new Date(System.currentTimeMillis())); pw.println(dateString + ": " + msg); pw.close(); FileUtils.setPermissions( fname.toString(), FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH, -1, -1); } catch (java.io.IOException e) { } } public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) { if (callingUid == Process.SHELL_UID) { if (userHandle >= 0 Loading @@ -331,4 +429,240 @@ public class PackageManagerServiceUtils { } } } /** * Derive the value of the {@code cpuAbiOverride} based on the provided * value and an optional stored value from the package settings. */ public static String deriveAbiOverride(String abiOverride, PackageSetting settings) { String cpuAbiOverride = null; if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) { cpuAbiOverride = null; } else if (abiOverride != null) { cpuAbiOverride = abiOverride; } else if (settings != null) { cpuAbiOverride = settings.cpuAbiOverrideString; } return cpuAbiOverride; } /** * Compares two sets of signatures. Returns: * <br /> * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null, * <br /> * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null, * <br /> * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null, * <br /> * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical, * <br /> * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ. */ public static int compareSignatures(Signature[] s1, Signature[] s2) { if (s1 == null) { return s2 == null ? PackageManager.SIGNATURE_NEITHER_SIGNED : PackageManager.SIGNATURE_FIRST_NOT_SIGNED; } if (s2 == null) { return PackageManager.SIGNATURE_SECOND_NOT_SIGNED; } if (s1.length != s2.length) { return PackageManager.SIGNATURE_NO_MATCH; } // Since both signature sets are of size 1, we can compare without HashSets. if (s1.length == 1) { return s1[0].equals(s2[0]) ? PackageManager.SIGNATURE_MATCH : PackageManager.SIGNATURE_NO_MATCH; } ArraySet<Signature> set1 = new ArraySet<Signature>(); for (Signature sig : s1) { set1.add(sig); } ArraySet<Signature> set2 = new ArraySet<Signature>(); for (Signature sig : s2) { set2.add(sig); } // Make sure s2 contains all signatures in s1. if (set1.equals(set2)) { return PackageManager.SIGNATURE_MATCH; } return PackageManager.SIGNATURE_NO_MATCH; } /** * Used for backward compatibility to make sure any packages with * certificate chains get upgraded to the new style. {@code existingSigs} * will be in the old format (since they were stored on disk from before the * system upgrade) and {@code scannedSigs} will be in the newer format. */ private static boolean matchSignaturesCompat(String packageName, PackageSignatures packageSignatures, Signature[] parsedSignatures) { ArraySet<Signature> existingSet = new ArraySet<Signature>(); for (Signature sig : packageSignatures.mSignatures) { existingSet.add(sig); } ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>(); for (Signature sig : parsedSignatures) { try { Signature[] chainSignatures = sig.getChainSignatures(); for (Signature chainSig : chainSignatures) { scannedCompatSet.add(chainSig); } } catch (CertificateEncodingException e) { scannedCompatSet.add(sig); } } // make sure the expanded scanned set contains all signatures in the existing one if (scannedCompatSet.equals(existingSet)) { // migrate the old signatures to the new scheme packageSignatures.assignSignatures(parsedSignatures); return true; } return false; } private static boolean matchSignaturesRecover(String packageName, Signature[] existingSignatures, Signature[] parsedSignatures) { String msg = null; try { if (Signature.areEffectiveMatch(existingSignatures, parsedSignatures)) { logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for " + packageName); return true; } } catch (CertificateException e) { msg = e.getMessage(); } logCriticalInfo(Log.INFO, "Failed to recover certificates for " + packageName + ": " + msg); return false; } /** * Verifies that signatures match. * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}. * @throws PackageManagerException if the signatures did not match. */ public static boolean verifySignatures(PackageSetting pkgSetting, Signature[] parsedSignatures, boolean compareCompat, boolean compareRecover) throws PackageManagerException { final String packageName = pkgSetting.name; boolean compatMatch = false; if (pkgSetting.signatures.mSignatures != null) { // Already existing package. Make sure signatures match boolean match = compareSignatures(pkgSetting.signatures.mSignatures, parsedSignatures) == PackageManager.SIGNATURE_MATCH; if (!match && compareCompat) { match = matchSignaturesCompat(packageName, pkgSetting.signatures, parsedSignatures); compatMatch = match; } if (!match && compareRecover) { match = matchSignaturesRecover( packageName, pkgSetting.signatures.mSignatures, parsedSignatures); } if (!match) { throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + packageName + " signatures don't match previously installed version; ignoring!"); } } // Check for shared user signatures if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) { // Already existing package. Make sure signatures match boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures) == PackageManager.SIGNATURE_MATCH; if (!match) { match = matchSignaturesCompat( packageName, pkgSetting.sharedUser.signatures, parsedSignatures); } if (!match && compareCompat) { match = matchSignaturesRecover( packageName, pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures); compatMatch |= match; } if (!match && compareRecover) { throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, "Package " + packageName + " has no signatures that match those in shared user " + pkgSetting.sharedUser.name + "; ignoring!"); } } return compatMatch; } public static int decompressFile(File srcFile, File dstFile) throws ErrnoException { if (DEBUG_COMPRESSION) { Slog.i(TAG, "Decompress file" + "; src: " + srcFile.getAbsolutePath() + ", dst: " + dstFile.getAbsolutePath()); } try ( InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile)); OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/); ) { Streams.copy(fileIn, fileOut); Os.chmod(dstFile.getAbsolutePath(), 0644); return PackageManager.INSTALL_SUCCEEDED; } catch (IOException e) { logCriticalInfo(Log.ERROR, "Failed to decompress file" + "; src: " + srcFile.getAbsolutePath() + ", dst: " + dstFile.getAbsolutePath()); } return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; } public static File[] getCompressedFiles(String codePath) { final File stubCodePath = new File(codePath); final String stubName = stubCodePath.getName(); // The layout of a compressed package on a given partition is as follows : // // Compressed artifacts: // // /partition/ModuleName/foo.gz // /partation/ModuleName/bar.gz // // Stub artifact: // // /partition/ModuleName-Stub/ModuleName-Stub.apk // // In other words, stub is on the same partition as the compressed artifacts // and in a directory that's suffixed with "-Stub". int idx = stubName.lastIndexOf(STUB_SUFFIX); if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) { return null; } final File stubParentDir = stubCodePath.getParentFile(); if (stubParentDir == null) { Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath); return null; } final File compressedPath = new File(stubParentDir, stubName.substring(0, idx)); final File[] files = compressedPath.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.toLowerCase().endsWith(COMPRESSED_EXTENSION); } }); if (DEBUG_COMPRESSION && files != null && files.length > 0) { Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files)); } return files; } public static boolean compressedFileExists(String codePath) { final File[] compressedFiles = getCompressedFiles(codePath); return compressedFiles != null && compressedFiles.length > 0; } } services/core/java/com/android/server/pm/UserDataPreparer.java +2 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.pm; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import android.content.Context; import android.content.pm.UserInfo; import android.os.Environment; Loading @@ -40,8 +42,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; import static com.android.server.pm.PackageManagerService.logCriticalInfo; /** * Helper class for preparing and destroying user storage */ Loading Loading
services/core/java/com/android/server/RescueParty.java +5 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import android.content.ContentResolver; import android.content.Context; import android.os.Build; Loading Loading @@ -146,7 +148,7 @@ public class RescueParty { SystemProperties.set(PROP_RESCUE_LEVEL, Integer.toString(level)); EventLogTags.writeRescueLevel(level, triggerUid); PackageManagerService.logCriticalInfo(Log.WARN, "Incremented rescue level to " logCriticalInfo(Log.WARN, "Incremented rescue level to " + levelToString(level) + " triggered by UID " + triggerUid); } Loading @@ -166,12 +168,12 @@ public class RescueParty { try { executeRescueLevelInternal(context, level); EventLogTags.writeRescueSuccess(level); PackageManagerService.logCriticalInfo(Log.DEBUG, logCriticalInfo(Log.DEBUG, "Finished rescue level " + levelToString(level)); } catch (Throwable t) { final String msg = ExceptionUtils.getCompleteMessage(t); EventLogTags.writeRescueFailure(level, msg); PackageManagerService.logCriticalInfo(Log.ERROR, logCriticalInfo(Log.ERROR, "Failed rescue level " + levelToString(level) + ": " + msg); } } Loading
services/core/java/com/android/server/pm/KeySetManagerService.java +37 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static com.android.server.pm.PackageManagerService.SCAN_INITIAL; import com.android.internal.util.Preconditions; import android.content.pm.PackageParser; import android.util.ArrayMap; Loading Loading @@ -341,6 +343,41 @@ public class KeySetManagerService { return mKeySets.get(id) != null; } public boolean shouldCheckUpgradeKeySetLocked(PackageSettingBase oldPs, int scanFlags) { // Can't rotate keys during boot or if sharedUser. if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.isSharedUser() || !oldPs.keySetData.isUsingUpgradeKeySets()) { return false; } // app is using upgradeKeySets; make sure all are valid long[] upgradeKeySets = oldPs.keySetData.getUpgradeKeySets(); for (int i = 0; i < upgradeKeySets.length; i++) { if (!isIdValidKeySetId(upgradeKeySets[i])) { Slog.wtf(TAG, "Package " + (oldPs.name != null ? oldPs.name : "<null>") + " contains upgrade-key-set reference to unknown key-set: " + upgradeKeySets[i] + " reverting to signatures check."); return false; } } return true; } public boolean checkUpgradeKeySetLocked(PackageSettingBase oldPS, PackageParser.Package newPkg) { // Upgrade keysets are being used. Determine if new package has a superset of the // required keys. long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets(); for (int i = 0; i < upgradeKeySets.length; i++) { Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]); if (upgradeSet != null && newPkg.mSigningKeys.containsAll(upgradeSet)) { return true; } } return false; } /** * Fetches the {@link PublicKey public keys} which belong to the specified * KeySet id. Loading
services/core/java/com/android/server/pm/PackageManagerService.java +55 −385 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +338 −4 Original line number Diff line number Diff line Loading @@ -16,42 +16,74 @@ package com.android.server.pm; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION; import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; import static com.android.server.pm.PackageManagerService.STUB_SUFFIX; import static com.android.server.pm.PackageManagerService.TAG; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.server.EventLogTags; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; import android.annotation.NonNull; import android.app.AppGlobals; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.ResolveInfo; import android.content.pm.Signature; import android.os.Build; import android.os.Debug; import android.os.Environment; import android.os.FileUtils; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.service.pm.PackageServiceDumpProto; import android.system.ErrnoException; import android.system.Os; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.jar.StrictJarFile; import android.util.proto.ProtoOutputStream; import dalvik.system.VMRuntime; import libcore.io.IoUtils; import libcore.io.Libcore; import libcore.io.Streams; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.function.Predicate; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; /** Loading Loading @@ -200,7 +232,7 @@ public class PackageManagerServiceUtils { * * If it doesn't have sufficient information about the package, it return <code>false</code>. */ static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, public static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis, long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo, long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) { Loading Loading @@ -263,6 +295,21 @@ public class PackageManagerServiceUtils { return false; } public static long getLastModifiedTime(PackageParser.Package pkg, File srcFile) { if (srcFile.isDirectory()) { final File baseFile = new File(pkg.baseCodePath); long maxModifiedTime = baseFile.lastModified(); if (pkg.splitCodePaths != null) { for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) { final File splitFile = new File(pkg.splitCodePaths[i]); maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified()); } } return maxModifiedTime; } return srcFile.lastModified(); } /** * Checks that the archive located at {@code fileName} has uncompressed dex file and so * files that can be direclty mapped. Loading Loading @@ -318,6 +365,57 @@ public class PackageManagerServiceUtils { } } private static File getSettingsProblemFile() { File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); File fname = new File(systemDir, "uiderrors.txt"); return fname; } public static void dumpCriticalInfo(ProtoOutputStream proto) { try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) { String line = null; while ((line = in.readLine()) != null) { if (line.contains("ignored: updated version")) continue; proto.write(PackageServiceDumpProto.MESSAGES, line); } } catch (IOException ignored) { } } public static void dumpCriticalInfo(PrintWriter pw, String msg) { try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) { String line = null; while ((line = in.readLine()) != null) { if (line.contains("ignored: updated version")) continue; if (msg != null) { pw.print(msg); } pw.println(line); } } catch (IOException ignored) { } } public static void logCriticalInfo(int priority, String msg) { Slog.println(priority, TAG, msg); EventLogTags.writePmCriticalInfo(msg); try { File fname = getSettingsProblemFile(); FileOutputStream out = new FileOutputStream(fname, true); PrintWriter pw = new FastPrintWriter(out); SimpleDateFormat formatter = new SimpleDateFormat(); String dateString = formatter.format(new Date(System.currentTimeMillis())); pw.println(dateString + ": " + msg); pw.close(); FileUtils.setPermissions( fname.toString(), FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH, -1, -1); } catch (java.io.IOException e) { } } public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) { if (callingUid == Process.SHELL_UID) { if (userHandle >= 0 Loading @@ -331,4 +429,240 @@ public class PackageManagerServiceUtils { } } } /** * Derive the value of the {@code cpuAbiOverride} based on the provided * value and an optional stored value from the package settings. */ public static String deriveAbiOverride(String abiOverride, PackageSetting settings) { String cpuAbiOverride = null; if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) { cpuAbiOverride = null; } else if (abiOverride != null) { cpuAbiOverride = abiOverride; } else if (settings != null) { cpuAbiOverride = settings.cpuAbiOverrideString; } return cpuAbiOverride; } /** * Compares two sets of signatures. Returns: * <br /> * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null, * <br /> * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null, * <br /> * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null, * <br /> * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical, * <br /> * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ. */ public static int compareSignatures(Signature[] s1, Signature[] s2) { if (s1 == null) { return s2 == null ? PackageManager.SIGNATURE_NEITHER_SIGNED : PackageManager.SIGNATURE_FIRST_NOT_SIGNED; } if (s2 == null) { return PackageManager.SIGNATURE_SECOND_NOT_SIGNED; } if (s1.length != s2.length) { return PackageManager.SIGNATURE_NO_MATCH; } // Since both signature sets are of size 1, we can compare without HashSets. if (s1.length == 1) { return s1[0].equals(s2[0]) ? PackageManager.SIGNATURE_MATCH : PackageManager.SIGNATURE_NO_MATCH; } ArraySet<Signature> set1 = new ArraySet<Signature>(); for (Signature sig : s1) { set1.add(sig); } ArraySet<Signature> set2 = new ArraySet<Signature>(); for (Signature sig : s2) { set2.add(sig); } // Make sure s2 contains all signatures in s1. if (set1.equals(set2)) { return PackageManager.SIGNATURE_MATCH; } return PackageManager.SIGNATURE_NO_MATCH; } /** * Used for backward compatibility to make sure any packages with * certificate chains get upgraded to the new style. {@code existingSigs} * will be in the old format (since they were stored on disk from before the * system upgrade) and {@code scannedSigs} will be in the newer format. */ private static boolean matchSignaturesCompat(String packageName, PackageSignatures packageSignatures, Signature[] parsedSignatures) { ArraySet<Signature> existingSet = new ArraySet<Signature>(); for (Signature sig : packageSignatures.mSignatures) { existingSet.add(sig); } ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>(); for (Signature sig : parsedSignatures) { try { Signature[] chainSignatures = sig.getChainSignatures(); for (Signature chainSig : chainSignatures) { scannedCompatSet.add(chainSig); } } catch (CertificateEncodingException e) { scannedCompatSet.add(sig); } } // make sure the expanded scanned set contains all signatures in the existing one if (scannedCompatSet.equals(existingSet)) { // migrate the old signatures to the new scheme packageSignatures.assignSignatures(parsedSignatures); return true; } return false; } private static boolean matchSignaturesRecover(String packageName, Signature[] existingSignatures, Signature[] parsedSignatures) { String msg = null; try { if (Signature.areEffectiveMatch(existingSignatures, parsedSignatures)) { logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for " + packageName); return true; } } catch (CertificateException e) { msg = e.getMessage(); } logCriticalInfo(Log.INFO, "Failed to recover certificates for " + packageName + ": " + msg); return false; } /** * Verifies that signatures match. * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}. * @throws PackageManagerException if the signatures did not match. */ public static boolean verifySignatures(PackageSetting pkgSetting, Signature[] parsedSignatures, boolean compareCompat, boolean compareRecover) throws PackageManagerException { final String packageName = pkgSetting.name; boolean compatMatch = false; if (pkgSetting.signatures.mSignatures != null) { // Already existing package. Make sure signatures match boolean match = compareSignatures(pkgSetting.signatures.mSignatures, parsedSignatures) == PackageManager.SIGNATURE_MATCH; if (!match && compareCompat) { match = matchSignaturesCompat(packageName, pkgSetting.signatures, parsedSignatures); compatMatch = match; } if (!match && compareRecover) { match = matchSignaturesRecover( packageName, pkgSetting.signatures.mSignatures, parsedSignatures); } if (!match) { throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + packageName + " signatures don't match previously installed version; ignoring!"); } } // Check for shared user signatures if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) { // Already existing package. Make sure signatures match boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures) == PackageManager.SIGNATURE_MATCH; if (!match) { match = matchSignaturesCompat( packageName, pkgSetting.sharedUser.signatures, parsedSignatures); } if (!match && compareCompat) { match = matchSignaturesRecover( packageName, pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures); compatMatch |= match; } if (!match && compareRecover) { throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, "Package " + packageName + " has no signatures that match those in shared user " + pkgSetting.sharedUser.name + "; ignoring!"); } } return compatMatch; } public static int decompressFile(File srcFile, File dstFile) throws ErrnoException { if (DEBUG_COMPRESSION) { Slog.i(TAG, "Decompress file" + "; src: " + srcFile.getAbsolutePath() + ", dst: " + dstFile.getAbsolutePath()); } try ( InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile)); OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/); ) { Streams.copy(fileIn, fileOut); Os.chmod(dstFile.getAbsolutePath(), 0644); return PackageManager.INSTALL_SUCCEEDED; } catch (IOException e) { logCriticalInfo(Log.ERROR, "Failed to decompress file" + "; src: " + srcFile.getAbsolutePath() + ", dst: " + dstFile.getAbsolutePath()); } return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; } public static File[] getCompressedFiles(String codePath) { final File stubCodePath = new File(codePath); final String stubName = stubCodePath.getName(); // The layout of a compressed package on a given partition is as follows : // // Compressed artifacts: // // /partition/ModuleName/foo.gz // /partation/ModuleName/bar.gz // // Stub artifact: // // /partition/ModuleName-Stub/ModuleName-Stub.apk // // In other words, stub is on the same partition as the compressed artifacts // and in a directory that's suffixed with "-Stub". int idx = stubName.lastIndexOf(STUB_SUFFIX); if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) { return null; } final File stubParentDir = stubCodePath.getParentFile(); if (stubParentDir == null) { Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath); return null; } final File compressedPath = new File(stubParentDir, stubName.substring(0, idx)); final File[] files = compressedPath.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.toLowerCase().endsWith(COMPRESSED_EXTENSION); } }); if (DEBUG_COMPRESSION && files != null && files.length > 0) { Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files)); } return files; } public static boolean compressedFileExists(String codePath) { final File[] compressedFiles = getCompressedFiles(codePath); return compressedFiles != null && compressedFiles.length > 0; } }
services/core/java/com/android/server/pm/UserDataPreparer.java +2 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.pm; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import android.content.Context; import android.content.pm.UserInfo; import android.os.Environment; Loading @@ -40,8 +42,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; import static com.android.server.pm.PackageManagerService.logCriticalInfo; /** * Helper class for preparing and destroying user storage */ Loading