Home | History | Annotate | Download | only in autofill
      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          */
     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 
     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
     99     Rect getVirtualBounds() {
    100         return mVirtualBounds;
    101     }
    102 
    103     /**
    104      * Gets the current value of the view.
    105      */
    106     @Nullable
    107     AutofillValue getCurrentValue() {
    108         return mCurrentValue;
    109     }
    110 
    111     void setCurrentValue(AutofillValue value) {
    112         mCurrentValue = value;
    113     }
    114 
    115     @Nullable
    116     AutofillValue getAutofilledValue() {
    117         return mAutofilledValue;
    118     }
    119 
    120     void setAutofilledValue(@Nullable AutofillValue value) {
    121         mAutofilledValue = value;
    122     }
    123 
    124     @Nullable
    125     AutofillValue getSanitizedValue() {
    126         return mSanitizedValue;
    127     }
    128 
    129     void setSanitizedValue(@Nullable AutofillValue value) {
    130         mSanitizedValue = value;
    131     }
    132 
    133     @Nullable
    134     FillResponse getResponse() {
    135         return mResponse;
    136     }
    137 
    138     void setResponse(FillResponse response) {
    139         mResponse = response;
    140     }
    141 
    142     CharSequence getServiceName() {
    143         return mSession.getServiceName();
    144     }
    145 
    146     int getState() {
    147         return mState;
    148     }
    149 
    150     String getStateAsString() {
    151         return getStateAsString(mState);
    152     }
    153 
    154     static String getStateAsString(int state) {
    155         return DebugUtils.flagsToString(ViewState.class, "STATE_", state);
    156     }
    157 
    158     void setState(int state) {
    159         if (mState == STATE_INITIAL) {
    160             mState = state;
    161         } else {
    162             mState |= state;
    163         }
    164     }
    165 
    166     void resetState(int state) {
    167         mState &= ~state;
    168     }
    169 
    170     @Nullable
    171     String getDatasetId() {
    172         return mDatasetId;
    173     }
    174 
    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.
    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      */
    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
    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 
    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 }