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 android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE; 21 22 import static com.android.server.autofill.Helper.sDebug; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.graphics.Rect; 27 import android.service.autofill.FillResponse; 28 import android.util.DebugUtils; 29 import android.util.Slog; 30 import android.view.autofill.AutofillId; 31 import android.view.autofill.AutofillValue; 32 33 import java.io.PrintWriter; 34 35 /** 36 * State for a given view with a AutofillId. 37 * 38 * <p>This class holds state about a view and calls its listener when the fill UI is ready to 39 * be displayed for the view. 40 */ 41 final class ViewState { 42 interface Listener { 43 /** 44 * Called when the fill UI is ready to be shown for this view. 45 */ onFillReady(@onNull FillResponse fillResponse, @NonNull AutofillId focusedId, @Nullable AutofillValue value, int flags)46 void onFillReady(@NonNull FillResponse fillResponse, @NonNull AutofillId focusedId, 47 @Nullable AutofillValue value, int flags); 48 } 49 50 private static final String TAG = "ViewState"; 51 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 autofill but failed to do so. */ 73 public static final int STATE_AUTOFILL_FAILED = 0x400; 74 /** View has been autofilled at least once. */ 75 public static final int STATE_AUTOFILLED_ONCE = 0x800; 76 /** View triggered the latest augmented autofill request. */ 77 public static final int STATE_TRIGGERED_AUGMENTED_AUTOFILL = 0x1000; 78 /** Inline suggestions were shown for this View. */ 79 public static final int STATE_INLINE_SHOWN = 0x2000; 80 /** A character was removed from the View value (not by the service). */ 81 public static final int STATE_CHAR_REMOVED = 0x4000; 82 /** Showing inline suggestions is not allowed for this View. */ 83 public static final int STATE_INLINE_DISABLED = 0x8000; 84 /** The View is waiting for an inline suggestions request from IME.*/ 85 public static final int STATE_PENDING_CREATE_INLINE_REQUEST = 0x10000; 86 /** Fill dialog were shown for this View. */ 87 public static final int STATE_FILL_DIALOG_SHOWN = 0x20000; 88 89 public final AutofillId id; 90 91 private final Listener mListener; 92 93 private final boolean mIsPrimaryCredential; 94 95 /** 96 * There are two sources of fill response. The fill response from the session's remote fill 97 * service and the fill response from the secondary provider handler. Primary Fill Response 98 * stores the fill response from the session's remote fill service. 99 */ 100 private FillResponse mPrimaryFillResponse; 101 102 /** 103 * Secondary fill response stores the fill response from the secondary provider handler. Based 104 * on whether the user focuses on a credential view or an autofill view, the relevant fill 105 * response will be used to show the autofill suggestions. 106 */ 107 private FillResponse mSecondaryFillResponse; 108 private AutofillValue mCurrentValue; 109 110 /** 111 * Some apps clear the form before navigating to another activity. The mCandidateSaveValue 112 * caches the value when a field with string longer than 2 characters are cleared. 113 * 114 * When showing save UI, if mCurrentValue of view state is empty, session would use 115 * mCandidateSaveValue to prompt save instead. 116 */ 117 private AutofillValue mCandidateSaveValue; 118 private AutofillValue mAutofilledValue; 119 private AutofillValue mSanitizedValue; 120 private Rect mVirtualBounds; 121 private int mState; 122 private String mDatasetId; 123 ViewState(AutofillId id, Listener listener, int state, boolean isPrimaryCredential)124 ViewState(AutofillId id, Listener listener, int state, boolean isPrimaryCredential) { 125 this.id = id; 126 mListener = listener; 127 mState = state; 128 mIsPrimaryCredential = isPrimaryCredential; 129 } 130 131 /** 132 * Gets the boundaries of the virtual view, or {@code null} if the the view is not virtual. 133 */ 134 @Nullable getVirtualBounds()135 Rect getVirtualBounds() { 136 return mVirtualBounds; 137 } 138 139 /** 140 * Gets the current value of the view. 141 */ 142 @Nullable getCurrentValue()143 AutofillValue getCurrentValue() { 144 return mCurrentValue; 145 } 146 setCurrentValue(AutofillValue value)147 void setCurrentValue(AutofillValue value) { 148 mCurrentValue = value; 149 } 150 151 /** 152 * Gets the candidate save value of the view. 153 */ 154 @Nullable getCandidateSaveValue()155 AutofillValue getCandidateSaveValue() { 156 return mCandidateSaveValue; 157 } 158 setCandidateSaveValue(AutofillValue value)159 void setCandidateSaveValue(AutofillValue value) { 160 mCandidateSaveValue = value; 161 } 162 163 @Nullable getAutofilledValue()164 AutofillValue getAutofilledValue() { 165 return mAutofilledValue; 166 } 167 setAutofilledValue(@ullable AutofillValue value)168 void setAutofilledValue(@Nullable AutofillValue value) { 169 mAutofilledValue = value; 170 } 171 172 @Nullable getSanitizedValue()173 AutofillValue getSanitizedValue() { 174 return mSanitizedValue; 175 } 176 setSanitizedValue(@ullable AutofillValue value)177 void setSanitizedValue(@Nullable AutofillValue value) { 178 mSanitizedValue = value; 179 } 180 181 @Nullable getResponse()182 FillResponse getResponse() { 183 return mPrimaryFillResponse; 184 } 185 186 @Nullable getSecondaryResponse()187 FillResponse getSecondaryResponse() { 188 return mSecondaryFillResponse; 189 } 190 setResponse(FillResponse response)191 void setResponse(FillResponse response) { 192 setResponse(response, /* isPrimary= */ true); 193 } 194 setResponse(@ullable FillResponse response, boolean isPrimary)195 void setResponse(@Nullable FillResponse response, boolean isPrimary) { 196 if (isPrimary) { 197 mPrimaryFillResponse = response; 198 } else { 199 mSecondaryFillResponse = response; 200 } 201 } 202 getState()203 int getState() { 204 return mState; 205 } 206 getStateAsString()207 String getStateAsString() { 208 return getStateAsString(mState); 209 } 210 getStateAsString(int state)211 static String getStateAsString(int state) { 212 return DebugUtils.flagsToString(ViewState.class, "STATE_", state); 213 } 214 setState(int state)215 void setState(int state) { 216 if (mState == STATE_INITIAL) { 217 mState = state; 218 } else { 219 mState |= state; 220 } 221 if (state == STATE_AUTOFILLED) { 222 mState |= STATE_AUTOFILLED_ONCE; 223 } 224 } 225 resetState(int state)226 void resetState(int state) { 227 mState &= ~state; 228 } 229 230 @Nullable getDatasetId()231 String getDatasetId() { 232 return mDatasetId; 233 } 234 setDatasetId(String datasetId)235 void setDatasetId(String datasetId) { 236 mDatasetId = datasetId; 237 } 238 239 // TODO: refactor / rename / document this method (and maybeCallOnFillReady) to make it clear 240 // that it can change the value and update the UI; similarly, should replace code that 241 // directly sets mAutofillValue to use encapsulation. update(@ullable AutofillValue autofillValue, @Nullable Rect virtualBounds, int flags)242 void update(@Nullable AutofillValue autofillValue, @Nullable Rect virtualBounds, int flags) { 243 if (autofillValue != null) { 244 mCurrentValue = autofillValue; 245 } 246 if (virtualBounds != null) { 247 mVirtualBounds = virtualBounds; 248 } 249 250 maybeCallOnFillReady(flags); 251 } 252 253 /** 254 * Calls {@link 255 * Listener#onFillReady(FillResponse, AutofillId, AutofillValue, int)} if the 256 * fill UI is ready to be displayed (i.e. when response and bounds are set). 257 */ maybeCallOnFillReady(int flags)258 void maybeCallOnFillReady(int flags) { 259 if ((mState & STATE_AUTOFILLED) != 0 && (flags & FLAG_MANUAL_REQUEST) == 0) { 260 if (sDebug) Slog.d(TAG, "Ignoring UI for " + id + " on " + getStateAsString()); 261 return; 262 } 263 // First try the current response associated with this View. 264 FillResponse requestedResponse = requestingPrimaryResponse(flags) 265 ? mPrimaryFillResponse : mSecondaryFillResponse; 266 if (requestedResponse != null) { 267 if (requestedResponse.getDatasets() != null 268 || requestedResponse.getAuthentication() != null) { 269 mListener.onFillReady(requestedResponse, this.id, mCurrentValue, flags); 270 } 271 } 272 } 273 requestingPrimaryResponse(int flags)274 private boolean requestingPrimaryResponse(int flags) { 275 if (mIsPrimaryCredential) { 276 return (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0; 277 } else { 278 return (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) == 0; 279 } 280 } 281 282 @Override toString()283 public String toString() { 284 final StringBuilder builder = new StringBuilder("ViewState: [id=").append(id); 285 if (mDatasetId != null) { 286 builder.append(", datasetId:" ).append(mDatasetId); 287 } 288 builder.append(", state:").append(getStateAsString()); 289 if (mCurrentValue != null) { 290 builder.append(", currentValue:" ).append(mCurrentValue); 291 } 292 if (mCandidateSaveValue != null) { 293 builder.append(", candidateSaveValue:").append(mCandidateSaveValue); 294 } 295 if (mAutofilledValue != null) { 296 builder.append(", autofilledValue:" ).append(mAutofilledValue); 297 } 298 if (mSanitizedValue != null) { 299 builder.append(", sanitizedValue:" ).append(mSanitizedValue); 300 } 301 if (mVirtualBounds != null) { 302 builder.append(", virtualBounds:" ).append(mVirtualBounds); 303 } 304 builder.append("]"); 305 return builder.toString(); 306 } 307 dump(String prefix, PrintWriter pw)308 void dump(String prefix, PrintWriter pw) { 309 pw.print(prefix); pw.print("id:" ); pw.println(id); 310 if (mDatasetId != null) { 311 pw.print(prefix); pw.print("datasetId:" ); pw.println(mDatasetId); 312 } 313 pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString()); 314 pw.print(prefix); pw.print("is primary credential:"); pw.println(mIsPrimaryCredential); 315 if (mPrimaryFillResponse != null) { 316 pw.print(prefix); pw.print("primary response id:"); 317 pw.println(mPrimaryFillResponse.getRequestId()); 318 } 319 if (mSecondaryFillResponse != null) { 320 pw.print(prefix); pw.print("secondary response id:"); 321 pw.println(mSecondaryFillResponse.getRequestId()); 322 } 323 if (mCurrentValue != null) { 324 pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue); 325 } 326 if (mAutofilledValue != null) { 327 pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue); 328 } 329 if (mCandidateSaveValue != null) { 330 pw.print(prefix); pw.print("candidateSaveValue:"); pw.println(mCandidateSaveValue); 331 } 332 if (mSanitizedValue != null) { 333 pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue); 334 } 335 if (mVirtualBounds != null) { 336 pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds); 337 } 338 } 339 }