aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2018-04-19 07:24:09 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2018-04-19 07:24:09 +0000
commitc49d02ccc3e5ebd5f05a4fc077bd9ebf3d291870 (patch)
treef9520a6504e42ccf510d0ca3fa61a96b0221fdbf
parentb2669f3838ee85a805cba453a69d2a165c3b5666 (diff)
parente44363c82c9187230ba85cdc1beb3d2aba9595e6 (diff)
downloadandroid-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
-rw-r--r--input/autofill/AutofillFramework/Application/build.gradle15
-rw-r--r--input/autofill/AutofillFramework/afservice/build.gradle12
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml12
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicHeuristicsService.java105
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java94
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();
}