1 /* 2 * Copyright (C) 2008 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.view.inputmethod.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 import static org.mockito.Matchers.anyString; 23 import static org.mockito.Mockito.atLeastOnce; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.verify; 26 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.pm.ApplicationInfo; 31 import android.content.pm.PackageManager; 32 import android.content.pm.ResolveInfo; 33 import android.content.pm.ServiceInfo; 34 import android.os.Parcel; 35 import android.os.ParcelFileDescriptor; 36 import android.text.TextUtils; 37 import android.util.Printer; 38 import android.view.inputmethod.InputMethod; 39 import android.view.inputmethod.InputMethodInfo; 40 import android.view.inputmethod.InputMethodManager; 41 import android.view.inputmethod.InputMethodSubtype; 42 43 import androidx.test.InstrumentationRegistry; 44 import androidx.test.filters.SmallTest; 45 import androidx.test.runner.AndroidJUnit4; 46 47 import org.junit.Before; 48 import org.junit.Test; 49 import org.junit.runner.RunWith; 50 import org.xmlpull.v1.XmlPullParserException; 51 52 import java.io.BufferedReader; 53 import java.io.IOException; 54 import java.io.InputStreamReader; 55 import java.nio.charset.StandardCharsets; 56 import java.util.List; 57 58 @SmallTest 59 @RunWith(AndroidJUnit4.class) 60 public class InputMethodInfoTest { 61 private Context mContext; 62 63 private InputMethodInfo mInputMethodInfo; 64 private String mPackageName; 65 private String mClassName; 66 private CharSequence mLabel; 67 private String mSettingsActivity; 68 69 private int mSubtypeNameResId; 70 private int mSubtypeIconResId; 71 private String mSubtypeLocale; 72 private String mSubtypeMode; 73 private String mSubtypeExtraValueKey; 74 private String mSubtypeExtraValueValue; 75 private String mSubtypeExtraValue; 76 private boolean mSubtypeIsAuxiliary; 77 private boolean mSubtypeOverridesImplicitlyEnabledSubtype; 78 private int mSubtypeId; 79 private InputMethodSubtype mInputMethodSubtype; 80 81 @Before 82 public void setup() { 83 mContext = InstrumentationRegistry.getTargetContext(); 84 mPackageName = mContext.getPackageName(); 85 mClassName = InputMethodSettingsActivityStub.class.getName(); 86 mLabel = "test"; 87 mSettingsActivity = "android.view.inputmethod.cts.InputMethodSettingsActivityStub"; 88 mInputMethodInfo = new InputMethodInfo(mPackageName, mClassName, mLabel, mSettingsActivity); 89 90 mSubtypeNameResId = 0; 91 mSubtypeIconResId = 0; 92 mSubtypeLocale = "en_US"; 93 mSubtypeMode = "keyboard"; 94 mSubtypeExtraValueKey = "key1"; 95 mSubtypeExtraValueValue = "value1"; 96 mSubtypeExtraValue = "tag," + mSubtypeExtraValueKey + "=" + mSubtypeExtraValueValue; 97 mSubtypeIsAuxiliary = false; 98 mSubtypeOverridesImplicitlyEnabledSubtype = false; 99 mSubtypeId = 99; 100 mInputMethodSubtype = new InputMethodSubtype(mSubtypeNameResId, mSubtypeIconResId, 101 mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue, mSubtypeIsAuxiliary, 102 mSubtypeOverridesImplicitlyEnabledSubtype, mSubtypeId); 103 } 104 105 @Test 106 public void testInputMethodInfoProperties() throws XmlPullParserException, IOException { 107 assertEquals(0, mInputMethodInfo.describeContents()); 108 assertNotNull(mInputMethodInfo.toString()); 109 110 assertInfo(mInputMethodInfo); 111 assertEquals(0, mInputMethodInfo.getIsDefaultResourceId()); 112 113 Intent intent = new Intent(InputMethod.SERVICE_INTERFACE); 114 intent.setClass(mContext, InputMethodSettingsActivityStub.class); 115 PackageManager pm = mContext.getPackageManager(); 116 List<ResolveInfo> ris = pm.queryIntentServices(intent, PackageManager.GET_META_DATA); 117 for (int i = 0; i < ris.size(); i++) { 118 ResolveInfo resolveInfo = ris.get(i); 119 mInputMethodInfo = new InputMethodInfo(mContext, resolveInfo); 120 assertService(resolveInfo.serviceInfo, mInputMethodInfo.getServiceInfo()); 121 assertInfo(mInputMethodInfo); 122 } 123 } 124 125 @Test 126 public void testInputMethodSubtypeProperties() { 127 // TODO: Test InputMethodSubtype.getDisplayName() 128 assertEquals(mSubtypeNameResId, mInputMethodSubtype.getNameResId()); 129 assertEquals(mSubtypeIconResId, mInputMethodSubtype.getIconResId()); 130 assertEquals(mSubtypeLocale, mInputMethodSubtype.getLocale()); 131 assertEquals(mSubtypeMode, mInputMethodSubtype.getMode()); 132 assertEquals(mSubtypeExtraValue, mInputMethodSubtype.getExtraValue()); 133 assertTrue(mInputMethodSubtype.containsExtraValueKey(mSubtypeExtraValueKey)); 134 assertEquals(mSubtypeExtraValueValue, 135 mInputMethodSubtype.getExtraValueOf(mSubtypeExtraValueKey)); 136 assertEquals(mSubtypeIsAuxiliary, mInputMethodSubtype.isAuxiliary()); 137 assertEquals(mSubtypeOverridesImplicitlyEnabledSubtype, 138 mInputMethodSubtype.overridesImplicitlyEnabledSubtype()); 139 assertEquals(mSubtypeId, mInputMethodSubtype.hashCode()); 140 } 141 142 private void assertService(ServiceInfo expected, ServiceInfo actual) { 143 assertEquals(expected.getIconResource(), actual.getIconResource()); 144 assertEquals(expected.labelRes, actual.labelRes); 145 assertEquals(expected.nonLocalizedLabel, actual.nonLocalizedLabel); 146 assertEquals(expected.icon, actual.icon); 147 assertEquals(expected.permission, actual.permission); 148 } 149 150 private void assertInfo(InputMethodInfo info) { 151 assertEquals(mPackageName, info.getPackageName()); 152 assertEquals(mSettingsActivity, info.getSettingsActivity()); 153 ComponentName component = info.getComponent(); 154 assertEquals(mClassName, component.getClassName()); 155 String expectedId = component.flattenToShortString(); 156 assertEquals(expectedId, info.getId()); 157 assertEquals(mClassName, info.getServiceName()); 158 } 159 160 @Test 161 public void testDump() { 162 Printer printer = mock(Printer.class); 163 String prefix = "test"; 164 mInputMethodInfo.dump(printer, prefix); 165 verify(printer, atLeastOnce()).println(anyString()); 166 } 167 168 @Test 169 public void testLoadIcon() { 170 PackageManager pm = mContext.getPackageManager(); 171 assertNotNull(mInputMethodInfo.loadIcon(pm)); 172 } 173 174 @Test 175 public void testEquals() { 176 InputMethodInfo inputMethodInfo = new InputMethodInfo(mPackageName, mClassName, mLabel, 177 mSettingsActivity); 178 assertTrue(inputMethodInfo.equals(mInputMethodInfo)); 179 } 180 181 @Test 182 public void testLoadLabel() { 183 CharSequence expected = "test"; 184 PackageManager pm = mContext.getPackageManager(); 185 assertEquals(expected.toString(), mInputMethodInfo.loadLabel(pm).toString()); 186 } 187 188 @Test 189 public void testInputMethodInfoWriteToParcel() { 190 final Parcel p = Parcel.obtain(); 191 mInputMethodInfo.writeToParcel(p, 0); 192 p.setDataPosition(0); 193 final InputMethodInfo imi = InputMethodInfo.CREATOR.createFromParcel(p); 194 p.recycle(); 195 196 assertEquals(mInputMethodInfo.getPackageName(), imi.getPackageName()); 197 assertEquals(mInputMethodInfo.getServiceName(), imi.getServiceName()); 198 assertEquals(mInputMethodInfo.getSettingsActivity(), imi.getSettingsActivity()); 199 assertEquals(mInputMethodInfo.getId(), imi.getId()); 200 assertEquals(mInputMethodInfo.getIsDefaultResourceId(), imi.getIsDefaultResourceId()); 201 assertService(mInputMethodInfo.getServiceInfo(), imi.getServiceInfo()); 202 } 203 204 @Test 205 public void testInputMethodSubtypeWriteToParcel() { 206 final Parcel p = Parcel.obtain(); 207 mInputMethodSubtype.writeToParcel(p, 0); 208 p.setDataPosition(0); 209 final InputMethodSubtype subtype = InputMethodSubtype.CREATOR.createFromParcel(p); 210 p.recycle(); 211 212 assertEquals(mInputMethodSubtype.containsExtraValueKey(mSubtypeExtraValueKey), 213 subtype.containsExtraValueKey(mSubtypeExtraValueKey)); 214 assertEquals(mInputMethodSubtype.getExtraValue(), subtype.getExtraValue()); 215 assertEquals(mInputMethodSubtype.getExtraValueOf(mSubtypeExtraValueKey), 216 subtype.getExtraValueOf(mSubtypeExtraValueKey)); 217 assertEquals(mInputMethodSubtype.getIconResId(), subtype.getIconResId()); 218 assertEquals(mInputMethodSubtype.getLocale(), subtype.getLocale()); 219 assertEquals(mInputMethodSubtype.getMode(), subtype.getMode()); 220 assertEquals(mInputMethodSubtype.getNameResId(), subtype.getNameResId()); 221 assertEquals(mInputMethodSubtype.hashCode(), subtype.hashCode()); 222 assertEquals(mInputMethodSubtype.isAuxiliary(), subtype.isAuxiliary()); 223 assertEquals(mInputMethodSubtype.overridesImplicitlyEnabledSubtype(), 224 subtype.overridesImplicitlyEnabledSubtype()); 225 } 226 227 @Test 228 public void testAtLeastOneEncryptionAwareInputMethodIsAvailable() { 229 if (!mContext.getPackageManager().hasSystemFeature( 230 PackageManager.FEATURE_INPUT_METHODS)) { 231 return; 232 } 233 234 if (!TextUtils.equals("native", getFbeMode())) { 235 // Skip the test unless the device is in native FBE mode. 236 return; 237 } 238 239 final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); 240 final List<InputMethodInfo> imis = imm.getInputMethodList(); 241 boolean hasEncryptionAwareInputMethod = false; 242 for (final InputMethodInfo imi : imis) { 243 final ServiceInfo serviceInfo = imi.getServiceInfo(); 244 if (serviceInfo == null) { 245 continue; 246 } 247 if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 248 != ApplicationInfo.FLAG_SYSTEM) { 249 continue; 250 } 251 if (serviceInfo.directBootAware) { 252 hasEncryptionAwareInputMethod = true; 253 break; 254 } 255 } 256 assertTrue(hasEncryptionAwareInputMethod); 257 } 258 259 private String getFbeMode() { 260 try (ParcelFileDescriptor.AutoCloseInputStream in = 261 new ParcelFileDescriptor.AutoCloseInputStream(InstrumentationRegistry 262 .getInstrumentation() 263 .getUiAutomation() 264 .executeShellCommand("sm get-fbe-mode"))) { 265 try (BufferedReader br = 266 new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { 267 // Assume that the output of "sm get-fbe-mode" is always one-line. 268 final String line = br.readLine(); 269 return line != null ? line.trim() : ""; 270 } 271 } catch (IOException e) { 272 return ""; 273 } 274 } 275 } 276