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 com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED;
20 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_EXPLICITLY_REQUESTED;
21 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_NORMAL_TRIGGER;
22 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_PRE_TRIGGER;
23 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_RETRIGGER;
24 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
25 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_UNKNOWN;
26 import static com.android.server.autofill.Helper.sVerbose;
27 
28 import android.annotation.IntDef;
29 import android.content.ComponentName;
30 import android.content.Context;
31 import android.content.pm.PackageManager;
32 import android.provider.Settings;
33 import android.text.TextUtils;
34 import android.util.Slog;
35 
36 import com.android.internal.util.FrameworkStatsLog;
37 
38 import java.lang.annotation.Retention;
39 import java.lang.annotation.RetentionPolicy;
40 import java.util.Optional;
41 
42 /**
43  * Helper class to log Autofill FillRequest filing stats.
44  */
45 public final class FillRequestEventLogger {
46     private static final String TAG = "FillRequestEventLogger";
47 
48     /**
49      * Reasons why presentation was not shown. These are wrappers around
50      * {@link com.android.os.AtomsProto.AutofillFillRequestReported.RequestTriggerReason}.
51      */
52     @IntDef(prefix = {"TRIGGER_REASON"}, value = {
53             TRIGGER_REASON_UNKNOWN,
54             TRIGGER_REASON_EXPLICITLY_REQUESTED,
55             TRIGGER_REASON_RETRIGGER,
56             TRIGGER_REASON_PRE_TRIGGER,
57             TRIGGER_REASON_NORMAL_TRIGGER,
58             TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE
59     })
60     @Retention(RetentionPolicy.SOURCE)
61     public @interface TriggerReason {
62     }
63 
64     public static final int TRIGGER_REASON_UNKNOWN =
65             AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_UNKNOWN;
66     public static final int TRIGGER_REASON_EXPLICITLY_REQUESTED =
67             AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_EXPLICITLY_REQUESTED;
68     public static final int TRIGGER_REASON_RETRIGGER =
69             AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_RETRIGGER;
70     public static final int TRIGGER_REASON_PRE_TRIGGER =
71             AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_PRE_TRIGGER;
72     public static final int TRIGGER_REASON_NORMAL_TRIGGER =
73             AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_NORMAL_TRIGGER;
74     public static final int TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE =
75             AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
76 
77     private final int mSessionId;
78     private Optional<FillRequestEventInternal> mEventInternal;
79 
FillRequestEventLogger(int sessionId)80     private FillRequestEventLogger(int sessionId) {
81         mSessionId = sessionId;
82         mEventInternal = Optional.empty();
83     }
84 
85     /**
86      * A factory constructor to create FillRequestEventLogger.
87      */
forSessionId(int sessionId)88     public static FillRequestEventLogger forSessionId(int sessionId) {
89         return new FillRequestEventLogger(sessionId);
90     }
91     /**
92      * Reset mEventInternal before logging for a new request. It shall be called for each
93      * FillRequest.
94      */
startLogForNewRequest()95     public void startLogForNewRequest() {
96         if (!mEventInternal.isEmpty()) {
97             Slog.w(TAG, "FillRequestEventLogger is not empty before starting for a new " +
98                     "request");
99         }
100         mEventInternal = Optional.of(new FillRequestEventInternal());
101     }
102 
103     /**
104      * Set request_id as long as mEventInternal presents.
105      * For the case of Augmented Autofill, set to -2.
106      */
maybeSetRequestId(int requestId)107     public void maybeSetRequestId(int requestId) {
108         mEventInternal.ifPresent(event -> event.mRequestId = requestId);
109     }
110 
111     /**
112      * Set service_uid as long as mEventInternal presents.
113      */
maybeSetAutofillServiceUid(int uid)114     public void maybeSetAutofillServiceUid(int uid) {
115         mEventInternal.ifPresent(event -> {
116             event.mAutofillServiceUid = uid;
117         });
118     }
119 
120     /**
121      * Set inline_suggestion_host_uid as long as mEventInternal presents.
122      */
maybeSetInlineSuggestionHostUid(Context context, int userId)123     public void maybeSetInlineSuggestionHostUid(Context context, int userId) {
124         mEventInternal.ifPresent(event -> {
125             String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
126                     Settings.Secure.DEFAULT_INPUT_METHOD, userId);
127             if (TextUtils.isEmpty(imeString)) {
128                 Slog.w(TAG, "No default IME found");
129                 return;
130             }
131             ComponentName imeComponent = ComponentName.unflattenFromString(imeString);
132             if (imeComponent == null) {
133                 Slog.w(TAG, "No default IME found");
134                 return;
135             }
136             int imeUid;
137             String packageName = imeComponent.getPackageName();
138             try {
139                 imeUid = context.getPackageManager().getApplicationInfoAsUser(packageName,
140                         PackageManager.ApplicationInfoFlags.of(0), userId).uid;
141             } catch (PackageManager.NameNotFoundException e) {
142                 Slog.w(TAG, "Couldn't find packageName: " + packageName);
143                 return;
144             }
145             event.mInlineSuggestionHostUid = imeUid;
146         });
147     }
148 
149 
150     /**
151      * Set flags as long as mEventInternal presents.
152      */
maybeSetFlags(int flags)153     public void maybeSetFlags(int flags) {
154         mEventInternal.ifPresent(event -> {
155             event.mFlags = flags;
156         });
157     }
158 
159     /**
160      * Set request_trigger_reason as long as mEventInternal presents.
161      */
maybeSetRequestTriggerReason(@riggerReason int reason)162     public void maybeSetRequestTriggerReason(@TriggerReason int reason) {
163         mEventInternal.ifPresent(event -> {
164             event.mRequestTriggerReason = reason;
165         });
166     }
167 
168     /**
169      * Set is_augmented as long as mEventInternal presents.
170      */
maybeSetIsAugmented(boolean val)171     public void maybeSetIsAugmented(boolean val) {
172         mEventInternal.ifPresent(event -> {
173             event.mIsAugmented = val;
174         });
175     }
176 
177     /**
178      * Set is_client_suggestion as long as mEventInternal presents.
179      */
maybeSetIsClientSuggestionFallback(boolean val)180     public void maybeSetIsClientSuggestionFallback(boolean val) {
181         mEventInternal.ifPresent(event -> {
182             event.mIsClientSuggestionFallback = val;
183         });
184     }
185 
186     /**
187      * Set is_fill_dialog_eligible as long as mEventInternal presents.
188      */
maybeSetIsFillDialogEligible(boolean val)189     public void maybeSetIsFillDialogEligible(boolean val) {
190         mEventInternal.ifPresent(event -> {
191             event.mIsFillDialogEligible = val;
192         });
193     }
194 
195     /**
196      * Set latency_fill_request_sent_millis as long as mEventInternal presents.
197      */
maybeSetLatencyFillRequestSentMillis(int timestamp)198     public void maybeSetLatencyFillRequestSentMillis(int timestamp) {
199         mEventInternal.ifPresent(event -> {
200             event.mLatencyFillRequestSentMillis = timestamp;
201         });
202     }
203 
204     /**
205      * Set app_package_uid as long as mEventInternal presents.
206      */
maybeSetAppPackageUid(int uid)207     public void maybeSetAppPackageUid(int uid) {
208         mEventInternal.ifPresent(event -> {
209             event.mAppPackageUid = uid;
210         });
211     }
212 
213     /**
214      * Log an AUTOFILL_FILL_REQUEST_REPORTED event.
215      */
logAndEndEvent()216     public void logAndEndEvent() {
217         if (!mEventInternal.isPresent()) {
218             Slog.w(TAG, "Shouldn't be logging AutofillFillRequestReported again for same "
219                     + "event");
220             return;
221         }
222         FillRequestEventInternal event = mEventInternal.get();
223         if (sVerbose) {
224             Slog.v(TAG, "Log AutofillFillRequestReported:"
225                     + " requestId=" + event.mRequestId
226                     + " sessionId=" + mSessionId
227                     + " mAutofillServiceUid=" + event.mAutofillServiceUid
228                     + " mInlineSuggestionHostUid=" + event.mInlineSuggestionHostUid
229                     + " mIsAugmented=" + event.mIsAugmented
230                     + " mIsClientSuggestionFallback=" + event.mIsClientSuggestionFallback
231                     + " mIsFillDialogEligible=" + event.mIsFillDialogEligible
232                     + " mRequestTriggerReason=" + event.mRequestTriggerReason
233                     + " mFlags=" + event.mFlags
234                     + " mLatencyFillRequestSentMillis=" + event.mLatencyFillRequestSentMillis
235                     + " mAppPackageUid=" + event.mAppPackageUid);
236         }
237         FrameworkStatsLog.write(
238                 AUTOFILL_FILL_REQUEST_REPORTED,
239                 event.mRequestId,
240                 mSessionId,
241                 event.mAutofillServiceUid,
242                 event.mInlineSuggestionHostUid,
243                 event.mIsAugmented,
244                 event.mIsClientSuggestionFallback,
245                 event.mIsFillDialogEligible,
246                 event.mRequestTriggerReason,
247                 event.mFlags,
248                 event.mLatencyFillRequestSentMillis,
249                 event.mAppPackageUid);
250         mEventInternal = Optional.empty();
251     }
252 
253     private static final class FillRequestEventInternal {
254         int mRequestId;
255         int mAppPackageUid = -1;
256         int mAutofillServiceUid = -1;
257         int mInlineSuggestionHostUid = -1;
258         boolean mIsAugmented = false;
259         boolean mIsClientSuggestionFallback = false;
260         boolean mIsFillDialogEligible = false;
261         int mRequestTriggerReason =
262                 AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_UNKNOWN;
263         int mFlags = -1;
264         int mLatencyFillRequestSentMillis = -1;
265 
FillRequestEventInternal()266         FillRequestEventInternal() {
267         }
268     }
269 }
270