Loading core/java/android/os/incremental/IIncrementalService.aidl +8 −0 Original line number Diff line number Diff line Loading @@ -89,6 +89,14 @@ interface IIncrementalService { */ int unlink(int storageId, in @utf8InCpp String path); /** * Checks if a file is fully loaded. File is specified by its path. * 0 - fully loaded * >0 - certain pages missing * <0 - -errcode */ int isFileFullyLoaded(int storageId, in @utf8InCpp String path); /** * Returns overall loading progress of all the files on a storage, progress value between [0,1]. * Returns a negative value on error. Loading core/java/android/os/incremental/IncrementalStorage.java +19 −0 Original line number Diff line number Diff line Loading @@ -303,6 +303,25 @@ public final class IncrementalStorage { } } /** * Checks whether a file under the current storage directory is fully loaded. * * @param path The relative path of the file. * @return True if the file is fully loaded. */ public boolean isFileFullyLoaded(@NonNull String path) throws IOException { try { int res = mService.isFileFullyLoaded(mId, path); if (res < 0) { throw new IOException("isFileFullyLoaded() failed, errno " + -res); } return res == 0; } catch (RemoteException e) { e.rethrowFromSystemServer(); return false; } } /** * Returns the loading progress of a storage * Loading services/core/java/com/android/server/pm/ApkChecksums.java +220 −20 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.EXTRA_CHECKSUMS; import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA256; import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA512; import static android.content.pm.PackageManager.WHOLE_MD5; Loading @@ -27,11 +28,20 @@ import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA25 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.pm.FileChecksum; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.os.Handler; import android.os.SystemClock; import android.os.incremental.IncrementalManager; import android.os.incremental.IncrementalStorage; import android.util.ArrayMap; import android.util.Pair; import android.util.Slog; import android.util.apk.ApkSignatureSchemeV2Verifier; import android.util.apk.ApkSignatureSchemeV3Verifier; Loading @@ -43,6 +53,7 @@ import android.util.apk.SignatureInfo; import android.util.apk.SignatureNotFoundException; import android.util.apk.VerityBuilder; import com.android.internal.annotations.VisibleForTesting; import com.android.server.security.VerityUtils; import java.io.BufferedInputStream; Loading Loading @@ -72,32 +83,163 @@ public class ApkChecksums { static final String ALGO_SHA512 = "SHA512"; /** * Fetch or calculate checksums for the specific file. * Check back in 1 second after we detected we needed to wait for the APK to be fully available. */ private static final long PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS = 1000; /** * 24 hours timeout to wait till all files are loaded. */ private static final long PROCESS_REQUIRED_CHECKSUMS_TIMEOUT_MILLIS = 1000 * 3600 * 24; /** * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors. * * @param split split name, null for base * @param file to fetch checksums for * NOTE: All getters should return the same instance for every call. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) static class Injector { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) interface Producer<T> { /** Produce an instance of type {@link T} */ T produce(); } private final Producer<Context> mContext; private final Producer<Handler> mHandlerProducer; private final Producer<IncrementalManager> mIncrementalManagerProducer; Injector(Producer<Context> context, Producer<Handler> handlerProducer, Producer<IncrementalManager> incrementalManagerProducer) { mContext = context; mHandlerProducer = handlerProducer; mIncrementalManagerProducer = incrementalManagerProducer; } public Context getContext() { return mContext.produce(); } public Handler getHandler() { return mHandlerProducer.produce(); } public IncrementalManager getIncrementalManager() { return mIncrementalManagerProducer.produce(); } } /** * Fetch or calculate checksums for the collection of files. * * @param filesToChecksum split name, null for base and File to fetch checksums for * @param optional mask to fetch readily available checksums * @param required mask to forcefully calculate if not available * @param trustedInstallers array of certificate to trust, two specific cases: * null - trust anybody, * [] - trust nobody. * @param statusReceiver to receive the resulting checksums */ public static List<FileChecksum> getFileChecksums(String split, File file, public static void getChecksums(List<Pair<String, File>> filesToChecksum, @PackageManager.FileChecksumKind int optional, @PackageManager.FileChecksumKind int required, @Nullable Certificate[] trustedInstallers) { final String filePath = file.getAbsolutePath(); @Nullable Certificate[] trustedInstallers, @NonNull IntentSender statusReceiver, @NonNull Injector injector) { List<Map<Integer, FileChecksum>> result = new ArrayList<>(filesToChecksum.size()); for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { final String split = filesToChecksum.get(i).first; final File file = filesToChecksum.get(i).second; Map<Integer, FileChecksum> checksums = new ArrayMap<>(); final int kinds = (optional | required); // System enforced: FSI or v2/v3/v4 signatures. if ((kinds & WHOLE_MERKLE_ROOT_4K_SHA256) != 0) { result.add(checksums); try { getAvailableFileChecksums(split, file, optional | required, trustedInstallers, checksums); } catch (Throwable e) { Slog.e(TAG, "Preferred checksum calculation error", e); } } long startTime = SystemClock.uptimeMillis(); processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector, startTime); } private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum, List<Map<Integer, FileChecksum>> result, @PackageManager.FileChecksumKind int required, @NonNull IntentSender statusReceiver, @NonNull Injector injector, long startTime) { final boolean timeout = SystemClock.uptimeMillis() - startTime >= PROCESS_REQUIRED_CHECKSUMS_TIMEOUT_MILLIS; List<FileChecksum> allChecksums = new ArrayList<>(); for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { final String split = filesToChecksum.get(i).first; final File file = filesToChecksum.get(i).second; Map<Integer, FileChecksum> checksums = result.get(i); try { if (!timeout || required != 0) { if (needToWait(file, required, checksums, injector)) { // Not ready, come back later. injector.getHandler().postDelayed(() -> { processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector, startTime); }, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS); return; } getRequiredFileChecksums(split, file, required, checksums); } allChecksums.addAll(checksums.values()); } catch (Throwable e) { Slog.e(TAG, "Required checksum calculation error", e); } } final Intent intent = new Intent(); intent.putExtra(EXTRA_CHECKSUMS, allChecksums.toArray(new FileChecksum[allChecksums.size()])); try { statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null); } catch (IntentSender.SendIntentException e) { Slog.w(TAG, e); } } /** * Fetch readily available checksums - enforced by kernel or provided by Installer. * * @param split split name, null for base * @param file to fetch checksums for * @param kinds mask to fetch checksums * @param trustedInstallers array of certificate to trust, two specific cases: * null - trust anybody, * [] - trust nobody. * @param checksums resulting checksums */ private static void getAvailableFileChecksums(String split, File file, @PackageManager.FileChecksumKind int kinds, @Nullable Certificate[] trustedInstallers, Map<Integer, FileChecksum> checksums) { final String filePath = file.getAbsolutePath(); // Always available: FSI or IncFs. if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) { // Hashes in fs-verity and IncFS are always verified. FileChecksum checksum = extractHashFromFS(split, filePath); if (checksum != null) { checksums.put(checksum.getKind(), checksum); } } if ((kinds & (PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512)) != 0) { // System enforced: v2/v3. if (isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) || isRequired( PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) { Map<Integer, FileChecksum> v2v3checksums = extractHashFromV2V3Signature( split, filePath, kinds); if (v2v3checksums != null) { Loading @@ -106,11 +248,58 @@ public class ApkChecksums { } // TODO(b/160605420): Installer provided. // TODO(b/160605420): Wait for Incremental to be fully loaded. } /** * Whether the file is available for checksumming or we need to wait. */ private static boolean needToWait(File file, @PackageManager.FileChecksumKind int kinds, Map<Integer, FileChecksum> checksums, @NonNull Injector injector) throws IOException { if (!isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums) && !isRequired(WHOLE_MD5, kinds, checksums) && !isRequired(WHOLE_SHA1, kinds, checksums) && !isRequired(WHOLE_SHA256, kinds, checksums) && !isRequired(WHOLE_SHA512, kinds, checksums) && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) { return false; } final String filePath = file.getAbsolutePath(); if (!IncrementalManager.isIncrementalPath(filePath)) { return false; } IncrementalManager manager = injector.getIncrementalManager(); if (manager == null) { throw new IllegalStateException("IncrementalManager is missing."); } IncrementalStorage storage = manager.openStorage(filePath); if (storage == null) { throw new IllegalStateException( "IncrementalStorage is missing for a path on IncFs: " + filePath); } return !storage.isFileFullyLoaded(filePath); } /** * Fetch or calculate checksums for the specific file. * * @param split split name, null for base * @param file to fetch checksums for * @param kinds mask to forcefully calculate if not available * @param checksums resulting checksums */ private static void getRequiredFileChecksums(String split, File file, @PackageManager.FileChecksumKind int kinds, Map<Integer, FileChecksum> checksums) { final String filePath = file.getAbsolutePath(); // Manually calculating required checksums if not readily available. if ((required & WHOLE_MERKLE_ROOT_4K_SHA256) != 0 && !checksums.containsKey( WHOLE_MERKLE_ROOT_4K_SHA256)) { if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) { try { byte[] generatedRootHash = VerityBuilder.generateFsVerityRootHash( filePath, /*salt=*/null, Loading @@ -127,14 +316,23 @@ public class ApkChecksums { } } calculateChecksumIfRequested(checksums, split, file, required, WHOLE_MD5); calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA1); calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA256); calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA512); calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_MD5); calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA1); calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA256); calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA512); calculatePartialChecksumsIfRequested(checksums, split, file, required); calculatePartialChecksumsIfRequested(checksums, split, file, kinds); } return new ArrayList<>(checksums.values()); private static boolean isRequired(@PackageManager.FileChecksumKind int kind, @PackageManager.FileChecksumKind int kinds, Map<Integer, FileChecksum> checksums) { if ((kinds & kind) == 0) { return false; } if (checksums.containsKey(kind)) { return false; } return true; } private static FileChecksum extractHashFromFS(String split, String filePath) { Loading Loading @@ -170,8 +368,10 @@ public class ApkChecksums { PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, false).contentDigests; } catch (PackageParser.PackageParserException e) { if (!(e.getCause() instanceof SignatureNotFoundException)) { Slog.e(TAG, "Signature verification error", e); } } if (contentDigests == null) { return null; Loading services/core/java/com/android/server/pm/PackageManagerService.java +15 −26 Original line number Diff line number Diff line Loading @@ -40,7 +40,6 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.EXTRA_CHECKSUMS; import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; Loading Loading @@ -169,7 +168,6 @@ import android.content.pm.ComponentInfo; import android.content.pm.DataLoaderType; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureInfo; import android.content.pm.FileChecksum; import android.content.pm.IDexModuleRegisterCallback; import android.content.pm.IPackageChangeObserver; import android.content.pm.IPackageDataObserver; Loading Loading @@ -933,6 +931,7 @@ public class PackageManagerService extends IPackageManager.Stub private final Object mLock; private final Installer mInstaller; private final Object mInstallLock; private final Handler mBackgroundHandler; private final Executor mBackgroundExecutor; // ----- producers ----- Loading @@ -955,7 +954,7 @@ public class PackageManagerService extends IPackageManager.Stub Injector(Context context, Object lock, Installer installer, Object installLock, PackageAbiHelper abiHelper, Executor backgroundExecutor, Handler backgroundHandler, Producer<ComponentResolver> componentResolverProducer, Producer<PermissionManagerServiceInternal> permissionManagerProducer, Producer<UserManagerService> userManagerProducer, Loading @@ -977,7 +976,8 @@ public class PackageManagerService extends IPackageManager.Stub mInstaller = installer; mAbiHelper = abiHelper; mInstallLock = installLock; mBackgroundExecutor = backgroundExecutor; mBackgroundHandler = backgroundHandler; mBackgroundExecutor = new HandlerExecutor(backgroundHandler); mComponentResolverProducer = new Singleton<>(componentResolverProducer); mPermissionManagerProducer = new Singleton<>(permissionManagerProducer); mUserManagerProducer = new Singleton<>(userManagerProducer); Loading Loading @@ -1092,6 +1092,10 @@ public class PackageManagerService extends IPackageManager.Stub return mPlatformCompatProducer.get(this, mPackageManager); } public Handler getBackgroundHandler() { return mBackgroundHandler; } public Executor getBackgroundExecutor() { return mBackgroundExecutor; } Loading Loading @@ -2489,29 +2493,14 @@ public class PackageManagerService extends IPackageManager.Stub final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates( trustedInstallers) : null; final Context context = mContext; mInjector.getBackgroundExecutor().execute(() -> { final Intent intent = new Intent(); List<FileChecksum> result = new ArrayList<>(); for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { final String split = filesToChecksum.get(i).first; final File file = filesToChecksum.get(i).second; try { result.addAll(ApkChecksums.getFileChecksums(split, file, optional, required, trustedCerts)); } catch (Throwable e) { Slog.e(TAG, "Checksum calculation error", e); } } intent.putExtra(EXTRA_CHECKSUMS, result.toArray(new FileChecksum[result.size()])); try { statusReceiver.sendIntent(context, 1, intent, null, null); } catch (SendIntentException e) { Slog.w(TAG, e); } ApkChecksums.Injector injector = new ApkChecksums.Injector( () -> mContext, () -> mInjector.getBackgroundHandler(), () -> mContext.getSystemService(IncrementalManager.class)); ApkChecksums.getChecksums(filesToChecksum, optional, required, trustedCerts, statusReceiver, injector); }); } Loading Loading @@ -2684,7 +2673,7 @@ public class PackageManagerService extends IPackageManager.Stub Injector injector = new Injector( context, lock, installer, installLock, new PackageAbiHelperImpl(), new HandlerExecutor(backgroundHandler), backgroundHandler, (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock), (i, pm) -> services/core/java/com/android/server/pm/TEST_MAPPING +3 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,9 @@ { "name": "CtsContentTestCases", "options": [ { "include-filter": "android.content.pm.cts.ChecksumsTest" }, { "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest" }, Loading Loading
core/java/android/os/incremental/IIncrementalService.aidl +8 −0 Original line number Diff line number Diff line Loading @@ -89,6 +89,14 @@ interface IIncrementalService { */ int unlink(int storageId, in @utf8InCpp String path); /** * Checks if a file is fully loaded. File is specified by its path. * 0 - fully loaded * >0 - certain pages missing * <0 - -errcode */ int isFileFullyLoaded(int storageId, in @utf8InCpp String path); /** * Returns overall loading progress of all the files on a storage, progress value between [0,1]. * Returns a negative value on error. Loading
core/java/android/os/incremental/IncrementalStorage.java +19 −0 Original line number Diff line number Diff line Loading @@ -303,6 +303,25 @@ public final class IncrementalStorage { } } /** * Checks whether a file under the current storage directory is fully loaded. * * @param path The relative path of the file. * @return True if the file is fully loaded. */ public boolean isFileFullyLoaded(@NonNull String path) throws IOException { try { int res = mService.isFileFullyLoaded(mId, path); if (res < 0) { throw new IOException("isFileFullyLoaded() failed, errno " + -res); } return res == 0; } catch (RemoteException e) { e.rethrowFromSystemServer(); return false; } } /** * Returns the loading progress of a storage * Loading
services/core/java/com/android/server/pm/ApkChecksums.java +220 −20 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.EXTRA_CHECKSUMS; import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA256; import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA512; import static android.content.pm.PackageManager.WHOLE_MD5; Loading @@ -27,11 +28,20 @@ import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA25 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.pm.FileChecksum; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.os.Handler; import android.os.SystemClock; import android.os.incremental.IncrementalManager; import android.os.incremental.IncrementalStorage; import android.util.ArrayMap; import android.util.Pair; import android.util.Slog; import android.util.apk.ApkSignatureSchemeV2Verifier; import android.util.apk.ApkSignatureSchemeV3Verifier; Loading @@ -43,6 +53,7 @@ import android.util.apk.SignatureInfo; import android.util.apk.SignatureNotFoundException; import android.util.apk.VerityBuilder; import com.android.internal.annotations.VisibleForTesting; import com.android.server.security.VerityUtils; import java.io.BufferedInputStream; Loading Loading @@ -72,32 +83,163 @@ public class ApkChecksums { static final String ALGO_SHA512 = "SHA512"; /** * Fetch or calculate checksums for the specific file. * Check back in 1 second after we detected we needed to wait for the APK to be fully available. */ private static final long PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS = 1000; /** * 24 hours timeout to wait till all files are loaded. */ private static final long PROCESS_REQUIRED_CHECKSUMS_TIMEOUT_MILLIS = 1000 * 3600 * 24; /** * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors. * * @param split split name, null for base * @param file to fetch checksums for * NOTE: All getters should return the same instance for every call. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) static class Injector { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) interface Producer<T> { /** Produce an instance of type {@link T} */ T produce(); } private final Producer<Context> mContext; private final Producer<Handler> mHandlerProducer; private final Producer<IncrementalManager> mIncrementalManagerProducer; Injector(Producer<Context> context, Producer<Handler> handlerProducer, Producer<IncrementalManager> incrementalManagerProducer) { mContext = context; mHandlerProducer = handlerProducer; mIncrementalManagerProducer = incrementalManagerProducer; } public Context getContext() { return mContext.produce(); } public Handler getHandler() { return mHandlerProducer.produce(); } public IncrementalManager getIncrementalManager() { return mIncrementalManagerProducer.produce(); } } /** * Fetch or calculate checksums for the collection of files. * * @param filesToChecksum split name, null for base and File to fetch checksums for * @param optional mask to fetch readily available checksums * @param required mask to forcefully calculate if not available * @param trustedInstallers array of certificate to trust, two specific cases: * null - trust anybody, * [] - trust nobody. * @param statusReceiver to receive the resulting checksums */ public static List<FileChecksum> getFileChecksums(String split, File file, public static void getChecksums(List<Pair<String, File>> filesToChecksum, @PackageManager.FileChecksumKind int optional, @PackageManager.FileChecksumKind int required, @Nullable Certificate[] trustedInstallers) { final String filePath = file.getAbsolutePath(); @Nullable Certificate[] trustedInstallers, @NonNull IntentSender statusReceiver, @NonNull Injector injector) { List<Map<Integer, FileChecksum>> result = new ArrayList<>(filesToChecksum.size()); for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { final String split = filesToChecksum.get(i).first; final File file = filesToChecksum.get(i).second; Map<Integer, FileChecksum> checksums = new ArrayMap<>(); final int kinds = (optional | required); // System enforced: FSI or v2/v3/v4 signatures. if ((kinds & WHOLE_MERKLE_ROOT_4K_SHA256) != 0) { result.add(checksums); try { getAvailableFileChecksums(split, file, optional | required, trustedInstallers, checksums); } catch (Throwable e) { Slog.e(TAG, "Preferred checksum calculation error", e); } } long startTime = SystemClock.uptimeMillis(); processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector, startTime); } private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum, List<Map<Integer, FileChecksum>> result, @PackageManager.FileChecksumKind int required, @NonNull IntentSender statusReceiver, @NonNull Injector injector, long startTime) { final boolean timeout = SystemClock.uptimeMillis() - startTime >= PROCESS_REQUIRED_CHECKSUMS_TIMEOUT_MILLIS; List<FileChecksum> allChecksums = new ArrayList<>(); for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { final String split = filesToChecksum.get(i).first; final File file = filesToChecksum.get(i).second; Map<Integer, FileChecksum> checksums = result.get(i); try { if (!timeout || required != 0) { if (needToWait(file, required, checksums, injector)) { // Not ready, come back later. injector.getHandler().postDelayed(() -> { processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector, startTime); }, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS); return; } getRequiredFileChecksums(split, file, required, checksums); } allChecksums.addAll(checksums.values()); } catch (Throwable e) { Slog.e(TAG, "Required checksum calculation error", e); } } final Intent intent = new Intent(); intent.putExtra(EXTRA_CHECKSUMS, allChecksums.toArray(new FileChecksum[allChecksums.size()])); try { statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null); } catch (IntentSender.SendIntentException e) { Slog.w(TAG, e); } } /** * Fetch readily available checksums - enforced by kernel or provided by Installer. * * @param split split name, null for base * @param file to fetch checksums for * @param kinds mask to fetch checksums * @param trustedInstallers array of certificate to trust, two specific cases: * null - trust anybody, * [] - trust nobody. * @param checksums resulting checksums */ private static void getAvailableFileChecksums(String split, File file, @PackageManager.FileChecksumKind int kinds, @Nullable Certificate[] trustedInstallers, Map<Integer, FileChecksum> checksums) { final String filePath = file.getAbsolutePath(); // Always available: FSI or IncFs. if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) { // Hashes in fs-verity and IncFS are always verified. FileChecksum checksum = extractHashFromFS(split, filePath); if (checksum != null) { checksums.put(checksum.getKind(), checksum); } } if ((kinds & (PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512)) != 0) { // System enforced: v2/v3. if (isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) || isRequired( PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) { Map<Integer, FileChecksum> v2v3checksums = extractHashFromV2V3Signature( split, filePath, kinds); if (v2v3checksums != null) { Loading @@ -106,11 +248,58 @@ public class ApkChecksums { } // TODO(b/160605420): Installer provided. // TODO(b/160605420): Wait for Incremental to be fully loaded. } /** * Whether the file is available for checksumming or we need to wait. */ private static boolean needToWait(File file, @PackageManager.FileChecksumKind int kinds, Map<Integer, FileChecksum> checksums, @NonNull Injector injector) throws IOException { if (!isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums) && !isRequired(WHOLE_MD5, kinds, checksums) && !isRequired(WHOLE_SHA1, kinds, checksums) && !isRequired(WHOLE_SHA256, kinds, checksums) && !isRequired(WHOLE_SHA512, kinds, checksums) && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) { return false; } final String filePath = file.getAbsolutePath(); if (!IncrementalManager.isIncrementalPath(filePath)) { return false; } IncrementalManager manager = injector.getIncrementalManager(); if (manager == null) { throw new IllegalStateException("IncrementalManager is missing."); } IncrementalStorage storage = manager.openStorage(filePath); if (storage == null) { throw new IllegalStateException( "IncrementalStorage is missing for a path on IncFs: " + filePath); } return !storage.isFileFullyLoaded(filePath); } /** * Fetch or calculate checksums for the specific file. * * @param split split name, null for base * @param file to fetch checksums for * @param kinds mask to forcefully calculate if not available * @param checksums resulting checksums */ private static void getRequiredFileChecksums(String split, File file, @PackageManager.FileChecksumKind int kinds, Map<Integer, FileChecksum> checksums) { final String filePath = file.getAbsolutePath(); // Manually calculating required checksums if not readily available. if ((required & WHOLE_MERKLE_ROOT_4K_SHA256) != 0 && !checksums.containsKey( WHOLE_MERKLE_ROOT_4K_SHA256)) { if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) { try { byte[] generatedRootHash = VerityBuilder.generateFsVerityRootHash( filePath, /*salt=*/null, Loading @@ -127,14 +316,23 @@ public class ApkChecksums { } } calculateChecksumIfRequested(checksums, split, file, required, WHOLE_MD5); calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA1); calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA256); calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA512); calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_MD5); calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA1); calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA256); calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA512); calculatePartialChecksumsIfRequested(checksums, split, file, required); calculatePartialChecksumsIfRequested(checksums, split, file, kinds); } return new ArrayList<>(checksums.values()); private static boolean isRequired(@PackageManager.FileChecksumKind int kind, @PackageManager.FileChecksumKind int kinds, Map<Integer, FileChecksum> checksums) { if ((kinds & kind) == 0) { return false; } if (checksums.containsKey(kind)) { return false; } return true; } private static FileChecksum extractHashFromFS(String split, String filePath) { Loading Loading @@ -170,8 +368,10 @@ public class ApkChecksums { PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, false).contentDigests; } catch (PackageParser.PackageParserException e) { if (!(e.getCause() instanceof SignatureNotFoundException)) { Slog.e(TAG, "Signature verification error", e); } } if (contentDigests == null) { return null; Loading
services/core/java/com/android/server/pm/PackageManagerService.java +15 −26 Original line number Diff line number Diff line Loading @@ -40,7 +40,6 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.EXTRA_CHECKSUMS; import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; Loading Loading @@ -169,7 +168,6 @@ import android.content.pm.ComponentInfo; import android.content.pm.DataLoaderType; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureInfo; import android.content.pm.FileChecksum; import android.content.pm.IDexModuleRegisterCallback; import android.content.pm.IPackageChangeObserver; import android.content.pm.IPackageDataObserver; Loading Loading @@ -933,6 +931,7 @@ public class PackageManagerService extends IPackageManager.Stub private final Object mLock; private final Installer mInstaller; private final Object mInstallLock; private final Handler mBackgroundHandler; private final Executor mBackgroundExecutor; // ----- producers ----- Loading @@ -955,7 +954,7 @@ public class PackageManagerService extends IPackageManager.Stub Injector(Context context, Object lock, Installer installer, Object installLock, PackageAbiHelper abiHelper, Executor backgroundExecutor, Handler backgroundHandler, Producer<ComponentResolver> componentResolverProducer, Producer<PermissionManagerServiceInternal> permissionManagerProducer, Producer<UserManagerService> userManagerProducer, Loading @@ -977,7 +976,8 @@ public class PackageManagerService extends IPackageManager.Stub mInstaller = installer; mAbiHelper = abiHelper; mInstallLock = installLock; mBackgroundExecutor = backgroundExecutor; mBackgroundHandler = backgroundHandler; mBackgroundExecutor = new HandlerExecutor(backgroundHandler); mComponentResolverProducer = new Singleton<>(componentResolverProducer); mPermissionManagerProducer = new Singleton<>(permissionManagerProducer); mUserManagerProducer = new Singleton<>(userManagerProducer); Loading Loading @@ -1092,6 +1092,10 @@ public class PackageManagerService extends IPackageManager.Stub return mPlatformCompatProducer.get(this, mPackageManager); } public Handler getBackgroundHandler() { return mBackgroundHandler; } public Executor getBackgroundExecutor() { return mBackgroundExecutor; } Loading Loading @@ -2489,29 +2493,14 @@ public class PackageManagerService extends IPackageManager.Stub final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates( trustedInstallers) : null; final Context context = mContext; mInjector.getBackgroundExecutor().execute(() -> { final Intent intent = new Intent(); List<FileChecksum> result = new ArrayList<>(); for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { final String split = filesToChecksum.get(i).first; final File file = filesToChecksum.get(i).second; try { result.addAll(ApkChecksums.getFileChecksums(split, file, optional, required, trustedCerts)); } catch (Throwable e) { Slog.e(TAG, "Checksum calculation error", e); } } intent.putExtra(EXTRA_CHECKSUMS, result.toArray(new FileChecksum[result.size()])); try { statusReceiver.sendIntent(context, 1, intent, null, null); } catch (SendIntentException e) { Slog.w(TAG, e); } ApkChecksums.Injector injector = new ApkChecksums.Injector( () -> mContext, () -> mInjector.getBackgroundHandler(), () -> mContext.getSystemService(IncrementalManager.class)); ApkChecksums.getChecksums(filesToChecksum, optional, required, trustedCerts, statusReceiver, injector); }); } Loading Loading @@ -2684,7 +2673,7 @@ public class PackageManagerService extends IPackageManager.Stub Injector injector = new Injector( context, lock, installer, installLock, new PackageAbiHelperImpl(), new HandlerExecutor(backgroundHandler), backgroundHandler, (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock), (i, pm) ->
services/core/java/com/android/server/pm/TEST_MAPPING +3 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,9 @@ { "name": "CtsContentTestCases", "options": [ { "include-filter": "android.content.pm.cts.ChecksumsTest" }, { "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest" }, Loading