CTS tests for augmented autofill auth flow

Tests for Id21c8f074bd0f49992e01445d50b1503af4720b6

Bug: 157863999
Test: atest CtsAutoFillServiceTestCases:InlineAugmentedAuthTest
Change-Id: Iacc5a17cb3d2f1b991b78c95330cbd0f0254cbe5
Merged-In: Iacc5a17cb3d2f1b991b78c95330cbd0f0254cbe5
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index d4d5459..f690678 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -134,6 +134,7 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name=".augmented.AugmentedAuthActivity" />
         <activity android:name=".SimpleAfterLoginActivity" />
         <activity android:name=".SimpleBeforeLoginActivity" />
         <activity android:name=".NonAutofillableActivity" />
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 6c229e0..838d4ab 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -26,6 +26,7 @@
 
 import android.app.PendingIntent;
 import android.autofillservice.cts.InstrumentedAutoFillService.Replier;
+import android.autofillservice.cts.augmented.AugmentedAuthActivity;
 import android.autofillservice.cts.inline.InlineUiBot;
 import android.content.ClipboardManager;
 import android.content.Context;
@@ -388,6 +389,7 @@
 
             InstrumentedAutoFillService.resetStaticState();
             AuthenticationActivity.resetStaticState();
+            AugmentedAuthActivity.resetStaticState();
             sReplier.reset();
         }
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAuthActivity.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAuthActivity.java
new file mode 100644
index 0000000..8de9eb7
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAuthActivity.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+package android.autofillservice.cts.augmented;
+
+import android.app.PendingIntent;
+import android.autofillservice.cts.AbstractAutoFillActivity;
+import android.autofillservice.cts.R;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.service.autofill.Dataset;
+import android.util.Log;
+import android.view.autofill.AutofillManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Activity for testing Augmented Autofill authentication flow. This activity shows a simple UI;
+ * when the UI is tapped, it returns whatever data was configured via the auth intent.
+ */
+public class AugmentedAuthActivity extends AbstractAutoFillActivity {
+    private static final String TAG = "AugmentedAuthActivity";
+
+    public static final String ID_AUTH_ACTIVITY_BUTTON = "button";
+
+    private static final String EXTRA_DATASET_TO_RETURN = "dataset_to_return";
+    private static final String EXTRA_CLIENT_STATE_TO_RETURN = "client_state_to_return";
+    private static final String EXTRA_RESULT_CODE_TO_RETURN = "result_code_to_return";
+
+    private static final List<PendingIntent> sPendingIntents = new ArrayList<>(1);
+
+    public static void resetStaticState() {
+        for (PendingIntent pendingIntent : sPendingIntents) {
+            pendingIntent.cancel();
+        }
+        sPendingIntents.clear();
+    }
+
+    public static IntentSender createSender(Context context, int requestCode,
+            Dataset datasetToReturn, Bundle clientStateToReturn, int resultCodeToReturn) {
+        Intent intent = new Intent(context, AugmentedAuthActivity.class);
+        intent.putExtra(EXTRA_DATASET_TO_RETURN, datasetToReturn);
+        intent.putExtra(EXTRA_CLIENT_STATE_TO_RETURN, clientStateToReturn);
+        intent.putExtra(EXTRA_RESULT_CODE_TO_RETURN, resultCodeToReturn);
+        PendingIntent pendingIntent = PendingIntent.getActivity(context, requestCode, intent, 0);
+        sPendingIntents.add(pendingIntent);
+        return pendingIntent.getIntentSender();
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Log.d(TAG, "Auth activity invoked, showing auth UI");
+        setContentView(R.layout.single_button_activity);
+        findViewById(R.id.button).setOnClickListener((v) -> {
+            Log.d(TAG, "Auth UI tapped, returning result");
+
+            Intent intent = getIntent();
+            Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET_TO_RETURN);
+            Bundle clientState = intent.getParcelableExtra(EXTRA_CLIENT_STATE_TO_RETURN);
+            int resultCode = intent.getIntExtra(EXTRA_RESULT_CODE_TO_RETURN, RESULT_OK);
+            Log.d(TAG, "Output: dataset=" + dataset + ", clientState=" + clientState
+                    + ", resultCode=" + resultCode);
+
+            Intent result = new Intent();
+            result.putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, dataset);
+            result.putExtra(AutofillManager.EXTRA_CLIENT_STATE, clientState);
+            setResult(resultCode, result);
+
+            finish();
+        });
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/CannedAugmentedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/CannedAugmentedFillResponse.java
index a8a26a3..af1229b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/augmented/CannedAugmentedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/CannedAugmentedFillResponse.java
@@ -19,6 +19,7 @@
 
 import android.autofillservice.cts.R;
 import android.content.Context;
+import android.content.IntentSender;
 import android.os.Bundle;
 import android.service.autofill.InlinePresentation;
 import android.service.autofill.augmented.FillCallback;
@@ -189,6 +190,7 @@
                     final AutofillId id = pair.first;
                     datasetBuilder.setFieldInlinePresentation(id, pair.second, null,
                             dataset.mFieldPresentationById.get(id));
+                    datasetBuilder.setAuthentication(dataset.mAuthentication);
                 }
                 list.add(datasetBuilder.build());
             }
@@ -279,12 +281,14 @@
         private final Map<AutofillId, InlinePresentation> mFieldPresentationById;
         private final String mPresentation;
         private final AutofillValue mOnlyFieldValue;
+        private final IntentSender mAuthentication;
 
         private Dataset(@NonNull Builder builder) {
             mFieldValuesById = builder.mFieldValuesById;
             mPresentation = builder.mPresentation;
             mOnlyFieldValue = builder.mOnlyFieldValue;
             mFieldPresentationById = builder.mFieldPresentationById;
+            this.mAuthentication = builder.mAuthentication;
         }
 
         @NonNull
@@ -304,6 +308,7 @@
             return "Dataset: [presentation=" + mPresentation
                     + ", onlyField=" + mOnlyFieldValue
                     + ", fields=" + mFieldValuesById
+                    + ", auth=" + mAuthentication
                     + "]";
         }
 
@@ -314,6 +319,7 @@
 
             private final String mPresentation;
             private AutofillValue mOnlyFieldValue;
+            private IntentSender mAuthentication;
 
             public Builder(@NonNull String presentation) {
                 mPresentation = Objects.requireNonNull(presentation);
@@ -356,6 +362,13 @@
                 return this;
             }
 
+            /**
+             * Sets the authentication intent for this dataset.
+             */
+            public Builder setAuthentication(IntentSender authentication) {
+                mAuthentication = authentication;
+                return this;
+            }
 
             public Dataset build() {
                 return new Dataset(this);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedAuthTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedAuthTest.java
new file mode 100644
index 0000000..bb81399
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedAuthTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+package android.autofillservice.cts.inline;
+
+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_USERNAME;
+import static android.autofillservice.cts.augmented.AugmentedHelper.assertBasicRequestInfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.autofillservice.cts.AutofillActivityTestRule;
+import android.autofillservice.cts.augmented.AugmentedAuthActivity;
+import android.autofillservice.cts.augmented.AugmentedAutofillAutoActivityLaunchTestCase;
+import android.autofillservice.cts.augmented.AugmentedLoginActivity;
+import android.autofillservice.cts.augmented.CannedAugmentedFillResponse;
+import android.autofillservice.cts.augmented.CtsAugmentedAutofillService;
+import android.content.IntentSender;
+import android.service.autofill.Dataset;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.EditText;
+
+import org.junit.Test;
+
+public class InlineAugmentedAuthTest
+        extends AugmentedAutofillAutoActivityLaunchTestCase<AugmentedLoginActivity> {
+
+    protected AugmentedLoginActivity mActivity;
+
+    public InlineAugmentedAuthTest() {
+        super(getInlineUiBot());
+    }
+
+    @Override
+    protected AutofillActivityTestRule<AugmentedLoginActivity> getActivityRule() {
+        return new AutofillActivityTestRule<AugmentedLoginActivity>(AugmentedLoginActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
+    }
+
+    @Test
+    public void testDatasetAuth_resultOk_validDataset() throws Exception {
+        // Set services
+        enableService();
+        enableAugmentedService();
+
+        // Set expectations
+        final EditText unField = mActivity.getUsername();
+        final AutofillId unFieldId = unField.getAutofillId();
+        final AutofillValue unValue = unField.getAutofillValue();
+        sReplier.addResponse(NO_RESPONSE);
+        Dataset authResult = new Dataset.Builder(createInlinePresentation("auth"))
+                .setId("dummyId")
+                .setValue(unFieldId, AutofillValue.forText("Auth Result"))
+                .build();
+        IntentSender authAction = AugmentedAuthActivity.createSender(mContext, 1,
+                authResult, null, RESULT_OK);
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("bla").build(),
+                        unFieldId)
+                .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("inline")
+                        .setField(unFieldId, "John Smith", createInlinePresentation("John"))
+                        .setAuthentication(authAction)
+                        .build())
+                .build());
+
+        // Trigger autofill request
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdle();
+        sReplier.getNextFillRequest();
+        CtsAugmentedAutofillService.AugmentedFillRequest request1 =
+                sAugmentedReplier.getNextFillRequest();
+
+        // Assert request
+        assertBasicRequestInfo(request1, mActivity, unFieldId, unValue);
+
+        // Confirm suggestions
+        mUiBot.assertDatasets("John");
+
+        // Tap on suggestion
+        mUiBot.selectDataset("John");
+        mUiBot.waitForIdle();
+
+        // Tap on the auth activity button and assert that the dataset from the auth activity is
+        // filled into the field.
+        mActivity.expectAutoFill("Auth Result");
+        mUiBot.selectByRelativeId(AugmentedAuthActivity.ID_AUTH_ACTIVITY_BUTTON);
+        mUiBot.waitForIdle();
+        mActivity.assertAutoFilled();
+        assertThat(unField.getText().toString()).isEqualTo("Auth Result");
+        mUiBot.assertNoDatasets();
+    }
+
+    @Test
+    public void testDatasetAuth_resultOk_nullDataset() throws Exception {
+        // Set services
+        enableService();
+        enableAugmentedService();
+
+        // Set expectations
+        final EditText unField = mActivity.getUsername();
+        final AutofillId unFieldId = unField.getAutofillId();
+        final AutofillValue unValue = unField.getAutofillValue();
+        sReplier.addResponse(NO_RESPONSE);
+        IntentSender authAction = AugmentedAuthActivity.createSender(mContext, 1,
+                null, null, RESULT_OK);
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("bla").build(),
+                        unFieldId)
+                .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("inline")
+                        .setField(unFieldId, "John Smith", createInlinePresentation("John"))
+                        .setAuthentication(authAction)
+                        .build())
+                .build());
+
+        // Trigger autofill request
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdle();
+        sReplier.getNextFillRequest();
+        CtsAugmentedAutofillService.AugmentedFillRequest request1 =
+                sAugmentedReplier.getNextFillRequest();
+
+        // Assert request
+        assertBasicRequestInfo(request1, mActivity, unFieldId, unValue);
+
+        // Confirm suggestions
+        mUiBot.assertDatasets("John");
+
+        // Tap on suggestion
+        mUiBot.selectDataset("John");
+        mUiBot.waitForIdle();
+
+        // Tap on the auth activity button and assert that the field is left unchanged (since the
+        // dataset returned from the auth activity is null).
+        mUiBot.selectByRelativeId(AugmentedAuthActivity.ID_AUTH_ACTIVITY_BUTTON);
+        mUiBot.waitForIdle();
+        assertThat(unField.getText().toString()).isEqualTo("");
+    }
+
+    @Test
+    public void testDatasetAuth_resultCancel() throws Exception {
+        // Set services
+        enableService();
+        enableAugmentedService();
+
+        // Set expectations
+        final EditText unField = mActivity.getUsername();
+        final AutofillId unFieldId = unField.getAutofillId();
+        final AutofillValue unValue = unField.getAutofillValue();
+        sReplier.addResponse(NO_RESPONSE);
+        Dataset authResult = new Dataset.Builder(createInlinePresentation("auth"))
+                .setId("dummyId")
+                .setValue(unFieldId, AutofillValue.forText("Auth Result"))
+                .build();
+        IntentSender authAction = AugmentedAuthActivity.createSender(mContext, 1,
+                authResult, null, RESULT_CANCELED);
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("bla").build(),
+                        unFieldId)
+                .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("inline")
+                        .setField(unFieldId, "John Smith", createInlinePresentation("John"))
+                        .setAuthentication(authAction)
+                        .build())
+                .build());
+
+        // Trigger autofill request
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdle();
+        sReplier.getNextFillRequest();
+        CtsAugmentedAutofillService.AugmentedFillRequest request1 =
+                sAugmentedReplier.getNextFillRequest();
+
+        // Assert request
+        assertBasicRequestInfo(request1, mActivity, unFieldId, unValue);
+
+        // Confirm suggestions
+        mUiBot.assertDatasets("John");
+
+        // Tap on suggestion
+        mUiBot.selectDataset("John");
+        mUiBot.waitForIdle();
+
+        // Tap on the auth activity button and assert that the field is left unchanged (since the
+        // result code returned by the auth activity is RESULT_CANCELED).
+        mUiBot.selectByRelativeId(AugmentedAuthActivity.ID_AUTH_ACTIVITY_BUTTON);
+        mUiBot.waitForIdle();
+        assertThat(unField.getText().toString()).isEqualTo("");
+    }
+}