• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.autofill;
18 
19 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
20 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
21 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
22 import static android.service.autofill.FillEventHistory.Event.UiType;
23 import static android.view.autofill.AutofillManager.COMMIT_REASON_ACTIVITY_FINISHED;
24 import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_CHANGED;
25 import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_CLICKED;
26 import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_COMMITTED;
27 
28 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED;
29 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__DIALOG;
30 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
31 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
32 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
33 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
34 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED;
35 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_FILL_REQUEST_FAILED;
36 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_NO_FOCUS;
37 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT;
38 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
39 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
40 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
41 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
42 import static com.android.server.autofill.Helper.sVerbose;
43 
44 import android.annotation.IntDef;
45 import android.annotation.Nullable;
46 import android.content.ComponentName;
47 import android.content.Context;
48 import android.content.pm.PackageManager;
49 import android.provider.Settings;
50 import android.service.autofill.Dataset;
51 import android.text.TextUtils;
52 import android.util.Slog;
53 import android.view.autofill.AutofillId;
54 import android.view.autofill.AutofillManager;
55 
56 import com.android.internal.util.FrameworkStatsLog;
57 
58 import java.lang.annotation.Retention;
59 import java.lang.annotation.RetentionPolicy;
60 import java.util.List;
61 import java.util.Optional;
62 
63 /** Helper class to track and log Autofill presentation stats. */
64 public final class PresentationStatsEventLogger {
65     private static final String TAG = "PresentationStatsEventLogger";
66 
67     /**
68      * Reasons why presentation was not shown. These are wrappers around
69      * {@link com.android.os.AtomsProto.AutofillPresentationEventReported.PresentationEventResult}.
70      */
71     @IntDef(prefix = {"NOT_SHOWN_REASON"}, value = {
72             NOT_SHOWN_REASON_ANY_SHOWN,
73             NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED,
74             NOT_SHOWN_REASON_VIEW_CHANGED,
75             NOT_SHOWN_REASON_ACTIVITY_FINISHED,
76             NOT_SHOWN_REASON_REQUEST_TIMEOUT,
77             NOT_SHOWN_REASON_REQUEST_FAILED,
78             NOT_SHOWN_REASON_NO_FOCUS,
79             NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY,
80             NOT_SHOWN_REASON_UNKNOWN
81     })
82     @Retention(RetentionPolicy.SOURCE)
83     public @interface NotShownReason {}
84 
85     public static final int NOT_SHOWN_REASON_ANY_SHOWN =
86             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
87     public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED =
88             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
89     public static final int NOT_SHOWN_REASON_VIEW_CHANGED =
90             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
91     public static final int NOT_SHOWN_REASON_ACTIVITY_FINISHED =
92             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED;
93     public static final int NOT_SHOWN_REASON_REQUEST_TIMEOUT =
94             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT;
95     public static final int NOT_SHOWN_REASON_REQUEST_FAILED =
96             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_FILL_REQUEST_FAILED;
97     public static final int NOT_SHOWN_REASON_NO_FOCUS =
98             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_NO_FOCUS;
99     public static final int NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY =
100             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
101     public static final int NOT_SHOWN_REASON_UNKNOWN =
102             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
103 
104     private final int mSessionId;
105     private Optional<PresentationStatsEventInternal> mEventInternal;
106 
PresentationStatsEventLogger(int sessionId)107     private PresentationStatsEventLogger(int sessionId) {
108         mSessionId = sessionId;
109         mEventInternal = Optional.empty();
110     }
111 
forSessionId(int sessionId)112     public static PresentationStatsEventLogger forSessionId(int sessionId) {
113         return new PresentationStatsEventLogger(sessionId);
114     }
115 
startNewEvent()116     public void startNewEvent() {
117         if (mEventInternal.isPresent()) {
118             Slog.e(TAG, "Failed to start new event because already have active event.");
119             return;
120         }
121         mEventInternal = Optional.of(new PresentationStatsEventInternal());
122     }
123 
maybeSetRequestId(int requestId)124     public void maybeSetRequestId(int requestId) {
125         mEventInternal.ifPresent(event -> event.mRequestId = requestId);
126     }
127 
maybeSetNoPresentationEventReason(@otShownReason int reason)128     public void maybeSetNoPresentationEventReason(@NotShownReason int reason) {
129         mEventInternal.ifPresent(event -> {
130             if (event.mCountShown == 0) {
131                 event.mNoPresentationReason = reason;
132             }
133         });
134     }
135 
maybeSetNoPresentationEventReasonIfNoReasonExists(@otShownReason int reason)136     public void maybeSetNoPresentationEventReasonIfNoReasonExists(@NotShownReason int reason) {
137         mEventInternal.ifPresent(event -> {
138             if (event.mCountShown == 0 && event.mNoPresentationReason == NOT_SHOWN_REASON_UNKNOWN) {
139                 event.mNoPresentationReason = reason;
140             }
141         });
142     }
143 
maybeSetAvailableCount(@ullable List<Dataset> datasetList, AutofillId currentViewId)144     public void maybeSetAvailableCount(@Nullable List<Dataset> datasetList,
145             AutofillId currentViewId) {
146         mEventInternal.ifPresent(event -> {
147             int availableCount = getDatasetCountForAutofillId(datasetList, currentViewId);
148             event.mAvailableCount = availableCount;
149             event.mIsDatasetAvailable = availableCount > 0;
150         });
151     }
152 
maybeSetCountShown(@ullable List<Dataset> datasetList, AutofillId currentViewId)153     public void maybeSetCountShown(@Nullable List<Dataset> datasetList,
154             AutofillId currentViewId) {
155         mEventInternal.ifPresent(event -> {
156             int countShown = getDatasetCountForAutofillId(datasetList, currentViewId);
157             event.mCountShown = countShown;
158             if (countShown > 0) {
159                 event.mNoPresentationReason = NOT_SHOWN_REASON_ANY_SHOWN;
160             }
161         });
162     }
163 
getDatasetCountForAutofillId(@ullable List<Dataset> datasetList, AutofillId currentViewId)164     private static int getDatasetCountForAutofillId(@Nullable List<Dataset> datasetList,
165             AutofillId currentViewId) {
166         int availableCount = 0;
167         if (datasetList != null) {
168             for (int i = 0; i < datasetList.size(); i++) {
169                 Dataset data = datasetList.get(i);
170                 if (data != null && data.getFieldIds() != null
171                         && data.getFieldIds().contains(currentViewId)) {
172                     availableCount += 1;
173                 }
174             }
175         }
176         return availableCount;
177     }
178 
maybeSetCountFilteredUserTyping(int countFilteredUserTyping)179     public void maybeSetCountFilteredUserTyping(int countFilteredUserTyping) {
180         mEventInternal.ifPresent(event -> {
181             event.mCountFilteredUserTyping = countFilteredUserTyping;
182         });
183     }
184 
maybeSetCountNotShownImePresentationNotDrawn( int countNotShownImePresentationNotDrawn)185     public void maybeSetCountNotShownImePresentationNotDrawn(
186             int countNotShownImePresentationNotDrawn) {
187         mEventInternal.ifPresent(event -> {
188             event.mCountNotShownImePresentationNotDrawn = countNotShownImePresentationNotDrawn;
189         });
190     }
191 
maybeSetCountNotShownImeUserNotSeen(int countNotShownImeUserNotSeen)192     public void maybeSetCountNotShownImeUserNotSeen(int countNotShownImeUserNotSeen) {
193         mEventInternal.ifPresent(event -> {
194             event.mCountNotShownImeUserNotSeen = countNotShownImeUserNotSeen;
195         });
196     }
197 
maybeSetDisplayPresentationType(@iType int uiType)198     public void maybeSetDisplayPresentationType(@UiType int uiType) {
199         mEventInternal.ifPresent(event -> {
200             event.mDisplayPresentationType = getDisplayPresentationType(uiType);
201         });
202     }
203 
maybeSetFillRequestSentTimestampMs(int timestamp)204     public void maybeSetFillRequestSentTimestampMs(int timestamp) {
205         mEventInternal.ifPresent(event -> {
206             event.mFillRequestSentTimestampMs = timestamp;
207         });
208     }
209 
maybeSetFillResponseReceivedTimestampMs(int timestamp)210     public void maybeSetFillResponseReceivedTimestampMs(int timestamp) {
211         mEventInternal.ifPresent(event -> {
212             event.mFillResponseReceivedTimestampMs = timestamp;
213         });
214     }
215 
maybeSetSuggestionSentTimestampMs(int timestamp)216     public void maybeSetSuggestionSentTimestampMs(int timestamp) {
217         mEventInternal.ifPresent(event -> {
218             event.mSuggestionSentTimestampMs = timestamp;
219         });
220     }
221 
maybeSetSuggestionPresentedTimestampMs(int timestamp)222     public void maybeSetSuggestionPresentedTimestampMs(int timestamp) {
223         mEventInternal.ifPresent(event -> {
224             event.mSuggestionPresentedTimestampMs = timestamp;
225         });
226     }
227 
maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId)228     public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) {
229         mEventInternal.ifPresent(event -> {
230             event.mDisplayPresentationType =
231                 AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
232             String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
233                     Settings.Secure.DEFAULT_INPUT_METHOD, userId);
234             if (TextUtils.isEmpty(imeString)) {
235                 Slog.w(TAG, "No default IME found");
236                 return;
237             }
238             ComponentName imeComponent = ComponentName.unflattenFromString(imeString);
239             if (imeComponent == null) {
240                 Slog.w(TAG, "No default IME found");
241                 return;
242             }
243             int imeUid;
244             String packageName = imeComponent.getPackageName();
245             try {
246                 imeUid = context.getPackageManager().getApplicationInfoAsUser(packageName,
247                         PackageManager.ApplicationInfoFlags.of(0), userId).uid;
248             } catch (PackageManager.NameNotFoundException e) {
249                 Slog.w(TAG, "Couldn't find packageName: " + packageName);
250                 return;
251             }
252             event.mInlineSuggestionHostUid = imeUid;
253         });
254     }
255 
maybeSetAutofillServiceUid(int uid)256     public void maybeSetAutofillServiceUid(int uid) {
257         mEventInternal.ifPresent(event -> {
258             event.mAutofillServiceUid = uid;
259         });
260     }
261 
maybeSetIsNewRequest(boolean isRequestTriggered)262     public void maybeSetIsNewRequest(boolean isRequestTriggered) {
263         mEventInternal.ifPresent(event -> {
264             event.mIsRequestTriggered = isRequestTriggered;
265         });
266     }
267 
logAndEndEvent()268     public void logAndEndEvent() {
269         if (!mEventInternal.isPresent()) {
270             Slog.w(TAG, "Shouldn't be logging AutofillPresentationEventReported again for same "
271                     + "event");
272             return;
273         }
274         PresentationStatsEventInternal event = mEventInternal.get();
275         if (sVerbose) {
276             Slog.v(TAG, "Log AutofillPresentationEventReported:"
277                     + " requestId=" + event.mRequestId
278                     + " sessionId=" + mSessionId
279                     + " mNoPresentationEventReason=" + event.mNoPresentationReason
280                     + " mAvailableCount=" + event.mAvailableCount
281                     + " mCountShown=" + event.mCountShown
282                     + " mCountFilteredUserTyping=" + event.mCountFilteredUserTyping
283                     + " mCountNotShownImePresentationNotDrawn="
284                     + event.mCountNotShownImePresentationNotDrawn
285                     + " mCountNotShownImeUserNotSeen=" + event.mCountNotShownImeUserNotSeen
286                     + " mDisplayPresentationType=" + event.mDisplayPresentationType
287                     + " mAutofillServiceUid=" + event.mAutofillServiceUid
288                     + " mInlineSuggestionHostUid=" + event.mInlineSuggestionHostUid
289                     + " mIsRequestTriggered=" + event.mIsRequestTriggered
290                     + " mFillRequestSentTimestampMs=" + event.mFillRequestSentTimestampMs
291                     + " mFillResponseReceivedTimestampMs=" + event.mFillResponseReceivedTimestampMs
292                     + " mSuggestionSentTimestampMs=" + event.mSuggestionSentTimestampMs
293                     + " mSuggestionPresentedTimestampMs=" + event.mSuggestionPresentedTimestampMs);
294         }
295 
296         // TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
297         if (!event.mIsDatasetAvailable) {
298             mEventInternal = Optional.empty();
299             return;
300         }
301         FrameworkStatsLog.write(
302                 AUTOFILL_PRESENTATION_EVENT_REPORTED,
303                 event.mRequestId,
304                 mSessionId,
305                 event.mNoPresentationReason,
306                 event.mAvailableCount,
307                 event.mCountShown,
308                 event.mCountFilteredUserTyping,
309                 event.mCountNotShownImePresentationNotDrawn,
310                 event.mCountNotShownImeUserNotSeen,
311                 event.mDisplayPresentationType,
312                 event.mAutofillServiceUid,
313                 event.mInlineSuggestionHostUid,
314                 event.mIsRequestTriggered,
315                 event.mFillRequestSentTimestampMs,
316                 event.mFillResponseReceivedTimestampMs,
317                 event.mSuggestionSentTimestampMs,
318                 event.mSuggestionPresentedTimestampMs);
319         mEventInternal = Optional.empty();
320     }
321 
322     private final class PresentationStatsEventInternal {
323         int mRequestId;
324         @NotShownReason int mNoPresentationReason = NOT_SHOWN_REASON_UNKNOWN;
325         boolean mIsDatasetAvailable;
326         int mAvailableCount;
327         int mCountShown;
328         int mCountFilteredUserTyping;
329         int mCountNotShownImePresentationNotDrawn;
330         int mCountNotShownImeUserNotSeen;
331         int mDisplayPresentationType = AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
332         int mAutofillServiceUid = -1;
333         int mInlineSuggestionHostUid = -1;
334         boolean mIsRequestTriggered;
335         int mFillRequestSentTimestampMs;
336         int mFillResponseReceivedTimestampMs;
337         int mSuggestionSentTimestampMs;
338         int mSuggestionPresentedTimestampMs;
339 
PresentationStatsEventInternal()340         PresentationStatsEventInternal() {}
341     }
342 
getNoPresentationEventReason( @utofillManager.AutofillCommitReason int commitReason)343     static int getNoPresentationEventReason(
344             @AutofillManager.AutofillCommitReason int commitReason) {
345         switch (commitReason) {
346             case COMMIT_REASON_VIEW_COMMITTED:
347                 return NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY;
348             case COMMIT_REASON_ACTIVITY_FINISHED:
349                 return NOT_SHOWN_REASON_ACTIVITY_FINISHED;
350             case COMMIT_REASON_VIEW_CHANGED:
351                 return NOT_SHOWN_REASON_VIEW_CHANGED;
352             case COMMIT_REASON_VIEW_CLICKED:
353                 // TODO(b/234185326): Add separate reason for view clicked.
354             default:
355                 return NOT_SHOWN_REASON_UNKNOWN;
356         }
357     }
358 
getDisplayPresentationType(@iType int uiType)359     private static int getDisplayPresentationType(@UiType int uiType) {
360         switch (uiType) {
361             case UI_TYPE_MENU:
362                 return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
363             case UI_TYPE_INLINE:
364                 return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
365             case UI_TYPE_DIALOG:
366                 return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__DIALOG;
367             default:
368                 return AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
369         }
370     }
371 }
372