Home | History | Annotate | Download | only in service
      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 package com.example.android.autofill.service;
     17 
     18 import android.app.assist.AssistStructure;
     19 import android.app.assist.AssistStructure.ViewNode;
     20 import android.app.assist.AssistStructure.WindowNode;
     21 import android.content.Context;
     22 import android.util.Log;
     23 import android.view.autofill.AutofillValue;
     24 
     25 import com.example.android.autofill.service.datasource.SharedPrefsDigitalAssetLinksRepository;
     26 import com.example.android.autofill.service.model.FilledAutofillField;
     27 import com.example.android.autofill.service.model.FilledAutofillFieldCollection;
     28 
     29 import static com.example.android.autofill.service.CommonUtil.DEBUG;
     30 import static com.example.android.autofill.service.CommonUtil.TAG;
     31 
     32 /**
     33  * Parser for an AssistStructure object. This is invoked when the Autofill Service receives an
     34  * AssistStructure from the client Activity, representing its View hierarchy. In this sample, it
     35  * parses the hierarchy and collects autofill metadata from {@link ViewNode}s along the way.
     36  */
     37 final class StructureParser {
     38     private final AutofillFieldMetadataCollection mAutofillFields =
     39             new AutofillFieldMetadataCollection();
     40     private final Context mContext;
     41     private final AssistStructure mStructure;
     42     private FilledAutofillFieldCollection mFilledAutofillFieldCollection;
     43 
     44     StructureParser(Context context, AssistStructure structure) {
     45         mContext = context;
     46         mStructure = structure;
     47     }
     48 
     49     public void parseForFill() {
     50         parse(true);
     51     }
     52 
     53     public void parseForSave() {
     54         parse(false);
     55     }
     56 
     57     /**
     58      * Traverse AssistStructure and add ViewNode metadata to a flat list.
     59      */
     60     private void parse(boolean forFill) {
     61         if (DEBUG) Log.d(TAG, "Parsing structure for " + mStructure.getActivityComponent());
     62         int nodes = mStructure.getWindowNodeCount();
     63         mFilledAutofillFieldCollection = new FilledAutofillFieldCollection();
     64         StringBuilder webDomain = new StringBuilder();
     65         for (int i = 0; i < nodes; i++) {
     66             WindowNode node = mStructure.getWindowNodeAt(i);
     67             ViewNode view = node.getRootViewNode();
     68             parseLocked(forFill, view, webDomain);
     69         }
     70         if (webDomain.length() > 0) {
     71             String packageName = mStructure.getActivityComponent().getPackageName();
     72             boolean valid = SharedPrefsDigitalAssetLinksRepository.getInstance().isValid(mContext,
     73                     webDomain.toString(), packageName);
     74             if (!valid) {
     75                 throw new SecurityException(mContext.getString(
     76                         R.string.invalid_link_association, webDomain, packageName));
     77             }
     78             if (DEBUG) Log.d(TAG, "Domain " + webDomain + " is valid for " + packageName);
     79         } else {
     80             if (DEBUG) Log.d(TAG, "no web domain");
     81         }
     82     }
     83 
     84     private void parseLocked(boolean forFill, ViewNode viewNode, StringBuilder validWebDomain) {
     85         String webDomain = viewNode.getWebDomain();
     86         if (webDomain != null) {
     87             if (DEBUG) Log.d(TAG, "child web domain: " + webDomain);
     88             if (validWebDomain.length() > 0) {
     89                 if (!webDomain.equals(validWebDomain.toString())) {
     90                     throw new SecurityException("Found multiple web domains: valid= "
     91                             + validWebDomain + ", child=" + webDomain);
     92                 }
     93             } else {
     94                 validWebDomain.append(webDomain);
     95             }
     96         }
     97 
     98         if (viewNode.getAutofillHints() != null) {
     99             String[] filteredHints = AutofillHints.filterForSupportedHints(
    100                     viewNode.getAutofillHints());
    101             if (filteredHints != null && filteredHints.length > 0) {
    102                 if (forFill) {
    103                     mAutofillFields.add(new AutofillFieldMetadata(viewNode));
    104                 } else {
    105                     FilledAutofillField filledAutofillField =
    106                             new FilledAutofillField(viewNode.getAutofillHints());
    107                     AutofillValue autofillValue = viewNode.getAutofillValue();
    108                     if (autofillValue.isText()) {
    109                         // Using toString of AutofillValue.getTextValue in order to save it to
    110                         // SharedPreferences.
    111                         filledAutofillField.setTextValue(autofillValue.getTextValue().toString());
    112                     } else if (autofillValue.isDate()) {
    113                         filledAutofillField.setDateValue(autofillValue.getDateValue());
    114                     } else if (autofillValue.isList()) {
    115                         filledAutofillField.setListValue(viewNode.getAutofillOptions(),
    116                                 autofillValue.getListValue());
    117                     }
    118                     mFilledAutofillFieldCollection.add(filledAutofillField);
    119                 }
    120             }
    121         }
    122         int childrenSize = viewNode.getChildCount();
    123         if (childrenSize > 0) {
    124             for (int i = 0; i < childrenSize; i++) {
    125                 parseLocked(forFill, viewNode.getChildAt(i), validWebDomain);
    126             }
    127         }
    128     }
    129 
    130     public AutofillFieldMetadataCollection getAutofillFields() {
    131         return mAutofillFields;
    132     }
    133 
    134     public FilledAutofillFieldCollection getClientFormData() {
    135         return mFilledAutofillFieldCollection;
    136     }
    137 }
    138