1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.appenumeration.cts;
18 
19 import static android.appenumeration.cts.Constants.ACTION_GET_ENABLED_INPUT_METHOD_LIST;
20 import static android.appenumeration.cts.Constants.ACTION_GET_ENABLED_INPUT_METHOD_SUBTYPE_LIST;
21 import static android.appenumeration.cts.Constants.ACTION_GET_INPUT_METHOD_LIST;
22 import static android.appenumeration.cts.Constants.CTS_MOCK_IME_APK;
23 import static android.appenumeration.cts.Constants.EXTRA_INPUT_METHOD_INFO;
24 import static android.appenumeration.cts.Constants.MOCK_IME_PKG;
25 import static android.appenumeration.cts.Constants.QUERIES_NOTHING;
26 import static android.appenumeration.cts.Constants.QUERIES_PACKAGE;
27 import static android.appenumeration.cts.Utils.installPackage;
28 import static android.appenumeration.cts.Utils.uninstallPackage;
29 import static android.content.Intent.EXTRA_RETURN_RESULT;
30 
31 import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
32 
33 import static org.hamcrest.MatcherAssert.assertThat;
34 import static org.hamcrest.core.IsNull.notNullValue;
35 
36 import android.content.ComponentName;
37 import android.os.Bundle;
38 import android.os.Process;
39 import android.view.inputmethod.InputMethodInfo;
40 import android.view.inputmethod.InputMethodManager;
41 import android.view.inputmethod.InputMethodSubtype;
42 
43 import androidx.test.ext.junit.runners.AndroidJUnit4;
44 
45 import com.android.compatibility.common.util.PollingCheck;
46 
47 import org.junit.AfterClass;
48 import org.junit.BeforeClass;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 
52 import java.util.List;
53 
54 @RunWith(AndroidJUnit4.class)
55 public class InputMethodEnumerationTests extends AppEnumerationTestsBase {
56     private static final String MOCK_IME_ID =
57             ComponentName.createRelative(MOCK_IME_PKG, ".CtsInputMethod1").flattenToShortString();
58 
59     @BeforeClass
prepareInputMethod()60     public static void prepareInputMethod() throws Exception {
61         installPackage(CTS_MOCK_IME_APK);
62         PollingCheck.check("Failed to wait for " + MOCK_IME_PKG
63                 + " getting ready", DEFAULT_TIMEOUT_MS, () -> hasInputMethod(MOCK_IME_PKG));
64 
65         runShellCommand(enableIme(MOCK_IME_ID, myUserId()));
66         PollingCheck.check("Failed to wait for " + MOCK_IME_PKG
67                 + " enabled", DEFAULT_TIMEOUT_MS, () -> hasEnabledInputMethod(MOCK_IME_PKG));
68     }
69 
70     @AfterClass
cleanUp()71     public static void cleanUp() throws Exception {
72         runShellCommand(disableIme(MOCK_IME_ID, myUserId()));
73         uninstallPackage(MOCK_IME_PKG);
74     }
75 
76     @Test
queriesNothing_getInputMethodList_cannotSeeMockIme()77     public void queriesNothing_getInputMethodList_cannotSeeMockIme() throws Exception {
78         assertNotVisible(QUERIES_NOTHING, MOCK_IME_PKG, this::getInputMethodList);
79     }
80 
81     @Test
queriesPackage_getInputMethodList_canSeeMockIme()82     public void queriesPackage_getInputMethodList_canSeeMockIme() throws Exception {
83         assertVisible(QUERIES_PACKAGE, MOCK_IME_PKG, this::getInputMethodList);
84     }
85 
86     @Test
queriesNothing_getEnabledInputMethodList_cannotSeeMockIme()87     public void queriesNothing_getEnabledInputMethodList_cannotSeeMockIme() throws Exception {
88         assertNotVisible(QUERIES_NOTHING, MOCK_IME_PKG, this::getEnabledInputMethodList);
89     }
90 
91     @Test
queriesPackage_getEnabledInputMethodList_canSeeMockIme()92     public void queriesPackage_getEnabledInputMethodList_canSeeMockIme() throws Exception {
93         assertVisible(QUERIES_PACKAGE, MOCK_IME_PKG, this::getEnabledInputMethodList);
94     }
95 
96     @Test
queriesNothing_getEnabledInputMethodSubtypeList_cannotSeeMockIme()97     public void queriesNothing_getEnabledInputMethodSubtypeList_cannotSeeMockIme()
98             throws Exception {
99         assertNotVisible(QUERIES_NOTHING, MOCK_IME_PKG, this::getEnabledInputMethodSubtypeList);
100     }
101 
102     @Test
queriesPackage_getEnabledInputMethodSubtypeList_canSeeMockIme()103     public void queriesPackage_getEnabledInputMethodSubtypeList_canSeeMockIme() throws Exception {
104         assertVisible(QUERIES_PACKAGE, MOCK_IME_PKG, this::getEnabledInputMethodSubtypeList);
105     }
106 
getInputMethodList(String sourcePackage)107     private String[] getInputMethodList(String sourcePackage) throws Exception {
108         final Bundle response = sendCommandBlocking(sourcePackage, null /* targetPackageName */,
109                 null /* intentExtra */, ACTION_GET_INPUT_METHOD_LIST);
110         final List<InputMethodInfo> infos =
111                 response.getParcelableArrayList(EXTRA_RETURN_RESULT, InputMethodInfo.class);
112         return infos.stream()
113                 .map(info -> info.getPackageName())
114                 .distinct()
115                 .toArray(String[]::new);
116     }
117 
getEnabledInputMethodList(String sourcePackage)118     private String[] getEnabledInputMethodList(String sourcePackage) throws Exception {
119         final Bundle response = sendCommandBlocking(sourcePackage, null /* targetPackageName */,
120                 null /* intentExtra */, ACTION_GET_ENABLED_INPUT_METHOD_LIST);
121         final List<InputMethodInfo> infos =
122                 response.getParcelableArrayList(EXTRA_RETURN_RESULT, InputMethodInfo.class);
123         return infos.stream()
124                 .map(info -> info.getPackageName())
125                 .distinct()
126                 .toArray(String[]::new);
127     }
128 
getEnabledInputMethodSubtypeList(String sourcePackage, String targetPackage)129     private String[] getEnabledInputMethodSubtypeList(String sourcePackage, String targetPackage)
130             throws Exception {
131         final InputMethodInfo imi = getInputMethod(targetPackage);
132         assertThat(imi, notNullValue());
133         final Bundle extras = new Bundle();
134         extras.putParcelable(EXTRA_INPUT_METHOD_INFO, imi);
135         final Bundle response = sendCommandBlocking(sourcePackage, null /* targetPackageName */,
136                 extras, ACTION_GET_ENABLED_INPUT_METHOD_SUBTYPE_LIST);
137         final List<InputMethodSubtype> infos =
138                 response.getParcelableArrayList(EXTRA_RETURN_RESULT, InputMethodSubtype.class);
139         return !infos.isEmpty() ? new String[]{targetPackage} : new String[0];
140     }
141 
hasInputMethod(String packageName)142     private static boolean hasInputMethod(String packageName) {
143         final List<InputMethodInfo> inputMethods =
144                 sContext.getSystemService(InputMethodManager.class).getInputMethodList();
145         return inputMethods.stream()
146                 .anyMatch(info -> info.getPackageName().equals(packageName));
147     }
148 
hasEnabledInputMethod(String packageName)149     private static boolean hasEnabledInputMethod(String packageName) {
150         final List<InputMethodInfo> inputMethods =
151                 sContext.getSystemService(InputMethodManager.class).getEnabledInputMethodList();
152         return inputMethods.stream()
153                 .anyMatch(info -> info.getPackageName().equals(packageName));
154     }
155 
getInputMethod(String packageName)156     private static InputMethodInfo getInputMethod(String packageName) {
157         final List<InputMethodInfo> inputMethods =
158                 sContext.getSystemService(InputMethodManager.class).getInputMethodList();
159         return inputMethods.stream().filter(imi -> imi.getPackageName().equals(packageName))
160                 .findFirst().orElse(null);
161     }
162 
myUserId()163     private static int myUserId() {
164         return Process.myUserHandle().getIdentifier();
165     }
166 
enableIme(String imeId, int userId)167     private static String enableIme(String imeId, int userId) {
168         return String.format("ime enable --user %d %s", userId, imeId);
169     }
170 
disableIme(String imeId, int userId)171     private static String disableIme(String imeId, int userId) {
172         return String.format("ime disable --user %d %s", userId, imeId);
173     }
174 }
175