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