文章目录
需求
要求系统语言跟随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
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() {
@Override
public void onAnimationsFinished() {
if (mLocalesToSetNext == null || mLocalesToSetNext.equals(mLocalesSetLast)) {
// All animations finished, but the locale list did not change
return;
}
// 语言条目发生改变,调用到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
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.
*/
@UnsupportedAppUsage
public 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();
// 切换后的语言信息更新到Configuration
config.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
java
@Override
public 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就是ActivityTaskManagerService
mActivityTaskManager.updatePersistentConfiguration(values, userId);
}
继续传递到ActivityTaskManagerService中处理updateConfigurationLocked()
- frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
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()
java
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
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
java
@Override
public boolean updateConfiguration(Configuration values) {
return mActivityTaskManager.updateConfiguration(values);
}
@Override
public 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
java
@Override
public 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 manager
values = 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
java
// add for SIM language adaptive
import 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 adaptive
mncInt = 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 adaptive
try {
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 end
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");
}
}
}
// add for SIM language adaptive
private 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
java
import android.content.res.Configuration;
// add for SIM language adaptive
public 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卡自适应变化.