Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +121 −28 Original line number Diff line number Diff line Loading @@ -456,12 +456,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * assigned to this session or the default policy currently used by the system. */ private final AtomicInteger mCurrentVerificationPolicy; /** * Tracks how many times the developer verification has been retried as requested by the user. */ private AtomicInteger mDeveloperVerificationRetryCount = new AtomicInteger(0); /** * Note all calls must be done outside {@link #mLock} to prevent lock inversion. */ private final StagingManager mStagingManager; @NonNull private final DeveloperVerifierController mDeveloperVerifierController; private final DeveloperVerifierCallback mDeveloperVerifierCallback = new DeveloperVerifierCallback(); private final InstallDependencyHelper mInstallDependencyHelper; Loading Loading @@ -1377,16 +1383,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mMetrics = new SessionMetrics(mHandler, sessionId, userId, installerUid, params, createdMillis, committedMillis, committed, childSessionIds, parentSessionId, sessionErrorCode); sessionErrorCode, mInitialVerificationPolicy); if (shouldUseVerificationService()) { // Start binding to the verification service, if not bound already. mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mPm::snapshotComputer, userId); userId, mDeveloperVerifierCallback); if (!TextUtils.isEmpty(params.appPackageName)) { // Opportunistically notify verifier about package name so no need to check results. mDeveloperVerifierController.notifyPackageNameAvailable(params.appPackageName, userId); } synchronized (mMetrics) { mMetrics.onDeveloperVerificationBindStarted(); } } } Loading Loading @@ -2734,6 +2744,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void onSessionVerificationFailure(int error, String msg, Bundle extras) { Slog.e(TAG, "Failed to verify session " + sessionId); // Logging of developer verification failure. if (extras != null && extras.containsKey(EXTRA_DEVELOPER_VERIFICATION_FAILURE_REASON)) { final String packageName = getPackageName(); synchronized (mMetrics) { mMetrics.onDeveloperVerificationFailed( extras.getInt(EXTRA_DEVELOPER_VERIFICATION_FAILURE_REASON), packageName); } } // Dispatch message to remove session from PackageInstallerService. dispatchSessionFinished(error, msg, extras); maybeFinishChildSessions(error, msg); Loading Loading @@ -3030,15 +3048,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } // Send the request to the verifier and wait for its response before the rest of // the installation can proceed. final VerifierCallback verifierCallback = new VerifierCallback(); if (!mDeveloperVerifierController.startVerificationSession(mPm::snapshotComputer, userId, sessionId, getPackageName(), stageDir == null ? Uri.EMPTY : Uri.fromFile(stageDir), signingInfo, declaredLibraries, mCurrentVerificationPolicy.get(), /* extensionParams= */ params.extensionParams, verifierCallback, /* retry= */ false)) { // A verifier is installed but cannot be connected. verifierCallback.onConnectionFailed(); mDeveloperVerifierCallback, /* retry= */ false)) { // A verifier is installed but cannot be connected. Maybe notify user. mDeveloperVerifierCallback.onConnectionInfeasible(); } synchronized (mMetrics) { mMetrics.onDeveloperVerificationRequestSent(); } } else { // No need to check with verifier. Proceed with the rest of the verification. Loading @@ -3058,6 +3078,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if ((params.installFlags & PackageManager.INSTALL_FROM_ADB) != 0) { // adb installs are exempted from verification unless explicitly requested if (!params.forceVerification) { synchronized (mMetrics) { mMetrics.onDeveloperVerificationBypassedByAdb(); } return false; } } Loading @@ -3072,7 +3095,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return true; } private void startVerificationSession(Supplier<Computer> snapshotSupplier, boolean retry) { private void retryDeveloperVerificationSession(Supplier<Computer> snapshotSupplier) { final SigningInfo signingInfo; final List<SharedLibraryInfo> declaredLibraries; synchronized (mLock) { Loading @@ -3080,22 +3103,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { declaredLibraries = mPackageLite == null ? null : mPackageLite.getDeclaredLibraries(); } // TODO (b/360130528): limit the number of times user can retry mDeveloperVerificationRetryCount.getAndIncrement(); // Send the request to the verifier and wait for its response before the rest of // the installation can proceed. final VerifierCallback verifierCallback = new VerifierCallback(); if (!mDeveloperVerifierController.startVerificationSession(snapshotSupplier, userId, sessionId, getPackageName(), stageDir == null ? Uri.EMPTY : Uri.fromFile(stageDir), signingInfo, declaredLibraries, mCurrentVerificationPolicy.get(), /* extensionParams= */ null, verifierCallback, retry)) { // A verifier is installed but cannot be connected. verifierCallback.onConnectionFailed(); String errorMsg = "A verifier agent is available on device but cannot be connected."; setSessionFailed(INSTALL_FAILED_INTERNAL_ERROR, errorMsg); onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, errorMsg, /* extras= */ null); // TODO (b/360130528): maybe show error dialog to the user mDeveloperVerifierCallback, /* retry = */ true)) { // A verifier is installed but cannot be connected. Maybe prompt the user again. mDeveloperVerifierCallback.onConnectionInfeasible(); } synchronized (mMetrics) { mMetrics.onDeveloperVerificationRetryRequestSent( mDeveloperVerificationRetryCount.get()); } } Loading Loading @@ -3167,34 +3189,68 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** * Used for the VerifierController to report status back. */ public class VerifierCallback { public class DeveloperVerifierCallback { /** * Called by the VerifierController when the verifier requests to get the current * verification policy for this session. * Called by the VerifierController to indicate that the verifier has been connected. Only * used for metrics logging for now. */ public @PackageInstaller.DeveloperVerificationPolicy int getVerificationPolicy() { return mCurrentVerificationPolicy.get(); public void onConnectionEstablished(int verifierUid) { synchronized (mMetrics) { mMetrics.onDeveloperVerifierConnectionEstablished(verifierUid); } } /** * Called by the VerifierController when the verifier requests to change the verification * policy for this session. */ public boolean setVerificationPolicy( public boolean onVerificationPolicyOverridden( @PackageInstaller.DeveloperVerificationPolicy int policy) { if (!isValidVerificationPolicy(policy)) { return false; } mCurrentVerificationPolicy.set(policy); synchronized (mMetrics) { mMetrics.onDeveloperVerificationPolicyOverridden(policy); } return true; } /** * Called by the session itself when the verifier cannot be connected because of infeasible * reasons such as it's not installed on the target user. */ private void onConnectionInfeasible() { mHandler.post(() -> { synchronized (mMetrics) { mMetrics.onDeveloperVerifierResponseReceived( SessionMetrics.DeveloperVerifierResponse.OTHER); } if (mCurrentVerificationPolicy.get() != DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED) { // Continue with the rest of the verification and installation. resumeVerify(); return; } mVerificationUserActionNeededReason = DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_UNKNOWN; mVerificationFailedMessage = "A verifier agent is specified on device but cannot " + "be connected because of unknown error."; maybeSendUserActionForVerification(/* blockingFailure= */ false, /* extensionResponse= */ null); }); } /** * Called by the VerifierController when the connection has failed. */ public void onConnectionFailed() { mHandler.post(() -> { synchronized (mMetrics) { mMetrics.onDeveloperVerifierResponseReceived( SessionMetrics.DeveloperVerifierResponse.DISCONNECTED); } if (mCurrentVerificationPolicy.get() != DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED) { // Continue with the rest of the verification and installation. Loading @@ -3211,6 +3267,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { }); } /** * Called by the VerifierController when the verifier has requested a timeout extension. */ public void onTimeoutExtensionRequested() { synchronized (mMetrics) { mMetrics.onDeveloperVerificationTimeoutExtensionRequested(); } } /** * Called by the VerifierController when the verification request has timed out. */ Loading @@ -3218,6 +3283,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Always notify the verifier, regardless of the policy. mDeveloperVerifierController.notifyVerificationTimeout(sessionId, userId); mHandler.post(() -> { synchronized (mMetrics) { mMetrics.onDeveloperVerifierResponseReceived( SessionMetrics.DeveloperVerifierResponse.TIMEOUT); } if (mCurrentVerificationPolicy.get() != DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED) { // Continue with the rest of the verification and installation. Loading @@ -3242,6 +3311,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @NonNull DeveloperVerificationStatus statusReceived, @Nullable PersistableBundle extensionResponse) { mHandler.post(() -> { synchronized (mMetrics) { mMetrics.onDeveloperVerifierResponseReceived(statusReceived.isVerified() ? SessionMetrics.DeveloperVerifierResponse.COMPLETE_WITH_PASS : SessionMetrics.DeveloperVerifierResponse.COMPLETE_WITH_REJECT); mMetrics.onAslStatusReceived(statusReceived.getAslStatus()); } if (mCurrentVerificationPolicy.get() == DEVELOPER_VERIFICATION_POLICY_NONE) { // No policy applied. Continue with the rest of the verification and install. resumeVerify(); Loading @@ -3250,6 +3325,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (statusReceived.isVerified()) { if (statusReceived.isLite()) { mVerificationLiteEnabled = true; synchronized (mMetrics) { mMetrics.onDeveloperVerificationLiteEnabled(); } // This is a lite verification. Need further user action. mVerificationUserActionNeededReason = DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_LITE_VERIFICATION; Loading Loading @@ -3286,6 +3364,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { */ public void onVerificationIncompleteReceived(int incompleteReason) { mHandler.post(() -> { final boolean isNetworkUnavailable = incompleteReason == DEVELOPER_VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE; synchronized (mMetrics) { mMetrics.onDeveloperVerifierResponseReceived(isNetworkUnavailable ? SessionMetrics.DeveloperVerifierResponse.INCOMPLETE_NETWORK : SessionMetrics.DeveloperVerifierResponse.INCOMPLETE_UNKNOWN); } if (mCurrentVerificationPolicy.get() == DEVELOPER_VERIFICATION_POLICY_NONE) { // Continue with the rest of the verification and installation. resumeVerify(); Loading @@ -3294,7 +3379,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { StringBuilder sb = new StringBuilder( "Verification cannot be completed because of "); if (incompleteReason == DEVELOPER_VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE) { if (isNetworkUnavailable) { mVerificationUserActionNeededReason = DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_NETWORK_UNAVAILABLE; sb.append("unavailable network."); Loading @@ -3316,6 +3401,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mVerificationUserActionNeeded = true; sendOnUserActionRequired(mContext, getRemoteStatusReceiver(), sessionId, intent); synchronized (mMetrics) { mMetrics.onDeveloperVerificationUserActionRequired( mVerificationUserActionNeededReason); } return; } // Not sending user action. Directly return the failure to the installer. Loading Loading @@ -5021,7 +5110,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { + "SessionID: " + sessionId); return; } synchronized (mMetrics) { mMetrics.onDeveloperVerificationUserResponseReceived(userResponse); } switch (userResponse) { case DEVELOPER_VERIFICATION_USER_RESPONSE_ERROR -> { String errorMsg = "User could not be notified about the pending verification."; Loading Loading @@ -5060,8 +5151,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } case DEVELOPER_VERIFICATION_USER_RESPONSE_RETRY -> { // TODO (b/360130528): limit the number of times user can retry startVerificationSession(mPm::snapshotComputer, /* retry= */ true); retryDeveloperVerificationSession(mPm::snapshotComputer /* retry= */); } case DEVELOPER_VERIFICATION_USER_RESPONSE_INSTALL_ANYWAY -> resumeVerify(); Loading Loading @@ -6076,6 +6166,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // been sent out, which happens right after commit() is called. mDeveloperVerifierController.notifyVerificationCancelled( params.appPackageName, userId); synchronized (mMetrics) { mMetrics.onDeveloperVerificationCancelled(); } } } Loading services/core/java/com/android/server/pm/SessionMetrics.java +259 −21 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/pm/verify/developer/DeveloperVerifierController.java +13 −8 Original line number Diff line number Diff line Loading @@ -183,9 +183,10 @@ public class DeveloperVerifierController { * If the verifier agent exists but cannot be started for some reason, all the notify* methods * in this class will fail asynchronously and quietly. The system will learn about the failure * after receiving the failure from * {@link PackageInstallerSession.VerifierCallback#onConnectionFailed}. * {@link PackageInstallerSession.DeveloperVerifierCallback#onConnectionFailed}. */ public boolean bindToVerifierServiceIfNeeded(Supplier<Computer> snapshotSupplier, int userId) { public boolean bindToVerifierServiceIfNeeded(Supplier<Computer> snapshotSupplier, int userId, PackageInstallerSession.DeveloperVerifierCallback callback) { if (DEBUG) { Slog.i(TAG, "Requesting to bind to the verifier service for user " + userId); } Loading Loading @@ -230,6 +231,8 @@ public class DeveloperVerifierController { public void onConnected(@NonNull IDeveloperVerifierService service) { Slog.i(TAG, "Verifier " + verifierPackageName + " is connected" + " on user " + userId); // Logging the success of connecting to the verifier. callback.onConnectionEstablished(verifierUid); // Aggressively auto-disconnect until verification requests are sent out startAutoDisconnectCountdown( remoteServiceWrapper.getAutoDisconnectCallback()); Loading Loading @@ -354,10 +357,10 @@ public class DeveloperVerifierController { List<SharedLibraryInfo> declaredLibraries, @PackageInstaller.DeveloperVerificationPolicy int verificationPolicy, @Nullable PersistableBundle extensionParams, PackageInstallerSession.VerifierCallback callback, PackageInstallerSession.DeveloperVerifierCallback callback, boolean retry) { // Try connecting to the verifier if not already connected if (!bindToVerifierServiceIfNeeded(snapshotSupplier, userId)) { if (!bindToVerifierServiceIfNeeded(snapshotSupplier, userId, callback)) { return false; } // For now, the verification id is the same as the installation session id. Loading Loading @@ -425,7 +428,7 @@ public class DeveloperVerifierController { private void startTimeoutCountdown(int verificationId, DeveloperVerificationRequestStatusTracker tracker, PackageInstallerSession.VerifierCallback callback, long delayMillis) { PackageInstallerSession.DeveloperVerifierCallback callback, long delayMillis) { mHandler.postDelayed(() -> { if (DEBUG) { Slog.i(TAG, "Checking request timeout for " + verificationId); Loading Loading @@ -538,9 +541,10 @@ public class DeveloperVerifierController { // This class handles requests from the remote verifier private class DeveloperVerificationSessionInterface extends IDeveloperVerificationSessionInterface.Stub { private final PackageInstallerSession.VerifierCallback mCallback; private final PackageInstallerSession.DeveloperVerifierCallback mCallback; DeveloperVerificationSessionInterface(PackageInstallerSession.VerifierCallback callback) { DeveloperVerificationSessionInterface( PackageInstallerSession.DeveloperVerifierCallback callback) { mCallback = callback; } Loading Loading @@ -569,6 +573,7 @@ public class DeveloperVerifierController { throw new IllegalStateException("Verification session " + verificationId + " doesn't exist or has finished"); } mCallback.onTimeoutExtensionRequested(); return tracker.extendTimeRemaining(additionalMs); } } Loading @@ -585,7 +590,7 @@ public class DeveloperVerifierController { + " doesn't exist or has finished"); } } return mCallback.setVerificationPolicy(policy); return mCallback.onVerificationPolicyOverridden(policy); } @Override Loading services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/developer/DeveloperVerifierControllerTest.java +12 −10 Original line number Diff line number Diff line Loading @@ -123,9 +123,9 @@ public class DeveloperVerifierControllerTest { Computer mSnapshot; Supplier<Computer> mSnapshotSupplier = () -> mSnapshot; @Mock PackageInstallerSession.VerifierCallback mSessionCallback; PackageInstallerSession.DeveloperVerifierCallback mSessionCallback; @Mock PackageInstallerSession.VerifierCallback mSessionCallbackSecondaryUser; PackageInstallerSession.DeveloperVerifierCallback mSessionCallbackSecondaryUser; private DeveloperVerifierController mDeveloperVerifierController; private String mPackageName; Loading Loading @@ -187,14 +187,15 @@ public class DeveloperVerifierControllerTest { public void testRebindService() { ArgumentCaptor<ServiceConnector.ServiceLifecycleCallbacks> captor = ArgumentCaptor.forClass( ServiceConnector.ServiceLifecycleCallbacks.class); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0)) .isTrue(); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0, mSessionCallback)).isTrue(); verify(mMockServiceConnector).setServiceLifecycleCallbacks(captor.capture()); ServiceConnector.ServiceLifecycleCallbacks<IDeveloperVerifierService> callbacks = captor.getValue(); // Verify that the countdown to auto-disconnect has started ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); callbacks.onConnected(mMockService); verify(mSessionCallback, times(1)).onConnectionEstablished(anyInt()); verify(mInjector, times(1)).removeCallbacks(eq(mHandler), runnableCaptor.capture()); Runnable autoDisconnectRunnable = runnableCaptor.getValue(); Loading @@ -207,8 +208,8 @@ public class DeveloperVerifierControllerTest { when(mSnapshot.getPackageUidInternal( eq(mPackageName), anyLong(), anyInt(), anyInt() )).thenReturn(INVALID_UID); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0)) .isFalse(); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0, mSessionCallback)).isFalse(); // Test that nothing crashes if the verifier is available even though there's no bound mDeveloperVerifierController.notifyPackageNameAvailable(TEST_PACKAGE_NAME, 0); mDeveloperVerifierController.notifyVerificationCancelled(TEST_PACKAGE_NAME, 0); Loading @@ -221,8 +222,8 @@ public class DeveloperVerifierControllerTest { public void testUnbindService() throws Exception { ArgumentCaptor<ServiceConnector.ServiceLifecycleCallbacks> captor = ArgumentCaptor.forClass( ServiceConnector.ServiceLifecycleCallbacks.class); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0)) .isTrue(); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0, mSessionCallback)).isTrue(); verify(mMockServiceConnector).setServiceLifecycleCallbacks(captor.capture()); ServiceConnector.ServiceLifecycleCallbacks<IDeveloperVerifierService> callbacks = captor.getValue(); Loading Loading @@ -523,6 +524,7 @@ public class DeveloperVerifierControllerTest { final long extendTimeMillis = TEST_TIMEOUT_DURATION_MILLIS; assertThat(session.extendTimeRemaining(extendTimeMillis)).isEqualTo(extendTimeMillis); assertThat(session.getTimeoutTime()).isEqualTo(initialTimeoutTime + extendTimeMillis); verify(mSessionCallback, times(1)).onTimeoutExtensionRequested(); } @Test Loading Loading @@ -588,10 +590,10 @@ public class DeveloperVerifierControllerTest { verify(mMockService).onVerificationRequired(captor.capture()); DeveloperVerificationSession session = captor.getValue(); final int policy = DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_OPEN; when(mSessionCallback.setVerificationPolicy(eq(policy))).thenReturn(true); when(mSessionCallback.onVerificationPolicyOverridden(eq(policy))).thenReturn(true); assertThat(session.setVerificationPolicy(policy)).isTrue(); assertThat(session.getVerificationPolicy()).isEqualTo(policy); verify(mSessionCallback, times(1)).setVerificationPolicy(eq(policy)); verify(mSessionCallback, times(1)).onVerificationPolicyOverridden(eq(policy)); } @Test Loading Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +121 −28 Original line number Diff line number Diff line Loading @@ -456,12 +456,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * assigned to this session or the default policy currently used by the system. */ private final AtomicInteger mCurrentVerificationPolicy; /** * Tracks how many times the developer verification has been retried as requested by the user. */ private AtomicInteger mDeveloperVerificationRetryCount = new AtomicInteger(0); /** * Note all calls must be done outside {@link #mLock} to prevent lock inversion. */ private final StagingManager mStagingManager; @NonNull private final DeveloperVerifierController mDeveloperVerifierController; private final DeveloperVerifierCallback mDeveloperVerifierCallback = new DeveloperVerifierCallback(); private final InstallDependencyHelper mInstallDependencyHelper; Loading Loading @@ -1377,16 +1383,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mMetrics = new SessionMetrics(mHandler, sessionId, userId, installerUid, params, createdMillis, committedMillis, committed, childSessionIds, parentSessionId, sessionErrorCode); sessionErrorCode, mInitialVerificationPolicy); if (shouldUseVerificationService()) { // Start binding to the verification service, if not bound already. mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mPm::snapshotComputer, userId); userId, mDeveloperVerifierCallback); if (!TextUtils.isEmpty(params.appPackageName)) { // Opportunistically notify verifier about package name so no need to check results. mDeveloperVerifierController.notifyPackageNameAvailable(params.appPackageName, userId); } synchronized (mMetrics) { mMetrics.onDeveloperVerificationBindStarted(); } } } Loading Loading @@ -2734,6 +2744,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void onSessionVerificationFailure(int error, String msg, Bundle extras) { Slog.e(TAG, "Failed to verify session " + sessionId); // Logging of developer verification failure. if (extras != null && extras.containsKey(EXTRA_DEVELOPER_VERIFICATION_FAILURE_REASON)) { final String packageName = getPackageName(); synchronized (mMetrics) { mMetrics.onDeveloperVerificationFailed( extras.getInt(EXTRA_DEVELOPER_VERIFICATION_FAILURE_REASON), packageName); } } // Dispatch message to remove session from PackageInstallerService. dispatchSessionFinished(error, msg, extras); maybeFinishChildSessions(error, msg); Loading Loading @@ -3030,15 +3048,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } // Send the request to the verifier and wait for its response before the rest of // the installation can proceed. final VerifierCallback verifierCallback = new VerifierCallback(); if (!mDeveloperVerifierController.startVerificationSession(mPm::snapshotComputer, userId, sessionId, getPackageName(), stageDir == null ? Uri.EMPTY : Uri.fromFile(stageDir), signingInfo, declaredLibraries, mCurrentVerificationPolicy.get(), /* extensionParams= */ params.extensionParams, verifierCallback, /* retry= */ false)) { // A verifier is installed but cannot be connected. verifierCallback.onConnectionFailed(); mDeveloperVerifierCallback, /* retry= */ false)) { // A verifier is installed but cannot be connected. Maybe notify user. mDeveloperVerifierCallback.onConnectionInfeasible(); } synchronized (mMetrics) { mMetrics.onDeveloperVerificationRequestSent(); } } else { // No need to check with verifier. Proceed with the rest of the verification. Loading @@ -3058,6 +3078,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if ((params.installFlags & PackageManager.INSTALL_FROM_ADB) != 0) { // adb installs are exempted from verification unless explicitly requested if (!params.forceVerification) { synchronized (mMetrics) { mMetrics.onDeveloperVerificationBypassedByAdb(); } return false; } } Loading @@ -3072,7 +3095,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return true; } private void startVerificationSession(Supplier<Computer> snapshotSupplier, boolean retry) { private void retryDeveloperVerificationSession(Supplier<Computer> snapshotSupplier) { final SigningInfo signingInfo; final List<SharedLibraryInfo> declaredLibraries; synchronized (mLock) { Loading @@ -3080,22 +3103,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { declaredLibraries = mPackageLite == null ? null : mPackageLite.getDeclaredLibraries(); } // TODO (b/360130528): limit the number of times user can retry mDeveloperVerificationRetryCount.getAndIncrement(); // Send the request to the verifier and wait for its response before the rest of // the installation can proceed. final VerifierCallback verifierCallback = new VerifierCallback(); if (!mDeveloperVerifierController.startVerificationSession(snapshotSupplier, userId, sessionId, getPackageName(), stageDir == null ? Uri.EMPTY : Uri.fromFile(stageDir), signingInfo, declaredLibraries, mCurrentVerificationPolicy.get(), /* extensionParams= */ null, verifierCallback, retry)) { // A verifier is installed but cannot be connected. verifierCallback.onConnectionFailed(); String errorMsg = "A verifier agent is available on device but cannot be connected."; setSessionFailed(INSTALL_FAILED_INTERNAL_ERROR, errorMsg); onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, errorMsg, /* extras= */ null); // TODO (b/360130528): maybe show error dialog to the user mDeveloperVerifierCallback, /* retry = */ true)) { // A verifier is installed but cannot be connected. Maybe prompt the user again. mDeveloperVerifierCallback.onConnectionInfeasible(); } synchronized (mMetrics) { mMetrics.onDeveloperVerificationRetryRequestSent( mDeveloperVerificationRetryCount.get()); } } Loading Loading @@ -3167,34 +3189,68 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** * Used for the VerifierController to report status back. */ public class VerifierCallback { public class DeveloperVerifierCallback { /** * Called by the VerifierController when the verifier requests to get the current * verification policy for this session. * Called by the VerifierController to indicate that the verifier has been connected. Only * used for metrics logging for now. */ public @PackageInstaller.DeveloperVerificationPolicy int getVerificationPolicy() { return mCurrentVerificationPolicy.get(); public void onConnectionEstablished(int verifierUid) { synchronized (mMetrics) { mMetrics.onDeveloperVerifierConnectionEstablished(verifierUid); } } /** * Called by the VerifierController when the verifier requests to change the verification * policy for this session. */ public boolean setVerificationPolicy( public boolean onVerificationPolicyOverridden( @PackageInstaller.DeveloperVerificationPolicy int policy) { if (!isValidVerificationPolicy(policy)) { return false; } mCurrentVerificationPolicy.set(policy); synchronized (mMetrics) { mMetrics.onDeveloperVerificationPolicyOverridden(policy); } return true; } /** * Called by the session itself when the verifier cannot be connected because of infeasible * reasons such as it's not installed on the target user. */ private void onConnectionInfeasible() { mHandler.post(() -> { synchronized (mMetrics) { mMetrics.onDeveloperVerifierResponseReceived( SessionMetrics.DeveloperVerifierResponse.OTHER); } if (mCurrentVerificationPolicy.get() != DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED) { // Continue with the rest of the verification and installation. resumeVerify(); return; } mVerificationUserActionNeededReason = DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_UNKNOWN; mVerificationFailedMessage = "A verifier agent is specified on device but cannot " + "be connected because of unknown error."; maybeSendUserActionForVerification(/* blockingFailure= */ false, /* extensionResponse= */ null); }); } /** * Called by the VerifierController when the connection has failed. */ public void onConnectionFailed() { mHandler.post(() -> { synchronized (mMetrics) { mMetrics.onDeveloperVerifierResponseReceived( SessionMetrics.DeveloperVerifierResponse.DISCONNECTED); } if (mCurrentVerificationPolicy.get() != DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED) { // Continue with the rest of the verification and installation. Loading @@ -3211,6 +3267,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { }); } /** * Called by the VerifierController when the verifier has requested a timeout extension. */ public void onTimeoutExtensionRequested() { synchronized (mMetrics) { mMetrics.onDeveloperVerificationTimeoutExtensionRequested(); } } /** * Called by the VerifierController when the verification request has timed out. */ Loading @@ -3218,6 +3283,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Always notify the verifier, regardless of the policy. mDeveloperVerifierController.notifyVerificationTimeout(sessionId, userId); mHandler.post(() -> { synchronized (mMetrics) { mMetrics.onDeveloperVerifierResponseReceived( SessionMetrics.DeveloperVerifierResponse.TIMEOUT); } if (mCurrentVerificationPolicy.get() != DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED) { // Continue with the rest of the verification and installation. Loading @@ -3242,6 +3311,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @NonNull DeveloperVerificationStatus statusReceived, @Nullable PersistableBundle extensionResponse) { mHandler.post(() -> { synchronized (mMetrics) { mMetrics.onDeveloperVerifierResponseReceived(statusReceived.isVerified() ? SessionMetrics.DeveloperVerifierResponse.COMPLETE_WITH_PASS : SessionMetrics.DeveloperVerifierResponse.COMPLETE_WITH_REJECT); mMetrics.onAslStatusReceived(statusReceived.getAslStatus()); } if (mCurrentVerificationPolicy.get() == DEVELOPER_VERIFICATION_POLICY_NONE) { // No policy applied. Continue with the rest of the verification and install. resumeVerify(); Loading @@ -3250,6 +3325,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (statusReceived.isVerified()) { if (statusReceived.isLite()) { mVerificationLiteEnabled = true; synchronized (mMetrics) { mMetrics.onDeveloperVerificationLiteEnabled(); } // This is a lite verification. Need further user action. mVerificationUserActionNeededReason = DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_LITE_VERIFICATION; Loading Loading @@ -3286,6 +3364,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { */ public void onVerificationIncompleteReceived(int incompleteReason) { mHandler.post(() -> { final boolean isNetworkUnavailable = incompleteReason == DEVELOPER_VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE; synchronized (mMetrics) { mMetrics.onDeveloperVerifierResponseReceived(isNetworkUnavailable ? SessionMetrics.DeveloperVerifierResponse.INCOMPLETE_NETWORK : SessionMetrics.DeveloperVerifierResponse.INCOMPLETE_UNKNOWN); } if (mCurrentVerificationPolicy.get() == DEVELOPER_VERIFICATION_POLICY_NONE) { // Continue with the rest of the verification and installation. resumeVerify(); Loading @@ -3294,7 +3379,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { StringBuilder sb = new StringBuilder( "Verification cannot be completed because of "); if (incompleteReason == DEVELOPER_VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE) { if (isNetworkUnavailable) { mVerificationUserActionNeededReason = DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_NETWORK_UNAVAILABLE; sb.append("unavailable network."); Loading @@ -3316,6 +3401,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mVerificationUserActionNeeded = true; sendOnUserActionRequired(mContext, getRemoteStatusReceiver(), sessionId, intent); synchronized (mMetrics) { mMetrics.onDeveloperVerificationUserActionRequired( mVerificationUserActionNeededReason); } return; } // Not sending user action. Directly return the failure to the installer. Loading Loading @@ -5021,7 +5110,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { + "SessionID: " + sessionId); return; } synchronized (mMetrics) { mMetrics.onDeveloperVerificationUserResponseReceived(userResponse); } switch (userResponse) { case DEVELOPER_VERIFICATION_USER_RESPONSE_ERROR -> { String errorMsg = "User could not be notified about the pending verification."; Loading Loading @@ -5060,8 +5151,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } case DEVELOPER_VERIFICATION_USER_RESPONSE_RETRY -> { // TODO (b/360130528): limit the number of times user can retry startVerificationSession(mPm::snapshotComputer, /* retry= */ true); retryDeveloperVerificationSession(mPm::snapshotComputer /* retry= */); } case DEVELOPER_VERIFICATION_USER_RESPONSE_INSTALL_ANYWAY -> resumeVerify(); Loading Loading @@ -6076,6 +6166,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // been sent out, which happens right after commit() is called. mDeveloperVerifierController.notifyVerificationCancelled( params.appPackageName, userId); synchronized (mMetrics) { mMetrics.onDeveloperVerificationCancelled(); } } } Loading
services/core/java/com/android/server/pm/SessionMetrics.java +259 −21 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/pm/verify/developer/DeveloperVerifierController.java +13 −8 Original line number Diff line number Diff line Loading @@ -183,9 +183,10 @@ public class DeveloperVerifierController { * If the verifier agent exists but cannot be started for some reason, all the notify* methods * in this class will fail asynchronously and quietly. The system will learn about the failure * after receiving the failure from * {@link PackageInstallerSession.VerifierCallback#onConnectionFailed}. * {@link PackageInstallerSession.DeveloperVerifierCallback#onConnectionFailed}. */ public boolean bindToVerifierServiceIfNeeded(Supplier<Computer> snapshotSupplier, int userId) { public boolean bindToVerifierServiceIfNeeded(Supplier<Computer> snapshotSupplier, int userId, PackageInstallerSession.DeveloperVerifierCallback callback) { if (DEBUG) { Slog.i(TAG, "Requesting to bind to the verifier service for user " + userId); } Loading Loading @@ -230,6 +231,8 @@ public class DeveloperVerifierController { public void onConnected(@NonNull IDeveloperVerifierService service) { Slog.i(TAG, "Verifier " + verifierPackageName + " is connected" + " on user " + userId); // Logging the success of connecting to the verifier. callback.onConnectionEstablished(verifierUid); // Aggressively auto-disconnect until verification requests are sent out startAutoDisconnectCountdown( remoteServiceWrapper.getAutoDisconnectCallback()); Loading Loading @@ -354,10 +357,10 @@ public class DeveloperVerifierController { List<SharedLibraryInfo> declaredLibraries, @PackageInstaller.DeveloperVerificationPolicy int verificationPolicy, @Nullable PersistableBundle extensionParams, PackageInstallerSession.VerifierCallback callback, PackageInstallerSession.DeveloperVerifierCallback callback, boolean retry) { // Try connecting to the verifier if not already connected if (!bindToVerifierServiceIfNeeded(snapshotSupplier, userId)) { if (!bindToVerifierServiceIfNeeded(snapshotSupplier, userId, callback)) { return false; } // For now, the verification id is the same as the installation session id. Loading Loading @@ -425,7 +428,7 @@ public class DeveloperVerifierController { private void startTimeoutCountdown(int verificationId, DeveloperVerificationRequestStatusTracker tracker, PackageInstallerSession.VerifierCallback callback, long delayMillis) { PackageInstallerSession.DeveloperVerifierCallback callback, long delayMillis) { mHandler.postDelayed(() -> { if (DEBUG) { Slog.i(TAG, "Checking request timeout for " + verificationId); Loading Loading @@ -538,9 +541,10 @@ public class DeveloperVerifierController { // This class handles requests from the remote verifier private class DeveloperVerificationSessionInterface extends IDeveloperVerificationSessionInterface.Stub { private final PackageInstallerSession.VerifierCallback mCallback; private final PackageInstallerSession.DeveloperVerifierCallback mCallback; DeveloperVerificationSessionInterface(PackageInstallerSession.VerifierCallback callback) { DeveloperVerificationSessionInterface( PackageInstallerSession.DeveloperVerifierCallback callback) { mCallback = callback; } Loading Loading @@ -569,6 +573,7 @@ public class DeveloperVerifierController { throw new IllegalStateException("Verification session " + verificationId + " doesn't exist or has finished"); } mCallback.onTimeoutExtensionRequested(); return tracker.extendTimeRemaining(additionalMs); } } Loading @@ -585,7 +590,7 @@ public class DeveloperVerifierController { + " doesn't exist or has finished"); } } return mCallback.setVerificationPolicy(policy); return mCallback.onVerificationPolicyOverridden(policy); } @Override Loading
services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/developer/DeveloperVerifierControllerTest.java +12 −10 Original line number Diff line number Diff line Loading @@ -123,9 +123,9 @@ public class DeveloperVerifierControllerTest { Computer mSnapshot; Supplier<Computer> mSnapshotSupplier = () -> mSnapshot; @Mock PackageInstallerSession.VerifierCallback mSessionCallback; PackageInstallerSession.DeveloperVerifierCallback mSessionCallback; @Mock PackageInstallerSession.VerifierCallback mSessionCallbackSecondaryUser; PackageInstallerSession.DeveloperVerifierCallback mSessionCallbackSecondaryUser; private DeveloperVerifierController mDeveloperVerifierController; private String mPackageName; Loading Loading @@ -187,14 +187,15 @@ public class DeveloperVerifierControllerTest { public void testRebindService() { ArgumentCaptor<ServiceConnector.ServiceLifecycleCallbacks> captor = ArgumentCaptor.forClass( ServiceConnector.ServiceLifecycleCallbacks.class); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0)) .isTrue(); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0, mSessionCallback)).isTrue(); verify(mMockServiceConnector).setServiceLifecycleCallbacks(captor.capture()); ServiceConnector.ServiceLifecycleCallbacks<IDeveloperVerifierService> callbacks = captor.getValue(); // Verify that the countdown to auto-disconnect has started ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); callbacks.onConnected(mMockService); verify(mSessionCallback, times(1)).onConnectionEstablished(anyInt()); verify(mInjector, times(1)).removeCallbacks(eq(mHandler), runnableCaptor.capture()); Runnable autoDisconnectRunnable = runnableCaptor.getValue(); Loading @@ -207,8 +208,8 @@ public class DeveloperVerifierControllerTest { when(mSnapshot.getPackageUidInternal( eq(mPackageName), anyLong(), anyInt(), anyInt() )).thenReturn(INVALID_UID); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0)) .isFalse(); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0, mSessionCallback)).isFalse(); // Test that nothing crashes if the verifier is available even though there's no bound mDeveloperVerifierController.notifyPackageNameAvailable(TEST_PACKAGE_NAME, 0); mDeveloperVerifierController.notifyVerificationCancelled(TEST_PACKAGE_NAME, 0); Loading @@ -221,8 +222,8 @@ public class DeveloperVerifierControllerTest { public void testUnbindService() throws Exception { ArgumentCaptor<ServiceConnector.ServiceLifecycleCallbacks> captor = ArgumentCaptor.forClass( ServiceConnector.ServiceLifecycleCallbacks.class); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0)) .isTrue(); assertThat(mDeveloperVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0, mSessionCallback)).isTrue(); verify(mMockServiceConnector).setServiceLifecycleCallbacks(captor.capture()); ServiceConnector.ServiceLifecycleCallbacks<IDeveloperVerifierService> callbacks = captor.getValue(); Loading Loading @@ -523,6 +524,7 @@ public class DeveloperVerifierControllerTest { final long extendTimeMillis = TEST_TIMEOUT_DURATION_MILLIS; assertThat(session.extendTimeRemaining(extendTimeMillis)).isEqualTo(extendTimeMillis); assertThat(session.getTimeoutTime()).isEqualTo(initialTimeoutTime + extendTimeMillis); verify(mSessionCallback, times(1)).onTimeoutExtensionRequested(); } @Test Loading Loading @@ -588,10 +590,10 @@ public class DeveloperVerifierControllerTest { verify(mMockService).onVerificationRequired(captor.capture()); DeveloperVerificationSession session = captor.getValue(); final int policy = DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_OPEN; when(mSessionCallback.setVerificationPolicy(eq(policy))).thenReturn(true); when(mSessionCallback.onVerificationPolicyOverridden(eq(policy))).thenReturn(true); assertThat(session.setVerificationPolicy(policy)).isTrue(); assertThat(session.getVerificationPolicy()).isEqualTo(policy); verify(mSessionCallback, times(1)).setVerificationPolicy(eq(policy)); verify(mSessionCallback, times(1)).onVerificationPolicyOverridden(eq(policy)); } @Test Loading