Android12.0 SIM卡语言自适应

时间:2024-06-03 07:39:51

文章目录

        • 需求
        • 语言设定
        • 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() {
            @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
    /**
     * 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
	@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
	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
	@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
@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
	// 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.updateMccMncConfigur