平台
RK3288 + Android 5.1
问题
1. 系统初始版本为1.0.0, 包含了默认输入法 LatinIME(拉丁输入法) 和 OpenWnn(日文输入法) 2. OTA升级中, 内置讯飞输入法, 同时删除1.0.0版本中的两个输入法. 3. OTA升级后, 文本输入框无法正常调起输入法软键盘执行输入. 4. 打开设置 > 语言和输入法 > 键盘和输入法中, 当前输入法为空, 输入法中也没有看到 讯飞输入法
分析
Logcat 分析:
01-02 03:14:58.469 system_process W/InputMethodManagerService: Couldn't create dir.: /data/system/inputmethod 01-02 03:14:58.479 system_process W/InputMethodManagerService: Default IME is uninstalled. Choose new default IME. 01-02 03:14:58.480 system_process W/InputMethodManagerService: Unknown input method from prefs: com.android.inputmethod.latin/.LatinIME java.lang.IllegalArgumentException: Unknown id: com.android.inputmethod.latin/.LatinIME at com.android.server.InputMethodManagerService.setInputMethodLocked(InputMethodManagerService.java:1806) at com.android.server.InputMethodManagerService.updateInputMethodsFromSettingsLocked(InputMethodManagerService.java:1756) at com.android.server.InputMethodManagerService.updateFromSettingsLocked(InputMethodManagerService.java:1715) at com.android.server.InputMethodManagerService.<init>(InputMethodManagerService.java:744) at com.android.server.SystemServer.startOtherServices(SystemServer.java:553) at com.android.server.SystemServer.run(SystemServer.java:261) at com.android.server.SystemServer.main(SystemServer.java:175) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:963) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:758)
相关代码如下:
|--frameworks/base/services/core/java/com/android/server/InputMethodManagerService.java void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) { if (enabledMayChange) { List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked(); for (int i=0; i<enabled.size(); i++) { // We allow the user to select "disabled until used" apps, so if they // are enabling one of those here we now need to make it enabled. InputMethodInfo imm = enabled.get(i); try { ApplicationInfo ai = mIPackageManager.getApplicationInfo(imm.getPackageName(), PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, mSettings.getCurrentUserId()); if (ai != null && ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { if (DEBUG) { Slog.d(TAG, "Update state(" + imm.getId() + "): DISABLED_UNTIL_USED -> DEFAULT"); } mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(), PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId(), mContext.getBasePackageName()); } } catch (RemoteException e) { } } } // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and // ENABLED_INPUT_METHODS is taking care of keeping them correctly in // sync, so we will never have a DEFAULT_INPUT_METHOD that is not // enabled. String id = mSettings.getSelectedInputMethod(); // There is no input method selected, try to choose new applicable input method. if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) { id = mSettings.getSelectedInputMethod(); } if (!TextUtils.isEmpty(id)) { try { setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id)); mPreScanHelper.addScanItem(PreScanHelper.SCAN_TYPE_IME, mContext, id); mPreScanHelper.flushToFile(); } catch (IllegalArgumentException e) { //LOG输出的地方. Slog.w(TAG, "Unknown input method from prefs: " + id, e); mCurMethodId = null; unbindCurrentMethodLocked(true, false); } mShortcutInputMethodsAndSubtypes.clear(); } else { // There is no longer an input method set, so stop any current one. mCurMethodId = null; unbindCurrentMethodLocked(true, false); } // Here is not the perfect place to reset the switching controller. Ideally // mSwitchingController and mSettings should be able to share the same state. // TODO: Make sure that mSwitchingController and mSettings are sharing the // the same enabled IMEs list. mSwitchingController.resetCircularListLocked(mContext); }
于是, 出错的位置, 加上重置输入法的代码:
//("start reset IME"); mCurMethodId = null; unbindCurrentMethodLocked(true, false); //AnsonCode clear input method after error. //mSettings.putSelectedInputMethod(""); Settings.Secure.putStringForUser(mContext.getContentResolver(), Settings.Secure.ENABLED_INPUT_METHODS, "", mSettings.getCurrentUserId()); //("enable All IMES"); mSettings.enableAllIMEsIfThereIsNoEnabledIME(); //("reset Default IME"); resetDefaultImeLocked(mContext);
1. 清空数据库中ENABLED_INPUT_METHODS已启用的输入法 2. 重置启用输入法 3. 重置默认输入法.
最开始, 并没有去清空数据库, 导致, 不管如何重置输入法, 系统都无法正常启用讯飞.
private void resetDefaultImeLocked(Context context) { // Do not reset the default (current) IME when it is a 3rd-party IME if (mCurMethodId != null && !InputMethodUtils.isSystemIme(mMethodMap.get(mCurMethodId))) { return; } InputMethodInfo defIm = null; for (InputMethodInfo imi : mMethodList) { if (defIm == null) { if (InputMethodUtils.isValidSystemDefaultIme( mSystemReady, imi, context)) { defIm = imi; Slog.i(TAG, "Selected default: " + imi.getId()); } } } if (defIm == null && mMethodList.size() > 0) { defIm = InputMethodUtils.getMostApplicableDefaultIME( mSettings.getEnabledInputMethodListLocked()); //在这里, 因为数据库中启用的输入法依然是latinIME, 所以, defIm一直为空. if (defIm != null) { Slog.i(TAG, "Default found, using " + defIm.getId()); } else { Slog.i(TAG, "No default found"); } } if (defIm != null) { setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false); } }
也就是说, 若不清空数据库, 则会输入LOG: No default found
defIm为空的原因, 见下面代码.
|--frameworks/base/core/java/com/android/internal/inputmethod/InputMethodUtils.java public static InputMethodInfo getMostApplicableDefaultIME(List<InputMethodInfo> enabledImes) { if (enabledImes == null || enabledImes.isEmpty()) { return null; } // We'd prefer to fall back on a system IME, since that is safer. int i = enabledImes.size(); int firstFoundSystemIme = -1; while (i > 0) { i--; final InputMethodInfo imi = enabledImes.get(i); if (InputMethodUtils.isSystemImeThatHasEnglishKeyboardSubtype(imi) && !imi.isAuxiliaryIme()) { return imi; } if (firstFoundSystemIme < 0 && InputMethodUtils.isSystemIme(imi) && !imi.isAuxiliaryIme()) { firstFoundSystemIme = i; } } return enabledImes.get(Math.max(firstFoundSystemIme, 0)); } public List<InputMethodInfo> getEnabledInputMethodListLocked() { return createEnabledInputMethodListLocked( getEnabledInputMethodsAndSubtypeListLocked()); } public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() { ArrayList<Pair<String, ArrayList<String>>> imsList = new ArrayList<Pair<String, ArrayList<String>>>(); final String enabledInputMethodsStr = getEnabledInputMethodsStr(); if (TextUtils.isEmpty(enabledInputMethodsStr)) { return imsList; } mInputMethodSplitter.setString(enabledInputMethodsStr); while (mInputMethodSplitter.hasNext()) { String nextImsStr = mInputMethodSplitter.next(); mSubtypeSplitter.setString(nextImsStr); if (mSubtypeSplitter.hasNext()) { ArrayList<String> subtypeHashes = new ArrayList<String>(); // The first element is ime id. String imeId = mSubtypeSplitter.next(); while (mSubtypeSplitter.hasNext()) { subtypeHashes.add(mSubtypeSplitter.next()); } imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes)); } } return imsList; } public String getEnabledInputMethodsStr() { mEnabledInputMethodsStrCache = Settings.Secure.getStringForUser( mResolver, Settings.Secure.ENABLED_INPUT_METHODS, mCurrentUserId); if (DEBUG) { Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache + ", " + mCurrentUserId); } return mEnabledInputMethodsStrCache; }
补充
跟输入法相关的两个数据库变量: Settings.Secure.ENABLED_INPUT_METHODS 使用/启用输入法, 这会显示在设置中的输入法列表 Settings.Secure.DEFAULT_INPUT_METHOD 默认输入法 查看数据库: sqlite3 /data/data/com.android.providers.settings/databases/settings.db select * from secure; 可以看出当前输入法的值: 50|enabled_input_methods|com.iflytek.inputmethod/.FlyIME 51|default_input_method|com.iflytek.inputmethod/.FlyIME