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

Commit 713a6d84 authored by Kate Xu's avatar Kate Xu Committed by Android (Google) Code Review
Browse files

Merge "Log why presentation was not shown"

parents 58ed308c 29606549
Loading
Loading
Loading
Loading
+66 −11
Original line number Diff line number Diff line
@@ -536,6 +536,61 @@ public final class AutofillManager {
    /** @hide */
    public static final int RESULT_CODE_NOT_SERVICE = -1;

    /**
     *  Reasons to commit the Autofill context.
     *
     *  <p>If adding a new reason, modify
     *  {@link com.android.server.autofill.PresentationStatsEventLogger#getNoPresentationEventReason(int)}
     *  as well.</p>
     *
     *  TODO(b/233833662): Expose this as a public API in U.
     *  @hide
     */
    @IntDef(prefix = { "COMMIT_REASON_" }, value = {
            COMMIT_REASON_UNKNOWN,
            COMMIT_REASON_ACTIVITY_FINISHED,
            COMMIT_REASON_VIEW_COMMITTED,
            COMMIT_REASON_VIEW_CLICKED,
            COMMIT_REASON_VIEW_CHANGED
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface AutofillCommitReason {}

    /**
     * Autofill context was committed because of an unknown reason.
     *
     * @hide
     */
    public static final int COMMIT_REASON_UNKNOWN = 0;

    /**
     * Autofill context was committed because activity finished.
     *
     * @hide
     */
    public static final int COMMIT_REASON_ACTIVITY_FINISHED = 1;

    /**
     * Autofill context was committed because {@link #commit()} was called.
     *
     * @hide
     */
    public static final int COMMIT_REASON_VIEW_COMMITTED = 2;

    /**
     * Autofill context was committed because view was clicked.
     *
     * @hide
     */
    public static final int COMMIT_REASON_VIEW_CLICKED = 3;

    /**
     * Autofill context was committed because of view changed.
     *
     * @hide
     */
    public static final int COMMIT_REASON_VIEW_CHANGED = 4;

    /**
     * Makes an authentication id from a request id and a dataset id.
     *
@@ -1605,7 +1660,7 @@ public final class AutofillManager {
            }
            if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
                if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
                commitLocked();
                commitLocked(/* commitReason= */ COMMIT_REASON_VIEW_CLICKED);
                mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED));
            }
        }
@@ -1623,7 +1678,7 @@ public final class AutofillManager {
        synchronized (mLock) {
            if (mSaveOnFinish) {
                if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
                commitLocked();
                commitLocked(/* commitReason= */ COMMIT_REASON_ACTIVITY_FINISHED);
            } else {
                if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
                cancelLocked();
@@ -1648,16 +1703,16 @@ public final class AutofillManager {
        }
        if (sVerbose) Log.v(TAG, "commit() called by app");
        synchronized (mLock) {
            commitLocked();
            commitLocked(/* commitReason= */ COMMIT_REASON_VIEW_COMMITTED);
        }
    }

    @GuardedBy("mLock")
    private void commitLocked() {
    private void commitLocked(@AutofillCommitReason int commitReason) {
        if (!mEnabled && !isActiveLocked()) {
            return;
        }
        finishSessionLocked();
        finishSessionLocked(/* commitReason= */ commitReason);
    }

    /**
@@ -2123,13 +2178,13 @@ public final class AutofillManager {
    }

    @GuardedBy("mLock")
    private void finishSessionLocked() {
    private void finishSessionLocked(@AutofillCommitReason int commitReason) {
        if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());

        if (!isActiveLocked()) return;

        try {
            mService.finishSession(mSessionId, mContext.getUserId());
            mService.finishSession(mSessionId, mContext.getUserId(), commitReason);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -3624,7 +3679,7 @@ public final class AutofillManager {
            }

            if (mVisibleTrackedIds == null) {
                finishSessionLocked();
                finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
            }
        }

@@ -3657,9 +3712,9 @@ public final class AutofillManager {

            if (mVisibleTrackedIds == null) {
                if (sVerbose) {
                    Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
                    Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleTrackedIds);
                }
                finishSessionLocked();
                finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
            }
        }

@@ -3731,7 +3786,7 @@ public final class AutofillManager {
                if (sVerbose) {
                    Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
                }
                finishSessionLocked();
                finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
            }
        }
    }
+1 −1
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ oneway interface IAutoFillManager {
    void updateSession(int sessionId, in AutofillId id, in Rect bounds,
        in AutofillValue value, int action, int flags, int userId);
    void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
    void finishSession(int sessionId, int userId);
    void finishSession(int sessionId, int userId, int commitReason);
    void cancelSession(int sessionId, int userId);
    void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);
    void setHasCallback(int sessionId, int userId, boolean hasIt);
+4 −2
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.AutofillCommitReason;
import android.view.autofill.AutofillManager.SmartSuggestionMode;
import android.view.autofill.AutofillManagerInternal;
import android.view.autofill.AutofillValue;
@@ -1657,11 +1658,12 @@ public final class AutofillManagerService
        }

        @Override
        public void finishSession(int sessionId, int userId) {
        public void finishSession(int sessionId, int userId,
                @AutofillCommitReason int commitReason) {
            synchronized (mLock) {
                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                if (service != null) {
                    service.finishSessionLocked(sessionId, getCallingUid());
                    service.finishSessionLocked(sessionId, getCallingUid(), commitReason);
                } else if (sVerbose) {
                    Slog.v(TAG, "finishSession(): no service for " + userId);
                }
+3 −2
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.AutofillCommitReason;
import android.view.autofill.AutofillManager.SmartSuggestionMode;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
@@ -423,7 +424,7 @@ final class AutofillManagerServiceImpl
    }

    @GuardedBy("mLock")
    void finishSessionLocked(int sessionId, int uid) {
    void finishSessionLocked(int sessionId, int uid, @AutofillCommitReason int commitReason) {
        if (!isEnabledLocked()) {
            return;
        }
@@ -438,7 +439,7 @@ final class AutofillManagerServiceImpl

        final Session.SaveResult saveResult = session.showSaveLocked();

        session.logContextCommitted(saveResult.getNoSaveUiReason());
        session.logContextCommitted(saveResult.getNoSaveUiReason(), commitReason);

        if (saveResult.isLogSaveShown()) {
            session.logSaveUiShown();
+257 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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 android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
import static android.service.autofill.FillEventHistory.Event.UiType;
import static android.view.autofill.AutofillManager.COMMIT_REASON_ACTIVITY_FINISHED;
import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_CHANGED;
import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_CLICKED;
import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_COMMITTED;

import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__DIALOG;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
import static com.android.server.autofill.Helper.sVerbose;

import android.annotation.IntDef;
import android.annotation.Nullable;
import android.service.autofill.Dataset;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;

import com.android.internal.util.FrameworkStatsLog;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Optional;

/** Helper class to track and log Autofill presentation stats. */
public final class PresentationStatsEventLogger {
    private static final String TAG = "PresentationStatsEventLogger";

    /**
     * Reasons why presentation was not shown. These are wrappers around
     * {@link com.android.os.AtomsProto.AutofillPresentationEventReported.PresentationEventResult}.
     */
    @IntDef(prefix = { "NOT_SHOWN_REASON" }, value = {
            NOT_SHOWN_REASON_ANY_SHOWN,
            NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED,
            NOT_SHOWN_REASON_VIEW_CHANGED,
            NOT_SHOWN_REASON_ACTIVITY_FINISHED,
            NOT_SHOWN_REASON_REQUEST_TIMEOUT,
            NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY,
            NOT_SHOWN_REASON_UNKNOWN
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface NotShownReason {}
    public static final int NOT_SHOWN_REASON_ANY_SHOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
    public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
    public static final int NOT_SHOWN_REASON_VIEW_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
    public static final int NOT_SHOWN_REASON_ACTIVITY_FINISHED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED;
    public static final int NOT_SHOWN_REASON_REQUEST_TIMEOUT = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT;
    public static final int NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
    public static final int NOT_SHOWN_REASON_UNKNOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;

    private final int mSessionId;
    private Optional<PresentationStatsEventInternal> mEventInternal;

    private PresentationStatsEventLogger(int sessionId) {
        mSessionId = sessionId;
        mEventInternal = Optional.empty();
    }

    public static PresentationStatsEventLogger forSessionId(int sessionId) {
        return new PresentationStatsEventLogger(sessionId);
    }

    public void startNewEvent() {
        if (mEventInternal.isPresent()) {
            Slog.e(TAG, "Failed to start new event because already have active event.");
            return;
        }
        mEventInternal = Optional.of(new PresentationStatsEventInternal());
    }

    public void maybeSetRequestId(int requestId) {
        mEventInternal.ifPresent(event -> event.mRequestId = requestId);
    }

    public void maybeSetNoPresentationEventReason(@NotShownReason int reason) {
        mEventInternal.ifPresent(event -> {
            if (event.mCountShown == 0) {
                event.mNoPresentationReason = reason;
            }
        });
    }

    public void maybeSetAvailableCount(@Nullable List<Dataset> datasetList,
            AutofillId currentViewId) {
        mEventInternal.ifPresent(event -> {
            int availableCount = getDatasetCountForAutofillId(datasetList, currentViewId);
            event.mAvailableCount = availableCount;
            event.mIsDatasetAvailable = availableCount > 0;
        });
    }

    public void maybeSetCountShown(@Nullable List<Dataset> datasetList,
            AutofillId currentViewId) {
        mEventInternal.ifPresent(event -> {
            int countShown = getDatasetCountForAutofillId(datasetList, currentViewId);
            event.mCountShown = countShown;
            if (countShown > 0) {
                event.mNoPresentationReason = NOT_SHOWN_REASON_ANY_SHOWN;
            }
        });
    }

    private static int getDatasetCountForAutofillId(@Nullable List<Dataset> datasetList,
            AutofillId currentViewId) {
        int availableCount = 0;
        if (datasetList != null) {
            for (int i = 0; i < datasetList.size(); i++) {
                Dataset data = datasetList.get(i);
                if (data != null && data.getFieldIds() != null
                        && data.getFieldIds().contains(currentViewId)) {
                    availableCount += 1;
                }
            }
        }
        return availableCount;
    }

    public void maybeSetCountFilteredUserTyping(int countFilteredUserTyping) {
        mEventInternal.ifPresent(event -> {
            event.mCountFilteredUserTyping = countFilteredUserTyping;
        });
    }

    public void maybeSetCountNotShownImePresentationNotDrawn(
            int countNotShownImePresentationNotDrawn) {
        mEventInternal.ifPresent(event -> {
            event.mCountNotShownImePresentationNotDrawn = countNotShownImePresentationNotDrawn;
        });
    }

    public void maybeSetCountNotShownImeUserNotSeen(int countNotShownImeUserNotSeen) {
        mEventInternal.ifPresent(event -> {
            event.mCountNotShownImeUserNotSeen = countNotShownImeUserNotSeen;
        });
    }

    public void maybeSetDisplayPresentationType(@UiType int uiType) {
        mEventInternal.ifPresent(event -> {
            event.mDisplayPresentationType = getDisplayPresentationType(uiType);
        });
    }

    public void logAndEndEvent() {
        if (!mEventInternal.isPresent()) {
            Slog.wtf(null, "Shouldn't be logging AutofillPresentationEventReported again for same "
                    + "event");
            return;
        }
        PresentationStatsEventInternal event = mEventInternal.get();
        if (sVerbose) {
            Slog.v(TAG, "Log AutofillPresentationEventReported:"
                    + " requestId=" + event.mRequestId
                    + " sessionId=" + mSessionId
                    + " mNoPresentationEventReason=" + event.mNoPresentationReason
                    + " mAvailableCount=" + event.mAvailableCount
                    + " mCountShown=" + event.mCountShown
                    + " mCountFilteredUserTyping=" + event.mCountFilteredUserTyping
                    + " mCountNotShownImePresentationNotDrawn="
                    + event.mCountNotShownImePresentationNotDrawn
                    + " mCountNotShownImeUserNotSeen=" + event.mCountNotShownImeUserNotSeen
                    + " mDisplayPresentationType=" + event.mDisplayPresentationType);
        }

        // TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
        if (!event.mIsDatasetAvailable) {
            mEventInternal = Optional.empty();
            return;
        }
        FrameworkStatsLog.write(
                AUTOFILL_PRESENTATION_EVENT_REPORTED,
                event.mRequestId,
                mSessionId,
                event.mNoPresentationReason,
                event.mAvailableCount,
                event.mCountShown,
                event.mCountFilteredUserTyping,
                event.mCountNotShownImePresentationNotDrawn,
                event.mCountNotShownImeUserNotSeen,
                event.mDisplayPresentationType);
        mEventInternal = Optional.empty();
    }

    private final class PresentationStatsEventInternal {
        int mRequestId;
        @NotShownReason int mNoPresentationReason = NOT_SHOWN_REASON_UNKNOWN;
        boolean mIsDatasetAvailable;
        int mAvailableCount;
        int mCountShown;
        int mCountFilteredUserTyping;
        int mCountNotShownImePresentationNotDrawn;
        int mCountNotShownImeUserNotSeen;
        int mDisplayPresentationType = AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;

        PresentationStatsEventInternal() {}
    }

    static int getNoPresentationEventReason(
            @AutofillManager.AutofillCommitReason int commitReason) {
        switch (commitReason) {
            case COMMIT_REASON_VIEW_COMMITTED:
                return NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY;
            case COMMIT_REASON_ACTIVITY_FINISHED:
                return NOT_SHOWN_REASON_ACTIVITY_FINISHED;
            case COMMIT_REASON_VIEW_CHANGED:
                return NOT_SHOWN_REASON_VIEW_CHANGED;
            case COMMIT_REASON_VIEW_CLICKED:
                // TODO(b/234185326): Add separate reason for view clicked.
            default:
                return NOT_SHOWN_REASON_UNKNOWN;
        }
    }

    private static int getDisplayPresentationType(@UiType int uiType) {
        switch (uiType) {
            case UI_TYPE_MENU:
                return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
            case UI_TYPE_INLINE:
                return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
            case UI_TYPE_DIALOG:
                return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__DIALOG;
            default:
                return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
        }
    }
}
Loading