1. 目的
输入法应用属于可识别类型且核心应用,不能进行拦截和kill
2. 展讯输入法判断
2.1 判断逻辑块
//handle inputmethod
if (isLaunchingIMEApp(intent, targetApp, targetUid, callerApp, reason)) {
return true;
}
2.2 判断是否为输入法
private boolean isLaunchingIMEApp(Intent intent, String targetApp, int targetUid,
String callerApp, String reason) {
//handle inputmethod
// 是否满足以下条件
// 1. caller 为系统发起
// 2. 启动类型为bind-services
// 3. action为输入法类型
if ("android".equals(callerApp)
&& REASON_BIND_SERVICE.equals(reason)
&& intent != null && "android.view.InputMethod".equals(intent.getAction())) {
// 判断为输入法应用
if (targetApp != null) { // a input method
// 如果未保存到当前可用输入法,则存入mEnabledInputMethodAppList列表中
int index = mEnabledInputMethodAppList.indexOf(targetApp);
if (index < 0) {
mEnabledInputMethodAppList.add(targetApp);
// 多用户模式下,保存输入法信息到对应列表中
int userId = UserHandle.getUserId(targetUid);
mAppStateInfoCollector.updateAppInputMethodState(targetApp, true, userId);
mAppStateInfoCollector.setDefaultInputMethodApp(targetApp, userId);
}
}
return true;
}
// allow to start input Method
// 是否满足下面条件
// 1. 启动类型为bind-services
// 2. caller 为系统发起
// 3. 该进程属于输入法列表
if (REASON_BIND_SERVICE.equals(reason) && "android".equals(callerApp) && isInputMethodApp(targetApp)) {
if (DEBUG) Slog.d(TAG, "isLaunchingIMEApp: "+targetApp
+ ", callingPackage = "+callerApp+", reason = "+reason +" allow for input method");
return true;
}
return false;
}
// input method app
private boolean isInputMethodApp(String pkgName) {
int index = mEnabledInputMethodAppList.indexOf(pkgName);
if (index >= 0) {
return true;
}
return false;
}
2.3 AppStateInfoCollector.updateAppInputMethodState
更新对应进程状态中,应用状态mIsEnabledInputMethod为输入法应用
// update the Input Method identify state of this app
public void updateAppInputMethodState(String pkgName, boolean isInputMethod, int userId) {
ArrayMap<String, AppState> mAppStateInfoList = getAppStateInfoList(userId);
int index = mAppStateInfoList.indexOfKey(pkgName);
if (index >= 0) {
AppState appState = mAppStateInfoList.valueAt(index);
appState.mIsEnabledInputMethod = isInputMethod;
}
}
2.4 AppStateInfoCollector.setDefaultInputMethodApp
更新为默认输入法
// update the Input Method identify state of this app
public void setDefaultInputMethodApp(String pkgName, int userId) {
ArrayMap<String, AppState> mAppStateInfoList = getAppStateInfoList(userId);
String mDefaultIMEAppName = mDefaultIMEAppNameForUsers.get(userId);
// 新值与默认输入法一致,则不需要重复更新
if (mDefaultIMEAppName != null
&& mDefaultIMEAppName.equals(pkgName)) {
return;
}
// 旧的默认输入法更新状态
// clear orignal
if (mDefaultIMEAppName != null) {
int index = mAppStateInfoList.indexOfKey(mDefaultIMEAppName);
if (index >= 0) {
AppState appState = mAppStateInfoList.valueAt(index);
appState.mIsDefaultInputMethod = false;
}
}
// set new
// 新值设置为默认输入法即当前使用的输入法
int index = mAppStateInfoList.indexOfKey(pkgName);
if (index >= 0) {
AppState appState = mAppStateInfoList.valueAt(index);
appState.mIsDefaultInputMethod = true;
}
mDefaultIMEAppName = pkgName;
mDefaultIMEAppNameForUsers.put(userId, mDefaultIMEAppName);
}
2.5 APPstate创建中会进行判断是否为输入法
// check if is input method
retVal.mIsEnabledInputMethod = isEnabledIMEApp(packageName);
// if this app is a input Method
private boolean isEnabledIMEApp(String pkgName){
if (pkgName == null) return false;
IInputMethodManager service = IInputMethodManager.Stub.asInterface(
ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
List<InputMethodInfo> inputMethods;
try {
inputMethods = service.getEnabledInputMethodList();
} catch (RemoteException e) {
return false;
}
if (inputMethods == null || inputMethods.size() == 0) return false;
for (InputMethodInfo info : inputMethods){
if (info == null || info.getPackageName() == null) continue;
if (info.getPackageName().equals(pkgName)) return true;
}
return false;
}
3. 应用上层的输入法判断
3.1 判断是否为默认输入法,或者正在使用的当前输入法
public static String getInputPackage(Context mContext) {
String result = null;
String input = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
if (input == null || input.equals("") || !input.contains("/")) {
// frameworks/base/packages/SettingsProvider/res/values/defaults.xml, default_input_method
} else {
result = input.substring(0, input.indexOf('/'));
}
Log.d(TAG, "getInputPackage = " + result);
return result;
}
3.2 根据AndroidManifest进行判断是否为输入法
public static boolean isInputMethodApp(Context context, String packageName) {
PackageManager pm = context.getPackageManager();
boolean isInputMethodApp = false;
try {
PackageInfo pkgInfo = pm.getPackageInfo(packageName, PackageManager.GET_SERVICES);
ServiceInfo[] sInfo = pkgInfo.services;
if (sInfo != null) {
for (int i = 0; i < sInfo.length; i++) {
ServiceInfo serviceInfo = sInfo[i];
if (serviceInfo.permission != null && serviceInfo.permission.equals("android.permission.BIND_INPUT_METHOD")) {
isInputMethodApp = true;
break;
}
}
}
} catch (NameNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return isInputMethodApp;
}
3.3 根据 InputMethodManager 进行获取输入法列表
public static ArrayList<String> getInputMethodAppList(Context context) {
ArrayList<String> list = new ArrayList<String>();
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
List<InputMethodInfo> methodList = imm.getInputMethodList();
List<String> mInputMethodBlackList = getInputMethodBlackList(context);
if (methodList != null) {
for (InputMethodInfo mi : methodList) {
String inputMethodPackageName = mi.getPackageName();
if (!mInputMethodBlackList.contains(inputMethodPackageName)) {
list.add(inputMethodPackageName);
}
}
}
Log.d(TAG, "getInputMethodAppList = " + list.toString());
return list;
}