Loading src/java/com/android/internal/telephony/GsmCdmaPhone.java +3 −6 Original line number Diff line number Diff line Loading @@ -337,7 +337,7 @@ public class GsmCdmaPhone extends Phone { setIsoCountryProperty(operatorNumeric); // Updates MCC MNC device configuration information logd("update mccmnc=" + operatorNumeric); MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false); MccTable.updateMccMncConfiguration(mContext, operatorNumeric); } // Sets current entry in the telephony carrier table Loading @@ -358,10 +358,7 @@ public class GsmCdmaPhone extends Phone { } else { String iso = ""; try { iso = MccTable.countryCodeForMcc(Integer.parseInt( operatorNumeric.substring(0,3))); } catch (NumberFormatException ex) { Rlog.e(LOG_TAG, "setIsoCountryProperty: countryCodeForMcc error", ex); iso = MccTable.countryCodeForMcc(operatorNumeric.substring(0, 3)); } catch (StringIndexOutOfBoundsException ex) { Rlog.e(LOG_TAG, "setIsoCountryProperty: countryCodeForMcc error", ex); } Loading Loading @@ -2709,7 +2706,7 @@ public class GsmCdmaPhone extends Phone { // Updates MCC MNC device configuration information logd("update mccmnc=" + operatorNumeric); MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false); MccTable.updateMccMncConfiguration(mContext, operatorNumeric); return true; } catch (SQLException e) { Loading src/java/com/android/internal/telephony/LocaleTracker.java 0 → 100644 +345 −0 Original line number Diff line number Diff line /* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.telephony.CellInfo; import android.telephony.CellInfoGsm; import android.telephony.CellInfoLte; import android.telephony.CellInfoWcdma; import android.telephony.Rlog; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.LocalLog; import com.android.internal.util.IndentingPrintWriter; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * The locale tracker keeps tracking the current locale of the phone. */ public class LocaleTracker extends Handler { private static final boolean DBG = true; private static final String TAG = LocaleTracker.class.getSimpleName(); /** Event to trigger get cell info from the modem */ private static final int EVENT_GET_CELL_INFO = 1; // Todo: Read this from Settings. /** The minimum delay to get cell info from the modem */ private static final long CELL_INFO_MIN_DELAY_MS = 2 * SECOND_IN_MILLIS; // Todo: Read this from Settings. /** The maximum delay to get cell info from the modem */ private static final long CELL_INFO_MAX_DELAY_MS = 1 * HOUR_IN_MILLIS; private final Phone mPhone; /** SIM card state. Must be one of TelephonyManager.SIM_STATE_XXX */ private int mSimState; /** Current serving PLMN's MCC/MNC */ @Nullable private String mOperatorNumeric; /** Current cell tower information */ @Nullable private List<CellInfo> mCellInfo; /** Count of invalid cell info we've got so far. Will reset once we get a successful one */ private int mFailCellInfoCount; /** The ISO-3166 code of device's current country */ @Nullable private String mCurrentCountryIso; private final LocalLog mLocalLog = new LocalLog(50); /** Broadcast receiver to get SIM card state changed event */ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED.equals(intent.getAction())) { int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, 0); if (phoneId == mPhone.getPhoneId()) { onSimCardStateChanged(intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE, TelephonyManager.SIM_STATE_UNKNOWN)); } } } }; /** * Message handler * * @param msg The message */ @Override public void handleMessage(Message msg) { switch (msg.what) { case EVENT_GET_CELL_INFO: synchronized (this) { getCellInfo(); updateLocale(); } break; default: throw new IllegalStateException("Unexpected message arrives. msg = " + msg.what); } } /** * Constructor * * @param phone The phone object * @param looper The looper message handler */ public LocaleTracker(Phone phone, Looper looper) { super(looper); mPhone = phone; mSimState = TelephonyManager.SIM_STATE_UNKNOWN; final IntentFilter filter = new IntentFilter(); filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED); mPhone.getContext().registerReceiver(mBroadcastReceiver, filter); } /** * Get the device's current country. * * @return The device's current country. Empty string if the information is not available. */ @NonNull public synchronized String getCurrentCountry() { return (mCurrentCountryIso != null) ? mCurrentCountryIso : ""; } /** * Get the MCC from cell tower information. * * @return MCC in string format. Null if the information is not available. */ @Nullable private String getMccFromCellInfo() { String selectedMcc = null; if (mCellInfo != null) { Map<String, Integer> countryCodeMap = new HashMap<>(); int maxCount = 0; for (CellInfo cellInfo : mCellInfo) { String mcc = null; if (cellInfo instanceof CellInfoGsm) { mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMccString(); } else if (cellInfo instanceof CellInfoLte) { mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMccString(); } else if (cellInfo instanceof CellInfoWcdma) { mcc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMccString(); } if (mcc != null) { int count = 1; if (countryCodeMap.containsKey(mcc)) { count = countryCodeMap.get(mcc) + 1; } countryCodeMap.put(mcc, count); // This is unlikely, but if MCC from cell info looks different, we choose the // MCC that occurs most. if (count > maxCount) { maxCount = count; selectedMcc = mcc; } } } } return selectedMcc; } /** * Called when SIM card state changed. Only when we absolutely know the SIM is absent, we get * cell info from the network. Other SIM states like NOT_READY might be just a transitioning * state. * * @param state SIM card state. Must be one of TelephonyManager.SIM_STATE_XXX. */ private synchronized void onSimCardStateChanged(int state) { if (mSimState != state && state == TelephonyManager.SIM_STATE_ABSENT) { if (DBG) log("Sim absent. Get latest cell info from the modem."); getCellInfo(); updateLocale(); } mSimState = state; } /** * Update MCC/MNC from network service state. * * @param operatorNumeric MCC/MNC of the operator */ public synchronized void updateOperatorNumeric(String operatorNumeric) { // Check if the operator numeric changes. String msg = "updateOperatorNumeric. mcc/mnc=" + operatorNumeric; if (DBG) log(msg); mLocalLog.log(msg); if (!Objects.equals(mOperatorNumeric, operatorNumeric)) { if (DBG) { log("onUpdateOperatorNumeric: operator numeric changes to " + operatorNumeric); } mOperatorNumeric = operatorNumeric; // If the operator numeric becomes unavailable, we need to get the latest cell info so // that we can get MCC from it. if (TextUtils.isEmpty(mOperatorNumeric)) { if (DBG) { log("Operator numeric unavailable. Get latest cell info from the modem."); } getCellInfo(); } updateLocale(); } } /** * Get the delay time to get cell info from modem. The delay time grows exponentially to prevent * battery draining. * * @param failCount Count of invalid cell info we've got so far. * @return The delay time for next get cell info */ private long getCellInfoDelayTime(int failCount) { // Exponentially grow the delay time long delay = CELL_INFO_MIN_DELAY_MS * (long) Math.pow(2, failCount - 1); if (delay < CELL_INFO_MIN_DELAY_MS) { delay = CELL_INFO_MIN_DELAY_MS; } else if (delay > CELL_INFO_MAX_DELAY_MS) { delay = CELL_INFO_MAX_DELAY_MS; } return delay; } /** * Get cell info from the modem. */ private void getCellInfo() { // Get all cell info. Passing null to use default worksource, which indicates the original // request is from telephony internally. mCellInfo = mPhone.getAllCellInfo(null); String msg = "getCellInfo: cell info=" + mCellInfo; if (DBG) log(msg); mLocalLog.log(msg); if (mCellInfo == null || mCellInfo.size() == 0) { // If we can't get a valid cell info. Try it again later. long delay = getCellInfoDelayTime(++mFailCellInfoCount); if (DBG) log("Can't get cell info. Try again in " + delay / 1000 + " secs."); sendMessageDelayed(obtainMessage(EVENT_GET_CELL_INFO), delay); } else { mFailCellInfoCount = 0; // We successfully got cell info from the modem. Cancel the queued get cell info event // if there is any. removeMessages(EVENT_GET_CELL_INFO); } } /** * Update the device's current locale */ private void updateLocale() { // If MCC is available from network service state, use it first. String mcc = null; String countryIso = null; if (!TextUtils.isEmpty(mOperatorNumeric)) { try { mcc = mOperatorNumeric.substring(0, 3); countryIso = MccTable.countryCodeForMcc(mcc); } catch (StringIndexOutOfBoundsException ex) { loge("updateLocale: Can't get country from operator numeric. mcc = " + mcc + ". ex=" + ex); } } // If for any reason we can't get country from operator numeric, try to get it from cell // info. if (TextUtils.isEmpty(countryIso)) { mcc = getMccFromCellInfo(); countryIso = MccTable.countryCodeForMcc(mcc); } String msg = "updateLocale: mcc = " + mcc + ", country = " + countryIso; log(msg); mLocalLog.log(msg); if (!Objects.equals(countryIso, mCurrentCountryIso)) { log("updateLocale: Change the current country to " + countryIso); mCurrentCountryIso = countryIso; TelephonyManager.setTelephonyProperty(mPhone.getPhoneId(), TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, mCurrentCountryIso); // Set the country code for wifi. This sets allowed wifi channels based on the // country of the carrier we see. If we can't see any, reset to 0 so we don't // broadcast on forbidden channels. ((WifiManager) mPhone.getContext().getSystemService(Context.WIFI_SERVICE)) .setCountryCode(countryIso, false); } } private void log(String msg) { Rlog.d(TAG, msg); } private void loge(String msg) { Rlog.e(TAG, msg); } /** * Print the DeviceStateMonitor into the given stream. * * @param fd The raw file descriptor that the dump is being sent to. * @param pw A PrintWriter to which the dump is to be set. * @param args Additional arguments to the dump request. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); pw.println("LocaleTracker:"); ipw.increaseIndent(); ipw.println("mOperatorNumeric = " + mOperatorNumeric); ipw.println("mSimState = " + mSimState); ipw.println("mCellInfo = " + mCellInfo); ipw.println("mCurrentCountryIso = " + mCurrentCountryIso); ipw.println("mFailCellInfoCount = " + mFailCellInfoCount); ipw.println("Local logs:"); ipw.increaseIndent(); mLocalLog.dump(fd, ipw, args); ipw.decreaseIndent(); ipw.decreaseIndent(); ipw.flush(); } } src/java/com/android/internal/telephony/MccTable.java +34 −56 Original line number Diff line number Diff line Loading @@ -19,11 +19,9 @@ package com.android.internal.telephony; import android.app.ActivityManager; import android.content.Context; import android.content.res.Configuration; import android.net.wifi.WifiManager; import android.os.Build; import android.os.RemoteException; import android.os.SystemProperties; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Slog; Loading Loading @@ -112,6 +110,19 @@ public final class MccTable { } } /** * Given a GSM Mobile Country Code, returns * an ISO two-character country code if available. * Returns empty string if unavailable. */ public static String countryCodeForMcc(String mcc) { try { return countryCodeForMcc(Integer.parseInt(mcc)); } catch (NumberFormatException ex) { return ""; } } /** * Given a GSM Mobile Country Code, returns * an ISO 2-3 character language code if available. Loading Loading @@ -159,11 +170,9 @@ public final class MccTable { * 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 * @param fromServiceState true if coming from the radio service state, false if from SIM */ public static void updateMccMncConfiguration(Context context, String mccmnc, boolean fromServiceState) { Slog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc + "' fromServiceState=" + fromServiceState); public static void updateMccMncConfiguration(Context context, String mccmnc) { Slog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc); if (Build.IS_DEBUGGABLE) { String overrideMcc = SystemProperties.get("persist.sys.override_mcc"); Loading @@ -176,19 +185,11 @@ public final class MccTable { if (!TextUtils.isEmpty(mccmnc)) { int mcc, mnc; String defaultMccMnc = TelephonyManager.getDefault().getSimOperatorNumeric(); Slog.d(LOG_TAG, "updateMccMncConfiguration defaultMccMnc=" + defaultMccMnc); //Update mccmnc only for default subscription in case of MultiSim. // if (!defaultMccMnc.equals(mccmnc)) { // Slog.d(LOG_TAG, "Not a Default subscription, ignoring mccmnc config update."); // return; // } try { mcc = Integer.parseInt(mccmnc.substring(0, 3)); mnc = Integer.parseInt(mccmnc.substring(3)); } catch (NumberFormatException e) { Slog.e(LOG_TAG, "Error parsing IMSI: " + mccmnc); } catch (NumberFormatException | StringIndexOutOfBoundsException ex) { Slog.e(LOG_TAG, "Error parsing IMSI: " + mccmnc + ". ex=" + ex); return; } Loading @@ -196,10 +197,7 @@ public final class MccTable { if (mcc != 0) { setTimezoneFromMccIfNeeded(context, mcc); } if (fromServiceState) { setWifiCountryCodeFromMcc(context, mcc); } else { // from SIM try { Configuration config = new Configuration(); boolean updateConfig = false; Loading @@ -219,12 +217,6 @@ public final class MccTable { Slog.e(LOG_TAG, "Can't update configuration", e); } } } else { if (fromServiceState) { // an empty mccmnc means no signal - tell wifi we don't know setWifiCountryCodeFromMcc(context, 0); } } } /** Loading Loading @@ -396,20 +388,6 @@ public final class MccTable { return locale; } /** * Set the country code for wifi. This sets allowed wifi channels based on the * country of the carrier we see. If we can't see any, reset to 0 so we don't * broadcast on forbidden channels. * @param context Context to act on. * @param mcc Mobile Country Code of the operator. 0 if not known */ private static void setWifiCountryCodeFromMcc(Context context, int mcc) { String country = MccTable.countryCodeForMcc(mcc); Slog.d(LOG_TAG, "WIFI_COUNTRY_CODE set to " + country); WifiManager wM = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); wM.setCountryCode(country, false); } static { sTable = new ArrayList<MccEntry>(240); Loading src/java/com/android/internal/telephony/ServiceStateTracker.java +26 −23 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.os.AsyncResult; import android.os.BaseBundle; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.os.PersistableBundle; import android.os.Registrant; Loading Loading @@ -259,6 +260,9 @@ public class ServiceStateTracker extends Handler { private final RatRatcheter mRatRatcheter; private final HandlerThread mHandlerThread; private final LocaleTracker mLocaleTracker; private final LocalLog mRoamingLog = new LocalLog(10); private final LocalLog mAttachLog = new LocalLog(10); private final LocalLog mPhoneTypeLog = new LocalLog(10); Loading Loading @@ -508,6 +512,13 @@ public class ServiceStateTracker extends Handler { this, EVENT_NETWORK_STATE_CHANGED, null); } // Create a new handler thread dedicated for locale tracker because the blocking // getAllCellInfo call requires clients calling from a different thread. mHandlerThread = new HandlerThread(LocaleTracker.class.getSimpleName()); mHandlerThread.start(); mLocaleTracker = TelephonyComponentFactory.getInstance().makeLocaleTracker( mPhone, mHandlerThread.getLooper()); mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null); mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null); Loading Loading @@ -655,6 +666,7 @@ public class ServiceStateTracker extends Handler { mCi.unregisterForPhysicalChannelConfiguration(this); mSubscriptionManager .removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener); mHandlerThread.quit(); mCi.unregisterForImsNetworkStateChanged(this); mPhone.getCarrierActionAgent().unregisterForCarrierAction(this, CARRIER_ACTION_SET_RADIO_ENABLED); Loading Loading @@ -2839,11 +2851,12 @@ public class ServiceStateTracker extends Handler { } tm.setNetworkOperatorNumericForPhone(mPhone.getPhoneId(), operatorNumeric); updateCarrierMccMncConfiguration(operatorNumeric, prevOperatorNumeric, mPhone.getContext()); if (isInvalidOperatorNumeric(operatorNumeric)) { if (DBG) log("operatorNumeric " + operatorNumeric + " is invalid"); tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), ""); // Passing empty string is important for the first update. The initial value of // operator numeric in locale tracker is null. mLocaleTracker.updateOperatorNumeric(""); mNitzState.handleNetworkUnavailable(); } else if (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) { // If the device is on IWLAN, modems manufacture a ServiceState with the MCC/MNC of Loading @@ -2855,15 +2868,8 @@ public class ServiceStateTracker extends Handler { setOperatorIdd(operatorNumeric); } // Update ISO. String countryIsoCode = ""; try { String mcc = operatorNumeric.substring(0, 3); countryIsoCode = MccTable.countryCodeForMcc(Integer.parseInt(mcc)); } catch (NumberFormatException | StringIndexOutOfBoundsException ex) { loge("pollStateDone: countryCodeForMcc error: " + ex); } tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), countryIsoCode); mLocaleTracker.updateOperatorNumeric(operatorNumeric); String countryIsoCode = mLocaleTracker.getCurrentCountry(); // Update Time Zone. boolean iccCardExists = iccCardExists(); Loading Loading @@ -4325,6 +4331,8 @@ public class ServiceStateTracker extends Handler { pw.println(" mLteRsrpBoost=" + mLteRsrpBoost); dumpEarfcnPairList(pw); mLocaleTracker.dump(fd, pw, args); pw.println(" Roaming Log:"); IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); Loading Loading @@ -4377,15 +4385,6 @@ public class ServiceStateTracker extends Handler { return value; } protected void updateCarrierMccMncConfiguration(String newOp, String oldOp, Context context) { // if we have a change in operator, notify wifi (even to/from none) if (((newOp == null) && (TextUtils.isEmpty(oldOp) == false)) || ((newOp != null) && (newOp.equals(oldOp) == false))) { log("update mccmnc=" + newOp + " fromServiceState=true"); MccTable.updateMccMncConfiguration(context, newOp, true); } } /** * Check ISO country by MCC to see if phone is roaming in same registered country */ Loading @@ -4402,8 +4401,8 @@ public class ServiceStateTracker extends Handler { boolean inSameCountry = true; final String networkMCC = operatorNumeric.substring(0, 3); final String homeMCC = homeNumeric.substring(0, 3); final String networkCountry = MccTable.countryCodeForMcc(Integer.parseInt(networkMCC)); final String homeCountry = MccTable.countryCodeForMcc(Integer.parseInt(homeMCC)); final String networkCountry = MccTable.countryCodeForMcc(networkMCC); final String homeCountry = MccTable.countryCodeForMcc(homeMCC); if (networkCountry.isEmpty() || homeCountry.isEmpty()) { // Not a valid country return false; Loading Loading @@ -4644,4 +4643,8 @@ public class ServiceStateTracker extends Handler { // Return static default defined in CarrierConfigManager. return CarrierConfigManager.getDefaultConfig(); } public LocaleTracker getLocaleTracker() { return mLocaleTracker; } } src/java/com/android/internal/telephony/SubscriptionController.java +1 −1 Original line number Diff line number Diff line Loading @@ -1793,7 +1793,7 @@ public class SubscriptionController extends ISub.Stub { mDefaultFallbackSubId = subId; // Update MCC MNC device configuration information String defaultMccMnc = mTelephonyManager.getSimOperatorNumericForPhone(phoneId); MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false); MccTable.updateMccMncConfiguration(mContext, defaultMccMnc); // Broadcast an Intent for default sub change Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED); Loading Loading
src/java/com/android/internal/telephony/GsmCdmaPhone.java +3 −6 Original line number Diff line number Diff line Loading @@ -337,7 +337,7 @@ public class GsmCdmaPhone extends Phone { setIsoCountryProperty(operatorNumeric); // Updates MCC MNC device configuration information logd("update mccmnc=" + operatorNumeric); MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false); MccTable.updateMccMncConfiguration(mContext, operatorNumeric); } // Sets current entry in the telephony carrier table Loading @@ -358,10 +358,7 @@ public class GsmCdmaPhone extends Phone { } else { String iso = ""; try { iso = MccTable.countryCodeForMcc(Integer.parseInt( operatorNumeric.substring(0,3))); } catch (NumberFormatException ex) { Rlog.e(LOG_TAG, "setIsoCountryProperty: countryCodeForMcc error", ex); iso = MccTable.countryCodeForMcc(operatorNumeric.substring(0, 3)); } catch (StringIndexOutOfBoundsException ex) { Rlog.e(LOG_TAG, "setIsoCountryProperty: countryCodeForMcc error", ex); } Loading Loading @@ -2709,7 +2706,7 @@ public class GsmCdmaPhone extends Phone { // Updates MCC MNC device configuration information logd("update mccmnc=" + operatorNumeric); MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false); MccTable.updateMccMncConfiguration(mContext, operatorNumeric); return true; } catch (SQLException e) { Loading
src/java/com/android/internal/telephony/LocaleTracker.java 0 → 100644 +345 −0 Original line number Diff line number Diff line /* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.telephony.CellInfo; import android.telephony.CellInfoGsm; import android.telephony.CellInfoLte; import android.telephony.CellInfoWcdma; import android.telephony.Rlog; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.LocalLog; import com.android.internal.util.IndentingPrintWriter; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * The locale tracker keeps tracking the current locale of the phone. */ public class LocaleTracker extends Handler { private static final boolean DBG = true; private static final String TAG = LocaleTracker.class.getSimpleName(); /** Event to trigger get cell info from the modem */ private static final int EVENT_GET_CELL_INFO = 1; // Todo: Read this from Settings. /** The minimum delay to get cell info from the modem */ private static final long CELL_INFO_MIN_DELAY_MS = 2 * SECOND_IN_MILLIS; // Todo: Read this from Settings. /** The maximum delay to get cell info from the modem */ private static final long CELL_INFO_MAX_DELAY_MS = 1 * HOUR_IN_MILLIS; private final Phone mPhone; /** SIM card state. Must be one of TelephonyManager.SIM_STATE_XXX */ private int mSimState; /** Current serving PLMN's MCC/MNC */ @Nullable private String mOperatorNumeric; /** Current cell tower information */ @Nullable private List<CellInfo> mCellInfo; /** Count of invalid cell info we've got so far. Will reset once we get a successful one */ private int mFailCellInfoCount; /** The ISO-3166 code of device's current country */ @Nullable private String mCurrentCountryIso; private final LocalLog mLocalLog = new LocalLog(50); /** Broadcast receiver to get SIM card state changed event */ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED.equals(intent.getAction())) { int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, 0); if (phoneId == mPhone.getPhoneId()) { onSimCardStateChanged(intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE, TelephonyManager.SIM_STATE_UNKNOWN)); } } } }; /** * Message handler * * @param msg The message */ @Override public void handleMessage(Message msg) { switch (msg.what) { case EVENT_GET_CELL_INFO: synchronized (this) { getCellInfo(); updateLocale(); } break; default: throw new IllegalStateException("Unexpected message arrives. msg = " + msg.what); } } /** * Constructor * * @param phone The phone object * @param looper The looper message handler */ public LocaleTracker(Phone phone, Looper looper) { super(looper); mPhone = phone; mSimState = TelephonyManager.SIM_STATE_UNKNOWN; final IntentFilter filter = new IntentFilter(); filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED); mPhone.getContext().registerReceiver(mBroadcastReceiver, filter); } /** * Get the device's current country. * * @return The device's current country. Empty string if the information is not available. */ @NonNull public synchronized String getCurrentCountry() { return (mCurrentCountryIso != null) ? mCurrentCountryIso : ""; } /** * Get the MCC from cell tower information. * * @return MCC in string format. Null if the information is not available. */ @Nullable private String getMccFromCellInfo() { String selectedMcc = null; if (mCellInfo != null) { Map<String, Integer> countryCodeMap = new HashMap<>(); int maxCount = 0; for (CellInfo cellInfo : mCellInfo) { String mcc = null; if (cellInfo instanceof CellInfoGsm) { mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMccString(); } else if (cellInfo instanceof CellInfoLte) { mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMccString(); } else if (cellInfo instanceof CellInfoWcdma) { mcc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMccString(); } if (mcc != null) { int count = 1; if (countryCodeMap.containsKey(mcc)) { count = countryCodeMap.get(mcc) + 1; } countryCodeMap.put(mcc, count); // This is unlikely, but if MCC from cell info looks different, we choose the // MCC that occurs most. if (count > maxCount) { maxCount = count; selectedMcc = mcc; } } } } return selectedMcc; } /** * Called when SIM card state changed. Only when we absolutely know the SIM is absent, we get * cell info from the network. Other SIM states like NOT_READY might be just a transitioning * state. * * @param state SIM card state. Must be one of TelephonyManager.SIM_STATE_XXX. */ private synchronized void onSimCardStateChanged(int state) { if (mSimState != state && state == TelephonyManager.SIM_STATE_ABSENT) { if (DBG) log("Sim absent. Get latest cell info from the modem."); getCellInfo(); updateLocale(); } mSimState = state; } /** * Update MCC/MNC from network service state. * * @param operatorNumeric MCC/MNC of the operator */ public synchronized void updateOperatorNumeric(String operatorNumeric) { // Check if the operator numeric changes. String msg = "updateOperatorNumeric. mcc/mnc=" + operatorNumeric; if (DBG) log(msg); mLocalLog.log(msg); if (!Objects.equals(mOperatorNumeric, operatorNumeric)) { if (DBG) { log("onUpdateOperatorNumeric: operator numeric changes to " + operatorNumeric); } mOperatorNumeric = operatorNumeric; // If the operator numeric becomes unavailable, we need to get the latest cell info so // that we can get MCC from it. if (TextUtils.isEmpty(mOperatorNumeric)) { if (DBG) { log("Operator numeric unavailable. Get latest cell info from the modem."); } getCellInfo(); } updateLocale(); } } /** * Get the delay time to get cell info from modem. The delay time grows exponentially to prevent * battery draining. * * @param failCount Count of invalid cell info we've got so far. * @return The delay time for next get cell info */ private long getCellInfoDelayTime(int failCount) { // Exponentially grow the delay time long delay = CELL_INFO_MIN_DELAY_MS * (long) Math.pow(2, failCount - 1); if (delay < CELL_INFO_MIN_DELAY_MS) { delay = CELL_INFO_MIN_DELAY_MS; } else if (delay > CELL_INFO_MAX_DELAY_MS) { delay = CELL_INFO_MAX_DELAY_MS; } return delay; } /** * Get cell info from the modem. */ private void getCellInfo() { // Get all cell info. Passing null to use default worksource, which indicates the original // request is from telephony internally. mCellInfo = mPhone.getAllCellInfo(null); String msg = "getCellInfo: cell info=" + mCellInfo; if (DBG) log(msg); mLocalLog.log(msg); if (mCellInfo == null || mCellInfo.size() == 0) { // If we can't get a valid cell info. Try it again later. long delay = getCellInfoDelayTime(++mFailCellInfoCount); if (DBG) log("Can't get cell info. Try again in " + delay / 1000 + " secs."); sendMessageDelayed(obtainMessage(EVENT_GET_CELL_INFO), delay); } else { mFailCellInfoCount = 0; // We successfully got cell info from the modem. Cancel the queued get cell info event // if there is any. removeMessages(EVENT_GET_CELL_INFO); } } /** * Update the device's current locale */ private void updateLocale() { // If MCC is available from network service state, use it first. String mcc = null; String countryIso = null; if (!TextUtils.isEmpty(mOperatorNumeric)) { try { mcc = mOperatorNumeric.substring(0, 3); countryIso = MccTable.countryCodeForMcc(mcc); } catch (StringIndexOutOfBoundsException ex) { loge("updateLocale: Can't get country from operator numeric. mcc = " + mcc + ". ex=" + ex); } } // If for any reason we can't get country from operator numeric, try to get it from cell // info. if (TextUtils.isEmpty(countryIso)) { mcc = getMccFromCellInfo(); countryIso = MccTable.countryCodeForMcc(mcc); } String msg = "updateLocale: mcc = " + mcc + ", country = " + countryIso; log(msg); mLocalLog.log(msg); if (!Objects.equals(countryIso, mCurrentCountryIso)) { log("updateLocale: Change the current country to " + countryIso); mCurrentCountryIso = countryIso; TelephonyManager.setTelephonyProperty(mPhone.getPhoneId(), TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, mCurrentCountryIso); // Set the country code for wifi. This sets allowed wifi channels based on the // country of the carrier we see. If we can't see any, reset to 0 so we don't // broadcast on forbidden channels. ((WifiManager) mPhone.getContext().getSystemService(Context.WIFI_SERVICE)) .setCountryCode(countryIso, false); } } private void log(String msg) { Rlog.d(TAG, msg); } private void loge(String msg) { Rlog.e(TAG, msg); } /** * Print the DeviceStateMonitor into the given stream. * * @param fd The raw file descriptor that the dump is being sent to. * @param pw A PrintWriter to which the dump is to be set. * @param args Additional arguments to the dump request. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); pw.println("LocaleTracker:"); ipw.increaseIndent(); ipw.println("mOperatorNumeric = " + mOperatorNumeric); ipw.println("mSimState = " + mSimState); ipw.println("mCellInfo = " + mCellInfo); ipw.println("mCurrentCountryIso = " + mCurrentCountryIso); ipw.println("mFailCellInfoCount = " + mFailCellInfoCount); ipw.println("Local logs:"); ipw.increaseIndent(); mLocalLog.dump(fd, ipw, args); ipw.decreaseIndent(); ipw.decreaseIndent(); ipw.flush(); } }
src/java/com/android/internal/telephony/MccTable.java +34 −56 Original line number Diff line number Diff line Loading @@ -19,11 +19,9 @@ package com.android.internal.telephony; import android.app.ActivityManager; import android.content.Context; import android.content.res.Configuration; import android.net.wifi.WifiManager; import android.os.Build; import android.os.RemoteException; import android.os.SystemProperties; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Slog; Loading Loading @@ -112,6 +110,19 @@ public final class MccTable { } } /** * Given a GSM Mobile Country Code, returns * an ISO two-character country code if available. * Returns empty string if unavailable. */ public static String countryCodeForMcc(String mcc) { try { return countryCodeForMcc(Integer.parseInt(mcc)); } catch (NumberFormatException ex) { return ""; } } /** * Given a GSM Mobile Country Code, returns * an ISO 2-3 character language code if available. Loading Loading @@ -159,11 +170,9 @@ public final class MccTable { * 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 * @param fromServiceState true if coming from the radio service state, false if from SIM */ public static void updateMccMncConfiguration(Context context, String mccmnc, boolean fromServiceState) { Slog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc + "' fromServiceState=" + fromServiceState); public static void updateMccMncConfiguration(Context context, String mccmnc) { Slog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc); if (Build.IS_DEBUGGABLE) { String overrideMcc = SystemProperties.get("persist.sys.override_mcc"); Loading @@ -176,19 +185,11 @@ public final class MccTable { if (!TextUtils.isEmpty(mccmnc)) { int mcc, mnc; String defaultMccMnc = TelephonyManager.getDefault().getSimOperatorNumeric(); Slog.d(LOG_TAG, "updateMccMncConfiguration defaultMccMnc=" + defaultMccMnc); //Update mccmnc only for default subscription in case of MultiSim. // if (!defaultMccMnc.equals(mccmnc)) { // Slog.d(LOG_TAG, "Not a Default subscription, ignoring mccmnc config update."); // return; // } try { mcc = Integer.parseInt(mccmnc.substring(0, 3)); mnc = Integer.parseInt(mccmnc.substring(3)); } catch (NumberFormatException e) { Slog.e(LOG_TAG, "Error parsing IMSI: " + mccmnc); } catch (NumberFormatException | StringIndexOutOfBoundsException ex) { Slog.e(LOG_TAG, "Error parsing IMSI: " + mccmnc + ". ex=" + ex); return; } Loading @@ -196,10 +197,7 @@ public final class MccTable { if (mcc != 0) { setTimezoneFromMccIfNeeded(context, mcc); } if (fromServiceState) { setWifiCountryCodeFromMcc(context, mcc); } else { // from SIM try { Configuration config = new Configuration(); boolean updateConfig = false; Loading @@ -219,12 +217,6 @@ public final class MccTable { Slog.e(LOG_TAG, "Can't update configuration", e); } } } else { if (fromServiceState) { // an empty mccmnc means no signal - tell wifi we don't know setWifiCountryCodeFromMcc(context, 0); } } } /** Loading Loading @@ -396,20 +388,6 @@ public final class MccTable { return locale; } /** * Set the country code for wifi. This sets allowed wifi channels based on the * country of the carrier we see. If we can't see any, reset to 0 so we don't * broadcast on forbidden channels. * @param context Context to act on. * @param mcc Mobile Country Code of the operator. 0 if not known */ private static void setWifiCountryCodeFromMcc(Context context, int mcc) { String country = MccTable.countryCodeForMcc(mcc); Slog.d(LOG_TAG, "WIFI_COUNTRY_CODE set to " + country); WifiManager wM = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); wM.setCountryCode(country, false); } static { sTable = new ArrayList<MccEntry>(240); Loading
src/java/com/android/internal/telephony/ServiceStateTracker.java +26 −23 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.os.AsyncResult; import android.os.BaseBundle; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.os.PersistableBundle; import android.os.Registrant; Loading Loading @@ -259,6 +260,9 @@ public class ServiceStateTracker extends Handler { private final RatRatcheter mRatRatcheter; private final HandlerThread mHandlerThread; private final LocaleTracker mLocaleTracker; private final LocalLog mRoamingLog = new LocalLog(10); private final LocalLog mAttachLog = new LocalLog(10); private final LocalLog mPhoneTypeLog = new LocalLog(10); Loading Loading @@ -508,6 +512,13 @@ public class ServiceStateTracker extends Handler { this, EVENT_NETWORK_STATE_CHANGED, null); } // Create a new handler thread dedicated for locale tracker because the blocking // getAllCellInfo call requires clients calling from a different thread. mHandlerThread = new HandlerThread(LocaleTracker.class.getSimpleName()); mHandlerThread.start(); mLocaleTracker = TelephonyComponentFactory.getInstance().makeLocaleTracker( mPhone, mHandlerThread.getLooper()); mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null); mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null); Loading Loading @@ -655,6 +666,7 @@ public class ServiceStateTracker extends Handler { mCi.unregisterForPhysicalChannelConfiguration(this); mSubscriptionManager .removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener); mHandlerThread.quit(); mCi.unregisterForImsNetworkStateChanged(this); mPhone.getCarrierActionAgent().unregisterForCarrierAction(this, CARRIER_ACTION_SET_RADIO_ENABLED); Loading Loading @@ -2839,11 +2851,12 @@ public class ServiceStateTracker extends Handler { } tm.setNetworkOperatorNumericForPhone(mPhone.getPhoneId(), operatorNumeric); updateCarrierMccMncConfiguration(operatorNumeric, prevOperatorNumeric, mPhone.getContext()); if (isInvalidOperatorNumeric(operatorNumeric)) { if (DBG) log("operatorNumeric " + operatorNumeric + " is invalid"); tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), ""); // Passing empty string is important for the first update. The initial value of // operator numeric in locale tracker is null. mLocaleTracker.updateOperatorNumeric(""); mNitzState.handleNetworkUnavailable(); } else if (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) { // If the device is on IWLAN, modems manufacture a ServiceState with the MCC/MNC of Loading @@ -2855,15 +2868,8 @@ public class ServiceStateTracker extends Handler { setOperatorIdd(operatorNumeric); } // Update ISO. String countryIsoCode = ""; try { String mcc = operatorNumeric.substring(0, 3); countryIsoCode = MccTable.countryCodeForMcc(Integer.parseInt(mcc)); } catch (NumberFormatException | StringIndexOutOfBoundsException ex) { loge("pollStateDone: countryCodeForMcc error: " + ex); } tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), countryIsoCode); mLocaleTracker.updateOperatorNumeric(operatorNumeric); String countryIsoCode = mLocaleTracker.getCurrentCountry(); // Update Time Zone. boolean iccCardExists = iccCardExists(); Loading Loading @@ -4325,6 +4331,8 @@ public class ServiceStateTracker extends Handler { pw.println(" mLteRsrpBoost=" + mLteRsrpBoost); dumpEarfcnPairList(pw); mLocaleTracker.dump(fd, pw, args); pw.println(" Roaming Log:"); IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); Loading Loading @@ -4377,15 +4385,6 @@ public class ServiceStateTracker extends Handler { return value; } protected void updateCarrierMccMncConfiguration(String newOp, String oldOp, Context context) { // if we have a change in operator, notify wifi (even to/from none) if (((newOp == null) && (TextUtils.isEmpty(oldOp) == false)) || ((newOp != null) && (newOp.equals(oldOp) == false))) { log("update mccmnc=" + newOp + " fromServiceState=true"); MccTable.updateMccMncConfiguration(context, newOp, true); } } /** * Check ISO country by MCC to see if phone is roaming in same registered country */ Loading @@ -4402,8 +4401,8 @@ public class ServiceStateTracker extends Handler { boolean inSameCountry = true; final String networkMCC = operatorNumeric.substring(0, 3); final String homeMCC = homeNumeric.substring(0, 3); final String networkCountry = MccTable.countryCodeForMcc(Integer.parseInt(networkMCC)); final String homeCountry = MccTable.countryCodeForMcc(Integer.parseInt(homeMCC)); final String networkCountry = MccTable.countryCodeForMcc(networkMCC); final String homeCountry = MccTable.countryCodeForMcc(homeMCC); if (networkCountry.isEmpty() || homeCountry.isEmpty()) { // Not a valid country return false; Loading Loading @@ -4644,4 +4643,8 @@ public class ServiceStateTracker extends Handler { // Return static default defined in CarrierConfigManager. return CarrierConfigManager.getDefaultConfig(); } public LocaleTracker getLocaleTracker() { return mLocaleTracker; } }
src/java/com/android/internal/telephony/SubscriptionController.java +1 −1 Original line number Diff line number Diff line Loading @@ -1793,7 +1793,7 @@ public class SubscriptionController extends ISub.Stub { mDefaultFallbackSubId = subId; // Update MCC MNC device configuration information String defaultMccMnc = mTelephonyManager.getSimOperatorNumericForPhone(phoneId); MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false); MccTable.updateMccMncConfiguration(mContext, defaultMccMnc); // Broadcast an Intent for default sub change Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED); Loading