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