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 Helper()78 private Helper() { 79 throw new UnsupportedOperationException("contains static members only"); 80 } 81 82 @Nullable toArray(@ullable ArraySet<AutofillId> set)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 paramsToString(@onNull WindowManager.LayoutParams params)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 getFields(@onNull Dataset dataset)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 newLogMaker(int category, @NonNull String servicePackageName, int sessionId, boolean compatMode)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 newLogMaker(int category, @NonNull String packageName, @NonNull String servicePackageName, int sessionId, boolean compatMode)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 newLogMaker(int category, @NonNull ComponentName componentName, @NonNull String servicePackageName, int sessionId, boolean compatMode)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 printlnRedactedText(@onNull PrintWriter pw, @Nullable CharSequence text)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 findViewNodeByAutofillId(@onNull AssistStructure structure, @NonNull AutofillId autofillId)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 findViewNode(@onNull AssistStructure structure, @NonNull ViewNodeFilter filter)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 sanitizeUrlBar(@onNull AssistStructure structure, @NonNull String[] urlBarIds)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 */ getNumericValue(@onNull LogMaker log, int tag)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 { matches(ViewNode node)219 boolean matches(ViewNode node); 220 } 221 } 222