Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 66e2050b authored by Song Chun Fan's avatar Song Chun Fan
Browse files

[ADI][metrics] implement metrics collection

FLAG: android.content.pm.verification_service
BUG: 418283971
Test: atest com.android.server.pm.verify.developer.DeveloperVerifierControllerTest
Test: manual; cts-root tests to be added in followup CLs.

Change-Id: Ifddce068e190cccec1fef72ce4111e69c5153d0d
parent bcd4d487
Loading
Loading
Loading
Loading
+121 −28
Original line number Diff line number Diff line
@@ -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;

@@ -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();
            }
        }
    }

@@ -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);
@@ -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.
@@ -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;
            }
        }
@@ -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) {
@@ -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());
        }
    }

@@ -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.
@@ -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.
         */
@@ -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.
@@ -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();
@@ -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;
@@ -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();
@@ -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.");
@@ -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.
@@ -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.";
@@ -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();
@@ -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();
            }
        }
    }

+259 −21

File changed.

Preview size limit exceeded, changes collapsed.

+13 −8
Original line number Diff line number Diff line
@@ -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);
        }
@@ -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());
@@ -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.
@@ -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);
@@ -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;
        }

@@ -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);
            }
        }
@@ -585,7 +590,7 @@ public class DeveloperVerifierController {
                            + " doesn't exist or has finished");
                }
            }
            return mCallback.setVerificationPolicy(policy);
            return mCallback.onVerificationPolicyOverridden(policy);
        }

        @Override
+12 −10
Original line number Diff line number Diff line
@@ -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;
@@ -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();
@@ -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);
@@ -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();
@@ -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
@@ -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