-
Notifications
You must be signed in to change notification settings - Fork 6.6k
/
AutofillManagerWrapper.java
233 lines (202 loc) · 8.88 KB
/
AutofillManagerWrapper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.autofill;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.view.View;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.CollectionUtil;
import org.chromium.base.Log;
import org.chromium.build.annotations.DoNotStripLogs;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
* The class to call Android's AutofillManager.
*/
@RequiresApi(Build.VERSION_CODES.O)
public class AutofillManagerWrapper {
// Don't change TAG, it is used for runtime log.
// NOTE: As a result of the above, the tag below still references the name of this class from
// when it was originally developed specifically for Android WebView.
public static final String TAG = "AwAutofillManager";
private static final String AWG_COMPONENT_NAME =
"com.google.android.gms/com.google.android.gms.autofill.service.AutofillService";
/**
* The observer of suggestion window.
*/
public static interface InputUIObserver { void onInputUIShown(); }
private static class AutofillInputUIMonitor extends AutofillManager.AutofillCallback {
private WeakReference<AutofillManagerWrapper> mManager;
public AutofillInputUIMonitor(AutofillManagerWrapper manager) {
mManager = new WeakReference<AutofillManagerWrapper>(manager);
}
@Override
public void onAutofillEvent(View view, int virtualId, int event) {
AutofillManagerWrapper manager = mManager.get();
if (manager == null) return;
manager.mIsAutofillInputUIShowing = (event == EVENT_INPUT_SHOWN);
if (event == EVENT_INPUT_SHOWN) manager.notifyInputUIChange();
}
}
private static boolean sIsLoggable;
private AutofillManager mAutofillManager;
private boolean mIsAutofillInputUIShowing;
private AutofillInputUIMonitor mMonitor;
private boolean mDestroyed;
private boolean mDisabled;
private ArrayList<WeakReference<InputUIObserver>> mInputUIObservers;
// Indicates if AwG is the current Android autofill service.
private final boolean mIsAwGCurrentAutofillService;
public AutofillManagerWrapper(Context context) {
updateLogStat();
if (isLoggable()) log("constructor");
mAutofillManager = context.getSystemService(AutofillManager.class);
mDisabled = mAutofillManager == null || !mAutofillManager.isEnabled();
if (mDisabled) {
mIsAwGCurrentAutofillService = false;
if (isLoggable()) log("disabled");
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ComponentName componentName = null;
try {
componentName = mAutofillManager.getAutofillServiceComponentName();
} catch (Exception e) {
// Can't catch com.android.internal.util.SyncResultReceiver.TimeoutException,
// because
// - The exception isn't Android API.
// - Different version of Android handle it differently.
// Uses Exception to catch various cases. (refer to crbug.com/1186406)
Log.e(TAG, "getAutofillServiceComponentName", e);
}
if (componentName != null) {
mIsAwGCurrentAutofillService =
AWG_COMPONENT_NAME.equals(componentName.flattenToString());
AutofillProviderUMA.logCurrentProvider(componentName.getPackageName());
} else {
mIsAwGCurrentAutofillService = false;
}
} else {
mIsAwGCurrentAutofillService = false;
}
mMonitor = new AutofillInputUIMonitor(this);
mAutofillManager.registerCallback(mMonitor);
}
public void notifyVirtualValueChanged(View parent, int childId, AutofillValue value) {
if (mDisabled || checkAndWarnIfDestroyed()) return;
if (isLoggable()) log("notifyVirtualValueChanged");
mAutofillManager.notifyValueChanged(parent, childId, value);
}
public void commit(int submissionSource) {
if (mDisabled || checkAndWarnIfDestroyed()) return;
if (isLoggable()) log("commit source:" + submissionSource);
mAutofillManager.commit();
}
public void cancel() {
if (mDisabled || checkAndWarnIfDestroyed()) return;
if (isLoggable()) log("cancel");
mAutofillManager.cancel();
}
public void notifyVirtualViewEntered(View parent, int childId, Rect absBounds) {
// Log warning only when the autofill is triggered.
if (mDisabled) {
Log.w(TAG, "Autofill is disabled: AutofillManager isn't available in given Context.");
return;
}
if (checkAndWarnIfDestroyed()) return;
if (isLoggable()) log("notifyVirtualViewEntered");
mAutofillManager.notifyViewEntered(parent, childId, absBounds);
}
public void notifyVirtualViewExited(View parent, int childId) {
if (mDisabled || checkAndWarnIfDestroyed()) return;
if (isLoggable()) log("notifyVirtualViewExited");
mAutofillManager.notifyViewExited(parent, childId);
}
public void requestAutofill(View parent, int virtualId, Rect absBounds) {
if (mDisabled || checkAndWarnIfDestroyed()) return;
if (isLoggable()) log("requestAutofill");
mAutofillManager.requestAutofill(parent, virtualId, absBounds);
}
public boolean isAutofillInputUIShowing() {
if (mDisabled || checkAndWarnIfDestroyed()) return false;
if (isLoggable()) log("isAutofillInputUIShowing: " + mIsAutofillInputUIShowing);
return mIsAutofillInputUIShowing;
}
public void destroy() {
if (mDisabled || checkAndWarnIfDestroyed()) return;
if (isLoggable()) log("destroy");
try {
// The binder in the autofill service side might already be dropped,
// unregisterCallback() will cause various exceptions in this
// scenario (see crbug.com/1078337), catching RuntimeException here prevents crash.
mAutofillManager.unregisterCallback(mMonitor);
} catch (RuntimeException e) {
// We are not logging anything here since some of the exceptions are raised as 'generic'
// RuntimeException which makes it difficult to catch and ignore separately; and the
// RuntimeException seemed only happen in Android O, therefore, isn't actionable.
} finally {
mAutofillManager = null;
mDestroyed = true;
}
}
public boolean isDisabled() {
return mDisabled;
}
/**
* Only work for Android P and beyond. Always return false for Android O.
* @return if the Autofill with Google is the current autofill service.
*/
public boolean isAwGCurrentAutofillService() {
return mIsAwGCurrentAutofillService;
}
private boolean checkAndWarnIfDestroyed() {
if (mDestroyed) {
Log.w(TAG, "Application attempted to call on a destroyed AutofillManagerWrapper",
new Throwable());
}
return mDestroyed;
}
public void addInputUIObserver(InputUIObserver observer) {
if (observer == null) return;
if (mInputUIObservers == null) {
mInputUIObservers = new ArrayList<WeakReference<InputUIObserver>>();
}
mInputUIObservers.add(new WeakReference<InputUIObserver>(observer));
}
@VisibleForTesting
public void notifyInputUIChange() {
for (InputUIObserver observer : CollectionUtil.strengthen(mInputUIObservers)) {
observer.onInputUIShown();
}
}
public void notifyNewSessionStarted(boolean hasServerPrediction) {
updateLogStat();
if (isLoggable()) log("Session starts, has server prediction = " + hasServerPrediction);
}
public void onQueryDone(boolean success) {
if (isLoggable()) log("Query " + (success ? "succeed" : "failed"));
}
/**
* Always check isLoggable() before call this method.
*/
public static void log(String log) {
// Log.i() instead of Log.d() is used here because log.d() is stripped out in release build.
Log.i(TAG, log);
}
public static boolean isLoggable() {
return sIsLoggable;
}
@DoNotStripLogs
private static void updateLogStat() {
// Use 'setprop log.tag.AwAutofillManager DEBUG' to enable the log at runtime.
// NOTE: See the comment on TAG above for why this is still AwAutofillManager.
// Check the system setting directly.
sIsLoggable = android.util.Log.isLoggable(TAG, Log.DEBUG);
}
}