Loading services/core/java/com/android/server/pm/PackageInstallerService.java +8 −2 Original line number Diff line number Diff line Loading @@ -126,6 +126,7 @@ import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.utils.RequestThrottle; import com.android.server.pm.verify.pkg.VerifierController; import libcore.io.IoUtils; Loading Loading @@ -213,6 +214,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private final StagingManager mStagingManager; private AppOpsManager mAppOps; private final VerifierController mVerifierController; private final HandlerThread mInstallThread; private final Handler mInstallHandler; Loading Loading @@ -325,6 +327,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mGentleUpdateHelper = new GentleUpdateHelper( context, mInstallThread.getLooper(), new AppStateHelper(context)); mPackageArchiver = new PackageArchiver(mContext, mPm); mVerifierController = new VerifierController(mContext, mInstallHandler); LocalServices.getService(SystemServiceManager.class).startService( new Lifecycle(context, this)); Loading Loading @@ -521,7 +524,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements try { session = PackageInstallerSession.readFromXml(in, mInternalCallback, mContext, mPm, mInstallThread.getLooper(), mStagingManager, mSessionsDir, this, mSilentUpdatePolicy); mSessionsDir, this, mSilentUpdatePolicy, mVerifierController); } catch (Exception e) { Slog.e(TAG, "Could not read session", e); continue; Loading Loading @@ -1037,7 +1041,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid, null, null, false, false, false, false, null, SessionInfo.INVALID_ID, false, false, false, PackageManager.INSTALL_UNKNOWN, "", null); false, false, false, PackageManager.INSTALL_UNKNOWN, "", null, mVerifierController); synchronized (mSessions) { mSessions.put(sessionId, session); Loading @@ -1047,6 +1052,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mCallbacks.notifySessionCreated(session.sessionId, session.userId); mSettingsWriteRequest.schedule(); if (LOGD) { Slog.d(TAG, "Created session id=" + sessionId + " staged=" + params.isStaged); } Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +119 −3 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_VERIFICATION_FAIL import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_STAGED; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.verify.pkg.VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN; import static android.os.Process.INVALID_UID; import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; import static android.system.OsConstants.O_CREAT; Loading Loading @@ -87,6 +88,7 @@ import android.content.pm.DataLoaderManager; import android.content.pm.DataLoaderParams; import android.content.pm.DataLoaderParamsParcel; import android.content.pm.FileSystemControlParcel; import android.content.pm.Flags; import android.content.pm.IDataLoader; import android.content.pm.IDataLoaderStatusListener; import android.content.pm.IOnChecksumsReadyListener; Loading @@ -108,6 +110,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.PackageManagerInternal; import android.content.pm.SigningDetails; import android.content.pm.SigningInfo; import android.content.pm.dex.DexMetadataHelper; import android.content.pm.parsing.ApkLite; import android.content.pm.parsing.ApkLiteParseUtils; Loading @@ -115,6 +118,7 @@ import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.content.pm.verify.domain.DomainSet; import android.content.pm.verify.pkg.VerificationStatus; import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; Loading @@ -122,6 +126,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.icu.util.ULocale; import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; Loading @@ -133,6 +138,7 @@ import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.ParcelableException; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.RevocableFileDescriptor; Loading Loading @@ -190,6 +196,7 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.dex.DexManager; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.verify.pkg.VerifierController; import libcore.io.IoUtils; import libcore.util.EmptyArray; Loading Loading @@ -218,6 +225,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.function.Supplier; public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String TAG = "PackageInstallerSession"; Loading Loading @@ -404,6 +412,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * Note all calls must be done outside {@link #mLock} to prevent lock inversion. */ private final StagingManager mStagingManager; @NonNull private final VerifierController mVerifierController; final int sessionId; final int userId; Loading Loading @@ -1156,7 +1165,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { boolean prepared, boolean committed, boolean destroyed, boolean sealed, @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, boolean isFailed, boolean isApplied, int sessionErrorCode, String sessionErrorMessage, DomainSet preVerifiedDomains) { String sessionErrorMessage, DomainSet preVerifiedDomains, @NonNull VerifierController verifierController) { mCallback = callback; mContext = context; mPm = pm; Loading @@ -1165,6 +1175,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSilentUpdatePolicy = silentUpdatePolicy; mHandler = new Handler(looper, mHandlerCallback); mStagingManager = stagingManager; mVerifierController = verifierController; this.sessionId = sessionId; this.userId = userId; Loading Loading @@ -1249,6 +1260,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Archived installation can only use Streaming System DataLoader."); } } if (Flags.verificationService()) { // Start binding to the verification service, if not bound already. mVerifierController.bindToVerifierServiceIfNeeded(() -> pm.snapshotComputer(), userId); if (!TextUtils.isEmpty(params.appPackageName)) { mVerifierController.notifyPackageNameAvailable(params.appPackageName); } } } PackageInstallerHistoricalSession createHistoricalSession() { Loading Loading @@ -2821,7 +2840,35 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // since installation is in progress. activate(); } if (Flags.verificationService()) { final Supplier<Computer> snapshotSupplier = mPm::snapshotComputer; if (mVerifierController.isVerifierInstalled(snapshotSupplier, userId)) { // TODO: extract shared library declarations final SigningInfo signingInfo; synchronized (mLock) { signingInfo = new SigningInfo(mSigningDetails); } // Send the request to the verifier and wait for its response before the rest of // the installation can proceed. if (!mVerifierController.startVerificationSession(snapshotSupplier, userId, sessionId, params.appPackageName, Uri.fromFile(stageDir), signingInfo, /* declaredLibraries= */null, /* extensionParams= */ null, new VerifierCallback(), /* retry= */ false)) { // A verifier is installed but cannot be connected. Installation disallowed. onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, "A verifier agent is available on device but cannot be connected."); } } else { // Verifier is not installed. Let the installation pass for now. resumeVerify(); } } else { // New verification feature is not enabled. Proceed to the rest of the verification. resumeVerify(); } } private void resumeVerify() { if (mVerificationInProgress) { Slog.w(TAG, "Verification is already in progress for session " + sessionId); return; Loading Loading @@ -2856,6 +2903,66 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } /** * Used for the VerifierController to report status back. */ public class VerifierCallback { /** * Called by the VerifierController when the connection has failed. */ public void onConnectionFailed() { mHandler.post(() -> { onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, "A verifier agent is available on device but cannot be connected."); }); } /** * Called by the VerifierController when the verification request has timed out. */ public void onTimeout() { mHandler.post(() -> { mVerifierController.notifyVerificationTimeout(sessionId); onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, "Verification timed out; missing a response from the verifier within the" + " time limit"); }); } /** * Called by the VerifierController when the verification request has received a complete * response. */ public void onVerificationCompleteReceived(@NonNull VerificationStatus statusReceived, @Nullable PersistableBundle extensionResponse) { // TODO: handle extension response mHandler.post(() -> { if (statusReceived.isVerified()) { // Continue with the rest of the verification and installation. resumeVerify(); } else { StringBuilder sb = new StringBuilder("Verifier rejected the installation"); if (!TextUtils.isEmpty(statusReceived.getFailureMessage())) { sb.append(" with message: ").append(statusReceived.getFailureMessage()); } onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, sb.toString()); } }); } /** * Called by the VerifierController when the verification request has received an incomplete * response. */ public void onVerificationIncompleteReceived(int incompleteReason) { mHandler.post(() -> { if (incompleteReason == VERIFICATION_INCOMPLETE_UNKNOWN) { // TODO: change this to a user confirmation and handle other incomplete reasons onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, "Verification cannot be completed for unknown reasons."); } }); } } private IntentSender getRemoteStatusReceiver() { synchronized (mLock) { return mRemoteStatusReceiver; Loading Loading @@ -5369,6 +5476,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } catch (InstallerException ignored) { } if (Flags.verificationService() && !TextUtils.isEmpty(params.appPackageName) && !isCommitted()) { // Only notify for the cancellation if the verification request has not // been sent out, which happens right after commit() is called. mVerifierController.notifyVerificationCancelled( params.appPackageName); } } void dump(IndentingPrintWriter pw) { Loading Loading @@ -5768,7 +5883,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @NonNull PackageManagerService pm, Looper installerThread, @NonNull StagingManager stagingManager, @NonNull File sessionsDir, @NonNull PackageSessionProvider sessionProvider, @NonNull SilentUpdatePolicy silentUpdatePolicy) @NonNull SilentUpdatePolicy silentUpdatePolicy, @NonNull VerifierController verifierController) throws IOException, XmlPullParserException { final int sessionId = in.getAttributeInt(null, ATTR_SESSION_ID); final int userId = in.getAttributeInt(null, ATTR_USER_ID); Loading Loading @@ -5972,6 +6088,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { installerUid, installSource, params, createdMillis, committedMillis, stageDir, stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied, sessionErrorCode, sessionErrorMessage, preVerifiedDomains); sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController); } } services/core/java/com/android/server/pm/verify/pkg/VerificationStatusTracker.java 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm.verify.pkg; import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; /** * This class keeps record of the current timeout status of a verification request. */ public final class VerificationStatusTracker { private final @CurrentTimeMillisLong long mStartTime; private @CurrentTimeMillisLong long mTimeoutTime; private final @CurrentTimeMillisLong long mMaxTimeoutTime; @NonNull private final VerifierController.Injector mInjector; // Record the package name associated with the verification result @NonNull private final String mPackageName; /** * By default, the timeout time is the default timeout duration plus the current time (when * the timer starts for a verification request). Both the default timeout time and the max * timeout time cannot be changed after the timer has started, but the actual timeout time * can be extended via {@link #extendTimeRemaining} to the maximum allowed. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) public VerificationStatusTracker(@NonNull String packageName, long defaultTimeoutMillis, long maxExtendedTimeoutMillis, @NonNull VerifierController.Injector injector) { mPackageName = packageName; mStartTime = injector.getCurrentTimeMillis(); mTimeoutTime = mStartTime + defaultTimeoutMillis; mMaxTimeoutTime = mStartTime + maxExtendedTimeoutMillis; mInjector = injector; } /** * Used by the controller to inform the verifier agent about the timestamp when the verification * request will timeout. */ public @CurrentTimeMillisLong long getTimeoutTime() { return mTimeoutTime; } /** * Used by the controller to decide when to check for timeout again. * @return 0 if the timeout time has been reached, otherwise the remaining time in milliseconds * before the timeout is reached. */ public @CurrentTimeMillisLong long getRemainingTime() { final long remainingTime = mTimeoutTime - mInjector.getCurrentTimeMillis(); if (remainingTime < 0) { return 0; } return remainingTime; } /** * Used by the controller to extend the timeout duration of the verification request, upon * receiving the callback from the verifier agent. * @return the amount of time in millis that the timeout has been extended, subject to the max * amount allowed. */ public long extendTimeRemaining(@CurrentTimeMillisLong long additionalMs) { if (mTimeoutTime + additionalMs > mMaxTimeoutTime) { additionalMs = mMaxTimeoutTime - mTimeoutTime; } mTimeoutTime += additionalMs; return additionalMs; } /** * Used by the controller to get the timeout status of the request. * @return False if the request still has some time left before timeout, otherwise return True. */ public boolean isTimeout() { return mInjector.getCurrentTimeMillis() >= mTimeoutTime; } @NonNull public String getPackageName() { return mPackageName; } } services/core/java/com/android/server/pm/verify/pkg/VerifierController.java 0 → 100644 +645 −0 File added.Preview size limit exceeded, changes collapsed. Show changes services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt +6 −3 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.util.AtomicFile import android.util.Slog import android.util.Xml import com.android.internal.os.BackgroundThread import com.android.server.pm.verify.pkg.VerifierController import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat import libcore.io.IoUtils Loading Loading @@ -195,7 +196,8 @@ class PackageInstallerSessionTest { /* isApplied */ false, /* stagedSessionErrorCode */ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, /* stagedSessionErrorMessage */ "some error", /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")) /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")), /* VerifierController */ mock(VerifierController::class.java) ) } Loading Loading @@ -249,7 +251,8 @@ class PackageInstallerSessionTest { mock(StagingManager::class.java), mTmpDir, mock(PackageSessionProvider::class.java), mock(SilentUpdatePolicy::class.java) mock(SilentUpdatePolicy::class.java), mock(VerifierController::class.java) ) ret.add(session) } catch (e: Exception) { Loading Loading
services/core/java/com/android/server/pm/PackageInstallerService.java +8 −2 Original line number Diff line number Diff line Loading @@ -126,6 +126,7 @@ import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.utils.RequestThrottle; import com.android.server.pm.verify.pkg.VerifierController; import libcore.io.IoUtils; Loading Loading @@ -213,6 +214,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private final StagingManager mStagingManager; private AppOpsManager mAppOps; private final VerifierController mVerifierController; private final HandlerThread mInstallThread; private final Handler mInstallHandler; Loading Loading @@ -325,6 +327,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mGentleUpdateHelper = new GentleUpdateHelper( context, mInstallThread.getLooper(), new AppStateHelper(context)); mPackageArchiver = new PackageArchiver(mContext, mPm); mVerifierController = new VerifierController(mContext, mInstallHandler); LocalServices.getService(SystemServiceManager.class).startService( new Lifecycle(context, this)); Loading Loading @@ -521,7 +524,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements try { session = PackageInstallerSession.readFromXml(in, mInternalCallback, mContext, mPm, mInstallThread.getLooper(), mStagingManager, mSessionsDir, this, mSilentUpdatePolicy); mSessionsDir, this, mSilentUpdatePolicy, mVerifierController); } catch (Exception e) { Slog.e(TAG, "Could not read session", e); continue; Loading Loading @@ -1037,7 +1041,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid, null, null, false, false, false, false, null, SessionInfo.INVALID_ID, false, false, false, PackageManager.INSTALL_UNKNOWN, "", null); false, false, false, PackageManager.INSTALL_UNKNOWN, "", null, mVerifierController); synchronized (mSessions) { mSessions.put(sessionId, session); Loading @@ -1047,6 +1052,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mCallbacks.notifySessionCreated(session.sessionId, session.userId); mSettingsWriteRequest.schedule(); if (LOGD) { Slog.d(TAG, "Created session id=" + sessionId + " staged=" + params.isStaged); } Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +119 −3 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_VERIFICATION_FAIL import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_STAGED; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.verify.pkg.VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN; import static android.os.Process.INVALID_UID; import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; import static android.system.OsConstants.O_CREAT; Loading Loading @@ -87,6 +88,7 @@ import android.content.pm.DataLoaderManager; import android.content.pm.DataLoaderParams; import android.content.pm.DataLoaderParamsParcel; import android.content.pm.FileSystemControlParcel; import android.content.pm.Flags; import android.content.pm.IDataLoader; import android.content.pm.IDataLoaderStatusListener; import android.content.pm.IOnChecksumsReadyListener; Loading @@ -108,6 +110,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.PackageManagerInternal; import android.content.pm.SigningDetails; import android.content.pm.SigningInfo; import android.content.pm.dex.DexMetadataHelper; import android.content.pm.parsing.ApkLite; import android.content.pm.parsing.ApkLiteParseUtils; Loading @@ -115,6 +118,7 @@ import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.content.pm.verify.domain.DomainSet; import android.content.pm.verify.pkg.VerificationStatus; import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; Loading @@ -122,6 +126,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.icu.util.ULocale; import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; Loading @@ -133,6 +138,7 @@ import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.ParcelableException; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.RevocableFileDescriptor; Loading Loading @@ -190,6 +196,7 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.dex.DexManager; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.verify.pkg.VerifierController; import libcore.io.IoUtils; import libcore.util.EmptyArray; Loading Loading @@ -218,6 +225,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.function.Supplier; public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String TAG = "PackageInstallerSession"; Loading Loading @@ -404,6 +412,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * Note all calls must be done outside {@link #mLock} to prevent lock inversion. */ private final StagingManager mStagingManager; @NonNull private final VerifierController mVerifierController; final int sessionId; final int userId; Loading Loading @@ -1156,7 +1165,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { boolean prepared, boolean committed, boolean destroyed, boolean sealed, @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, boolean isFailed, boolean isApplied, int sessionErrorCode, String sessionErrorMessage, DomainSet preVerifiedDomains) { String sessionErrorMessage, DomainSet preVerifiedDomains, @NonNull VerifierController verifierController) { mCallback = callback; mContext = context; mPm = pm; Loading @@ -1165,6 +1175,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSilentUpdatePolicy = silentUpdatePolicy; mHandler = new Handler(looper, mHandlerCallback); mStagingManager = stagingManager; mVerifierController = verifierController; this.sessionId = sessionId; this.userId = userId; Loading Loading @@ -1249,6 +1260,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Archived installation can only use Streaming System DataLoader."); } } if (Flags.verificationService()) { // Start binding to the verification service, if not bound already. mVerifierController.bindToVerifierServiceIfNeeded(() -> pm.snapshotComputer(), userId); if (!TextUtils.isEmpty(params.appPackageName)) { mVerifierController.notifyPackageNameAvailable(params.appPackageName); } } } PackageInstallerHistoricalSession createHistoricalSession() { Loading Loading @@ -2821,7 +2840,35 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // since installation is in progress. activate(); } if (Flags.verificationService()) { final Supplier<Computer> snapshotSupplier = mPm::snapshotComputer; if (mVerifierController.isVerifierInstalled(snapshotSupplier, userId)) { // TODO: extract shared library declarations final SigningInfo signingInfo; synchronized (mLock) { signingInfo = new SigningInfo(mSigningDetails); } // Send the request to the verifier and wait for its response before the rest of // the installation can proceed. if (!mVerifierController.startVerificationSession(snapshotSupplier, userId, sessionId, params.appPackageName, Uri.fromFile(stageDir), signingInfo, /* declaredLibraries= */null, /* extensionParams= */ null, new VerifierCallback(), /* retry= */ false)) { // A verifier is installed but cannot be connected. Installation disallowed. onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, "A verifier agent is available on device but cannot be connected."); } } else { // Verifier is not installed. Let the installation pass for now. resumeVerify(); } } else { // New verification feature is not enabled. Proceed to the rest of the verification. resumeVerify(); } } private void resumeVerify() { if (mVerificationInProgress) { Slog.w(TAG, "Verification is already in progress for session " + sessionId); return; Loading Loading @@ -2856,6 +2903,66 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } /** * Used for the VerifierController to report status back. */ public class VerifierCallback { /** * Called by the VerifierController when the connection has failed. */ public void onConnectionFailed() { mHandler.post(() -> { onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, "A verifier agent is available on device but cannot be connected."); }); } /** * Called by the VerifierController when the verification request has timed out. */ public void onTimeout() { mHandler.post(() -> { mVerifierController.notifyVerificationTimeout(sessionId); onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, "Verification timed out; missing a response from the verifier within the" + " time limit"); }); } /** * Called by the VerifierController when the verification request has received a complete * response. */ public void onVerificationCompleteReceived(@NonNull VerificationStatus statusReceived, @Nullable PersistableBundle extensionResponse) { // TODO: handle extension response mHandler.post(() -> { if (statusReceived.isVerified()) { // Continue with the rest of the verification and installation. resumeVerify(); } else { StringBuilder sb = new StringBuilder("Verifier rejected the installation"); if (!TextUtils.isEmpty(statusReceived.getFailureMessage())) { sb.append(" with message: ").append(statusReceived.getFailureMessage()); } onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, sb.toString()); } }); } /** * Called by the VerifierController when the verification request has received an incomplete * response. */ public void onVerificationIncompleteReceived(int incompleteReason) { mHandler.post(() -> { if (incompleteReason == VERIFICATION_INCOMPLETE_UNKNOWN) { // TODO: change this to a user confirmation and handle other incomplete reasons onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, "Verification cannot be completed for unknown reasons."); } }); } } private IntentSender getRemoteStatusReceiver() { synchronized (mLock) { return mRemoteStatusReceiver; Loading Loading @@ -5369,6 +5476,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } catch (InstallerException ignored) { } if (Flags.verificationService() && !TextUtils.isEmpty(params.appPackageName) && !isCommitted()) { // Only notify for the cancellation if the verification request has not // been sent out, which happens right after commit() is called. mVerifierController.notifyVerificationCancelled( params.appPackageName); } } void dump(IndentingPrintWriter pw) { Loading Loading @@ -5768,7 +5883,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @NonNull PackageManagerService pm, Looper installerThread, @NonNull StagingManager stagingManager, @NonNull File sessionsDir, @NonNull PackageSessionProvider sessionProvider, @NonNull SilentUpdatePolicy silentUpdatePolicy) @NonNull SilentUpdatePolicy silentUpdatePolicy, @NonNull VerifierController verifierController) throws IOException, XmlPullParserException { final int sessionId = in.getAttributeInt(null, ATTR_SESSION_ID); final int userId = in.getAttributeInt(null, ATTR_USER_ID); Loading Loading @@ -5972,6 +6088,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { installerUid, installSource, params, createdMillis, committedMillis, stageDir, stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied, sessionErrorCode, sessionErrorMessage, preVerifiedDomains); sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController); } }
services/core/java/com/android/server/pm/verify/pkg/VerificationStatusTracker.java 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm.verify.pkg; import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; /** * This class keeps record of the current timeout status of a verification request. */ public final class VerificationStatusTracker { private final @CurrentTimeMillisLong long mStartTime; private @CurrentTimeMillisLong long mTimeoutTime; private final @CurrentTimeMillisLong long mMaxTimeoutTime; @NonNull private final VerifierController.Injector mInjector; // Record the package name associated with the verification result @NonNull private final String mPackageName; /** * By default, the timeout time is the default timeout duration plus the current time (when * the timer starts for a verification request). Both the default timeout time and the max * timeout time cannot be changed after the timer has started, but the actual timeout time * can be extended via {@link #extendTimeRemaining} to the maximum allowed. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) public VerificationStatusTracker(@NonNull String packageName, long defaultTimeoutMillis, long maxExtendedTimeoutMillis, @NonNull VerifierController.Injector injector) { mPackageName = packageName; mStartTime = injector.getCurrentTimeMillis(); mTimeoutTime = mStartTime + defaultTimeoutMillis; mMaxTimeoutTime = mStartTime + maxExtendedTimeoutMillis; mInjector = injector; } /** * Used by the controller to inform the verifier agent about the timestamp when the verification * request will timeout. */ public @CurrentTimeMillisLong long getTimeoutTime() { return mTimeoutTime; } /** * Used by the controller to decide when to check for timeout again. * @return 0 if the timeout time has been reached, otherwise the remaining time in milliseconds * before the timeout is reached. */ public @CurrentTimeMillisLong long getRemainingTime() { final long remainingTime = mTimeoutTime - mInjector.getCurrentTimeMillis(); if (remainingTime < 0) { return 0; } return remainingTime; } /** * Used by the controller to extend the timeout duration of the verification request, upon * receiving the callback from the verifier agent. * @return the amount of time in millis that the timeout has been extended, subject to the max * amount allowed. */ public long extendTimeRemaining(@CurrentTimeMillisLong long additionalMs) { if (mTimeoutTime + additionalMs > mMaxTimeoutTime) { additionalMs = mMaxTimeoutTime - mTimeoutTime; } mTimeoutTime += additionalMs; return additionalMs; } /** * Used by the controller to get the timeout status of the request. * @return False if the request still has some time left before timeout, otherwise return True. */ public boolean isTimeout() { return mInjector.getCurrentTimeMillis() >= mTimeoutTime; } @NonNull public String getPackageName() { return mPackageName; } }
services/core/java/com/android/server/pm/verify/pkg/VerifierController.java 0 → 100644 +645 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt +6 −3 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.util.AtomicFile import android.util.Slog import android.util.Xml import com.android.internal.os.BackgroundThread import com.android.server.pm.verify.pkg.VerifierController import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat import libcore.io.IoUtils Loading Loading @@ -195,7 +196,8 @@ class PackageInstallerSessionTest { /* isApplied */ false, /* stagedSessionErrorCode */ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, /* stagedSessionErrorMessage */ "some error", /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")) /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")), /* VerifierController */ mock(VerifierController::class.java) ) } Loading Loading @@ -249,7 +251,8 @@ class PackageInstallerSessionTest { mock(StagingManager::class.java), mTmpDir, mock(PackageSessionProvider::class.java), mock(SilentUpdatePolicy::class.java) mock(SilentUpdatePolicy::class.java), mock(VerifierController::class.java) ) ret.add(session) } catch (e: Exception) { Loading