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 import static com.android.server.autofill.Helper.sDebug;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.graphics.Rect;
25 import android.service.autofill.FillResponse;
26 import android.util.DebugUtils;
27 import android.util.Slog;
28 import android.view.autofill.AutofillId;
29 import android.view.autofill.AutofillValue;
30 
31 import java.io.PrintWriter;
32 
33 /**
34  * State for a given view with a AutofillId.
35  *
36  * <p>This class holds state about a view and calls its listener when the fill UI is ready to
37  * be displayed for the view.
38  */
39 final class ViewState {
40     interface Listener {
41         /**
42          * Called when the fill UI is ready to be shown for this view.
43          */
onFillReady(@onNull FillResponse fillResponse, @NonNull AutofillId focusedId, @Nullable AutofillValue value)44         void onFillReady(@NonNull FillResponse fillResponse, @NonNull AutofillId focusedId,
45                 @Nullable AutofillValue value);
46     }
47 
48     private static final String TAG = "ViewState";
49 
50     // NOTE: state constants must be public because of flagstoString().
51     public static final int STATE_UNKNOWN = 0x000;
52     /** Initial state. */
53     public static final int STATE_INITIAL = 0x001;
54     /** View id is present in a dataset returned by the service. */
55     public static final int STATE_FILLABLE = 0x002;
56     /** View was autofilled after user selected a dataset. */
57     public static final int STATE_AUTOFILLED = 0x004;
58     /** View value was changed, but not by the service. */
59     public static final int STATE_CHANGED = 0x008;
60     /** Set only in the View that started a session. */
61     public static final int STATE_STARTED_SESSION = 0x010;
62     /** View that started a new partition when focused on. */
63     public static final int STATE_STARTED_PARTITION = 0x020;
64     /** User select a dataset in this view, but service must authenticate first. */
65     public static final int STATE_WAITING_DATASET_AUTH = 0x040;
66     /** Service does not care about this view. */
67     public static final int STATE_IGNORED = 0x080;
68     /** User manually request autofill in this view, after it was already autofilled. */
69     public static final int STATE_RESTARTED_SESSION = 0x100;
70     /** View is the URL bar of a package on compat mode. */
71     public  static final int STATE_URL_BAR = 0x200;
72     /** View was asked to autofil but failed to do so. */
73     public static final int STATE_AUTOFILL_FAILED = 0x400;
74 
75     public final AutofillId id;
76 
77     private final Listener mListener;
78     private final Session mSession;
79 
80     private FillResponse mResponse;
81     private AutofillValue mCurrentValue;
82     private AutofillValue mAutofilledValue;
83     private AutofillValue mSanitizedValue;
84     private Rect mVirtualBounds;
85     private int mState;
86     private String mDatasetId;
87 
ViewState(Session session, AutofillId id, Listener listener, int state)88     ViewState(Session session, AutofillId id, Listener listener, int state) {
89         mSession = session;
90         this.id = id;
91         mListener = listener;
92         mState = state;
93     }
94 
95     /**
96      * Gets the boundaries of the virtual view, or {@code null} if the the view is not virtual.
97      */
98     @Nullable
getVirtualBounds()99     Rect getVirtualBounds() {
100         return mVirtualBounds;
101     }
102 
103     /**
104      * Gets the current value of the view.
105      */
106     @Nullable
getCurrentValue()107     AutofillValue getCurrentValue() {
108         return mCurrentValue;
109     }
110 
setCurrentValue(AutofillValue value)111     void setCurrentValue(AutofillValue value) {
112         mCurrentValue = value;
113     }
114 
115     @Nullable
getAutofilledValue()116     AutofillValue getAutofilledValue() {
117         return mAutofilledValue;
118     }
119 
setAutofilledValue(@ullable AutofillValue value)120     void setAutofilledValue(@Nullable AutofillValue value) {
121         mAutofilledValue = value;
122     }
123 
124     @Nullable
getSanitizedValue()125     AutofillValue getSanitizedValue() {
126         return mSanitizedValue;
127     }
128 
setSanitizedValue(@ullable AutofillValue value)129     void setSanitizedValue(@Nullable AutofillValue value) {
130         mSanitizedValue = value;
131     }
132 
133     @Nullable
getResponse()134     FillResponse getResponse() {
135         return mResponse;
136     }
137 
setResponse(FillResponse response)138     void setResponse(FillResponse response) {
139         mResponse = response;
140     }
141 
getServiceName()142     CharSequence getServiceName() {
143         return mSession.getServiceName();
144     }
145 
getState()146     int getState() {
147         return mState;
148     }
149 
getStateAsString()150     String getStateAsString() {
151         return getStateAsString(mState);
152     }
153 
getStateAsString(int state)154     static String getStateAsString(int state) {
155         return DebugUtils.flagsToString(ViewState.class, "STATE_", state);
156     }
157 
setState(int state)158     void setState(int state) {
159         if (mState == STATE_INITIAL) {
160             mState = state;
161         } else {
162             mState |= state;
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         return builder.toString();
231     }
232 
dump(String prefix, PrintWriter pw)233     void dump(String prefix, PrintWriter pw) {
234         pw.print(prefix); pw.print("id:" ); pw.println(id);
235         if (mDatasetId != null) {
236             pw.print(prefix); pw.print("datasetId:" ); pw.println(mDatasetId);
237         }
238         pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString());
239         if (mResponse != null) {
240             pw.print(prefix); pw.print("response id:");pw.println(mResponse.getRequestId());
241         }
242         if (mCurrentValue != null) {
243             pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
244         }
245         if (mAutofilledValue != null) {
246             pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
247         }
248         if (mSanitizedValue != null) {
249             pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
250         }
251         if (mVirtualBounds != null) {
252             pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
253         }
254     }
255 }