Add the first test for multi-session IME
Bug: 327704045
Test: atest ConcurrentMultiSessionImeTest --iteration 100
Change-Id: I9d6f4a724bffc24068b1a92e97a9ca87f335dade
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
index 0e0d212..4c531b8 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
@@ -20,9 +20,12 @@
android_test {
name: "ConcurrentMultiSessionImeTest",
srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
libs: ["android.test.runner"],
static_libs: [
"androidx.test.ext.junit",
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
"platform-test-annotations",
"platform-test-rules",
"truth",
@@ -35,7 +38,11 @@
test_suites: [
"general-tests",
],
- sdk_version: "current",
+ sdk_version: "test_current",
+
+ data: [
+ ":CtsMockInputMethod",
+ ],
// Store test artifacts in separated directories for easier debugging.
per_testcase_directory: true,
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml
index 0defe5b..2e336ca 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml
@@ -17,6 +17,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.inputmethod.multisessiontest">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name=".MainActivity"
+ android:theme="@android:style/Theme.Material.NoActionBar"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+ </application>
+
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.server.inputmethod.multisessiontest"></instrumentation>
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml
index fd598c5..d5ed203 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml
@@ -17,13 +17,28 @@
<configuration description="Config for Concurrent Multi-Session IME tests">
<object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController"
type="module_controller">
- <option name="required-feature" value="android.software.input_methods" />
+ <!-- TODO(b/323372972): require this feature once the bug is fixed. -->
+ <!-- option name="required-feature" value="android.software.input_methods" -->
<!-- Currently enabled to automotive only -->
<option name="required-feature" value="android.hardware.type.automotive" />
</object>
<option name="test-suite-tag" value="apct" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="force-install-mode" value="FULL" />
+ <option name="test-file-name" value="ConcurrentMultiSessionImeTest.apk" />
+ <option name="test-file-name" value="CtsMockInputMethod.apk" />
+ </target_preparer>
+
+ <!-- RunOnSecondaryUserTargetPreparer must run after SuiteApkInstaller. -->
+ <target_preparer class="com.android.tradefed.targetprep.RunOnSecondaryUserTargetPreparer">
+ <option name="start-background-user" value="true" />
+ <option name="test-package-name" value="com.android.server.inputmethod.multisessiontest" />
+ <option name="test-package-name" value="com.android.cts.mockime" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" />
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
@@ -31,12 +46,6 @@
value="settings delete secure show_ime_with_hard_keyboard" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="force-install-mode" value="FULL" />
- <option name="test-file-name" value="ConcurrentMultiSessionImeTest.apk" />
- </target_preparer>
-
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.server.inputmethod.multisessiontest" />
</test>
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/res/layout/main_activity.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/res/layout/main_activity.xml
new file mode 100644
index 0000000..e16d286
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/res/layout/main_activity.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2024 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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
+ <EditText
+ android:id="@+id/edit_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:hint="Input text here"/>
+</FrameLayout>
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
index a5ce69d..56dbde0 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
@@ -16,16 +16,27 @@
package com.android.server.inputmethod.multisessiontest;
-import static com.google.common.truth.Truth.assertThat;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import android.content.Context;
-import android.content.pm.PackageManager;
+import static com.android.compatibility.common.util.concurrentuser.ConcurrentUserActivityUtils.getResponderUserId;
+import static com.android.compatibility.common.util.concurrentuser.ConcurrentUserActivityUtils.launchActivityAsUserSync;
+import static com.android.compatibility.common.util.concurrentuser.ConcurrentUserActivityUtils.sendBundleAndWaitForReply;
+import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.KEY_REQUEST_CODE;
+import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.KEY_RESULT_CODE;
+import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.REPLY_IME_HIDDEN;
+import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.REQUEST_IME_STATUS;
-import androidx.test.platform.app.InstrumentationRegistry;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.ComponentName;
+import android.os.Bundle;
+
+import androidx.test.core.app.ActivityScenario;
import com.android.bedstead.harrier.BedsteadJUnit4;
import com.android.bedstead.harrier.DeviceState;
+import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
@@ -39,19 +50,56 @@
@Rule
public static final DeviceState sDeviceState = new DeviceState();
+ private static final ComponentName TEST_ACTIVITY = new ComponentName(
+ getInstrumentation().getTargetContext().getPackageName(),
+ MainActivity.class.getName());
+
+ private ActivityScenario<MainActivity> mActivityScenario;
+ private MainActivity mActivity;
+ private int mPeerUserId;
+
@Before
- public void doBeforeEachTest() {
- // No op
+ public void setUp() {
+ // Launch passenger activity.
+ mPeerUserId = getResponderUserId();
+ launchActivityAsUserSync(TEST_ACTIVITY, mPeerUserId);
+
+ // Launch driver activity.
+ mActivityScenario = ActivityScenario.launch(MainActivity.class);
+ mActivityScenario.onActivity(activity -> mActivity = activity);
+ }
+
+ @After
+ public void tearDown() {
+ if (mActivityScenario != null) {
+ mActivityScenario.close();
+ }
}
@Test
- public void behaviorBeingTested_expectedResult() {
- // Sample test
- Context context =
- InstrumentationRegistry.getInstrumentation().getTargetContext();
- assertThat(context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE)).isTrue();
- assertThat(context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_INPUT_METHODS)).isTrue();
+ public void driverShowImeNotAffectPassenger() {
+ assertDriverImeHidden();
+ assertPassengerImeHidden();
+
+ showDriverImeAndAssert();
+ assertPassengerImeHidden();
+ }
+
+ private void assertDriverImeHidden() {
+ assertWithMessage("Driver IME should be hidden")
+ .that(mActivity.isMyImeVisible()).isFalse();
+ }
+
+ private void assertPassengerImeHidden() {
+ final Bundle bundleToSend = new Bundle();
+ bundleToSend.putInt(KEY_REQUEST_CODE, REQUEST_IME_STATUS);
+ Bundle receivedBundle = sendBundleAndWaitForReply(TEST_ACTIVITY.getPackageName(),
+ mPeerUserId, bundleToSend);
+ assertWithMessage("Passenger IME should be hidden")
+ .that(receivedBundle.getInt(KEY_RESULT_CODE)).isEqualTo(REPLY_IME_HIDDEN);
+ }
+
+ private void showDriverImeAndAssert() {
+ mActivity.showMyImeAndWait();
}
}
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/MainActivity.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/MainActivity.java
new file mode 100644
index 0000000..e3f84c1
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/MainActivity.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 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.android.server.inputmethod.multisessiontest;
+
+import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.KEY_REQUEST_CODE;
+import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.KEY_RESULT_CODE;
+import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.REPLY_IME_HIDDEN;
+import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.REPLY_IME_SHOWN;
+import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.REQUEST_IME_STATUS;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.concurrentuser.ConcurrentUserActivityBase;
+
+/**
+ * An {@link Activity} to test multiple concurrent session IME.
+ */
+public final class MainActivity extends ConcurrentUserActivityBase {
+ private static final String TAG = ConcurrentMultiUserTest.class.getSimpleName();
+ private static final long WAIT_IME_TIMEOUT_MS = 3000;
+
+ private EditText mEditor;
+ private InputMethodManager mImm;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.v(TAG, "Create MainActivity as user " + getUserId() + " on display "
+ + getDisplayId());
+ setContentView(R.layout.main_activity);
+ mImm = getSystemService(InputMethodManager.class);
+ mEditor = requireViewById(R.id.edit_text);
+ }
+
+ @Override
+ protected Bundle onBundleReceived(Bundle receivedBundle) {
+ final int requestCode = receivedBundle.getInt(KEY_REQUEST_CODE);
+ Log.v(TAG, "onBundleReceived() with request code:" + requestCode);
+ final Bundle replyBundle = new Bundle();
+ switch (requestCode) {
+ case REQUEST_IME_STATUS:
+ replyBundle.putInt(KEY_RESULT_CODE,
+ isMyImeVisible() ? REPLY_IME_SHOWN : REPLY_IME_HIDDEN);
+ break;
+ default:
+ throw new RuntimeException("Received undefined request code:" + requestCode);
+ }
+ return replyBundle;
+ }
+
+ boolean isMyImeVisible() {
+ final WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(mEditor);
+ return insets == null ? false : insets.isVisible(WindowInsetsCompat.Type.ime());
+ }
+
+ void showMyImeAndWait() {
+ Log.v(TAG, "showSoftInput");
+ runOnUiThread(() -> {
+ // requestFocus() must run on UI thread.
+ if (!mEditor.requestFocus()) {
+ Log.e(TAG, "Failed to focus on mEditor");
+ return;
+ }
+ if (!mImm.showSoftInput(mEditor, /* flags= */ 0)) {
+ Log.e(TAG, String.format("Failed to show my IME as user %d, "
+ + "mEditor:focused=%b,hasWindowFocus=%b", getUserId(),
+ mEditor.isFocused(), mEditor.hasWindowFocus()));
+ }
+ });
+ PollingCheck.waitFor(WAIT_IME_TIMEOUT_MS, () -> isMyImeVisible(),
+ String.format("My IME (user %d) didn't show up", getUserId()));
+ }
+}
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/TestRequestConstants.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/TestRequestConstants.java
new file mode 100644
index 0000000..1501bfb
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/TestRequestConstants.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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.android.server.inputmethod.multisessiontest;
+
+final class TestRequestConstants {
+ private TestRequestConstants() {
+ }
+
+ public static final String KEY_REQUEST_CODE = "key_request_code";
+ public static final String KEY_RESULT_CODE = "key_result_code";
+
+ public static final int REQUEST_IME_STATUS = 1;
+ public static final int REPLY_IME_SHOWN = 2;
+ public static final int REPLY_IME_HIDDEN = 3;
+}