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 }