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

Commit 29606549 authored by skxu's avatar skxu Committed by Kate Xu
Browse files

Log why presentation was not shown

Fixes: 219613425
Test: b/233833739
Change-Id: Ic95cc69b912e8144726124ebc8355c2d1e24079e
parent 265539f5
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