1 /*
2  * Copyright (C) 2017 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.FillRequest.FLAG_MANUAL_REQUEST;
20 
21 import static com.android.server.autofill.Helper.sDebug;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.graphics.Rect;
26 import android.service.autofill.FillResponse;
27 import android.util.DebugUtils;
28 import android.util.Slog;
29 import android.view.autofill.AutofillId;
30 import android.view.autofill.AutofillValue;
31 
32 import java.io.PrintWriter;
33 
34 /**
35  * State for a given view with a AutofillId.
36  *
37  * <p>This class holds state about a view and calls its listener when the fill UI is ready to
38  * be displayed for the view.
39  */
40 final class ViewState {
41     interface Listener {
42         /**
43          * Called when the fill UI is ready to be shown for this view.
44          */
onFillReady(@onNull FillResponse fillResponse, @NonNull AutofillId focusedId, @Nullable AutofillValue value, int flags)45         void onFillReady(@NonNull FillResponse fillResponse, @NonNull AutofillId focusedId,
46                 @Nullable AutofillValue value, int flags);
47     }
48 
49     private static final String TAG = "ViewState";
50 
51     /** Initial state. */
52     public static final int STATE_INITIAL = 0x001;
53     /** View id is present in a dataset returned by the service. */
54     public static final int STATE_FILLABLE = 0x002;
55     /** View was autofilled after user selected a dataset. */
56     public static final int STATE_AUTOFILLED = 0x004;
57     /** View value was changed, but not by the service. */
58     public static final int STATE_CHANGED = 0x008;
59     /** Set only in the View that started a session. */
60     public static final int STATE_STARTED_SESSION = 0x010;
61     /** View that started a new partition when focused on. */
62     public static final int STATE_STARTED_PARTITION = 0x020;
63     /** User select a dataset in this view, but service must authenticate first. */
64     public static final int STATE_WAITING_DATASET_AUTH = 0x040;
65     /** Service does not care about this view. */
66     public static final int STATE_IGNORED = 0x080;
67     /** User manually request autofill in this view, after it was already autofilled. */
68     public static final int STATE_RESTARTED_SESSION = 0x100;
69     /** View is the URL bar of a package on compat mode. */
70     public  static final int STATE_URL_BAR = 0x200;
71     /** View was asked to autofill but failed to do so. */
72     public static final int STATE_AUTOFILL_FAILED = 0x400;
73     /** View has been autofilled at least once. */
74     public static final int STATE_AUTOFILLED_ONCE = 0x800;
75     /** View triggered the latest augmented autofill request. */
76     public static final int STATE_TRIGGERED_AUGMENTED_AUTOFILL = 0x1000;
77     /** Inline suggestions were shown for this View. */
78     public static final int STATE_INLINE_SHOWN = 0x2000;
79     /** A character was removed from the View value (not by the service). */
80     public static final int STATE_CHAR_REMOVED = 0x4000;
81     /** Showing inline suggestions is not allowed for this View. */
82     public static final int STATE_INLINE_DISABLED = 0x8000;
83     /** The View is waiting for an inline suggestions request from IME.*/
84     public static final int STATE_PENDING_CREATE_INLINE_REQUEST = 0x10000;
85     /** Fill dialog were shown for this View. */
86     public static final int STATE_FILL_DIALOG_SHOWN = 0x20000;
87 
88     public final AutofillId id;
89 
90     private final Listener mListener;
91 
92     private FillResponse mResponse;
93     private AutofillValue mCurrentValue;
94     private AutofillValue mAutofilledValue;
95     private AutofillValue mSanitizedValue;
96     private Rect mVirtualBounds;
97     private int mState;
98     private String mDatasetId;
99 
ViewState(AutofillId id, Listener listener, int state)100     ViewState(AutofillId id, Listener listener, int state) {
101         this.id = id;
102         mListener = listener;
103         mState = state;
104     }
105 
106     /**
107      * Gets the boundaries of the virtual view, or {@code null} if the the view is not virtual.
108      */
109     @Nullable
getVirtualBounds()110     Rect getVirtualBounds() {
111         return mVirtualBounds;
112     }
113 
114     /**
115      * Gets the current value of the view.
116      */
117     @Nullable
getCurrentValue()118     AutofillValue getCurrentValue() {
119         return mCurrentValue;
120     }
121 
setCurrentValue(AutofillValue value)122     void setCurrentValue(AutofillValue value) {
123         mCurrentValue = value;
124     }
125 
126     @Nullable
getAutofilledValue()127     AutofillValue getAutofilledValue() {
128         return mAutofilledValue;
129     }
130 
setAutofilledValue(@ullable AutofillValue value)131     void setAutofilledValue(@Nullable AutofillValue value) {
132         mAutofilledValue = value;
133     }
134 
135     @Nullable
getSanitizedValue()136     AutofillValue getSanitizedValue() {
137         return mSanitizedValue;
138     }
139 
setSanitizedValue(@ullable AutofillValue value)140     void setSanitizedValue(@Nullable AutofillValue value) {
141         mSanitizedValue = value;
142     }
143 
144     @Nullable
getResponse()145     FillResponse getResponse() {
146         return mResponse;
147     }
148 
setResponse(FillResponse response)149     void setResponse(FillResponse response) {
150         mResponse = response;
151     }
152 
getState()153     int getState() {
154         return mState;
155     }
156 
getStateAsString()157     String getStateAsString() {
158         return getStateAsString(mState);
159     }
160 
getStateAsString(int state)161     static String getStateAsString(int state) {
162         return DebugUtils.flagsToString(ViewState.class, "STATE_", state);
163     }
164 
setState(int state)165     void setState(int state) {
166         if (mState == STATE_INITIAL) {
167             mState = state;
168         } else {
169             mState |= state;
170         }
171         if (state == STATE_AUTOFILLED) {
172             mState |= STATE_AUTOFILLED_ONCE;
173         }
174     }
175 
resetState(int state)176     void resetState(int state) {
177         mState &= ~state;
178     }
179 
180     @Nullable
getDatasetId()181     String getDatasetId() {
182         return mDatasetId;
183     }
184 
setDatasetId(String datasetId)185     void setDatasetId(String datasetId) {
186         mDatasetId = datasetId;
187     }
188 
189     // TODO: refactor / rename / document this method (and maybeCallOnFillReady) to make it clear
190     // that it can change the value and update the UI; similarly, should replace code that
191     // directly sets mAutofillValue to use encapsulation.
update(@ullable AutofillValue autofillValue, @Nullable Rect virtualBounds, int flags)192     void update(@Nullable AutofillValue autofillValue, @Nullable Rect virtualBounds, int flags) {
193         if (autofillValue != null) {
194             mCurrentValue = autofillValue;
195         }
196         if (virtualBounds != null) {
197             mVirtualBounds = virtualBounds;
198         }
199 
200         maybeCallOnFillReady(flags);
201     }
202 
203     /**
204      * Calls {@link
205      * Listener#onFillReady(FillResponse, AutofillId, AutofillValue, int)} if the
206      * fill UI is ready to be displayed (i.e. when response and bounds are set).
207      */
maybeCallOnFillReady(int flags)208     void maybeCallOnFillReady(int flags) {
209         if ((mState & STATE_AUTOFILLED) != 0 && (flags & FLAG_MANUAL_REQUEST) == 0) {
210             if (sDebug) Slog.d(TAG, "Ignoring UI for " + id + " on " + getStateAsString());
211             return;
212         }
213         // First try the current response associated with this View.
214         if (mResponse != null) {
215             if (mResponse.getDatasets() != null || mResponse.getAuthentication() != null) {
216                 mListener.onFillReady(mResponse, this.id, mCurrentValue, flags);
217             }
218         }
219     }
220 
221     @Override
toString()222     public String toString() {
223         final StringBuilder builder = new StringBuilder("ViewState: [id=").append(id);
224         if (mDatasetId != null) {
225             builder.append(", datasetId:" ).append(mDatasetId);
226         }
227         builder.append(", state:").append(getStateAsString());
228         if (mCurrentValue != null) {
229             builder.append(", currentValue:" ).append(mCurrentValue);
230         }
231         if (mAutofilledValue != null) {
232             builder.append(", autofilledValue:" ).append(mAutofilledValue);
233         }
234         if (mSanitizedValue != null) {
235             builder.append(", sanitizedValue:" ).append(mSanitizedValue);
236         }
237         if (mVirtualBounds != null) {
238             builder.append(", virtualBounds:" ).append(mVirtualBounds);
239         }
240         builder.append("]");
241         return builder.toString();
242     }
243 
dump(String prefix, PrintWriter pw)244     void dump(String prefix, PrintWriter pw) {
245         pw.print(prefix); pw.print("id:" ); pw.println(id);
246         if (mDatasetId != null) {
247             pw.print(prefix); pw.print("datasetId:" ); pw.println(mDatasetId);
248         }
249         pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString());
250         if (mResponse != null) {
251             pw.print(prefix); pw.print("response id:");pw.println(mResponse.getRequestId());
252         }
253         if (mCurrentValue != null) {
254             pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
255         }
256         if (mAutofilledValue != null) {
257             pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
258         }
259         if (mSanitizedValue != null) {
260             pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
261         }
262         if (mVirtualBounds != null) {
263             pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
264         }
265     }
266 }