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

Commit 5f033a51 authored by Tim Yu's avatar Tim Yu
Browse files

Autofill Fix Presentation Log Issues

Fix various issues with presentation logs.
1. Refactor some part of logging logic, especially with regards to fill
   dialog to get a cleaner lifecycle for presentation events
2. Fixed some logic that effected logging only

Flag: Flag: EXEMPT bugfix
Fixes: 352149628
Test: com.android.server.autofill.PresentationEventLoggerTest
Change-Id: I2260b72363c8426dc32e6ec674b0edb4e830b437
parent 43243dc4
Loading
Loading
Loading
Loading
+76 −35
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ import android.util.ArraySet;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;

import com.android.internal.util.FrameworkStatsLog;

@@ -244,9 +245,17 @@ public final class PresentationStatsEventLogger {
            Slog.e(TAG, "Failed to start new event because already have active event.");
            return;
        }
        Slog.d(TAG, "Started new PresentationStatsEvent");
        mEventInternal = Optional.of(new PresentationStatsEventInternal());
    }

    /**
     * Test use only, returns a copy of the events object
     */
    Optional<PresentationStatsEventInternal> getInternalEvent() {
        return mEventInternal;
    }

    /**
     * Set request_id
     */
@@ -339,10 +348,16 @@ public final class PresentationStatsEventLogger {
        });
    }

    public void maybeSetCountShown(int datasets) {
    /**
     * This is called when a dataset is shown to the user. Will set the count shown,
     * related timestamps and presentation reason.
     */
    public void logWhenDatasetShown(int datasets) {
        mEventInternal.ifPresent(
                event -> {
                    maybeSetSuggestionPresentedTimestampMs();
                    event.mCountShown = datasets;
                    event.mNoPresentationReason = NOT_SHOWN_REASON_ANY_SHOWN;
                });
    }

@@ -405,7 +420,12 @@ public final class PresentationStatsEventLogger {

    public void maybeSetDisplayPresentationType(@UiType int uiType) {
        mEventInternal.ifPresent(event -> {
            // There are cases in which another UI type will show up after selects a dataset
            // such as with Inline after Fill Dialog. Set as the first presentation type only.
            if (event.mDisplayPresentationType
                    == AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE) {
                event.mDisplayPresentationType = getDisplayPresentationType(uiType);
            }
        });
    }

@@ -430,8 +450,11 @@ public final class PresentationStatsEventLogger {
    }

    public void maybeSetSuggestionSentTimestampMs(int timestamp) {
        mEventInternal.ifPresent(event -> {
        mEventInternal.ifPresent(
                event -> {
                    if (event.mSuggestionSentTimestampMs == DEFAULT_VALUE_INT) {
                        event.mSuggestionSentTimestampMs = timestamp;
                    }
                });
    }

@@ -481,8 +504,6 @@ public final class PresentationStatsEventLogger {

    public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) {
        mEventInternal.ifPresent(event -> {
            event.mDisplayPresentationType =
                    AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
            String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
                    Settings.Secure.DEFAULT_INPUT_METHOD, userId);
            if (TextUtils.isEmpty(imeString)) {
@@ -601,13 +622,32 @@ public final class PresentationStatsEventLogger {
        });
    }

    /**
     * Sets the field length whenever the text changes. Will keep track of the first
     * and last modification lengths.
     */
    public void updateTextFieldLength(AutofillValue value) {
        mEventInternal.ifPresent(event -> {
            if (value == null || !value.isText()) {
                return;
            }

            int length = value.getTextValue().length();

            if (event.mFieldFirstLength == DEFAULT_VALUE_INT) {
                event.mFieldFirstLength = length;
            }
            event.mFieldLastLength = length;
        });
    }

    /**
     * Set various timestamps whenever the ViewState is modified
     *
     * <p>If the ViewState contains ViewState.STATE_AUTOFILLED, sets field_autofilled_timestamp_ms
     * else, set field_first_modified_timestamp_ms (if unset) and field_last_modified_timestamp_ms
     */
    public void onFieldTextUpdated(ViewState state, int length) {
    public void onFieldTextUpdated(ViewState state, AutofillValue value) {
        mEventInternal.ifPresent(event -> {
            int timestamp = getElapsedTime();
            // Focused id should be set before this is called
@@ -615,21 +655,18 @@ public final class PresentationStatsEventLogger {
                // if these don't match, the currently field different than before
                Slog.w(
                        TAG,
                                "Bad view state for: " + event.mFocusedId);
                        "Bad view state for: " + event.mFocusedId + ", state: " + state);
                return;
            }

            updateTextFieldLength(value);

            // Text changed because filling into form, just log Autofill timestamp
            if ((state.getState() & ViewState.STATE_AUTOFILLED) != 0) {
                event.mAutofilledTimestampMs = timestamp;
                return;
            }

                    // Set length variables
                    if (event.mFieldFirstLength == DEFAULT_VALUE_INT) {
                        event.mFieldFirstLength = length;
                    }
                    event.mFieldLastLength = length;

            // Set timestamp variables
            if (event.mFieldModifiedFirstTimestampMs == DEFAULT_VALUE_INT) {
@@ -796,7 +833,10 @@ public final class PresentationStatsEventLogger {
        });
    }

    public void logAndEndEvent() {
    /**
     * Finish and log the event.
     */
    public void logAndEndEvent(String caller) {
        if (!mEventInternal.isPresent()) {
            Slog.w(TAG, "Shouldn't be logging AutofillPresentationEventReported again for same "
                    + "event");
@@ -804,7 +844,8 @@ public final class PresentationStatsEventLogger {
        }
        PresentationStatsEventInternal event = mEventInternal.get();
        if (sVerbose) {
            Slog.v(TAG, "Log AutofillPresentationEventReported:"
            Slog.v(TAG, "(" + caller + ") "
                    + "Log AutofillPresentationEventReported:"
                    + " requestId=" + event.mRequestId
                    + " sessionId=" + mSessionId
                    + " mNoPresentationEventReason=" + event.mNoPresentationReason
@@ -926,7 +967,7 @@ public final class PresentationStatsEventLogger {
        mEventInternal = Optional.empty();
    }

    private static final class PresentationStatsEventInternal {
    static final class PresentationStatsEventInternal {
        int mRequestId;
        @NotShownReason int mNoPresentationReason = NOT_SHOWN_REASON_UNKNOWN;
        boolean mIsDatasetAvailable;
+86 −82

File changed.

Preview size limit exceeded, changes collapsed.

+2 −2
Original line number Diff line number Diff line
@@ -461,9 +461,9 @@ public final class AutoFillUI {
                        }

                        @Override
                        public void onShown() {
                        public void onShown(int datasetsShown) {
                            if (mCallback != null) {
                                mCallback.onShown(UI_TYPE_DIALOG, response.getDatasets().size());
                                mCallback.onShown(UI_TYPE_DIALOG, datasetsShown);
                            }
                        }

+3 −2
Original line number Diff line number Diff line
@@ -85,7 +85,7 @@ final class DialogFillUi {
        void onDatasetPicked(@NonNull Dataset dataset);
        void onDismissed();
        void onCanceled();
        void onShown();
        void onShown(int datasetsShown);
        void startIntentSender(IntentSender intentSender);
    }

@@ -151,7 +151,8 @@ final class DialogFillUi {
        mDialog.setContentView(decor);
        setDialogParamsAsBottomSheet();
        mDialog.setOnCancelListener((d) -> mCallback.onCanceled());
        mDialog.setOnShowListener((d) -> mCallback.onShown());
        int datasetsShown = (mAdapter != null) ? mAdapter.getCount() : 0;
        mDialog.setOnShowListener((d) -> mCallback.onShown(datasetsShown));
        show();
    }

+132 −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.autofill;


import static com.google.common.truth.Truth.assertThat;

import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class PresentationEventLoggerTest {

    @Test
    public void testViewEntered() {
        PresentationStatsEventLogger pEventLogger =
                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);

        AutofillId id = new AutofillId(13);
        AutofillValue initialValue = AutofillValue.forText("hello");
        AutofillValue lastValue = AutofillValue.forText("hello world");
        ViewState vState = new ViewState(id, null, 0, false);

        pEventLogger.startNewEvent();
        pEventLogger.maybeSetFocusedId(id);
        pEventLogger.onFieldTextUpdated(vState, initialValue);
        pEventLogger.onFieldTextUpdated(vState, lastValue);

        PresentationStatsEventLogger.PresentationStatsEventInternal event =
                pEventLogger.getInternalEvent().get();
        assertThat(event).isNotNull();
        assertThat(event.mFieldFirstLength).isEqualTo(initialValue.getTextValue().length());
        assertThat(event.mFieldLastLength).isEqualTo(lastValue.getTextValue().length());
        assertThat(event.mFieldModifiedFirstTimestampMs).isNotEqualTo(-1);
        assertThat(event.mFieldModifiedLastTimestampMs).isNotEqualTo(-1);
    }

    @Test
    public void testViewAutofilled() {
        PresentationStatsEventLogger pEventLogger =
                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);

        String newTextValue = "hello";
        AutofillValue value = AutofillValue.forText(newTextValue);
        AutofillId id = new AutofillId(13);
        ViewState vState = new ViewState(id, null, ViewState.STATE_AUTOFILLED, false);

        pEventLogger.startNewEvent();
        pEventLogger.maybeSetFocusedId(id);
        pEventLogger.onFieldTextUpdated(vState, value);

        PresentationStatsEventLogger.PresentationStatsEventInternal event =
                pEventLogger.getInternalEvent().get();
        assertThat(event).isNotNull();
        assertThat(event.mFieldFirstLength).isEqualTo(newTextValue.length());
        assertThat(event.mFieldLastLength).isEqualTo(newTextValue.length());
        assertThat(event.mAutofilledTimestampMs).isNotEqualTo(-1);
        assertThat(event.mFieldModifiedFirstTimestampMs).isEqualTo(-1);
        assertThat(event.mFieldModifiedLastTimestampMs).isEqualTo(-1);
    }

    @Test
    public void testModifiedOnDifferentView() {
        PresentationStatsEventLogger pEventLogger =
                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);

        String newTextValue = "hello";
        AutofillValue value = AutofillValue.forText(newTextValue);
        AutofillId id = new AutofillId(13);
        ViewState vState = new ViewState(id, null, ViewState.STATE_AUTOFILLED, false);

        pEventLogger.startNewEvent();
        pEventLogger.onFieldTextUpdated(vState, value);

        PresentationStatsEventLogger.PresentationStatsEventInternal event =
                pEventLogger.getInternalEvent().get();
        assertThat(event).isNotNull();
        assertThat(event.mFieldFirstLength).isEqualTo(-1);
        assertThat(event.mFieldLastLength).isEqualTo(-1);
        assertThat(event.mFieldModifiedFirstTimestampMs).isEqualTo(-1);
        assertThat(event.mFieldModifiedLastTimestampMs).isEqualTo(-1);
        assertThat(event.mAutofilledTimestampMs).isEqualTo(-1);
    }

    @Test
    public void testSetCountShown() {
        PresentationStatsEventLogger pEventLogger =
                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);

        pEventLogger.startNewEvent();
        pEventLogger.logWhenDatasetShown(7);

        PresentationStatsEventLogger.PresentationStatsEventInternal event =
                pEventLogger.getInternalEvent().get();
        assertThat(event).isNotNull();
        assertThat(event.mCountShown).isEqualTo(7);
        assertThat(event.mNoPresentationReason)
                .isEqualTo(PresentationStatsEventLogger.NOT_SHOWN_REASON_ANY_SHOWN);
    }

    @Test
    public void testFillDialogShownThenInline() {
        PresentationStatsEventLogger pEventLogger =
                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);

        pEventLogger.startNewEvent();
        pEventLogger.maybeSetDisplayPresentationType(3);
        pEventLogger.maybeSetDisplayPresentationType(2);

        PresentationStatsEventLogger.PresentationStatsEventInternal event =
                pEventLogger.getInternalEvent().get();
        assertThat(event).isNotNull();
        assertThat(event.mDisplayPresentationType).isEqualTo(3);
    }
}