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

Commit 98d4af54 authored by Daniel's avatar Daniel
Browse files

Log when webview requests credential

Log when webview requests credential from credential manager. Credential
Autofill Service determines that a credential response is flagged as
webview requested when the assistStrcture has a viewnode that has both
webdomain and credential option.

Bug: 322036652
Test: built and deployed locally. Checked the log of the metric with
formgen and addressbook

Change-Id: I496c82eb98aacb87fbf2eef0753ce83331e4846e
parent 4c38c543
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -600,6 +600,14 @@ public abstract class AutofillService extends Service {
     */
    public static final String EXTRA_ERROR = "error";

    /**
     * Name of the key used to mark whether the fill response is for a webview.
     *
     * @hide
     */
    public static final String WEBVIEW_REQUESTED_CREDENTIAL_KEY = "webview_requested_credential";


    private final IAutoFillService mInterface = new IAutoFillService.Stub() {
        @Override
        public void onConnectedStateChanged(boolean connected) {
+23 −11
Original line number Diff line number Diff line
@@ -116,8 +116,10 @@ class CredentialAutofillService : AutofillService() {
            return
        }

        val responseClientState = Bundle()
        responseClientState.putBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, false)
        val getCredRequest: GetCredentialRequest? = getCredManRequest(structure, sessionId,
                requestId)
                requestId, responseClientState)
        if (getCredRequest == null) {
            Log.i(TAG, "No credential manager request found")
            callback.onFailure("No credential manager request found")
@@ -153,7 +155,8 @@ class CredentialAutofillService : AutofillService() {
                    return
                }

                val fillResponse = convertToFillResponse(result, request)
                val fillResponse = convertToFillResponse(result, request,
                    responseClientState)
                if (fillResponse != null) {
                    callback.onSuccess(fillResponse)
                } else {
@@ -260,7 +263,8 @@ class CredentialAutofillService : AutofillService() {

    private fun convertToFillResponse(
            getCredResponse: GetCandidateCredentialsResponse,
            filLRequest: FillRequest
            filLRequest: FillRequest,
            responseClientState: Bundle
    ): FillResponse? {
        val candidateProviders = getCredResponse.candidateProviderDataList
        if (candidateProviders.isEmpty()) {
@@ -281,6 +285,7 @@ class CredentialAutofillService : AutofillService() {
        if (!validFillResponse) {
            return null
        }
        fillResponseBuilder.setClientState(responseClientState)
        return fillResponseBuilder.build()
    }

@@ -578,10 +583,11 @@ class CredentialAutofillService : AutofillService() {
    private fun getCredManRequest(
            structure: AssistStructure,
            sessionId: Int,
            requestId: Int
            requestId: Int,
            responseClientState: Bundle
    ): GetCredentialRequest? {
        val credentialOptions: MutableList<CredentialOption> = mutableListOf()
        traverseStructure(structure, credentialOptions)
        traverseStructure(structure, credentialOptions, responseClientState)

        if (credentialOptions.isNotEmpty()) {
            val dataBundle = Bundle()
@@ -596,7 +602,8 @@ class CredentialAutofillService : AutofillService() {

    private fun traverseStructure(
            structure: AssistStructure,
            cmRequests: MutableList<CredentialOption>
            cmRequests: MutableList<CredentialOption>,
            responseClientState: Bundle
    ) {
        val windowNodes: List<AssistStructure.WindowNode> =
                structure.run {
@@ -604,16 +611,17 @@ class CredentialAutofillService : AutofillService() {
                }

        windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
            traverseNode(windowNode.rootViewNode, cmRequests)
            traverseNode(windowNode.rootViewNode, cmRequests, responseClientState)
        }
    }

    private fun traverseNode(
            viewNode: AssistStructure.ViewNode,
            cmRequests: MutableList<CredentialOption>
            cmRequests: MutableList<CredentialOption>,
            responseClientState: Bundle
    ) {
        viewNode.autofillId?.let {
            val options = getCredentialOptionsFromViewNode(viewNode, it)
            val options = getCredentialOptionsFromViewNode(viewNode, it, responseClientState)
            cmRequests.addAll(options)
        }

@@ -623,13 +631,14 @@ class CredentialAutofillService : AutofillService() {
                }

        children.forEach { childNode: AssistStructure.ViewNode ->
            traverseNode(childNode, cmRequests)
            traverseNode(childNode, cmRequests, responseClientState)
        }
    }

    private fun getCredentialOptionsFromViewNode(
            viewNode: AssistStructure.ViewNode,
            autofillId: AutofillId
            autofillId: AutofillId,
            responseClientState: Bundle
    ): List<CredentialOption> {
        // TODO(b/293945193) Replace with isCredential check from viewNode
        val credentialHints: MutableList<String> = mutableListOf()
@@ -637,6 +646,9 @@ class CredentialAutofillService : AutofillService() {
            for (hint in viewNode.autofillHints!!) {
                if (hint.startsWith(CRED_HINT_PREFIX)) {
                    credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX))
                    if (viewNode.webDomain != null) {
                        responseClientState.putBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, true)
                    }
                }
            }
        }
+13 −2
Original line number Diff line number Diff line
@@ -254,6 +254,14 @@ public final class PresentationStatsEventLogger {
        mEventInternal.ifPresent(event -> event.mIsCredentialRequest = isCredentialRequest);
    }

    /**
     * Set webview_requested_credential
     */
    public void maybeSetWebviewRequestedCredential(boolean webviewRequestedCredential) {
        mEventInternal.ifPresent(event ->
                event.mWebviewRequestedCredential = webviewRequestedCredential);
    }

    public void maybeSetNoPresentationEventReason(@NotShownReason int reason) {
        mEventInternal.ifPresent(event -> {
            if (event.mCountShown == 0) {
@@ -578,7 +586,8 @@ public final class PresentationStatsEventLogger {
                    + " mDetectionPreference=" + event.mDetectionPreference
                    + " mFieldClassificationRequestId=" + event.mFieldClassificationRequestId
                    + " mAppPackageUid=" + mCallingAppUid
                    + " mIsCredentialRequest=" + event.mIsCredentialRequest);
                    + " mIsCredentialRequest=" + event.mIsCredentialRequest
                    + " mWebviewRequestedCredential=" + event.mWebviewRequestedCredential);
        }

        // TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -618,7 +627,8 @@ public final class PresentationStatsEventLogger {
                event.mDetectionPreference,
                event.mFieldClassificationRequestId,
                mCallingAppUid,
                event.mIsCredentialRequest);
                event.mIsCredentialRequest,
                event.mWebviewRequestedCredential);
        mEventInternal = Optional.empty();
    }

@@ -653,6 +663,7 @@ public final class PresentationStatsEventLogger {
        @DetectionPreference int mDetectionPreference = DETECTION_PREFER_UNKNOWN;
        int mFieldClassificationRequestId = -1;
        boolean mIsCredentialRequest = false;
        boolean mWebviewRequestedCredential = false;

        PresentationStatsEventInternal() {}
    }
+4 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.autofill;

import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
import static android.service.autofill.AutofillService.EXTRA_FILL_RESPONSE;
import static android.service.autofill.AutofillService.WEBVIEW_REQUESTED_CREDENTIAL_KEY;
import static android.service.autofill.Dataset.PICK_REASON_NO_PCC;
import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_ONLY;
import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER;
@@ -5516,8 +5517,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        mResponses.put(requestId, newResponse);
        mClientState = newClientState != null ? newClientState : newResponse.getClientState();

        boolean webviewRequestedCredman = newClientState != null && newClientState.getBoolean(
                WEBVIEW_REQUESTED_CREDENTIAL_KEY, false);
        List<Dataset> datasetList = newResponse.getDatasets();

        mPresentationStatsEventLogger.maybeSetWebviewRequestedCredential(webviewRequestedCredman);
        mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(sIdCounterForPcc.get());
        mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId);
        mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList);