文章目錄
- 需求
- 語言設定
- Settings中語言切換流程
- 檢測到SIM卡,更新系統語言
- 最終修改
需求
要求系統語言跟隨SIM卡的語言變化。
語言設定
(1)系統預置語言, 即在makefile中指定的語言
(2)重啟, 如果未插卡, 則系統語言為預置的語言
(3)重啟插入SIM卡開機, 會自適應為SIM卡的語言
(4)如果有手動設置語言, 以后開機, 不管插入的是哪個國家的卡, 都會顯示設置的語言, 不會根據SIM卡自適應變化.
Settings中語言切換流程
當在系統設置中手動設置語言拖拽結束后,會調用updateLocalesWhenAnimationStops(ll)方法
- vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
public void updateLocalesWhenAnimationStops(final LocaleList localeList) {if (localeList.equals(mLocalesToSetNext)) {return;}// This will only update the Settings application to make things feel more responsive,// the system will be updated later, when animation stopped.LocaleList.setDefault(localeList);mLocalesToSetNext = localeList;final RecyclerView.ItemAnimator itemAnimator = mParentView.getItemAnimator();itemAnimator.isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {@Overridepublic void onAnimationsFinished() {if (mLocalesToSetNext == null || mLocalesToSetNext.equals(mLocalesSetLast)) {// All animations finished, but the locale list did not changereturn;}// 語言條目發生改變,調用到framework下的LocalePicker進行更新LocalePicker.updateLocales(mLocalesToSetNext);mLocalesSetLast = mLocalesToSetNext;new ShortcutsUpdateTask(mContext).execute();mLocalesToSetNext = null;mNumberFormatter = NumberFormat.getNumberInstance(Locale.getDefault());}});}
然后調用LocalePicker的updateLocales()方法進行更新
- frameworks/base/core/java/com/android/internal/app/LocalePicker.java
/*** Requests the system to update the list of system locales.* Note that the system looks halted for a while during the Locale migration,* so the caller need to take care of it.*/@UnsupportedAppUsagepublic static void updateLocales(LocaleList locales) {if (locales != null) {locales = removeExcludedLocales(locales);}// Note: the empty list case is covered by Configuration.setLocales().try {final IActivityManager am = ActivityManager.getService();final Configuration config = am.getConfiguration();// 切換后的語言信息更新到Configurationconfig.setLocales(locales);config.userSetLocale = true; // 手動設置的標志am.updatePersistentConfigurationWithAttribution(config,ActivityThread.currentOpPackageName(), null);// Trigger the dirty bit for the Settings Provider.BackupManager.dataChanged("com.android.providers.settings");} catch (RemoteException e) {// Intentionally left blank}}
又轉入到ActivityManagerService中處理
- frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Overridepublic void updatePersistentConfigurationWithAttribution(Configuration values,String callingPackage, String callingAttributionTag) {enforceCallingPermission(CHANGE_CONFIGURATION, "updatePersistentConfiguration()");enforceWriteSettingsPermission("updatePersistentConfiguration()", callingPackage,callingAttributionTag);if (values == null) {throw new NullPointerException("Configuration must not be null");}int userId = UserHandle.getCallingUserId();// 這里的mActivityTaskManager就是ActivityTaskManagerServicemActivityTaskManager.updatePersistentConfiguration(values, userId);}
繼續傳遞到ActivityTaskManagerService中處理updateConfigurationLocked()
- frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
public void updatePersistentConfiguration(Configuration values, @UserIdInt int userId) {final long origId = Binder.clearCallingIdentity();try {synchronized (mGlobalLock) {// 配置發生改變(尺寸,字體),都會執行updateConfigurationLocked(values, null, false, true, userId,false /* deferResume */);}} finally {Binder.restoreCallingIdentity(origId);}}
在ActivityTaskManagerService內部經過一系列處理,最終執行到updateGlobalConfigurationLocked()
int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,boolean persistent, int userId) {mTempConfig.setTo(getGlobalConfiguration());// 判斷是否發生變化final int changes = mTempConfig.updateFrom(values);if (changes == 0) {return 0;}...if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {// 這里的locales包含所有已添加的語言,如果是第一次開機就是系統默認語言[en_US]final LocaleList locales = values.getLocales();int bestLocaleIndex = 0;if (locales.size() > 1) {if (mSupportedSystemLocales == null) {// 所有系統支持的語言mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();}bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));}// 如果是values.userSetLocale=true,設置系統屬性SystemProperties.set("persist.sys.locale",locales.get(bestLocaleIndex).toLanguageTag());LocaleList.setDefault(locales, bestLocaleIndex);final Message m = PooledLambda.obtainMessage(ActivityTaskManagerService::sendLocaleToMountDaemonMsg, this,locales.get(bestLocaleIndex));mH.sendMessage(m);}mTempConfig.seq = increaseConfigurationSeqLocked();Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);...// Update stored global config and notify everyone about the change.mRootWindowContainer.onConfigurationChanged(mTempConfig); // 整個系統界面進行更新return changes;}
檢測到SIM卡,更新系統語言
SIM卡ready后,會調用updateMccMncConfiguration()方法更新卡的MCC/MNC信息
- frameworks/opt/telephony/src/java/com/android/internal/telephony/MccTable.java
/*** Updates MCC and MNC device configuration information for application retrieving* correct version of resources. If MCC is 0, MCC and MNC will be ignored (not set).* @param context Context to act on.* @param mccmnc truncated imsi with just the MCC and MNC - MNC assumed to be from 4th to end*/public static void updateMccMncConfiguration(Context context, String mccmnc) {Rlog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc);if (TelephonyUtils.IS_DEBUGGABLE) {String overrideMcc = SystemProperties.get("persist.sys.override_mcc");if (!TextUtils.isEmpty(overrideMcc)) {mccmnc = overrideMcc;Rlog.d(LOG_TAG, "updateMccMncConfiguration overriding mccmnc='" + mccmnc + "'");}}if (!TextUtils.isEmpty(mccmnc)) {int mccInt;try {mccInt = Integer.parseInt(mccmnc.substring(0, 3));} catch (NumberFormatException | StringIndexOutOfBoundsException ex) {Rlog.e(LOG_TAG, "Error parsing mccmnc: " + mccmnc + ". ex=" + ex);return;}if (mccInt != 0) {ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);if (!activityManager.updateMccMncConfiguration(mccmnc.substring(0, 3), mccmnc.substring(3))) {Rlog.d(LOG_TAG, "updateMccMncConfiguration: update mccmnc="+ mccmnc + " failure");} else {Rlog.d(LOG_TAG, "updateMccMncConfiguration: update mccmnc="+ mccmnc + " success");}} else {Rlog.d(LOG_TAG, "updateMccMncConfiguration nothing to update");}}}
mcc參數不為0,繼續往下執行到ActivityManagerService的updateMccMncConfiguration
- frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Overridepublic boolean updateConfiguration(Configuration values) {return mActivityTaskManager.updateConfiguration(values);}@Overridepublic boolean updateMccMncConfiguration(String mcc, String mnc) {int mccInt, mncInt;try {mccInt = Integer.parseInt(mcc);mncInt = Integer.parseInt(mnc);} catch (NumberFormatException | StringIndexOutOfBoundsException ex) {Slog.e(TAG, "Error parsing mcc: " + mcc + " mnc: " + mnc + ". ex=" + ex);return false;}Configuration config = new Configuration();config.mcc = mccInt;config.mnc = mncInt == 0 ? Configuration.MNC_ZERO : mncInt;return mActivityTaskManager.updateConfiguration(config);}
mcc/mnc參數沒有問題更新Configuration,與Settings中設置語言一樣執行到ActivityTaskManagerService中處理
- frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@Overridepublic boolean updateConfiguration(Configuration values) {mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");synchronized (mGlobalLock) {if (mWindowManager == null) {Slog.w(TAG, "Skip updateConfiguration because mWindowManager isn't set");return false;}if (values == null) {// sentinel: fetch the current configuration from the window managervalues = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);}mH.sendMessage(PooledLambda.obtainMessage(ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal,DEFAULT_DISPLAY));final long origId = Binder.clearCallingIdentity();try {if (values != null) {Settings.System.clearConfiguration(values);}updateConfigurationLocked(values, null, false, false /* persistent */,UserHandle.USER_NULL, false /* deferResume */,mTmpUpdateConfigurationResult);return mTmpUpdateConfigurationResult.changes != 0;} finally {Binder.restoreCallingIdentity(origId);}}}
最終走updateConfigurationLocked(),與Settings中設置語言一樣的流程。
最終修改
- frameworks/opt/telephony/src/java/com/android/internal/telephony/MccTable.java
// add for SIM language adaptiveimport android.content.res.Configuration;import android.os.LocaleList;import android.os.RemoteException;import android.app.ActivityManagerNative;// add end/*** Updates MCC and MNC device configuration information for application retrieving* correct version of resources. If MCC is 0, MCC and MNC will be ignored (not set).* @param context Context to act on.* @param mccmnc truncated imsi with just the MCC and MNC - MNC assumed to be from 4th to end*/public static void updateMccMncConfiguration(Context context, String mccmnc) {Rlog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc);if (TelephonyUtils.IS_DEBUGGABLE) {String overrideMcc = SystemProperties.get("persist.sys.override_mcc");if (!TextUtils.isEmpty(overrideMcc)) {mccmnc = overrideMcc;Rlog.d(LOG_TAG, "updateMccMncConfiguration overriding mccmnc='" + mccmnc + "'");}}if (!TextUtils.isEmpty(mccmnc)) {int mccInt;int mncInt;try {mccInt = Integer.parseInt(mccmnc.substring(0, 3));// add for SIM language adaptivemncInt = Integer.parseInt(mccmnc.substring(3));// add end} catch (NumberFormatException | StringIndexOutOfBoundsException ex) {Rlog.e(LOG_TAG, "Error parsing mccmnc: " + mccmnc + ". ex=" + ex);return;}if (mccInt != 0) {// add for SIM language adaptivetry {Configuration config = new Configuration();config.mcc = mccInt;config.mnc = mncInt == 0 ? Configuration.MNC_ZERO : mncInt;// 根據MCC獲取語言和國家碼(對應的表是sTable)Locale mccLocale = LocaleUtils.getLocaleFromMcc(context, mccInt, null); // 根據sim卡的mcc參數獲取的Locale不為空并且沒有設置過語言,根據sim卡信息設置語言if (mccLocale != null && canUpdateLocale()){Configuration configLocal = new Configuration();configLocal = ActivityManagerNative.getDefault().getConfiguration();LocaleList userLocale = configLocal.getLocales();// sim卡語言置頂LocaleList newUserLocale = new LocaleList(mccLocale, userLocale);config.setLocales(newUserLocale);config.userSetLocale = true;config.fontScale = 1.0f;ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);if (!activityManager.updateConfiguration(config)) {Rlog.d(LOG_TAG, "updateConfiguration: update mccmnc="+ mccmnc + " failure");} else {Rlog.d(LOG_TAG, "updateConfiguration: update mccmnc="+ mccmnc + " success");}return;}} catch (RemoteException e) {throw e.rethrowFromSystemServer();}// add endActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);if (!activityManager.updateMccMncConfiguration(mccmnc.substring(0, 3), mccmnc.substring(3))) {Rlog.d(LOG_TAG, "updateMccMncConfiguration: update mccmnc="+ mccmnc + " failure");} else {Rlog.d(LOG_TAG, "updateMccMncConfiguration: update mccmnc="+ mccmnc + " success");}} else {Rlog.d(LOG_TAG, "updateMccMncConfiguration nothing to update");}}}// add for SIM language adaptiveprivate static boolean canUpdateLocale() {return !userHasPersistedLocale();}private static boolean userHasPersistedLocale() {String persistSysLanguage = SystemProperties.get("persist.sys.locale", "");return !(persistSysLanguage.isEmpty());}// add end
- frameworks/base/core/java/android/app/ActivityManager.java
import android.content.res.Configuration;// add for SIM language adaptivepublic boolean updateConfiguration(@NonNull Configuration values) {try {return getService().updateConfiguration(values);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}// add end
修改完之后
(1)系統預置語言, 即在makefile中指定的語言
(2)重啟, 如果未插卡, 則系統語言為預置的語言
(3)重啟插入SIM卡開機, 會自適應為SIM卡的語言
(4)如果有手動設置語言, 以后開機, 不管插入的是哪個國家的卡, 都會顯示設置的語言, 不會根據SIM卡自適應變化.