Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a727251a authored by Narayan Kamath's avatar Narayan Kamath
Browse files

Don't update Configuration#locale on MCC/carrier changes.

Also, handle the sim language indicator (EF LI) and the sim
preferred language (EF PL). We keep track of these values in
the IccRecords object and make the information available via
the associated "Phone" object.

This means language updates are always consistent, the last
SIM to load records doesn't always "win".

bug: 19818550

Change-Id: I719eb75cbf48f54167b596c677f883a2ffb99ff7
parent b757d071
Loading
Loading
Loading
Loading
+2 −75
Original line number Diff line number Diff line
@@ -184,11 +184,8 @@ public final class MccTable {
            }

            Slog.d(LOG_TAG, "updateMccMncConfiguration: mcc=" + mcc + ", mnc=" + mnc);

            Locale locale = null;
            if (mcc != 0) {
                setTimezoneFromMccIfNeeded(context, mcc);
                locale = getLocaleFromMcc(context, mcc);
            }
            if (fromServiceState) {
                setWifiCountryCodeFromMcc(context, mcc);
@@ -202,10 +199,7 @@ public final class MccTable {
                        config.mnc = mnc == 0 ? Configuration.MNC_ZERO : mnc;
                        updateConfig = true;
                    }
                    if (locale != null) {
                        config.setLocale(locale);
                        updateConfig = true;
                    }

                    if (updateConfig) {
                        Slog.d(LOG_TAG, "updateMccMncConfiguration updateConfig config=" + config);
                        ActivityManagerNative.getDefault().updateConfiguration(config);
@@ -224,29 +218,6 @@ public final class MccTable {
        }
    }

    // Bug 19232829: It is possible to get through provisioning without setting up a persistent
    // locale value. We don't modify the locale if the device has completed "provisioning" because
    // we don't want to change the locale if the user inserts a new SIM or a new version of Android
    // is better at recognizing MCC values than an older version.
    private static boolean canUpdateLocale(Context context) {
        return !(userHasPersistedLocale() || isDeviceProvisioned(context));
    }

    private static boolean userHasPersistedLocale() {
        String persistSysLanguage = SystemProperties.get("persist.sys.language", "");
        String persistSysCountry = SystemProperties.get("persist.sys.country", "");
        return !(persistSysLanguage.isEmpty() && persistSysCountry.isEmpty());
    }

    private static boolean isDeviceProvisioned(Context context) {
        try {
            return Settings.Global.getInt(
                    context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED) != 0;
        } catch (Settings.SettingNotFoundException e) {
            return false;
        }
    }

    /**
     * Return Locale for the language and country or null if no good match.
     *
@@ -266,15 +237,6 @@ public final class MccTable {
            country = ""; // The Locale constructor throws if passed null.
        }

        // Check whether a developer is trying to test an arbitrary MCC.
        boolean debuggingMccOverride = isDebuggingMccOverride();

        // If this is a regular user and they already have a persisted locale, we're done.
        if (!(debuggingMccOverride || canUpdateLocale(context))) {
            Slog.d(LOG_TAG, "getLocaleForLanguageCountry: not permitted to update locale");
            return null;
        }

        // Find the best match we actually have a localization for.
        // TODO: this should really follow the CLDR chain of parent locales!
        final Locale target = new Locale(language, country);
@@ -329,41 +291,6 @@ public final class MccTable {
        return null;
    }

    private static boolean isDebuggingMccOverride() {
        if (Build.IS_DEBUGGABLE) {
            String overrideMcc = SystemProperties.get("persist.sys.override_mcc", "");
            if (!overrideMcc.isEmpty()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Utility code to set the system locale if it's not set already
     * @param context Context to act on.
     * @param language Two character language code desired
     * @param country Two character country code desired
     *
     *  {@hide}
     */
    public static void setSystemLocale(Context context, String language, String country) {
        Locale locale = getLocaleForLanguageCountry(context, language, country);
        if (locale != null) {
            Configuration config = new Configuration();
            config.setLocale(locale);
            config.userSetLocale = false;
            Slog.d(LOG_TAG, "setSystemLocale: updateLocale config=" + config);
            try {
                ActivityManagerNative.getDefault().updateConfiguration(config);
            } catch (RemoteException e) {
                Slog.d(LOG_TAG, "setSystemLocale exception", e);
            }
        } else {
            Slog.d(LOG_TAG, "setSystemLocale: no locale");
        }
    }

    /**
     * If the timezone is not already set, set it based on the MCC of the SIM.
     * @param context Context to act on.
@@ -390,7 +317,7 @@ public final class MccTable {
     *
     * @return locale for the mcc or null if none
     */
    private static Locale getLocaleFromMcc(Context context, int mcc) {
    public static Locale getLocaleFromMcc(Context context, int mcc) {
        String language = MccTable.defaultLanguageForMcc(mcc);
        String country = MccTable.countryCodeForMcc(mcc);

+7 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.internal.telephony.uicc.UsimServiceTable;
import com.android.internal.telephony.PhoneConstants.*; // ????

import java.util.List;
import java.util.Locale;

/**
 * Internal interface used to control the phone; SDK developers cannot
@@ -2058,4 +2059,10 @@ public interface Phone {
     * Returns the status of Link Capacity Estimation (LCE) service.
     */
    public int getLceStatus();

    /**
     * Returns the locale based on the carrier properties (such as {@code ro.carrier}) and
     * SIM preferences.
     */
    public Locale getLocaleFromSimAndCarrierPrefs();
}
+38 −25
Original line number Diff line number Diff line
@@ -218,7 +218,7 @@ public abstract class PhoneBase extends Handler implements Phone {
    // to recover from the state. We cache it and notify listeners when they register.
    protected boolean mIsVideoCapable = false;
    protected UiccController mUiccController = null;
    public AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
    public final AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
    public SmsStorageMonitor mSmsStorageMonitor;
    public SmsUsageMonitor mSmsUsageMonitor;
    protected AtomicReference<UiccCardApplication> mUiccApplication =
@@ -387,7 +387,7 @@ public abstract class PhoneBase extends Handler implements Phone {
     * @param ci is CommandsInterface
     * @param unitTestMode when true, prevents notifications
     * of state change events
     * @param subscription is current phone subscription
     * @param phoneId the phone-id of this phone.
     */
    protected PhoneBase(String name, PhoneNotifier notifier, Context context, CommandsInterface ci,
            boolean unitTestMode, int phoneId) {
@@ -437,9 +437,26 @@ public abstract class PhoneBase extends Handler implements Phone {
                TelephonyProperties.PROPERTY_CALL_RING_DELAY, 3000);
        Rlog.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay);

        if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) return;
        if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
            return;
        }

        setPropertiesByCarrier();
        // The locale from the "ro.carrier" system property or R.array.carrier_properties.
        // This will be overwritten by the Locale from the SIM language settings (EF-PL, EF-LI)
        // if applicable.
        final Locale carrierLocale = getLocaleFromCarrierProperties(mContext);
        if (carrierLocale != null && !TextUtils.isEmpty(carrierLocale.getCountry())) {
            final String country = carrierLocale.getCountry();
            try {
                Settings.Global.getInt(mContext.getContentResolver(),
                        Settings.Global.WIFI_COUNTRY_CODE);
            } catch (Settings.SettingNotFoundException e) {
                // note this is not persisting
                WifiManager wM = (WifiManager)
                        mContext.getSystemService(Context.WIFI_SERVICE);
                wM.setCountryCode(country, false);
            }
        }

        // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
        mSmsStorageMonitor = new SmsStorageMonitor(this);
@@ -1161,37 +1178,23 @@ public abstract class PhoneBase extends Handler implements Phone {
     * Set the properties by matching the carrier string in
     * a string-array resource
     */
    private void setPropertiesByCarrier() {
    private static Locale getLocaleFromCarrierProperties(Context ctx) {
        String carrier = SystemProperties.get("ro.carrier");

        if (null == carrier || 0 == carrier.length() || "unknown".equals(carrier)) {
            return;
            return null;
        }

        CharSequence[] carrierLocales = mContext.
                getResources().getTextArray(R.array.carrier_properties);
        CharSequence[] carrierLocales = ctx.getResources().getTextArray(R.array.carrier_properties);

        for (int i = 0; i < carrierLocales.length; i+=3) {
            String c = carrierLocales[i].toString();
            if (carrier.equals(c)) {
                final Locale l = Locale.forLanguageTag(carrierLocales[i + 1].toString().replace('_', '-'));
                final String country = l.getCountry();
                MccTable.setSystemLocale(mContext, l.getLanguage(), country);

                if (!country.isEmpty()) {
                    try {
                        Settings.Global.getInt(mContext.getContentResolver(),
                                Settings.Global.WIFI_COUNTRY_CODE);
                    } catch (Settings.SettingNotFoundException e) {
                        // note this is not persisting
                        WifiManager wM = (WifiManager)
                                mContext.getSystemService(Context.WIFI_SERVICE);
                        wM.setCountryCode(country, false);
                    }
                }
                return;
                return Locale.forLanguageTag(carrierLocales[i + 1].toString().replace('_', '-'));
            }
        }

        return null;
    }

    /**
@@ -2381,6 +2384,16 @@ public abstract class PhoneBase extends Handler implements Phone {
        }
    }

    @Override
    public Locale getLocaleFromSimAndCarrierPrefs() {
        final IccRecords records = mIccRecords.get();
        if (records != null && records.getSimLanguage() != null) {
            return new Locale(records.getSimLanguage());
        }

        return getLocaleFromCarrierProperties(mContext);
    }

    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.println("PhoneBase: subId=" + getSubId());
        pw.println(" mPhoneId=" + mPhoneId);
+6 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import com.android.internal.telephony.uicc.UsimServiceTable;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
import java.util.Locale;

import com.android.internal.telephony.dataconnection.DctController;

@@ -1563,6 +1564,11 @@ public class PhoneProxy extends Handler implements Phone {
        return mActivePhone.getLceStatus();
    }

    @Override
    public Locale getLocaleFromSimAndCarrierPrefs() {
        return mActivePhone.getLocaleFromSimAndCarrierPrefs();
    }

    /**
     * @return true if we are in the emergency call back mode. This is a period where
     * the phone should be using as little power as possible and be ready to receive an
+47 −0
Original line number Diff line number Diff line
@@ -23,12 +23,15 @@ import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;

import android.telephony.Rlog;
import android.telephony.TelephonyManager;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;

/**
@@ -79,6 +82,7 @@ public abstract class IccRecords extends Handler implements IccConstants {

    protected String mGid1;
    protected String mGid2;
    protected String mPrefLang;

    private final Object mLock = new Object();

@@ -490,6 +494,49 @@ public abstract class IccRecords extends Handler implements IccConstants {
        }
    }

    /**
     * Returns the SIM language derived from the EF-LI and EF-PL sim records.
     */
    public String getSimLanguage() {
        return mPrefLang;
    }

    protected void setSimLanguage(byte[] efLi, byte[] efPl) {
        String[] locales = mContext.getAssets().getLocales();
        try {
            mPrefLang = findBestLanguage(efLi, locales);
        } catch (UnsupportedEncodingException uee) {
            log("Unable to parse EF-LI: " + Arrays.toString(efLi));
        }

        if (mPrefLang == null) {
            try {
                mPrefLang = findBestLanguage(efPl, locales);
            } catch (UnsupportedEncodingException uee) {
                log("Unable to parse EF-PL: " + Arrays.toString(efLi));
            }
        }
    }

    protected static String findBestLanguage(byte[] languages, String[] locales)
            throws UnsupportedEncodingException {
        if ((languages == null) || (locales == null)) return null;

        // Each 2-bytes consists of one language
        for (int i = 0; (i + 1) < languages.length; i += 2) {
            String lang = new String(languages, i, 2, "ISO-8859-1");
            for (int j = 0; j < locales.length; j++) {
                if (locales[j] != null && locales[j].length() >= 2 &&
                        locales[j].substring(0, 2).equalsIgnoreCase(lang)) {
                    return lang;
                }
            }
        }

        // no match found. return null
        return null;
    }

    protected abstract void onRecordLoaded();

    protected abstract void onAllRecordsLoaded();
Loading