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 android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.app.assist.AssistStructure;
     22 import android.app.assist.AssistStructure.ViewNode;
     23 import android.content.ComponentName;
     24 import android.metrics.LogMaker;
     25 import android.service.autofill.Dataset;
     26 import android.util.ArrayMap;
     27 import android.util.ArraySet;
     28 import android.util.Slog;
     29 import android.view.WindowManager;
     30 import android.view.autofill.AutofillId;
     31 import android.view.autofill.AutofillValue;
     32 
     33 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     34 import com.android.internal.util.ArrayUtils;
     35 
     36 import java.io.PrintWriter;
     37 import java.util.ArrayList;
     38 import java.util.LinkedList;
     39 
     40 public final class Helper {
     41 
     42     private static final String TAG = "AutofillHelper";
     43 
     44     /**
     45      * Defines a logging flag that can be dynamically changed at runtime using
     46      * {@code cmd autofill set log_level debug}.
     47      */
     48     public static boolean sDebug = false;
     49 
     50     /**
     51      * Defines a logging flag that can be dynamically changed at runtime using
     52      * {@code cmd autofill set log_level verbose}.
     53      */
     54     public static boolean sVerbose = false;
     55 
     56     /**
     57      * Maximum number of partitions that can be allowed in a session.
     58      *
     59      * <p>Can be modified using {@code cmd autofill set max_partitions}.
     60      */
     61     static int sPartitionMaxCount = 10;
     62 
     63     /**
     64      * Maximum number of visible datasets in the dataset picker UI.
     65      *
     66      * <p>Can be modified using {@code cmd autofill set max_visible_datasets}.
     67      */
     68     public static int sVisibleDatasetsMaxCount = 3;
     69 
     70     /**
     71      * When non-null, overrides whether the UI should be shown on full-screen mode.
     72      *
     73      * <p>Note: access to this variable is not synchronized because it's "final" on real usage -
     74      * it's only set by Shell cmd, for development purposes.
     75      */
     76     public static Boolean sFullScreenMode = null;
     77 
     78     private Helper() {
     79         throw new UnsupportedOperationException("contains static members only");
     80     }
     81 
     82     @Nullable
     83     static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) {
     84         if (set == null) return null;
     85 
     86         final AutofillId[] array = new AutofillId[set.size()];
     87         for (int i = 0; i < set.size(); i++) {
     88             array[i] = set.valueAt(i);
     89         }
     90         return array;
     91     }
     92 
     93     @NonNull
     94     public static String paramsToString(@NonNull WindowManager.LayoutParams params) {
     95         final StringBuilder builder = new StringBuilder(25);
     96         params.dumpDimensions(builder);
     97         return builder.toString();
     98     }
     99 
    100     @NonNull
    101     static ArrayMap<AutofillId, AutofillValue> getFields(@NonNull Dataset dataset) {
    102         final ArrayList<AutofillId> ids = dataset.getFieldIds();
    103         final ArrayList<AutofillValue> values = dataset.getFieldValues();
    104         final int size = ids == null ? 0 : ids.size();
    105         final ArrayMap<AutofillId, AutofillValue> fields = new ArrayMap<>(size);
    106         for (int i = 0; i < size; i++) {
    107             fields.put(ids.get(i), values.get(i));
    108         }
    109         return fields;
    110     }
    111 
    112     @NonNull
    113     private static LogMaker newLogMaker(int category, @NonNull String servicePackageName,
    114             int sessionId, boolean compatMode) {
    115         final LogMaker log = new LogMaker(category)
    116                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName)
    117                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, Integer.toString(sessionId));
    118         if (compatMode) {
    119             log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1);
    120         }
    121         return log;
    122     }
    123 
    124     @NonNull
    125     public static LogMaker newLogMaker(int category, @NonNull String packageName,
    126             @NonNull String servicePackageName, int sessionId, boolean compatMode) {
    127         return newLogMaker(category, servicePackageName, sessionId, compatMode)
    128                 .setPackageName(packageName);
    129     }
    130 
    131     @NonNull
    132     public static LogMaker newLogMaker(int category, @NonNull ComponentName componentName,
    133             @NonNull String servicePackageName, int sessionId, boolean compatMode) {
    134         return newLogMaker(category, servicePackageName, sessionId, compatMode)
    135                 .setComponentName(componentName);
    136     }
    137 
    138     public static void printlnRedactedText(@NonNull PrintWriter pw, @Nullable CharSequence text) {
    139         if (text == null) {
    140             pw.println("null");
    141         } else {
    142             pw.print(text.length()); pw.println("_chars");
    143         }
    144     }
    145 
    146     /**
    147      * Finds the {@link ViewNode} that has the requested {@code autofillId}, if any.
    148      */
    149     @Nullable
    150     public static ViewNode findViewNodeByAutofillId(@NonNull AssistStructure structure,
    151             @NonNull AutofillId autofillId) {
    152         return findViewNode(structure, (node) -> {
    153             return autofillId.equals(node.getAutofillId());
    154         });
    155     }
    156 
    157     private static ViewNode findViewNode(@NonNull AssistStructure structure,
    158             @NonNull ViewNodeFilter filter) {
    159         final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
    160         final int numWindowNodes = structure.getWindowNodeCount();
    161         for (int i = 0; i < numWindowNodes; i++) {
    162             nodesToProcess.add(structure.getWindowNodeAt(i).getRootViewNode());
    163         }
    164         while (!nodesToProcess.isEmpty()) {
    165             final ViewNode node = nodesToProcess.removeFirst();
    166             if (filter.matches(node)) {
    167                 return node;
    168             }
    169             for (int i = 0; i < node.getChildCount(); i++) {
    170                 nodesToProcess.addLast(node.getChildAt(i));
    171             }
    172         }
    173 
    174         return null;
    175     }
    176 
    177     /**
    178      * Sanitize the {@code webDomain} property of the URL bar node on compat mode.
    179      *
    180      * @param structure Assist structure
    181      * @param urlBarIds list of ids; only the first id found will be sanitized.
    182      *
    183      * @return the node containing the URL bar
    184      */
    185     @Nullable
    186     public static ViewNode sanitizeUrlBar(@NonNull AssistStructure structure,
    187             @NonNull String[] urlBarIds) {
    188         final ViewNode urlBarNode = findViewNode(structure, (node) -> {
    189             return ArrayUtils.contains(urlBarIds, node.getIdEntry());
    190         });
    191         if (urlBarNode != null) {
    192             final String domain = urlBarNode.getText().toString();
    193             if (domain.isEmpty()) {
    194                 if (sDebug) Slog.d(TAG, "sanitizeUrlBar(): empty on " + urlBarNode.getIdEntry());
    195                 return null;
    196             }
    197             urlBarNode.setWebDomain(domain);
    198             if (sDebug) {
    199                 Slog.d(TAG, "sanitizeUrlBar(): id=" + urlBarNode.getIdEntry() + ", domain="
    200                         + urlBarNode.getWebDomain());
    201             }
    202         }
    203         return urlBarNode;
    204     }
    205 
    206     /**
    207      * Gets the value of a metric tag, or {@code 0} if not found or NaN.
    208      */
    209     static int getNumericValue(@NonNull LogMaker log, int tag) {
    210         final Object value = log.getTaggedData(tag);
    211         if (!(value instanceof Number)) {
    212             return 0;
    213         } else {
    214             return ((Number) value).intValue();
    215         }
    216     }
    217 
    218     private interface ViewNodeFilter {
    219         boolean matches(ViewNode node);
    220     }
    221 }
    222