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)45         void onFillReady(@NonNull FillResponse fillResponse, @NonNull AutofillId focusedId,
46                 @Nullable AutofillValue value);
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 
86     public final AutofillId id;
87 
88     private final Listener mListener;
89 
90     private FillResponse mResponse;
91     private AutofillValue mCurrentValue;
92     private AutofillValue mAutofilledValue;
93     private AutofillValue mSanitizedValue;
94     private Rect mVirtualBounds;
95     private int mState;
96     private String mDatasetId;
97 
ViewState(AutofillId id, Listener listener, int state)98     ViewState(AutofillId id, Listener listener, int state) {
99         this.id = id;
100         mListener = listener;
101         mState = state;
102     }
103 
104     /**
105      * Gets the boundaries of the virtual view, or {@code null} if the the view is not virtual.
106      */
107     @Nullable
getVirtualBounds()108     Rect getVirtualBounds() {
109         return mVirtualBounds;
110     }
111 
112     /**
113      * Gets the current value of the view.
114      */
115     @Nullable
getCurrentValue()116     AutofillValue getCurrentValue() {
117         return mCurrentValue;
118     }
119 
setCurrentValue(AutofillValue value)120     void setCurrentValue(AutofillValue value) {
121         mCurrentValue = value;
122     }
123 
124     @Nullable
getAutofilledValue()125     AutofillValue getAutofilledValue() {
126         return mAutofilledValue;
127     }
128 
setAutofilledValue(@ullable AutofillValue value)129     void setAutofilledValue(@Nullable AutofillValue value) {
130         mAutofilledValue = value;
131     }
132 
133     @Nullable
getSanitizedValue()134     AutofillValue getSanitizedValue() {
135         return mSanitizedValue;
136     }
137 
setSanitizedValue(@ullable AutofillValue value)138     void setSanitizedValue(@Nullable AutofillValue value) {
139         mSanitizedValue = value;
140     }
141 
142     @Nullable
getResponse()143     FillResponse getResponse() {
144         return mResponse;
145     }
146 
setResponse(FillResponse response)147     void setResponse(FillResponse response) {
148         mResponse = response;
149     }
150 
getState()151     int getState() {
152         return mState;
153     }
154 
getStateAsString()155     String getStateAsString() {
156         return getStateAsString(mState);
157     }
158 
getStateAsString(int state)159     static String getStateAsString(int state) {
160         return DebugUtils.flagsToString(ViewState.class, "STATE_", state);
161     }
162 
setState(int state)163     void setState(int state) {
164         if (mState == STATE_INITIAL) {
165             mState = state;
166         } else {
167             mState |= state;
168         }
169         if (state == STATE_AUTOFILLED) {
170             mState |= STATE_AUTOFILLED_ONCE;
171         }
172     }
173 
resetState(int state)174     void resetState(int state) {
175         mState &= ~state;
176     }
177 
178     @Nullable
getDatasetId()179     String getDatasetId() {
180         return mDatasetId;
181     }
182 
setDatasetId(String datasetId)183     void setDatasetId(String datasetId) {
184         mDatasetId = datasetId;
185     }
186 
187     // TODO: refactor / rename / document this method (and maybeCallOnFillReady) to make it clear
188     // that it can change the value and update the UI; similarly, should replace code that
189     // directly sets mAutofillValue to use encapsulation.
update(@ullable AutofillValue autofillValue, @Nullable Rect virtualBounds, int flags)190     void update(@Nullable AutofillValue autofillValue, @Nullable Rect virtualBounds, int flags) {
191         if (autofillValue != null) {
192             mCurrentValue = autofillValue;
193         }
194         if (virtualBounds != null) {
195             mVirtualBounds = virtualBounds;
196         }
197 
198         maybeCallOnFillReady(flags);
199     }
200 
201     /**
202      * Calls {@link
203      * Listener#onFillReady(FillResponse, AutofillId, AutofillValue)} if the
204      * fill UI is ready to be displayed (i.e. when response and bounds are set).
205      */
maybeCallOnFillReady(int flags)206     void maybeCallOnFillReady(int flags) {
207         if ((mState & STATE_AUTOFILLED) != 0 && (flags & FLAG_MANUAL_REQUEST) == 0) {
208             if (sDebug) Slog.d(TAG, "Ignoring UI for " + id + " on " + getStateAsString());
209             return;
210         }
211         // First try the current response associated with this View.
212         if (mResponse != null) {
213             if (mResponse.getDatasets() != null || mResponse.getAuthentication() != null) {
214                 mListener.onFillReady(mResponse, this.id, mCurrentValue);
215             }
216         }
217     }
218 
219     @Override
toString()220     public String toString() {
221         final StringBuilder builder = new StringBuilder("ViewState: [id=").append(id);
222         if (mDatasetId != null) {
223             builder.append(", datasetId:" ).append(mDatasetId);
224         }
225         builder.append(", state:").append(getStateAsString());
226         if (mCurrentValue != null) {
227             builder.append(", currentValue:" ).append(mCurrentValue);
228         }
229         if (mAutofilledValue != null) {
230             builder.append(", autofilledValue:" ).append(mAutofilledValue);
231         }
232         if (mSanitizedValue != null) {
233             builder.append(", sanitizedValue:" ).append(mSanitizedValue);
234         }
235         if (mVirtualBounds != null) {
236             builder.append(", virtualBounds:" ).append(mVirtualBounds);
237         }
238         builder.append("]");
239         return builder.toString();
240     }
241 
dump(String prefix, PrintWriter pw)242     void dump(String prefix, PrintWriter pw) {
243         pw.print(prefix); pw.print("id:" ); pw.println(id);
244         if (mDatasetId != null) {
245             pw.print(prefix); pw.print("datasetId:" ); pw.println(mDatasetId);
246         }
247         pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString());
248         if (mResponse != null) {
249             pw.print(prefix); pw.print("response id:");pw.println(mResponse.getRequestId());
250         }
251         if (mCurrentValue != null) {
252             pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
253         }
254         if (mAutofilledValue != null) {
255             pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
256         }
257         if (mSanitizedValue != null) {
258             pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
259         }
260         if (mVirtualBounds != null) {
261             pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
262         }
263     }
264 }