diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2018-04-19 07:24:09 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2018-04-19 07:24:09 +0000 |
commit | c49d02ccc3e5ebd5f05a4fc077bd9ebf3d291870 (patch) | |
tree | f9520a6504e42ccf510d0ca3fa61a96b0221fdbf | |
parent | b2669f3838ee85a805cba453a69d2a165c3b5666 (diff) | |
parent | e44363c82c9187230ba85cdc1beb3d2aba9595e6 (diff) | |
download | android-pie-vts-release.tar.gz |
Snap for 4731145 from e44363c82c9187230ba85cdc1beb3d2aba9595e6 to pi-releaseandroid-wear-9.0.0_r9android-wear-9.0.0_r8android-wear-9.0.0_r7android-wear-9.0.0_r6android-wear-9.0.0_r5android-wear-9.0.0_r4android-wear-9.0.0_r34android-wear-9.0.0_r33android-wear-9.0.0_r32android-wear-9.0.0_r31android-wear-9.0.0_r30android-wear-9.0.0_r3android-wear-9.0.0_r29android-wear-9.0.0_r28android-wear-9.0.0_r27android-wear-9.0.0_r26android-wear-9.0.0_r25android-wear-9.0.0_r24android-wear-9.0.0_r23android-wear-9.0.0_r22android-wear-9.0.0_r21android-wear-9.0.0_r20android-wear-9.0.0_r2android-wear-9.0.0_r19android-wear-9.0.0_r18android-wear-9.0.0_r17android-wear-9.0.0_r16android-wear-9.0.0_r15android-wear-9.0.0_r14android-wear-9.0.0_r13android-wear-9.0.0_r12android-wear-9.0.0_r11android-wear-9.0.0_r10android-wear-9.0.0_r1android-vts-9.0_r9android-vts-9.0_r8android-vts-9.0_r7android-vts-9.0_r6android-vts-9.0_r5android-vts-9.0_r4android-vts-9.0_r19android-vts-9.0_r18android-vts-9.0_r17android-vts-9.0_r16android-vts-9.0_r15android-vts-9.0_r14android-vts-9.0_r13android-vts-9.0_r12android-vts-9.0_r11android-vts-9.0_r10android-security-9.0.0_r76android-security-9.0.0_r75android-security-9.0.0_r74android-security-9.0.0_r73android-security-9.0.0_r72android-security-9.0.0_r71android-security-9.0.0_r70android-security-9.0.0_r69android-security-9.0.0_r68android-security-9.0.0_r67android-security-9.0.0_r66android-security-9.0.0_r65android-security-9.0.0_r64android-security-9.0.0_r63android-security-9.0.0_r62android-cts-9.0_r9android-cts-9.0_r8android-cts-9.0_r7android-cts-9.0_r6android-cts-9.0_r5android-cts-9.0_r4android-cts-9.0_r3android-cts-9.0_r20android-cts-9.0_r2android-cts-9.0_r19android-cts-9.0_r18android-cts-9.0_r17android-cts-9.0_r16android-cts-9.0_r15android-cts-9.0_r14android-cts-9.0_r13android-cts-9.0_r12android-cts-9.0_r11android-cts-9.0_r10android-cts-9.0_r1android-9.0.0_r9android-9.0.0_r8android-9.0.0_r7android-9.0.0_r61android-9.0.0_r60android-9.0.0_r6android-9.0.0_r59android-9.0.0_r58android-9.0.0_r57android-9.0.0_r56android-9.0.0_r55android-9.0.0_r54android-9.0.0_r53android-9.0.0_r52android-9.0.0_r51android-9.0.0_r50android-9.0.0_r5android-9.0.0_r49android-9.0.0_r48android-9.0.0_r3android-9.0.0_r2android-9.0.0_r18android-9.0.0_r17android-9.0.0_r10android-9.0.0_r1security-pi-releasepie-vts-releasepie-security-releasepie-s2-releasepie-release-2pie-releasepie-r2-s2-releasepie-r2-s1-releasepie-r2-releasepie-cts-release
Change-Id: I94b2d73b24d3cc230aa1423e7b1ae2b07321eff4
5 files changed, 194 insertions, 44 deletions
diff --git a/input/autofill/AutofillFramework/Application/build.gradle b/input/autofill/AutofillFramework/Application/build.gradle index 4a877db7..50bbdc33 100644 --- a/input/autofill/AutofillFramework/Application/build.gradle +++ b/input/autofill/AutofillFramework/Application/build.gradle @@ -7,12 +7,11 @@ List<String> dirs = [ 'main'] // main sample code; look here for the interesting stuff. android { - compileSdkVersion 26 - buildToolsVersion '26.0.2' + compileSdkVersion "android-P" defaultConfig { minSdkVersion 26 - targetSdkVersion 26 + targetSdkVersion 28 } compileOptions { @@ -38,11 +37,11 @@ android { } dependencies { - implementation "com.android.support:support-v4:26.1.0" - implementation "com.android.support:support-v13:26.1.0" - implementation "com.android.support:cardview-v7:26.1.0" - implementation "com.android.support:appcompat-v7:26.1.0" - implementation 'com.android.support:design:26.1.0' + implementation "com.android.support:support-v4:28.0.0-alpha1" + implementation "com.android.support:support-v13:28.0.0-alpha1" + implementation "com.android.support:cardview-v7:28.0.0-alpha1" + implementation "com.android.support:appcompat-v7:28.0.0-alpha1" + implementation 'com.android.support:design:28.0.0-alpha1' implementation 'com.android.support.constraint:constraint-layout:1.0.2' implementation group: 'com.google.guava', name: 'guava', version: '22.0-android' } diff --git a/input/autofill/AutofillFramework/afservice/build.gradle b/input/autofill/AutofillFramework/afservice/build.gradle index 07ccafd4..f6142077 100644 --- a/input/autofill/AutofillFramework/afservice/build.gradle +++ b/input/autofill/AutofillFramework/afservice/build.gradle @@ -1,11 +1,11 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 27 + compileSdkVersion "android-P" defaultConfig { minSdkVersion 26 - targetSdkVersion 27 + targetSdkVersion 28 versionCode 1 versionName "1.0" @@ -30,17 +30,17 @@ android { } dependencies { - implementation 'com.android.support:appcompat-v7:27.0.2' + implementation 'com.android.support:appcompat-v7:28.0.0-alpha1' implementation "android.arch.persistence.room:runtime:1.0.0" annotationProcessor 'android.arch.persistence.room:compiler:1.0.0' implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:design:27.0.2' + implementation 'com.android.support:design:28.0.0-alpha1' implementation 'com.android.support.constraint:constraint-layout:1.0.2' - implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.1' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.2' implementation 'com.squareup.retrofit2:retrofit:2.3.0' implementation group: 'com.google.guava', name: 'guava', version: '22.0-android' implementation "com.android.support.test.espresso:espresso-idling-resource:3.0.1" - implementation "com.google.code.findbugs:jsr305:2.0.1" + implementation "com.google.code.findbugs:jsr305:3.0.2" androidTestImplementation "junit:junit:4.12" androidTestImplementation ("com.android.support.test.espresso:espresso-core:3.0.1") diff --git a/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml index 83ac4ee7..b02a591d 100644 --- a/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml +++ b/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml @@ -28,13 +28,23 @@ <service android:name=".simple.BasicService" - android:label="Basic Service" + android:label="Basic Autofill Service" android:permission="android.permission.BIND_AUTOFILL_SERVICE"> <intent-filter> <action android:name="android.service.autofill.AutofillService" /> </intent-filter> </service> + <service + android:name=".simple.BasicHeuristicsService" + android:label="Basic Heuristics Autofill Service" + android:permission="android.permission.BIND_AUTOFILL_SERVICE"> + + <intent-filter> + <action android:name="android.service.autofill.AutofillService" /> + </intent-filter> + </service> + <activity android:name=".AuthActivity" android:taskAffinity=".AuthActivity" diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicHeuristicsService.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicHeuristicsService.java new file mode 100644 index 00000000..843440c9 --- /dev/null +++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicHeuristicsService.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofill.service.simple; + +import android.app.assist.AssistStructure.ViewNode; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; + +import com.example.android.autofill.service.MyAutofillService; + +/** + * A basic service that uses some rudimentary heuristics to identify fields that are not explicitly + * marked with autofill hints. + * + * <p>The goal of this class is to provide a simple autofill service implementation that is easy + * to understand and extend, but it should <strong>not</strong> be used as-is on real apps because + * it lacks fundamental security requirements such as data partitioning and package verification + * &mdashthese requirements are fullfilled by {@link MyAutofillService}. * + */ +public class BasicHeuristicsService extends BasicService { + + private static final String TAG = "BasicHeuristicsService"; + + @Override + @Nullable + protected String getHint(@NonNull ViewNode node) { + + // First try the explicit autofill hints... + + String hint = super.getHint(node); + if (hint != null) return hint; + + // Then try some rudimentary heuristics based on other node properties + + String viewHint = node.getHint(); + hint = inferHint(viewHint); + if (hint != null) { + Log.d(TAG, "Found hint using view hint(" + viewHint + "): " + hint); + return hint; + } else if (!TextUtils.isEmpty(viewHint)) { + Log.v(TAG, "No hint using view hint: " + viewHint); + } + + String resourceId = node.getIdEntry(); + hint = inferHint(resourceId); + if (hint != null) { + Log.d(TAG, "Found hint using resourceId(" + resourceId + "): " + hint); + return hint; + } else if (!TextUtils.isEmpty(resourceId)) { + Log.v(TAG, "No hint using resourceId: " + resourceId); + } + + CharSequence text = node.getText(); + CharSequence className = node.getClassName(); + if (text != null && className != null && className.toString().contains("EditText")) { + hint = inferHint(text.toString()); + if (hint != null) { + // NODE: text should not be logged, as it could contain PII + Log.d(TAG, "Found hint using text(" + text + "): " + hint); + return hint; + } + } else if (!TextUtils.isEmpty(text)) { + // NODE: text should not be logged, as it could contain PII + Log.v(TAG, "No hint using text: " + text + " and class " + className); + } + return null; + } + + /** + * Uses heuristics to infer an autofill hint from a {@code string}. + * + * @return standard autofill hint, or {@code null} when it could not be inferred. + */ + @Nullable + protected String inferHint(@Nullable String string) { + if (string == null) return null; + + string = string.toLowerCase(); + if (string.contains("password")) return View.AUTOFILL_HINT_PASSWORD; + if (string.contains("username") + || (string.contains("login") && string.contains("id"))) + return View.AUTOFILL_HINT_USERNAME; + if (string.contains("email")) return View.AUTOFILL_HINT_EMAIL_ADDRESS; + if (string.contains("name")) return View.AUTOFILL_HINT_NAME; + if (string.contains("phone")) return View.AUTOFILL_HINT_PHONE; + + return null; + } +} diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java index af8a9afa..92d6436f 100644 --- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java +++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java @@ -28,13 +28,16 @@ import android.service.autofill.SaveCallback; import android.service.autofill.SaveInfo; import android.service.autofill.SaveRequest; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.util.ArrayMap; import android.util.Log; +import android.view.View; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; import android.widget.RemoteViews; import android.widget.Toast; +import com.example.android.autofill.service.MyAutofillService; import com.example.android.autofill.service.R; import java.util.Collection; @@ -46,27 +49,18 @@ import java.util.Map.Entry; * A very basic {@link AutofillService} implementation that only shows dynamic-generated datasets * and don't persist the saved data. * - * <p>This class has 2 goals: - * <ul> - * <li>Provide a simple service that app developers can use to see how their apps behave with - * autofill. - * <li>Explain the basic autofill workflow / provide a service that can be easily modified. - * </ul> - */ -/* - * TODO list: - * - improve documentation above, explaining - * - no-goals, security limitations, etc.. - * - toast usage - * - how dynamic datasets are created - * - how save works - * - use strings instead of hardcoded values - * - use different icons for different services + * <p>The goal of this class is to provide a simple autofill service implementation that is easy + * to understand and extend, but it should <strong>not</strong> be used as-is on real apps because + * it lacks fundamental security requirements such as data partitioning and package verification + * &mdashthese requirements are fullfilled by {@link MyAutofillService}. */ public class BasicService extends AutofillService { private static final String TAG = "BasicService"; + /** + * Number of datasets sent on each request - we're simple, that value is hardcoded in our DNA! + */ private static final int NUMBER_DATASETS = 4; @Override @@ -84,7 +78,6 @@ public class BasicService extends AutofillService { callback.onSuccess(null); return; } - Log.d(TAG, "autofill hints: " + fields); // Create the base response FillResponse.Builder response = new FillResponse.Builder(); @@ -97,6 +90,9 @@ public class BasicService extends AutofillService { String hint = field.getKey(); AutofillId id = field.getValue(); String value = hint + i; + // We're simple - our dataset values are hardcoded as "hintN" (for example, + // "username1", "username2") and they're displayed as such, except if they're a + // password String displayValue = hint.contains("password") ? "password for #" + i : value; RemoteViews presentation = newDatasetPresentation(packageName, displayValue); dataset.setValue(id, AutofillValue.forText(value), presentation); @@ -123,7 +119,13 @@ public class BasicService extends AutofillService { callback.onSuccess(); } - // TODO: document + /** + * Parses the {@link AssistStructure} representing the activity being autofilled, and returns a + * map of autofillable fields (represented by their autofill ids) mapped by the hint associate + * with them. + * + * <p>An autofillable field is a {@link ViewNode} whose {@link #getHint(ViewNode)} metho + */ @NonNull private Map<String, AutofillId> getAutofillableFields(@NonNull AssistStructure structure) { Map<String, AutofillId> fields = new ArrayMap<>(); @@ -135,16 +137,25 @@ public class BasicService extends AutofillService { return fields; } - // TODO: document + /** + * Adds any autofillable view from the {@link ViewNode} and its descendants to the map. + */ private void addAutofillableFields(@NonNull Map<String, AutofillId> fields, @NonNull ViewNode node) { - String[] hints = node.getAutofillHints(); - if (hints != null) { - AutofillId id = node.getAutofillId(); - // We're simple, we only care about the first hint - String hint = hints[0].toLowerCase(); - Log.v(TAG, "Found hint " + hint + " on " + id); - fields.put(hint, id); + int type = node.getAutofillType(); + // We're simple, we just autofill text fields. + if (type == View.AUTOFILL_TYPE_TEXT) { + String hint = getHint(node); + if (hint != null) { + AutofillId id = node.getAutofillId(); + if (!fields.containsKey(hint)) { + Log.v(TAG, "Setting hint " + hint + " on " + id); + fields.put(hint, id); + } else { + Log.v(TAG, "Ignoring hint " + hint + " on " + id + + " because it was already set"); + } + } } int childrenSize = node.getChildCount(); for (int i = 0; i < childrenSize; i++) { @@ -152,14 +163,37 @@ public class BasicService extends AutofillService { } } - // TODO: move to common code + /** + * Gets the autofill hint associated with the given node. + * + * <p>By default it just return the first entry on the node's + * {@link ViewNode#getAutofillHints() autofillHints} (when available), but subclasses could + * extend it to use heuristics when the app developer didn't explicitly provide these hints. + * + */ + @Nullable + protected String getHint(@NonNull ViewNode node) { + String[] hints = node.getAutofillHints(); + if (hints == null) return null; + + // We're simple, we only care about the first hint + String hint = hints[0].toLowerCase(); + return hint; + } + + /** + * Helper method to get the {@link AssistStructure} associated with the latest request + * in an autofill context. + */ @NonNull private static AssistStructure getLatestAssistStructure(@NonNull FillRequest request) { List<FillContext> fillContexts = request.getFillContexts(); return fillContexts.get(fillContexts.size() - 1).getStructure(); } - // TODO: move to common code + /** + * Helper method to create a dataset presentation with the given text. + */ @NonNull private static RemoteViews newDatasetPresentation(@NonNull String packageName, @NonNull CharSequence text) { @@ -170,7 +204,9 @@ public class BasicService extends AutofillService { return presentation; } - // TODO: move to common code + /** + * Displays a toast with the given message. + */ private void toast(@NonNull CharSequence message) { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); } |