DO NOT MERGE - Add CTS test for Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE
Test: atest android.autofillservice.cts.SettingsIntentTest
Fixes: 79529318
Bug: 79615759
Change-Id: I58a402534945b28b06373e6ce3586a63903e8725
Merged-In: I58a402534945b28b06373e6ce3586a63903e8725
(cherry picked from commit 40a157245445015307bc71b9d13b60afbc596eee)
(cherry picked from commit eb038272cbbd53f9f5c7abe4755fa88ef75c7948)
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index 08f0754..9303955 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -66,6 +66,10 @@
<activity android:name=".OutOfProcessLoginActivity"
android:process="android.autofillservice.cts.outside"/>
<activity android:name=".FragmentContainerActivity" />
+ <activity android:name=".WebViewActivity"/>
+ <activity android:name=".TrampolineWelcomeActivity"/>
+ <activity android:name=".AttachedContextActivity"/>
+ <activity android:name=".TrampolineForResultActivity" />
<service
android:name=".InstrumentedAutoFillService"
@@ -75,6 +79,22 @@
<action android:name="android.service.autofill.AutofillService" />
</intent-filter>
</service>
+ <service
+ android:name=".NoOpAutofillService"
+ android:label="NoOpAutofillService"
+ android:permission="android.permission.BIND_AUTOFILL_SERVICE" >
+ <intent-filter>
+ <action android:name="android.service.autofill.AutofillService" />
+ </intent-filter>
+ </service>
+ <!-- BadAutofillService does not declare the proper permission -->
+ <service
+ android:name=".BadAutofillService"
+ android:label="BadAutofillService">
+ <intent-filter>
+ <action android:name="android.service.autofill.AutofillService" />
+ </intent-filter>
+ </service>
</application>
<instrumentation
diff --git a/tests/autofillservice/src/android/autofillservice/cts/BadAutofillService.java b/tests/autofillservice/src/android/autofillservice/cts/BadAutofillService.java
new file mode 100644
index 0000000..19a8ec1
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/BadAutofillService.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.os.CancellationSignal;
+import android.service.autofill.AutofillService;
+import android.service.autofill.FillCallback;
+import android.service.autofill.FillRequest;
+import android.service.autofill.SaveCallback;
+import android.service.autofill.SaveRequest;
+import android.util.Log;
+
+/**
+ * An {@link AutofillService} implementation that does fails if called upon.
+ */
+public class BadAutofillService extends AutofillService {
+
+ private static final String TAG = "BadAutofillService";
+
+ static final String SERVICE_NAME = BadAutofillService.class.getPackage().getName()
+ + "/." + BadAutofillService.class.getSimpleName();
+ static final String SERVICE_LABEL = "BadAutofillService";
+
+ @Override
+ public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
+ FillCallback callback) {
+ Log.e(TAG, "onFillRequest() should never be called");
+ throw new UnsupportedOperationException("onFillRequest() should never be called");
+ }
+
+ @Override
+ public void onSaveRequest(SaveRequest request, SaveCallback callback) {
+ Log.e(TAG, "onSaveRequest() should never be called");
+ throw new UnsupportedOperationException("onSaveRequest() should never be called");
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 0280b44..0c384ff 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -59,6 +59,8 @@
static final boolean VERBOSE = false;
+ static final String MY_PACKAGE = "android.autofillservice.cts";
+
static final String ID_USERNAME_LABEL = "username_label";
static final String ID_USERNAME = "username";
static final String ID_PASSWORD_LABEL = "password_label";
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
index 9650fff..0840f28b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -53,8 +53,11 @@
*/
public class InstrumentedAutoFillService extends AutofillService {
- static final String SERVICE_NAME = InstrumentedAutoFillService.class.getPackage()
- .getName() + "/." + InstrumentedAutoFillService.class.getSimpleName();
+ static final String SERVICE_PACKAGE = Helper.MY_PACKAGE;
+ static final String SERVICE_CLASS = "InstrumentedAutoFillService";
+
+ static final String SERVICE_NAME = SERVICE_PACKAGE + "/." + SERVICE_CLASS;
+ protected static final String sServiceLabel = SERVICE_CLASS;
private static final String TAG = "InstrumentedAutoFillService";
diff --git a/tests/autofillservice/src/android/autofillservice/cts/NoOpAutofillService.java b/tests/autofillservice/src/android/autofillservice/cts/NoOpAutofillService.java
new file mode 100644
index 0000000..a4c2a10
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/NoOpAutofillService.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.os.CancellationSignal;
+import android.service.autofill.AutofillService;
+import android.service.autofill.FillCallback;
+import android.service.autofill.FillRequest;
+import android.service.autofill.SaveCallback;
+import android.service.autofill.SaveRequest;
+
+/**
+ * {@link AutofillService} implementation that does not do anything...
+ */
+public class NoOpAutofillService extends AutofillService {
+
+ static final String SERVICE_NAME = NoOpAutofillService.class.getPackage().getName()
+ + "/." + NoOpAutofillService.class.getSimpleName();
+ static final String SERVICE_LABEL = "NoOpAutofillService";
+
+ @Override
+ public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
+ FillCallback callback) {
+ }
+
+ @Override
+ public void onSaveRequest(SaveRequest request, SaveCallback callback) {
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java b/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
new file mode 100644
index 0000000..c91fd13
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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 android.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.runShellCommand;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.Settings;
+import android.support.test.uiautomator.UiObject2;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class SettingsIntentTest extends AutoFillServiceTestCase {
+
+ private static final int MY_REQUEST_CODE = 42;
+
+ @Rule
+ public final AutofillActivityTestRule<TrampolineForResultActivity> mActivityRule =
+ new AutofillActivityTestRule<TrampolineForResultActivity>(
+ TrampolineForResultActivity.class);
+
+ protected TrampolineForResultActivity mActivity;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @After
+ public void killSettings() {
+ // Make sure there's no Settings activity left , as it could fail future tests.
+ runShellCommand("am force-stop com.android.settings");
+ }
+
+ @Test
+ public void testMultipleServicesShown() throws Exception {
+ disableService();
+
+ // Launches Settings.
+ mActivity.startForResult(newSettingsIntent(), MY_REQUEST_CODE);
+
+ // Asserts services are shown.
+ sUiBot.assertShownByText(InstrumentedAutoFillService.sServiceLabel);
+ sUiBot.assertShownByText(NoOpAutofillService.SERVICE_LABEL);
+ sUiBot.assertNotShowingForSure(BadAutofillService.SERVICE_LABEL);
+
+ // Finishes and asserts result.
+ sUiBot.pressBack();
+ mActivity.assertResult(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ public void testWarningShown_userRejectsByTappingBack() throws Exception {
+ disableService();
+
+ // Launches Settings.
+ mActivity.startForResult(newSettingsIntent(), MY_REQUEST_CODE);
+
+ // Asserts services are shown.
+ final UiObject2 object = sUiBot
+ .assertShownByText(InstrumentedAutoFillService.sServiceLabel);
+ object.click();
+
+ // TODO(b/79615759): should assert that "autofill_confirmation_message" is shown, but that
+ // string belongs to Settings - we need to move it to frameworks/base first (and/or use
+ // a resource id, also on framework).
+ // So, for now, just asserts the service name is showing again (in the popup), and the other
+ // services are not showing (because the popup hides then).
+
+ final UiObject2 msgObj = sUiBot.assertShownById("android:id/message");
+ final String msg = msgObj.getText();
+ assertWithMessage("Wrong warning message").that(msg)
+ .contains(InstrumentedAutoFillService.sServiceLabel);
+
+ // NOTE: assertion below is fine because it looks for the full text, not a substring
+ sUiBot.assertNotShowingForSure(InstrumentedAutoFillService.sServiceLabel);
+ sUiBot.assertNotShowingForSure(NoOpAutofillService.SERVICE_LABEL);
+ sUiBot.assertNotShowingForSure(BadAutofillService.SERVICE_LABEL);
+
+ // Finishes and asserts result.
+ sUiBot.pressBack();
+ mActivity.assertResult(Activity.RESULT_CANCELED);
+ }
+
+ // TODO(b/79615759): add testWarningShown_userRejectsByTappingCancel() and
+ // testWarningShown_userAccepts() - these tests would require adding the strings and resource
+ // ids to frameworks/base
+
+ private Intent newSettingsIntent() {
+ return new Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .setData(Uri.parse("package:" + Helper.MY_PACKAGE));
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TrampolineForResultActivity.java b/tests/autofillservice/src/android/autofillservice/cts/TrampolineForResultActivity.java
new file mode 100644
index 0000000..6cdd33e
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/TrampolineForResultActivity.java
@@ -0,0 +1,67 @@
+/*
+ * 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 android.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.Intent;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity used to launch another activity for result.
+ */
+// TODO: move to common code
+public class TrampolineForResultActivity extends AbstractAutoFillActivity {
+ private static final String TAG = "TrampolineForResultActivity";
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+
+ private int mExpectedRequestCode;
+ private int mActualRequestCode;
+ private int mActualResultCode;
+
+ /**
+ * Starts an activity for result.
+ */
+ public void startForResult(Intent intent, int requestCode) {
+ mExpectedRequestCode = requestCode;
+ startActivityForResult(intent, requestCode);
+ }
+
+ /**
+ * Asserts the activity launched by {@link #startForResult(Intent, int)} was finished with the
+ * expected result code, or fails if it times out.
+ */
+ public void assertResult(int expectedResultCode) throws Exception {
+ final boolean called = mLatch.await(1000, TimeUnit.MILLISECONDS);
+ assertWithMessage("Result not received in 1s").that(called).isTrue();
+ assertWithMessage("Wrong actual code").that(mActualRequestCode)
+ .isEqualTo(mExpectedRequestCode);
+ assertWithMessage("Wrong result code").that(mActualResultCode)
+ .isEqualTo(expectedResultCode);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(TAG, "onActivityResult(): req=" + requestCode + ", res=" + resultCode);
+ mActualRequestCode = requestCode;
+ mActualResultCode = resultCode;
+ mLatch.countDown();
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 495b05c..774d7fe 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -177,8 +177,34 @@
* {@link #assertDatasets(String...)}.
*/
public UiObject2 assertShownByText(String text) {
- final UiObject2 object = waitForObject(By.text(text));
- assertWithMessage(text).that(object).isNotNull();
+ return assertShownByText(text, UI_TIMEOUT_MS);
+ }
+
+ public UiObject2 assertShownByText(String text, int timeoutMs) {
+ final UiObject2 object = waitForObject(By.text(text), timeoutMs);
+ assertWithMessage("No node with text '%s'", text).that(object).isNotNull();
+ return object;
+ }
+
+ /**
+ * Asserts that the text is not showing for sure in the screen "as is", i.e., without waiting
+ * for it.
+ *
+ * <p>Typically called after another assertion that waits for a condition to be shown.
+ */
+ public void assertNotShowingForSure(String text) throws Exception {
+ final UiObject2 object = mDevice.findObject(By.text(text));
+ assertWithMessage("Find node with text '%s'", text).that(object).isNull();
+ }
+
+ /**
+ * Asserts a node with the given content description is shown.
+ *
+ */
+ public UiObject2 assertShownByContentDescription(String contentDescription) {
+ final UiObject2 object = waitForObject(By.desc(contentDescription));
+ assertWithMessage("No node with content description '%s'", contentDescription).that(object)
+ .isNotNull();
return object;
}
@@ -204,8 +230,10 @@
/**
* Asserts the id is shown on the screen.
*/
- void assertShownById(String id) {
- assertThat(waitForObject(By.res(id))).isNotNull();
+ UiObject2 assertShownById(String id) throws Exception {
+ final UiObject2 object = waitForObject(By.res(id));
+ assertThat(object).isNotNull();
+ return object;
}
/**