Add tests to verify response authentication behavior.
testFillResponseAuthBothFields()
A basic response authentication test.
testFillResponseAuth_requestAutofillOnAuthenticationActivity()
A test that verifies Ib06edd823fa4478f34362164f3f7dd3544e51705. When
user requests autofill on Authentication Activity then back, it will
show dropdown suggestion.
Bug: 158877106
Test: atest CtsAutoFillServiceTestCases:InlineAuthenticationTest
Test: atest CtsAutoFillServiceTestCases:AuthenticationTest
Test: atest CtsAutoFillServiceTestCases:PartitionedActivityTest
Test: atest CtsAutoFillServiceTestCases:FillEventHistoryTest
Test: atest CtsAutoFillServiceTestCases:InlineFillEventHistoryTest
Test: atest CtsAutoFillServiceTestCases:DatasetFilteringDropdownTest
Test: atest CtsAutoFillServiceTestCases:DatasetFilteringInlineTest
Change-Id: Ie1d9055b0eabfcaa00861869467be8dcee25833e
diff --git a/tests/autofillservice/res/layout/authentication_activity.xml b/tests/autofillservice/res/layout/authentication_activity.xml
new file mode 100644
index 0000000..23e58e7
--- /dev/null
+++ b/tests/autofillservice/res/layout/authentication_activity.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Authenticate?" />
+
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword"
+ android:autofillHints="password" />
+
+ <Button
+ android:id="@+id/yes"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Yes" />
+
+</LinearLayout>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
index b247ec2..7ddfccc 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
@@ -34,6 +34,8 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.autofill.AutofillManager;
+import android.widget.Button;
+import android.widget.EditText;
import com.google.common.base.Preconditions;
@@ -58,6 +60,7 @@
private static final int MSG_WAIT_FOR_LATCH = 1;
+ private static final int MSG_REQUEST_AUTOFILL = 2;
private static Bundle sData;
private static final SparseArray<CannedDataset> sDatasets = new SparseArray<>();
@@ -73,10 +76,18 @@
// Used to block response until it's counted down.
private static CountDownLatch sResponseLatch;
+ // Guarded by sLock
+ // Used to request autofill for a autofillable view in AuthenticationActivity
+ private static boolean sRequestAutofill;
+
private Handler mHandler;
+ private EditText mPasswordEditText;
+ private Button mYesButton;
+
static void resetStaticState() {
- setResultCode(RESULT_OK);
+ setResultCode(null, RESULT_OK);
+ setRequestAutofillForAuthenticationActivity(/* requestAutofill */ false);
sDatasets.clear();
sResponses.clear();
for (int i = 0; i < sPendingIntents.size(); i++) {
@@ -168,15 +179,30 @@
}
}
+ public static void setRequestAutofillForAuthenticationActivity(boolean requestAutofill) {
+ synchronized (sLock) {
+ sRequestAutofill = requestAutofill;
+ }
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.authentication_activity);
+
+ mPasswordEditText = findViewById(R.id.password);
+ mYesButton = findViewById(R.id.yes);
+ mYesButton.setOnClickListener(view -> doIt());
+
mHandler = new Handler(Looper.getMainLooper(), (m) -> {
switch (m.what) {
case MSG_WAIT_FOR_LATCH:
waitForLatchAndDoIt();
break;
+ case MSG_REQUEST_AUTOFILL:
+ requestFocusOnPassword();
+ break;
default:
throw new IllegalArgumentException("invalid message: " + m);
}
@@ -186,11 +212,17 @@
if (sResponseLatch != null) {
Log.d(TAG, "Delaying message until latch is counted down");
mHandler.dispatchMessage(mHandler.obtainMessage(MSG_WAIT_FOR_LATCH));
+ } else if (sRequestAutofill) {
+ mHandler.dispatchMessage(mHandler.obtainMessage(MSG_REQUEST_AUTOFILL));
} else {
doIt();
}
}
+ private void requestFocusOnPassword() {
+ syncRunOnUiThread(() -> mPasswordEditText.requestFocus());
+ }
+
private void waitForLatchAndDoIt() {
try {
final boolean called = sResponseLatch.await(5, TimeUnit.SECONDS);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 838d4ab..f0e1179 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -101,6 +101,10 @@
return sDefaultUiBot2;
}
+ protected static UiBot getDropdownUiBot() {
+ return sDefaultUiBot;
+ }
+
@ClassRule
public static final SettingsStateKeeperRule sPublicServiceSettingsKeeper =
sTheRealServiceSettingsKeeper;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java
index 2b6afe7..cb30cdd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java
@@ -18,6 +18,7 @@
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
+import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
import static android.autofillservice.cts.Helper.ID_PASSWORD;
import static android.autofillservice.cts.Helper.ID_USERNAME;
import static android.autofillservice.cts.Helper.UNUSED_AUTOFILL_VALUE;
@@ -34,6 +35,7 @@
import android.autofillservice.cts.CannedFillResponse.CannedDataset;
import android.autofillservice.cts.Helper;
import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
+import android.autofillservice.cts.UiBot;
import android.content.IntentSender;
import android.platform.test.annotations.AppModeFull;
@@ -61,6 +63,101 @@
Helper.enableAutofillService(getContext(), SERVICE_NAME);
}
+ /**
+ * This test verifies the behavior that user starts a new AutofillSession in Authentication
+ * Activity during the FillResponse authentication flow, we will fallback to dropdown when
+ * authentication done and then back to original Activity.
+ */
+ @Test
+ public void testFillResponseAuth_withNewAutofillSessionStartByActivity()
+ throws Exception {
+ // Set service.
+ enableService();
+
+ // Prepare the authenticated response
+ final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+ new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setId("name")
+ .setInlinePresentation(createInlinePresentation("Dataset"))
+ .setPresentation(createPresentation("Dataset"))
+ .build()).build());
+ // Configure the service behavior
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
+ .setPresentation(createPresentation("Tap to auth!"))
+ .setInlinePresentation(createInlinePresentation("Tap to auth!"))
+ .build());
+
+ // Trigger auto-fill.
+ assertSuggestionShownBySelectViewId(ID_USERNAME, "Tap to auth!");
+ sReplier.getNextFillRequest();
+
+ // Need to trigger autofill on AuthenticationActivity
+ // Set expected response for autofill on AuthenticationActivity
+ AuthenticationActivity.setResultCode(RESULT_OK);
+ AuthenticationActivity.setRequestAutofillForAuthenticationActivity(true);
+ sReplier.addResponse(NO_RESPONSE);
+ // Select the dataset to start authentication
+ mUiBot.selectDataset("Tap to auth!");
+ mUiBot.waitForIdle();
+ sReplier.getNextFillRequest();
+ // Select yes button in AuthenticationActivity to finish authentication
+ mUiBot.selectByRelativeId("yes");
+ mUiBot.waitForIdle();
+
+ // Check fallback to dropdown
+ final UiBot dropDownUiBot = getDropdownUiBot();
+ dropDownUiBot.assertDatasets("Dataset");
+ }
+
+ @Test
+ public void testFillResponseAuth() throws Exception {
+ // Set service.
+ enableService();
+
+ // Prepare the authenticated response
+ final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+ new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setId("name")
+ .setInlinePresentation(createInlinePresentation("Dataset"))
+ .setPresentation(createPresentation("Dataset"))
+ .build()).build());
+ // Configure the service behavior
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
+ .setPresentation(createPresentation("Tap to auth!"))
+ .setInlinePresentation(createInlinePresentation("Tap to auth!"))
+ .build());
+
+ // Set expectation for the activity
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger auto-fill
+ assertSuggestionShownBySelectViewId(ID_USERNAME, "Tap to auth!");
+
+ sReplier.getNextFillRequest();
+
+ // Set AuthenticationActivity result code
+ AuthenticationActivity.setResultCode(RESULT_OK);
+ // Select the dataset to start authentication
+ mUiBot.selectDataset("Tap to auth!");
+ mUiBot.waitForIdle();
+ // Authentication done, show real dataset
+ mUiBot.assertDatasets("Dataset");
+
+ // Select the dataset and check the result is autofilled.
+ mUiBot.selectDataset("Dataset");
+ mUiBot.waitForIdle();
+ mUiBot.assertNoDatasets();
+ mActivity.assertAutoFilled();
+ }
+
@Test
public void testDatasetAuthTwoFields() throws Exception {
datasetAuthTwoFields(/* cancelFirstAttempt */ false);