Add CTS test for a11y ime apis
Test: test passes.
Bug: 187453053
Change-Id: Icc1f61be79f8e16e7c2e53031937701b847b2b06
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 584a8d5..2dfb7fd 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -214,6 +214,30 @@
<meta-data android:name="android.view.im"
android:resource="@xml/stub_ime"/>
</service>
+
+ <service android:name=".StubImeAccessibilityService"
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService"/>
+ <category android:name="android.accessibilityservice.category.FEEDBACK_GENERIC"/>
+ </intent-filter>
+
+ <meta-data android:name="android.accessibilityservice"
+ android:resource="@xml/stub_ime_accessibility_service"/>
+ </service>
+
+ <service android:name=".StubNonImeAccessibilityService"
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService"/>
+ <category android:name="android.accessibilityservice.category.FEEDBACK_GENERIC"/>
+ </intent-filter>
+
+ <meta-data android:name="android.accessibilityservice"
+ android:resource="@xml/stub_non_ime_accessibility_service"/>
+ </service>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/accessibilityservice/res/values/strings.xml b/tests/accessibilityservice/res/values/strings.xml
index bbc55619..c79d255 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -190,6 +190,10 @@
<string name="stub_focus_indicator_service_description">com.android.accessibilityservice.cts.StubFocusIndicatorService</string>
+ <string name="stub_ime_accessibility_service_description">com.android.accessibilityservice.cts.StubImeAccessibilityService</string>
+
+ <string name="stub_non_ime_accessibility_service_description">com.android.accessibilityservice.cts.StubNonImeAccessibilityService</string>
+
<!-- AccessibilityWindowQueryTest and AccessibilityReportingTest -->
<!-- String title of embedded display activity -->
diff --git a/tests/accessibilityservice/res/xml/stub_ime_accessibility_service.xml b/tests/accessibilityservice/res/xml/stub_ime_accessibility_service.xml
new file mode 100644
index 0000000..17a1aeb
--- /dev/null
+++ b/tests/accessibilityservice/res/xml/stub_ime_accessibility_service.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:description="@string/stub_ime_accessibility_service_description"
+ android:accessibilityEventTypes="typeAllMask"
+ android:accessibilityFeedbackType="feedbackGeneric"
+ android:accessibilityFlags="flagInputMethodEditor"
+ android:notificationTimeout="0" />
diff --git a/tests/accessibilityservice/res/xml/stub_non_ime_accessibility_service.xml b/tests/accessibilityservice/res/xml/stub_non_ime_accessibility_service.xml
new file mode 100644
index 0000000..35d7fb4
--- /dev/null
+++ b/tests/accessibilityservice/res/xml/stub_non_ime_accessibility_service.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:description="@string/stub_non_ime_accessibility_service_description"
+ android:accessibilityEventTypes="typeAllMask"
+ android:accessibilityFeedbackType="feedbackGeneric"
+ android:notificationTimeout="0" />
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java
new file mode 100644
index 0000000..2e599a2
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2022 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.accessibilityservice.cts;
+
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.disableAllServices;
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.enableService;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibilityservice.InputMethod;
+import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.platform.test.annotations.AppModeFull;
+import android.widget.EditText;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test one a11y service requiring ime capabilities and one doesn't.
+ */
+@AppModeFull
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityImeTest {
+ private static Instrumentation sInstrumentation;
+ private static UiAutomation sUiAutomation;
+
+ private AccessibilityEndToEndActivity mActivity;
+
+ private ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
+ new ActivityTestRule<>(AccessibilityEndToEndActivity.class, false, false);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ private EditText mEditText;
+ private String mInitialText;
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mDumpOnFailureRule);
+
+ @Before
+ public void setUp() throws Exception {
+ sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ sUiAutomation = sInstrumentation.getUiAutomation();
+ sInstrumentation
+ .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ mActivity = launchActivityAndWaitForItToBeOnscreen(
+ sInstrumentation, sUiAutomation, mActivityRule);
+ // focus the edit text
+ mEditText = mActivity.findViewById(R.id.edittext);
+ // initial text
+ mInitialText = mActivity.getString(R.string.text_input_blah);
+ disableAllServices();
+ }
+
+ @Test
+ public void testInputConnection_requestIme() throws InterruptedException {
+ StubImeAccessibilityService stubImeAccessibilityService =
+ enableService(StubImeAccessibilityService.class);
+
+ CountDownLatch startInputLatch = new CountDownLatch(1);
+ stubImeAccessibilityService.setStartInputCountDownLatch(startInputLatch);
+
+ mActivity.runOnUiThread(() -> {
+ mEditText.requestFocus();
+ mEditText.setSelection(mInitialText.length(), mInitialText.length());
+ });
+
+ startInputLatch.await(2, TimeUnit.SECONDS);
+ assertNotNull(stubImeAccessibilityService.getInputMethod());
+ InputMethod.AccessibilityInputConnection connection =
+ stubImeAccessibilityService.getInputMethod().getCurrentInputConnection();
+ assertNotNull(connection);
+
+ CountDownLatch selectionChangeLatch = new CountDownLatch(1);
+ stubImeAccessibilityService.setSelectionChangeLatch(selectionChangeLatch);
+ stubImeAccessibilityService.setSelectionTarget(mInitialText.length() * 2);
+
+ connection.commitText(mInitialText, 1, null);
+
+ selectionChangeLatch.await(2, TimeUnit.SECONDS);
+ assertEquals(mInitialText + mInitialText, mEditText.getText().toString());
+ }
+
+ @Test
+ public void testInputConnection_notRequestIme() throws InterruptedException {
+ StubNonImeAccessibilityService stubNonImeAccessibilityService =
+ enableService(StubNonImeAccessibilityService.class);
+
+ CountDownLatch startInputLatch = new CountDownLatch(1);
+ stubNonImeAccessibilityService.setStartInputCountDownLatch(startInputLatch);
+
+ mActivity.runOnUiThread(() -> {
+ mEditText.requestFocus();
+ mEditText.setSelection(mInitialText.length(), mInitialText.length());
+ });
+
+ startInputLatch.await(2, TimeUnit.SECONDS);
+ assertNull(stubNonImeAccessibilityService.getInputMethod());
+ }
+
+ @Test
+ public void testSelectionChange_requestIme() throws InterruptedException {
+ StubImeAccessibilityService stubImeAccessibilityService =
+ enableService(StubImeAccessibilityService.class);
+
+ CountDownLatch startInputLatch = new CountDownLatch(1);
+ stubImeAccessibilityService.setStartInputCountDownLatch(startInputLatch);
+
+ mActivity.runOnUiThread(() -> {
+ mEditText.requestFocus();
+ mEditText.setSelection(mInitialText.length(), mInitialText.length());
+ });
+
+ final int targetPos = mInitialText.length() - 1;
+ startInputLatch.await(2, TimeUnit.SECONDS);
+ assertNotNull(stubImeAccessibilityService.getInputMethod());
+ InputMethod.AccessibilityInputConnection connection =
+ stubImeAccessibilityService.getInputMethod().getCurrentInputConnection();
+ assertNotNull(connection);
+
+ CountDownLatch selectionChangeLatch = new CountDownLatch(1);
+ stubImeAccessibilityService.setSelectionChangeLatch(selectionChangeLatch);
+ stubImeAccessibilityService.setSelectionTarget(targetPos);
+
+ connection.setSelection(targetPos, targetPos);
+ selectionChangeLatch.await(2, TimeUnit.SECONDS);
+
+ assertEquals(targetPos, mEditText.getSelectionStart());
+ assertEquals(targetPos, mEditText.getSelectionEnd());
+
+ assertEquals(targetPos, stubImeAccessibilityService.selStart);
+ assertEquals(targetPos, stubImeAccessibilityService.selEnd);
+ }
+
+ @Test
+ public void testSelectionChange_notRequestIme() throws InterruptedException {
+ StubNonImeAccessibilityService stubNonImeAccessibilityService =
+ enableService(StubNonImeAccessibilityService.class);
+
+ mActivity.runOnUiThread(() -> {
+ mEditText.requestFocus();
+ mEditText.setSelection(mInitialText.length(), mInitialText.length());
+ });
+
+ final int targetPos = mInitialText.length() - 1;
+ CountDownLatch selectionChangeLatch = new CountDownLatch(1);
+ stubNonImeAccessibilityService.setSelectionChangeLatch(selectionChangeLatch);
+ stubNonImeAccessibilityService.setSelectionTarget(targetPos);
+
+ mActivity.runOnUiThread(() -> {
+ mEditText.setSelection(targetPos, targetPos);
+ });
+ selectionChangeLatch.await(2, TimeUnit.SECONDS);
+
+ assertEquals(targetPos, mEditText.getSelectionStart());
+ assertEquals(targetPos, mEditText.getSelectionEnd());
+
+ assertEquals(-1, stubNonImeAccessibilityService.oldSelStart);
+ assertEquals(-1, stubNonImeAccessibilityService.oldSelEnd);
+ assertEquals(-1, stubNonImeAccessibilityService.selStart);
+ assertEquals(-1, stubNonImeAccessibilityService.selEnd);
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubImeAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubImeAccessibilityService.java
new file mode 100644
index 0000000..6bfc66b
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubImeAccessibilityService.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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.accessibilityservice.cts;
+
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.InputMethod;
+import android.view.inputmethod.EditorInfo;
+
+import androidx.annotation.NonNull;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A stub accessibility service to install for testing IME APIs
+ */
+public class StubImeAccessibilityService extends InstrumentedAccessibilityService {
+ private static final int INVALID = -11;
+ public int selStart = -1;
+ public int selEnd = -1;
+ public int oldSelStart = -1;
+ public int oldSelEnd = -1;
+ private CountDownLatch mStartInputLatch = null;
+ private CountDownLatch mSelectionChangeLatch = null;
+ private int mSelectionTarget = INVALID;
+
+ public void setStartInputCountDownLatch(CountDownLatch latch) {
+ mStartInputLatch = latch;
+ }
+
+ public void setSelectionChangeLatch(CountDownLatch latch) {
+ mSelectionChangeLatch = latch;
+ }
+ public void setSelectionTarget(int target) {
+ mSelectionTarget = target;
+ }
+
+ class InputMethodImpl extends InputMethod {
+ InputMethodImpl(@NonNull AccessibilityService service) {
+ super(service);
+ }
+
+ @Override
+ public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart,
+ int newSelEnd, int candidatesStart, int candidatesEnd) {
+ super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart,
+ candidatesEnd);
+ // A11y receive placeholder notification.
+ if ((mSelectionChangeLatch != null) && (newSelStart == mSelectionTarget)) {
+ StubImeAccessibilityService.this.oldSelStart = oldSelStart;
+ StubImeAccessibilityService.this.oldSelEnd = oldSelEnd;
+ StubImeAccessibilityService.this.selStart = newSelStart;
+ StubImeAccessibilityService.this.selEnd = newSelEnd;
+ mSelectionChangeLatch.countDown();
+ mSelectionChangeLatch = null;
+ }
+ }
+
+ @Override
+ public void onStartInput(EditorInfo attribute, boolean restarting) {
+ super.onStartInput(attribute, restarting);
+ if (mStartInputLatch != null) {
+ mStartInputLatch.countDown();
+ mStartInputLatch = null;
+ }
+ }
+ }
+
+ @Override
+ public InputMethod onCreateInputMethod() {
+ return new InputMethodImpl(this);
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubNonImeAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubNonImeAccessibilityService.java
new file mode 100644
index 0000000..7f6ff33
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubNonImeAccessibilityService.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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.accessibilityservice.cts;
+
+/**
+ * A stub accessibility service to install for testing IME APIs
+ */
+public class StubNonImeAccessibilityService extends StubImeAccessibilityService {
+}