1 /* 2 * Copyright (C) 2023 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 com.android.server.inputmethod; 18 19 import static com.google.common.truth.Truth.assertWithMessage; 20 21 import static org.junit.Assert.assertEquals; 22 23 import android.content.ComponentName; 24 import android.content.Intent; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.ResolveInfo; 28 import android.util.ArrayMap; 29 import android.view.inputmethod.InputMethod; 30 import android.view.inputmethod.InputMethodInfo; 31 32 import androidx.test.ext.junit.runners.AndroidJUnit4; 33 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 37 import java.util.ArrayList; 38 import java.util.List; 39 40 @RunWith(AndroidJUnit4.class) 41 public class InputMethodManagerServiceRestrictImeAmountTest extends 42 InputMethodManagerServiceTestBase { 43 44 @Test testFilterInputMethodServices_loadsAllImesBelowThreshold()45 public void testFilterInputMethodServices_loadsAllImesBelowThreshold() { 46 List<ResolveInfo> resolveInfoList = new ArrayList<>(); 47 for (int i = 0; i < 5; i++) { 48 resolveInfoList.add( 49 createFakeResolveInfo("com.android.apps.inputmethod.simpleime", "IME" + i)); 50 } 51 52 final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList, 53 List.of()); 54 assertEquals(5, methodList.size()); 55 } 56 57 @Test testFilterInputMethodServices_ignoresImesBeyondThreshold()58 public void testFilterInputMethodServices_ignoresImesBeyondThreshold() { 59 List<ResolveInfo> resolveInfoList = new ArrayList<>(); 60 for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) { 61 resolveInfoList.add( 62 createFakeResolveInfo("com.android.apps.inputmethod.simpleime", "IME" + i)); 63 } 64 65 final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList, 66 List.of()); 67 assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo( 68 InputMethodInfo.MAX_IMES_PER_PACKAGE); 69 } 70 71 @Test testFilterInputMethodServices_loadsSystemImesBeyondThreshold()72 public void testFilterInputMethodServices_loadsSystemImesBeyondThreshold() { 73 List<ResolveInfo> resolveInfoList = new ArrayList<>(); 74 for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) { 75 resolveInfoList.add( 76 createFakeSystemResolveInfo("com.android.apps.inputmethod.systemime", 77 "SystemIME" + i)); 78 } 79 80 final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList, 81 List.of()); 82 assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo( 83 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE); 84 } 85 86 @Test testFilterInputMethodServices_ignoresImesBeyondThresholdFromTwoPackages()87 public void testFilterInputMethodServices_ignoresImesBeyondThresholdFromTwoPackages() { 88 List<ResolveInfo> resolveInfoList = new ArrayList<>(); 89 for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) { 90 resolveInfoList.add( 91 createFakeResolveInfo("com.android.apps.inputmethod.simpleime1", "IME1_" + i)); 92 } 93 for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) { 94 resolveInfoList.add( 95 createFakeResolveInfo("com.android.apps.inputmethod.simpleime2", "IME2_" + i)); 96 } 97 98 final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList, 99 List.of()); 100 assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo( 101 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE); 102 } 103 104 @Test testFilterInputMethodServices_stillLoadsEnabledImesBeyondThreshold()105 public void testFilterInputMethodServices_stillLoadsEnabledImesBeyondThreshold() { 106 final ResolveInfo enabledIme = createFakeResolveInfo( 107 "com.android.apps.inputmethod.simpleime_enabled", "EnabledIME"); 108 109 List<ResolveInfo> resolveInfoList = new ArrayList<>(); 110 for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) { 111 resolveInfoList.add( 112 createFakeResolveInfo("com.android.apps.inputmethod.simpleime", "IME" + i)); 113 } 114 resolveInfoList.add(enabledIme); 115 116 final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList, 117 List.of(new ComponentName(enabledIme.serviceInfo.packageName, 118 enabledIme.serviceInfo.name).flattenToShortString())); 119 120 assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo( 121 1 + InputMethodInfo.MAX_IMES_PER_PACKAGE); 122 } 123 filterInputMethodServices(List<ResolveInfo> resolveInfoList, List<String> enabledComponents)124 private List<InputMethodInfo> filterInputMethodServices(List<ResolveInfo> resolveInfoList, 125 List<String> enabledComponents) { 126 final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>(); 127 final ArrayList<InputMethodInfo> methodList = new ArrayList<>(); 128 InputMethodManagerService.filterInputMethodServices(new ArrayMap<>(), methodMap, methodList, 129 enabledComponents, mContext, resolveInfoList); 130 return methodList; 131 } 132 createFakeSystemResolveInfo(String packageName, String componentName)133 private ResolveInfo createFakeSystemResolveInfo(String packageName, String componentName) { 134 final ResolveInfo ime = createFakeResolveInfo(packageName, componentName); 135 ime.serviceInfo.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; 136 return ime; 137 } 138 createFakeResolveInfo(String packageName, String componentName)139 private ResolveInfo createFakeResolveInfo(String packageName, String componentName) { 140 final ResolveInfo ime = getResolveInfo("com.android.apps.inputmethod.simpleime"); 141 if (packageName != null) { 142 ime.serviceInfo.packageName = packageName; 143 } 144 if (componentName != null) { 145 ime.serviceInfo.name = componentName; 146 } 147 return ime; 148 } 149 getResolveInfo(String packageName)150 private ResolveInfo getResolveInfo(String packageName) { 151 final int flags = PackageManager.GET_META_DATA 152 | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; 153 final List<ResolveInfo> ime = mContext.getPackageManager().queryIntentServices( 154 new Intent(InputMethod.SERVICE_INTERFACE).setPackage(packageName), 155 PackageManager.ResolveInfoFlags.of(flags)); 156 assertWithMessage("Loaded IMEs").that(ime.size()).isGreaterThan(0); 157 return ime.get(0); 158 } 159 } 160