| /* |
| * Copyright 2017 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.provider.Telephony.CarrierId; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.content.BroadcastReceiver; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.database.ContentObserver; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.AsyncResult; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.provider.Telephony; |
| import android.service.carrier.CarrierIdentifier; |
| import android.telephony.CarrierConfigManager; |
| import android.telephony.PhoneStateListener; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| import android.text.TextUtils; |
| import android.util.LocalLog; |
| import android.util.Log; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.telephony.metrics.CarrierIdMatchStats; |
| import com.android.internal.telephony.metrics.TelephonyMetrics; |
| import com.android.internal.telephony.subscription.SubscriptionManagerService; |
| import com.android.internal.telephony.uicc.IccRecords; |
| import com.android.internal.telephony.uicc.UiccController; |
| import com.android.internal.telephony.util.TelephonyUtils; |
| import com.android.internal.util.IndentingPrintWriter; |
| import com.android.telephony.Rlog; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Locale; |
| |
| /** |
| * CarrierResolver identifies the subscription carrier and returns a canonical carrier Id |
| * and a user friendly carrier name. CarrierResolver reads subscription info and check against |
| * all carrier matching rules stored in CarrierIdProvider. It is msim aware, each phone has a |
| * dedicated CarrierResolver. |
| */ |
| public class CarrierResolver extends Handler { |
| private static final String LOG_TAG = CarrierResolver.class.getSimpleName(); |
| private static final boolean DBG = true; |
| private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE); |
| |
| // events to trigger carrier identification |
| private static final int SIM_LOAD_EVENT = 1; |
| private static final int ICC_CHANGED_EVENT = 2; |
| private static final int PREFER_APN_UPDATE_EVENT = 3; |
| private static final int CARRIER_ID_DB_UPDATE_EVENT = 4; |
| |
| private static final Uri CONTENT_URL_PREFER_APN = Uri.withAppendedPath( |
| Telephony.Carriers.CONTENT_URI, "preferapn"); |
| |
| // Test purpose only. |
| private static final String TEST_ACTION = "com.android.internal.telephony" |
| + ".ACTION_TEST_OVERRIDE_CARRIER_ID"; |
| |
| // cached version of the carrier list, so that we don't need to re-query it every time. |
| private Integer mCarrierListVersion; |
| // cached matching rules based mccmnc to speed up resolution |
| private List<CarrierMatchingRule> mCarrierMatchingRulesOnMccMnc = new ArrayList<>(); |
| // cached carrier Id |
| private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; |
| // cached specific carrier Id |
| private int mSpecificCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; |
| // cached MNO carrier Id. mno carrier shares the same mccmnc as cid and can be solely |
| // identified by mccmnc only. If there is no such mno carrier, mno carrier id equals to |
| // the cid. |
| private int mMnoCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; |
| // cached carrier name |
| private String mCarrierName; |
| private String mSpecificCarrierName; |
| // cached preferapn name |
| private String mPreferApn; |
| // override for testing purpose |
| private String mTestOverrideApn; |
| private String mTestOverrideCarrierPriviledgeRule; |
| // cached service provider name. telephonyManager API returns empty string as default value. |
| // some carriers need to target devices with Empty SPN. In that case, carrier matching rule |
| // should specify "" spn explicitly. |
| private String mSpn = ""; |
| |
| private Context mContext; |
| private Phone mPhone; |
| private IccRecords mIccRecords; |
| private final LocalLog mCarrierIdLocalLog = new LocalLog(16); |
| private final TelephonyManager mTelephonyMgr; |
| |
| private final ContentObserver mContentObserver = new ContentObserver(this) { |
| @Override |
| public void onChange(boolean selfChange, Uri uri) { |
| if (Telephony.Carriers.CONTENT_URI.equals(uri)) { |
| logd("onChange URI: " + uri); |
| sendEmptyMessage(PREFER_APN_UPDATE_EVENT); |
| } else if (CarrierId.All.CONTENT_URI.equals(uri)) { |
| logd("onChange URI: " + uri); |
| sendEmptyMessage(CARRIER_ID_DB_UPDATE_EVENT); |
| } |
| } |
| }; |
| |
| /** |
| * A broadcast receiver used for overriding carrier id for testing. There are six parameters, |
| * only override_carrier_id is required, the others are options. |
| * |
| * To override carrier id by adb command, e.g.: |
| * adb shell am broadcast -a com.android.internal.telephony.ACTION_TEST_OVERRIDE_CARRIER_ID \ |
| * --ei override_carrier_id 1 |
| * --ei override_specific_carrier_id 1 |
| * --ei override_mno_carrier_id 1 |
| * --es override_carrier_name test |
| * --es override_specific_carrier_name test |
| * --ei sub_id 1 |
| */ |
| private final BroadcastReceiver mCarrierIdTestReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| int phoneId = mPhone.getPhoneId(); |
| int carrierId = intent.getIntExtra("override_carrier_id", |
| TelephonyManager.UNKNOWN_CARRIER_ID); |
| int specificCarrierId = intent.getIntExtra("override_specific_carrier_id", carrierId); |
| int mnoCarrierId = intent.getIntExtra("override_mno_carrier_id", carrierId); |
| String carrierName = intent.getStringExtra("override_carrier_name"); |
| String specificCarrierName = intent.getStringExtra("override_specific_carrier_name"); |
| int subId = intent.getIntExtra("sub_id", |
| SubscriptionManager.getDefaultSubscriptionId()); |
| |
| if (carrierId <= 0) { |
| logd("Override carrier id must be greater than 0.", phoneId); |
| return; |
| } else if (subId != mPhone.getSubId()) { |
| logd("Override carrier id failed. The sub id doesn't same as phone's sub id.", |
| phoneId); |
| return; |
| } else { |
| logd("Override carrier id to: " + carrierId, phoneId); |
| logd("Override specific carrier id to: " + specificCarrierId, phoneId); |
| logd("Override mno carrier id to: " + mnoCarrierId, phoneId); |
| logd("Override carrier name to: " + carrierName, phoneId); |
| logd("Override specific carrier name to: " + specificCarrierName, phoneId); |
| updateCarrierIdAndName( |
| carrierId, carrierName != null ? carrierName : "", |
| specificCarrierId, specificCarrierName != null ? carrierName : "", |
| mnoCarrierId, false); |
| } |
| } |
| }; |
| |
| public CarrierResolver(Phone phone) { |
| logd("Creating CarrierResolver[" + phone.getPhoneId() + "]"); |
| mContext = phone.getContext(); |
| mPhone = phone; |
| mTelephonyMgr = TelephonyManager.from(mContext); |
| |
| // register events |
| mContext.getContentResolver().registerContentObserver(CONTENT_URL_PREFER_APN, false, |
| mContentObserver); |
| mContext.getContentResolver().registerContentObserver( |
| CarrierId.All.CONTENT_URI, false, mContentObserver); |
| UiccController.getInstance().registerForIccChanged(this, ICC_CHANGED_EVENT, null); |
| |
| if (TelephonyUtils.IS_DEBUGGABLE) { |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(TEST_ACTION); |
| mContext.registerReceiver(mCarrierIdTestReceiver, filter); |
| } |
| } |
| |
| /** |
| * This is triggered from UiccController after sim state change. |
| * The sequence of sim loading would be |
| * 1. OnSubscriptionsChangedListener |
| * 2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED |
| * /ACTION_SIM_APPLICATION_STATE_CHANGED |
| * 3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED |
| * |
| * For SIM refresh either reset or init refresh type, UiccController will re-trigger |
| * carrier identification with sim loaded state. Framework today silently handle single file |
| * refresh type. |
| * TODO: check fileId from single file refresh, if the refresh file is IMSI, gid1 or other |
| * records which might change carrier id, framework should trigger sim loaded state just like |
| * other refresh events: INIT or RESET and which will ultimately trigger carrier |
| * re-identification. |
| */ |
| public void resolveSubscriptionCarrierId(String simState) { |
| logd("[resolveSubscriptionCarrierId] simState: " + simState); |
| switch (simState) { |
| case IccCardConstants.INTENT_VALUE_ICC_ABSENT: |
| case IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR: |
| // only clear carrier id on absent to avoid transition to unknown carrier id during |
| // intermediate states of sim refresh |
| handleSimAbsent(); |
| break; |
| case IccCardConstants.INTENT_VALUE_ICC_LOADED: |
| handleSimLoaded(false); |
| break; |
| } |
| } |
| |
| private void handleSimLoaded(boolean isSimOverride) { |
| if (mIccRecords != null) { |
| /** |
| * returns empty string to be consistent with |
| * {@link TelephonyManager#getSimOperatorName()} |
| */ |
| mSpn = (mIccRecords.getServiceProviderName() == null) ? "" |
| : mIccRecords.getServiceProviderName(); |
| } else { |
| loge("mIccRecords is null on SIM_LOAD_EVENT, could not get SPN"); |
| } |
| mPreferApn = getPreferApn(); |
| loadCarrierMatchingRulesOnMccMnc( |
| false /* update carrier config */, |
| isSimOverride); |
| } |
| |
| private void handleSimAbsent() { |
| mCarrierMatchingRulesOnMccMnc.clear(); |
| mSpn = null; |
| mPreferApn = null; |
| updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null, |
| TelephonyManager.UNKNOWN_CARRIER_ID, null, |
| TelephonyManager.UNKNOWN_CARRIER_ID, false); |
| } |
| |
| private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { |
| @Override |
| public void onCallStateChanged(int state, String ignored) { |
| } |
| }; |
| |
| /** |
| * Entry point for the carrier identification. |
| * |
| * 1. SIM_LOAD_EVENT |
| * This indicates that all SIM records has been loaded and its first entry point for the |
| * carrier identification. Note, there are other attributes could be changed on the fly |
| * like APN. We cached all carrier matching rules based on MCCMNC to speed |
| * up carrier resolution on following trigger events. |
| * |
| * 2. PREFER_APN_UPDATE_EVENT |
| * This indicates prefer apn has been changed. It could be triggered when user modified |
| * APN settings or when default data connection first establishes on the current carrier. |
| * We follow up on this by querying prefer apn sqlite and re-issue carrier identification |
| * with the updated prefer apn name. |
| * |
| * 3. CARRIER_ID_DB_UPDATE_EVENT |
| * This indicates that carrierIdentification database which stores all matching rules |
| * has been updated. It could be triggered from OTA or assets update. |
| */ |
| @Override |
| public void handleMessage(Message msg) { |
| if (DBG) logd("handleMessage: " + msg.what); |
| switch (msg.what) { |
| case SIM_LOAD_EVENT: |
| AsyncResult result = (AsyncResult) msg.obj; |
| boolean isSimOverride = false; |
| if (result != null) { |
| isSimOverride = result.userObj instanceof Boolean && (Boolean) result.userObj; |
| } |
| handleSimLoaded(isSimOverride); |
| break; |
| case CARRIER_ID_DB_UPDATE_EVENT: |
| // clean the cached carrier list version, so that a new one will be queried. |
| mCarrierListVersion = null; |
| loadCarrierMatchingRulesOnMccMnc(true /* update carrier config*/, false); |
| break; |
| case PREFER_APN_UPDATE_EVENT: |
| String preferApn = getPreferApn(); |
| if (!equals(mPreferApn, preferApn, true)) { |
| logd("[updatePreferApn] from:" + mPreferApn + " to:" + preferApn); |
| mPreferApn = preferApn; |
| matchSubscriptionCarrier(true /* update carrier config*/, false); |
| } |
| break; |
| case ICC_CHANGED_EVENT: |
| // all records used for carrier identification are from SimRecord. |
| final IccRecords newIccRecords = UiccController.getInstance().getIccRecords( |
| mPhone.getPhoneId(), UiccController.APP_FAM_3GPP); |
| if (mIccRecords != newIccRecords) { |
| if (mIccRecords != null) { |
| logd("Removing stale icc objects."); |
| mIccRecords.unregisterForRecordsOverride(this); |
| mIccRecords = null; |
| } |
| if (newIccRecords != null) { |
| logd("new Icc object"); |
| newIccRecords.registerForRecordsOverride(this, SIM_LOAD_EVENT, |
| /* is sim override*/true); |
| mIccRecords = newIccRecords; |
| } |
| } |
| break; |
| default: |
| loge("invalid msg: " + msg.what); |
| break; |
| } |
| } |
| |
| private void loadCarrierMatchingRulesOnMccMnc( |
| boolean updateCarrierConfig, |
| boolean isSimOverride) { |
| try { |
| String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId()); |
| Cursor cursor = mContext.getContentResolver().query( |
| CarrierId.All.CONTENT_URI, |
| /* projection */ null, |
| /* selection */ CarrierId.All.MCCMNC + "=?", |
| /* selectionArgs */ new String[]{mccmnc}, null); |
| try { |
| if (cursor != null) { |
| if (VDBG) { |
| logd("[loadCarrierMatchingRules]- " + cursor.getCount() |
| + " Records(s) in DB" + " mccmnc: " + mccmnc); |
| } |
| mCarrierMatchingRulesOnMccMnc.clear(); |
| while (cursor.moveToNext()) { |
| mCarrierMatchingRulesOnMccMnc.add(makeCarrierMatchingRule(cursor)); |
| } |
| matchSubscriptionCarrier(updateCarrierConfig, isSimOverride); |
| |
| // Generate metrics related to carrier ID table version. |
| CarrierIdMatchStats.sendCarrierIdTableVersion(getCarrierListVersion()); |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| } catch (Exception ex) { |
| loge("[loadCarrierMatchingRules]- ex: " + ex); |
| } |
| } |
| |
| private String getCarrierNameFromId(int cid) { |
| try { |
| Cursor cursor = mContext.getContentResolver().query( |
| CarrierId.All.CONTENT_URI, |
| /* projection */ null, |
| /* selection */ CarrierId.CARRIER_ID + "=?", |
| /* selectionArgs */ new String[]{cid + ""}, null); |
| try { |
| if (cursor != null) { |
| if (VDBG) { |
| logd("[getCarrierNameFromId]- " + cursor.getCount() |
| + " Records(s) in DB" + " cid: " + cid); |
| } |
| while (cursor.moveToNext()) { |
| return cursor.getString(cursor.getColumnIndex(CarrierId.CARRIER_NAME)); |
| } |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| } catch (Exception ex) { |
| loge("[getCarrierNameFromId]- ex: " + ex); |
| } |
| return null; |
| } |
| |
| private static List<CarrierMatchingRule> getCarrierMatchingRulesFromMccMnc( |
| @NonNull Context context, String mccmnc) { |
| List<CarrierMatchingRule> rules = new ArrayList<>(); |
| try { |
| Cursor cursor = context.getContentResolver().query( |
| CarrierId.All.CONTENT_URI, |
| /* projection */ null, |
| /* selection */ CarrierId.All.MCCMNC + "=?", |
| /* selectionArgs */ new String[]{mccmnc}, null); |
| try { |
| if (cursor != null) { |
| if (VDBG) { |
| logd("[loadCarrierMatchingRules]- " + cursor.getCount() |
| + " Records(s) in DB" + " mccmnc: " + mccmnc); |
| } |
| rules.clear(); |
| while (cursor.moveToNext()) { |
| rules.add(makeCarrierMatchingRule(cursor)); |
| } |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| } catch (Exception ex) { |
| loge("[loadCarrierMatchingRules]- ex: " + ex); |
| } |
| return rules; |
| } |
| |
| private String getPreferApn() { |
| // return test overrides if present |
| if (!TextUtils.isEmpty(mTestOverrideApn)) { |
| logd("[getPreferApn]- " + mTestOverrideApn + " test override"); |
| return mTestOverrideApn; |
| } |
| Cursor cursor = mContext.getContentResolver().query( |
| Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapn/subId/" |
| + mPhone.getSubId()), /* projection */ new String[]{Telephony.Carriers.APN}, |
| /* selection */ null, /* selectionArgs */ null, /* sortOrder */ null); |
| try { |
| if (cursor != null) { |
| if (VDBG) { |
| logd("[getPreferApn]- " + cursor.getCount() + " Records(s) in DB"); |
| } |
| while (cursor.moveToNext()) { |
| String apn = cursor.getString(cursor.getColumnIndexOrThrow( |
| Telephony.Carriers.APN)); |
| logd("[getPreferApn]- " + apn); |
| return apn; |
| } |
| } |
| } catch (Exception ex) { |
| loge("[getPreferApn]- exception: " + ex); |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| return null; |
| } |
| |
| private boolean isPreferApnUserEdited(@NonNull String preferApn) { |
| try (Cursor cursor = mContext.getContentResolver().query( |
| Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, |
| "preferapn/subId/" + mPhone.getSubId()), |
| /* projection */ new String[]{Telephony.Carriers.EDITED_STATUS}, |
| /* selection */ Telephony.Carriers.APN + "=?", |
| /* selectionArgs */ new String[]{preferApn}, /* sortOrder */ null) ) { |
| if (cursor != null && cursor.moveToFirst()) { |
| return cursor.getInt(cursor.getColumnIndexOrThrow( |
| Telephony.Carriers.EDITED_STATUS)) == Telephony.Carriers.USER_EDITED; |
| } |
| } catch (Exception ex) { |
| loge("[isPreferApnUserEdited]- exception: " + ex); |
| } |
| return false; |
| } |
| |
| public void setTestOverrideApn(String apn) { |
| logd("[setTestOverrideApn]: " + apn); |
| mTestOverrideApn = apn; |
| } |
| |
| public void setTestOverrideCarrierPriviledgeRule(String rule) { |
| logd("[setTestOverrideCarrierPriviledgeRule]: " + rule); |
| mTestOverrideCarrierPriviledgeRule = rule; |
| } |
| |
| private void updateCarrierIdAndName(int cid, String name, |
| int specificCarrierId, String specificCarrierName, |
| int mnoCid, boolean isSimOverride) { |
| boolean update = false; |
| if (specificCarrierId != mSpecificCarrierId) { |
| logd("[updateSpecificCarrierId] from:" + mSpecificCarrierId + " to:" |
| + specificCarrierId); |
| mSpecificCarrierId = specificCarrierId; |
| update = true; |
| } |
| if (specificCarrierName != mSpecificCarrierName) { |
| logd("[updateSpecificCarrierName] from:" + mSpecificCarrierName + " to:" |
| + specificCarrierName); |
| mSpecificCarrierName = specificCarrierName; |
| update = true; |
| } |
| if (update) { |
| mCarrierIdLocalLog.log("[updateSpecificCarrierIdAndName] cid:" |
| + mSpecificCarrierId + " name:" + mSpecificCarrierName); |
| final Intent intent = new Intent(TelephonyManager |
| .ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED); |
| intent.putExtra(TelephonyManager.EXTRA_SPECIFIC_CARRIER_ID, mSpecificCarrierId); |
| intent.putExtra(TelephonyManager.EXTRA_SPECIFIC_CARRIER_NAME, mSpecificCarrierName); |
| intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId()); |
| mContext.sendBroadcast(intent); |
| |
| // notify content observers for specific carrier id change event. |
| ContentValues cv = new ContentValues(); |
| cv.put(CarrierId.SPECIFIC_CARRIER_ID, mSpecificCarrierId); |
| cv.put(CarrierId.SPECIFIC_CARRIER_ID_NAME, mSpecificCarrierName); |
| mContext.getContentResolver().update( |
| Telephony.CarrierId.getSpecificCarrierIdUriForSubscriptionId(mPhone.getSubId()), |
| cv, null, null); |
| } |
| |
| update = false; |
| if (!equals(name, mCarrierName, true)) { |
| logd("[updateCarrierName] from:" + mCarrierName + " to:" + name); |
| mCarrierName = name; |
| update = true; |
| } |
| if (cid != mCarrierId) { |
| logd("[updateCarrierId] from:" + mCarrierId + " to:" + cid); |
| mCarrierId = cid; |
| update = true; |
| } |
| if (mnoCid != mMnoCarrierId) { |
| logd("[updateMnoCarrierId] from:" + mMnoCarrierId + " to:" + mnoCid); |
| mMnoCarrierId = mnoCid; |
| update = true; |
| } |
| if (update) { |
| mCarrierIdLocalLog.log("[updateCarrierIdAndName] cid:" + mCarrierId + " name:" |
| + mCarrierName + " mnoCid:" + mMnoCarrierId); |
| final Intent intent = new Intent(TelephonyManager |
| .ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED); |
| intent.putExtra(TelephonyManager.EXTRA_CARRIER_ID, mCarrierId); |
| intent.putExtra(TelephonyManager.EXTRA_CARRIER_NAME, mCarrierName); |
| intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId()); |
| mContext.sendBroadcast(intent); |
| |
| // notify content observers for carrier id change event |
| ContentValues cv = new ContentValues(); |
| cv.put(CarrierId.CARRIER_ID, mCarrierId); |
| cv.put(CarrierId.CARRIER_NAME, mCarrierName); |
| mContext.getContentResolver().update( |
| Telephony.CarrierId.getUriForSubscriptionId(mPhone.getSubId()), cv, null, null); |
| } |
| // during esim profile switch, there is no sim absent thus carrier id will persist and |
| // might not trigger an update if switch profiles for the same carrier. thus always update |
| // subscriptioninfo db to make sure we have correct carrier id set. |
| if (SubscriptionManager.isValidSubscriptionId(mPhone.getSubId()) && !isSimOverride) { |
| // only persist carrier id to simInfo db when subId is valid. |
| SubscriptionManagerService.getInstance().setCarrierId(mPhone.getSubId(), mCarrierId); |
| } |
| } |
| |
| private static CarrierMatchingRule makeCarrierMatchingRule(Cursor cursor) { |
| String certs = cursor.getString( |
| cursor.getColumnIndexOrThrow(CarrierId.All.PRIVILEGE_ACCESS_RULE)); |
| return new CarrierMatchingRule( |
| cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.MCCMNC)), |
| cursor.getString(cursor.getColumnIndexOrThrow( |
| CarrierId.All.IMSI_PREFIX_XPATTERN)), |
| cursor.getString(cursor.getColumnIndexOrThrow( |
| CarrierId.All.ICCID_PREFIX)), |
| cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID1)), |
| cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID2)), |
| cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.PLMN)), |
| cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.SPN)), |
| cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.APN)), |
| (TextUtils.isEmpty(certs) ? null : new ArrayList<>(Arrays.asList(certs))), |
| cursor.getInt(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_ID)), |
| cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_NAME)), |
| cursor.getInt(cursor.getColumnIndexOrThrow(CarrierId.PARENT_CARRIER_ID))); |
| } |
| |
| /** |
| * carrier matching attributes with corresponding cid |
| */ |
| public static class CarrierMatchingRule { |
| /** |
| * These scores provide the hierarchical relationship between the attributes, intended to |
| * resolve conflicts in a deterministic way. The scores are constructed such that a match |
| * from a higher tier will beat any subsequent match which does not match at that tier, |
| * so MCCMNC beats everything else. This avoids problems when two (or more) carriers rule |
| * matches as the score helps to find the best match uniquely. e.g., |
| * rule 1 {mccmnc, imsi} rule 2 {mccmnc, imsi, gid1} and rule 3 {mccmnc, imsi, gid2} all |
| * matches with subscription data. rule 2 wins with the highest matching score. |
| */ |
| private static final int SCORE_MCCMNC = 1 << 8; |
| private static final int SCORE_IMSI_PREFIX = 1 << 7; |
| private static final int SCORE_ICCID_PREFIX = 1 << 6; |
| private static final int SCORE_GID1 = 1 << 5; |
| private static final int SCORE_GID2 = 1 << 4; |
| private static final int SCORE_PLMN = 1 << 3; |
| private static final int SCORE_PRIVILEGE_ACCESS_RULE = 1 << 2; |
| private static final int SCORE_SPN = 1 << 1; |
| private static final int SCORE_APN = 1 << 0; |
| |
| private static final int SCORE_INVALID = -1; |
| |
| // carrier matching attributes |
| public final String mccMnc; |
| public final String imsiPrefixPattern; |
| public final String iccidPrefix; |
| public final String gid1; |
| public final String gid2; |
| public final String plmn; |
| public final String spn; |
| public final String apn; |
| // there can be multiple certs configured in the UICC |
| public final List<String> privilegeAccessRule; |
| |
| // user-facing carrier name |
| private String mName; |
| // unique carrier id |
| private int mCid; |
| // unique parent carrier id |
| private int mParentCid; |
| |
| private int mScore = 0; |
| |
| @VisibleForTesting |
| public CarrierMatchingRule(String mccmnc, String imsiPrefixPattern, String iccidPrefix, |
| String gid1, String gid2, String plmn, String spn, String apn, |
| List<String> privilegeAccessRule, int cid, String name, int parentCid) { |
| mccMnc = mccmnc; |
| this.imsiPrefixPattern = imsiPrefixPattern; |
| this.iccidPrefix = iccidPrefix; |
| this.gid1 = gid1; |
| this.gid2 = gid2; |
| this.plmn = plmn; |
| this.spn = spn; |
| this.apn = apn; |
| this.privilegeAccessRule = privilegeAccessRule; |
| mCid = cid; |
| mName = name; |
| mParentCid = parentCid; |
| } |
| |
| private CarrierMatchingRule(CarrierMatchingRule rule) { |
| mccMnc = rule.mccMnc; |
| imsiPrefixPattern = rule.imsiPrefixPattern; |
| iccidPrefix = rule.iccidPrefix; |
| gid1 = rule.gid1; |
| gid2 = rule.gid2; |
| plmn = rule.plmn; |
| spn = rule.spn; |
| apn = rule.apn; |
| privilegeAccessRule = rule.privilegeAccessRule; |
| mCid = rule.mCid; |
| mName = rule.mName; |
| mParentCid = rule.mParentCid; |
| } |
| |
| // Calculate matching score. Values which aren't set in the rule are considered "wild". |
| // All values in the rule must match in order for the subscription to be considered part of |
| // the carrier. Otherwise, a invalid score -1 will be assigned. A match from a higher tier |
| // will beat any subsequent match which does not match at that tier. When there are multiple |
| // matches at the same tier, the match with highest score will be used. |
| public void match(CarrierMatchingRule subscriptionRule) { |
| mScore = 0; |
| if (mccMnc != null) { |
| if (!CarrierResolver.equals(subscriptionRule.mccMnc, mccMnc, false)) { |
| mScore = SCORE_INVALID; |
| return; |
| } |
| mScore += SCORE_MCCMNC; |
| } |
| if (imsiPrefixPattern != null) { |
| if (!imsiPrefixMatch(subscriptionRule.imsiPrefixPattern, imsiPrefixPattern)) { |
| mScore = SCORE_INVALID; |
| return; |
| } |
| mScore += SCORE_IMSI_PREFIX; |
| } |
| if (iccidPrefix != null) { |
| if (!iccidPrefixMatch(subscriptionRule.iccidPrefix, iccidPrefix)) { |
| mScore = SCORE_INVALID; |
| return; |
| } |
| mScore += SCORE_ICCID_PREFIX; |
| } |
| if (gid1 != null) { |
| if (!gidMatch(subscriptionRule.gid1, gid1)) { |
| mScore = SCORE_INVALID; |
| return; |
| } |
| mScore += SCORE_GID1; |
| } |
| if (gid2 != null) { |
| if (!gidMatch(subscriptionRule.gid2, gid2)) { |
| mScore = SCORE_INVALID; |
| return; |
| } |
| mScore += SCORE_GID2; |
| } |
| if (plmn != null) { |
| if (!CarrierResolver.equals(subscriptionRule.plmn, plmn, true)) { |
| mScore = SCORE_INVALID; |
| return; |
| } |
| mScore += SCORE_PLMN; |
| } |
| if (spn != null) { |
| if (!CarrierResolver.equals(subscriptionRule.spn, spn, true)) { |
| mScore = SCORE_INVALID; |
| return; |
| } |
| mScore += SCORE_SPN; |
| } |
| |
| if (privilegeAccessRule != null && !privilegeAccessRule.isEmpty()) { |
| if (!carrierPrivilegeRulesMatch(subscriptionRule.privilegeAccessRule, |
| privilegeAccessRule)) { |
| mScore = SCORE_INVALID; |
| return; |
| } |
| mScore += SCORE_PRIVILEGE_ACCESS_RULE; |
| } |
| |
| if (apn != null) { |
| if (!CarrierResolver.equals(subscriptionRule.apn, apn, true)) { |
| mScore = SCORE_INVALID; |
| return; |
| } |
| mScore += SCORE_APN; |
| } |
| } |
| |
| private boolean imsiPrefixMatch(String imsi, String prefixXPattern) { |
| if (TextUtils.isEmpty(prefixXPattern)) return true; |
| if (TextUtils.isEmpty(imsi)) return false; |
| if (imsi.length() < prefixXPattern.length()) { |
| return false; |
| } |
| for (int i = 0; i < prefixXPattern.length(); i++) { |
| if ((prefixXPattern.charAt(i) != 'x') && (prefixXPattern.charAt(i) != 'X') |
| && (prefixXPattern.charAt(i) != imsi.charAt(i))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private boolean iccidPrefixMatch(String iccid, String prefix) { |
| if (iccid == null || prefix == null) { |
| return false; |
| } |
| return iccid.startsWith(prefix); |
| } |
| |
| // We are doing prefix and case insensitive match. |
| // Ideally we should do full string match. However due to SIM manufacture issues |
| // gid from some SIM might has garbage tail. |
| private boolean gidMatch(String gidFromSim, String gid) { |
| return (gidFromSim != null) && gidFromSim.toLowerCase(Locale.ROOT) |
| .startsWith(gid.toLowerCase(Locale.ROOT)); |
| } |
| |
| private boolean carrierPrivilegeRulesMatch(List<String> certsFromSubscription, |
| List<String> certs) { |
| if (certsFromSubscription == null || certsFromSubscription.isEmpty()) { |
| return false; |
| } |
| for (String cert : certs) { |
| for (String certFromSubscription : certsFromSubscription) { |
| if (!TextUtils.isEmpty(cert) |
| && cert.equalsIgnoreCase(certFromSubscription)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| public String toString() { |
| return "[CarrierMatchingRule] -" |
| + " mccmnc: " + mccMnc |
| + " gid1: " + gid1 |
| + " gid2: " + gid2 |
| + " plmn: " + plmn |
| + " imsi_prefix: " + imsiPrefixPattern |
| + " iccid_prefix" + iccidPrefix |
| + " spn: " + spn |
| + " privilege_access_rule: " + privilegeAccessRule |
| + " apn: " + apn |
| + " name: " + mName |
| + " cid: " + mCid |
| + " score: " + mScore; |
| } |
| } |
| |
| private CarrierMatchingRule getSubscriptionMatchingRule() { |
| final String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId()); |
| final String iccid = mPhone.getIccSerialNumber(); |
| final String gid1 = mPhone.getGroupIdLevel1(); |
| final String gid2 = mPhone.getGroupIdLevel2(); |
| final String imsi = mPhone.getSubscriberId(); |
| final String plmn = mPhone.getPlmn(); |
| final String spn = mSpn; |
| final String apn = mPreferApn; |
| List<String> accessRules; |
| // check if test override present |
| if (!TextUtils.isEmpty(mTestOverrideCarrierPriviledgeRule)) { |
| accessRules = new ArrayList<>(Arrays.asList(mTestOverrideCarrierPriviledgeRule)); |
| } else { |
| accessRules = mTelephonyMgr.createForSubscriptionId(mPhone.getSubId()) |
| .getCertsFromCarrierPrivilegeAccessRules(); |
| } |
| |
| if (VDBG) { |
| logd("[matchSubscriptionCarrier]" |
| + " mnnmnc:" + mccmnc |
| + " gid1: " + gid1 |
| + " gid2: " + gid2 |
| + " imsi: " + Rlog.pii(LOG_TAG, imsi) |
| + " iccid: " + Rlog.pii(LOG_TAG, iccid) |
| + " plmn: " + plmn |
| + " spn: " + spn |
| + " apn: " + apn |
| + " accessRules: " + ((accessRules != null) ? accessRules : null)); |
| } |
| return new CarrierMatchingRule( |
| mccmnc, imsi, iccid, gid1, gid2, plmn, spn, apn, accessRules, |
| TelephonyManager.UNKNOWN_CARRIER_ID, null, |
| TelephonyManager.UNKNOWN_CARRIER_ID); |
| } |
| |
| private void updateCarrierConfig() { |
| IccCard iccCard = mPhone.getIccCard(); |
| IccCardConstants.State simState = IccCardConstants.State.UNKNOWN; |
| if (iccCard != null) { |
| simState = iccCard.getState(); |
| } |
| CarrierConfigManager configManager = (CarrierConfigManager) |
| mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| configManager.updateConfigForPhoneId(mPhone.getPhoneId(), |
| UiccController.getIccStateIntentString(simState)); |
| } |
| |
| /** |
| * find the best matching carrier from candidates with matched subscription MCCMNC. |
| */ |
| private void matchSubscriptionCarrier(boolean updateCarrierConfig, boolean isSimOverride) { |
| if (!SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) { |
| logd("[matchSubscriptionCarrier]" + "skip before sim records loaded"); |
| return; |
| } |
| int maxScore = CarrierMatchingRule.SCORE_INVALID; |
| /** |
| * For child-parent relationship. either child and parent have the same matching |
| * score, or child's matching score > parents' matching score. |
| */ |
| CarrierMatchingRule maxRule = null; |
| CarrierMatchingRule maxRuleParent = null; |
| /** |
| * matching rule with mccmnc only. If mnoRule is found, then mno carrier id equals to the |
| * cid from mnoRule. otherwise, mno carrier id is same as cid. |
| */ |
| CarrierMatchingRule mnoRule = null; |
| CarrierMatchingRule subscriptionRule = getSubscriptionMatchingRule(); |
| |
| for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) { |
| rule.match(subscriptionRule); |
| if (rule.mScore > maxScore) { |
| maxScore = rule.mScore; |
| maxRule = rule; |
| maxRuleParent = rule; |
| } else if (maxScore > CarrierMatchingRule.SCORE_INVALID && rule.mScore == maxScore) { |
| // to handle the case that child parent has the same matching score, we need to |
| // differentiate who is child who is parent. |
| if (rule.mParentCid == maxRule.mCid) { |
| maxRule = rule; |
| } else if (maxRule.mParentCid == rule.mCid) { |
| maxRuleParent = rule; |
| } |
| } |
| if (rule.mScore == CarrierMatchingRule.SCORE_MCCMNC) { |
| mnoRule = rule; |
| } |
| } |
| if (maxScore == CarrierMatchingRule.SCORE_INVALID) { |
| logd("[matchSubscriptionCarrier - no match] cid: " + TelephonyManager.UNKNOWN_CARRIER_ID |
| + " name: " + null); |
| updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null, |
| TelephonyManager.UNKNOWN_CARRIER_ID, null, |
| TelephonyManager.UNKNOWN_CARRIER_ID, isSimOverride); |
| } else { |
| // if there is a single matching result, check if this rule has parent cid assigned. |
| if ((maxRule == maxRuleParent) |
| && maxRule.mParentCid != TelephonyManager.UNKNOWN_CARRIER_ID) { |
| maxRuleParent = new CarrierMatchingRule(maxRule); |
| maxRuleParent.mCid = maxRuleParent.mParentCid; |
| maxRuleParent.mName = getCarrierNameFromId(maxRuleParent.mCid); |
| } |
| logd("[matchSubscriptionCarrier] specific cid: " + maxRule.mCid |
| + " specific name: " + maxRule.mName +" cid: " + maxRuleParent.mCid |
| + " name: " + maxRuleParent.mName); |
| updateCarrierIdAndName(maxRuleParent.mCid, maxRuleParent.mName, |
| maxRule.mCid, maxRule.mName, |
| (mnoRule == null) ? maxRule.mCid : mnoRule.mCid, isSimOverride); |
| |
| if (updateCarrierConfig) { |
| logd("[matchSubscriptionCarrier] - Calling updateCarrierConfig()"); |
| updateCarrierConfig(); |
| } |
| } |
| |
| /* |
| * Write Carrier Identification Matching event, logging with the |
| * carrierId, mccmnc, gid1 and carrier list version to differentiate below cases of metrics: |
| * 1) unknown mccmnc - the Carrier Id provider contains no rule that matches the |
| * read mccmnc. |
| * 2) the Carrier Id provider contains some rule(s) that match the read mccmnc, |
| * but the read gid1 is not matched within the highest-scored rule. |
| * 3) successfully found a matched carrier id in the provider. |
| * 4) use carrier list version to compare the unknown carrier ratio between each version. |
| */ |
| String unknownGid1ToLog = ((maxScore & CarrierMatchingRule.SCORE_GID1) == 0 |
| && !TextUtils.isEmpty(subscriptionRule.gid1)) ? subscriptionRule.gid1 : null; |
| String unknownMccmncToLog = ((maxScore == CarrierMatchingRule.SCORE_INVALID |
| || (maxScore & CarrierMatchingRule.SCORE_GID1) == 0) |
| && !TextUtils.isEmpty(subscriptionRule.mccMnc)) ? subscriptionRule.mccMnc : null; |
| |
| // pass subscription rule to metrics. scrub all possible PII before uploading. |
| // only log apn if not user edited. |
| String apn = (subscriptionRule.apn != null |
| && !isPreferApnUserEdited(subscriptionRule.apn)) |
| ? subscriptionRule.apn : null; |
| // only log first 7 bits of iccid |
| String iccidPrefix = (subscriptionRule.iccidPrefix != null) |
| && (subscriptionRule.iccidPrefix.length() >= 7) |
| ? subscriptionRule.iccidPrefix.substring(0, 7) : subscriptionRule.iccidPrefix; |
| // only log first 8 bits of imsi |
| String imsiPrefix = (subscriptionRule.imsiPrefixPattern != null) |
| && (subscriptionRule.imsiPrefixPattern.length() >= 8) |
| ? subscriptionRule.imsiPrefixPattern.substring(0, 8) |
| : subscriptionRule.imsiPrefixPattern; |
| |
| CarrierMatchingRule simInfo = new CarrierMatchingRule( |
| subscriptionRule.mccMnc, |
| imsiPrefix, |
| iccidPrefix, |
| subscriptionRule.gid1, |
| subscriptionRule.gid2, |
| subscriptionRule.plmn, |
| subscriptionRule.spn, |
| apn, |
| subscriptionRule.privilegeAccessRule, |
| -1, null, -1); |
| |
| TelephonyMetrics.getInstance().writeCarrierIdMatchingEvent( |
| mPhone.getPhoneId(), getCarrierListVersion(), mCarrierId, |
| unknownMccmncToLog, unknownGid1ToLog, simInfo); |
| |
| // Generate statsd metrics only when MCC/MNC is unknown or there is no match for GID1. |
| if (unknownMccmncToLog != null || unknownGid1ToLog != null) { |
| // Pass the PNN value to metrics only if the SPN is empty |
| String pnn = TextUtils.isEmpty(subscriptionRule.spn) ? subscriptionRule.plmn : ""; |
| CarrierIdMatchStats.onCarrierIdMismatch( |
| mCarrierId, unknownMccmncToLog, unknownGid1ToLog, subscriptionRule.spn, pnn); |
| } |
| } |
| |
| public int getCarrierListVersion() { |
| // Use the cached value if it exists, otherwise retrieve it. |
| if (mCarrierListVersion == null) { |
| // The auto closeable cursor will be closed after exiting try-block. |
| try (Cursor cursor = mContext.getContentResolver().query( |
| Uri.withAppendedPath(CarrierId.All.CONTENT_URI, |
| "get_version"), null, null, null)) { |
| cursor.moveToFirst(); |
| mCarrierListVersion = cursor.getInt(0); |
| } |
| } |
| return mCarrierListVersion; |
| } |
| |
| public int getCarrierId() { |
| return mCarrierId; |
| } |
| /** |
| * Returns fine-grained carrier id of the current subscription. Carrier ids with a valid parent |
| * id are specific carrier ids. |
| * |
| * A specific carrier ID can represent the fact that a carrier may be in effect an aggregation |
| * of other carriers (ie in an MVNO type scenario) where each of these specific carriers which |
| * are used to make up the actual carrier service may have different carrier configurations. |
| * A specific carrier ID could also be used, for example, in a scenario where a carrier requires |
| * different carrier configuration for different service offering such as a prepaid plan. |
| * e.g, {@link #getCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM, while |
| * {@link #getSpecificCarrierId()} can return Tracfone AT&T or Tracfone T-Mobile based on the |
| * IMSI from the current subscription. |
| * |
| * For carriers without any fine-grained carrier ids, return {@link #getCarrierId()} |
| */ |
| public int getSpecificCarrierId() { |
| return mSpecificCarrierId; |
| } |
| |
| public String getCarrierName() { |
| return mCarrierName; |
| } |
| |
| public String getSpecificCarrierName() { |
| return mSpecificCarrierName; |
| } |
| |
| public int getMnoCarrierId() { |
| return mMnoCarrierId; |
| } |
| |
| /** |
| * a util function to convert carrierIdentifier to the best matching carrier id. |
| * |
| * @return the best matching carrier id. |
| */ |
| public static int getCarrierIdFromIdentifier(@NonNull Context context, |
| @NonNull CarrierIdentifier carrierIdentifier) { |
| final String mccmnc = carrierIdentifier.getMcc() + carrierIdentifier.getMnc(); |
| final String gid1 = carrierIdentifier.getGid1(); |
| final String gid2 = carrierIdentifier.getGid2(); |
| final String imsi = carrierIdentifier.getImsi(); |
| final String spn = carrierIdentifier.getSpn(); |
| if (VDBG) { |
| logd("[getCarrierIdFromIdentifier]" |
| + " mnnmnc:" + mccmnc |
| + " gid1: " + gid1 |
| + " gid2: " + gid2 |
| + " imsi: " + Rlog.pii(LOG_TAG, imsi) |
| + " spn: " + spn); |
| } |
| // assign null to other fields which are not supported by carrierIdentifier. |
| CarrierMatchingRule targetRule = |
| new CarrierMatchingRule(mccmnc, imsi, null, gid1, gid2, null, |
| spn, null, null, |
| TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION, null, |
| TelephonyManager.UNKNOWN_CARRIER_ID); |
| |
| int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID; |
| int maxScore = CarrierMatchingRule.SCORE_INVALID; |
| List<CarrierMatchingRule> rules = getCarrierMatchingRulesFromMccMnc( |
| context, targetRule.mccMnc); |
| for (CarrierMatchingRule rule : rules) { |
| rule.match(targetRule); |
| if (rule.mScore > maxScore) { |
| maxScore = rule.mScore; |
| carrierId = rule.mCid; |
| } |
| } |
| return carrierId; |
| } |
| |
| /** |
| * a util function to convert {mccmnc, mvno_type, mvno_data} to all matching carrier ids. |
| * |
| * @return a list of id with matching {mccmnc, mvno_type, mvno_data} |
| */ |
| public static List<Integer> getCarrierIdsFromApnQuery(@NonNull Context context, |
| String mccmnc, String mvnoCase, |
| String mvnoData) { |
| String selection = CarrierId.All.MCCMNC + "=" + mccmnc; |
| // build the proper query |
| if ("spn".equals(mvnoCase) && mvnoData != null) { |
| selection += " AND " + CarrierId.All.SPN + "='" + mvnoData + "'"; |
| } else if ("imsi".equals(mvnoCase) && mvnoData != null) { |
| selection += " AND " + CarrierId.All.IMSI_PREFIX_XPATTERN + "='" + mvnoData + "'"; |
| } else if ("gid1".equals(mvnoCase) && mvnoData != null) { |
| selection += " AND " + CarrierId.All.GID1 + "='" + mvnoData + "'"; |
| } else if ("gid2".equals(mvnoCase) && mvnoData != null) { |
| selection += " AND " + CarrierId.All.GID2 + "='" + mvnoData +"'"; |
| } else { |
| logd("mvno case empty or other invalid values"); |
| } |
| |
| List<Integer> ids = new ArrayList<>(); |
| try { |
| Cursor cursor = context.getContentResolver().query( |
| CarrierId.All.CONTENT_URI, |
| /* projection */ null, |
| /* selection */ selection, |
| /* selectionArgs */ null, null); |
| try { |
| if (cursor != null) { |
| if (VDBG) { |
| logd("[getCarrierIdsFromApnQuery]- " + cursor.getCount() |
| + " Records(s) in DB"); |
| } |
| while (cursor.moveToNext()) { |
| int cid = cursor.getInt(cursor.getColumnIndex(CarrierId.CARRIER_ID)); |
| if (!ids.contains(cid)) { |
| ids.add(cid); |
| } |
| } |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| } catch (Exception ex) { |
| loge("[getCarrierIdsFromApnQuery]- ex: " + ex); |
| } |
| logd(selection + " " + ids); |
| return ids; |
| } |
| |
| // static helper function to get carrier id from mccmnc |
| public static int getCarrierIdFromMccMnc(@NonNull Context context, String mccmnc) { |
| try (Cursor cursor = getCursorForMccMnc(context, mccmnc)) { |
| if (cursor == null || !cursor.moveToNext()) return TelephonyManager.UNKNOWN_CARRIER_ID; |
| if (VDBG) { |
| logd("[getCarrierIdFromMccMnc]- " + cursor.getCount() |
| + " Records(s) in DB" + " mccmnc: " + mccmnc); |
| } |
| return cursor.getInt(cursor.getColumnIndex(CarrierId.CARRIER_ID)); |
| } catch (Exception ex) { |
| loge("[getCarrierIdFromMccMnc]- ex: " + ex); |
| } |
| return TelephonyManager.UNKNOWN_CARRIER_ID; |
| } |
| |
| /** |
| * Static helper function to get carrier name from mccmnc |
| * @param context Context |
| * @param mccmnc PLMN |
| * @return Carrier name string given mccmnc/PLMN |
| * |
| * @hide |
| */ |
| @Nullable |
| public static String getCarrierNameFromMccMnc(@NonNull Context context, String mccmnc) { |
| try (Cursor cursor = getCursorForMccMnc(context, mccmnc)) { |
| if (cursor == null || !cursor.moveToNext()) return null; |
| if (VDBG) { |
| logd("[getCarrierNameFromMccMnc]- " + cursor.getCount() |
| + " Records(s) in DB" + " mccmnc: " + mccmnc); |
| } |
| return cursor.getString(cursor.getColumnIndex(CarrierId.CARRIER_NAME)); |
| } catch (Exception ex) { |
| loge("[getCarrierNameFromMccMnc]- ex: " + ex); |
| } |
| return null; |
| } |
| |
| @Nullable |
| private static Cursor getCursorForMccMnc(@NonNull Context context, String mccmnc) { |
| try { |
| Cursor cursor = context.getContentResolver().query( |
| CarrierId.All.CONTENT_URI, |
| /* projection */ null, |
| /* selection */ CarrierId.All.MCCMNC + "=? AND " |
| + CarrierId.All.GID1 + " is NULL AND " |
| + CarrierId.All.GID2 + " is NULL AND " |
| + CarrierId.All.IMSI_PREFIX_XPATTERN + " is NULL AND " |
| + CarrierId.All.SPN + " is NULL AND " |
| + CarrierId.All.ICCID_PREFIX + " is NULL AND " |
| + CarrierId.All.PLMN + " is NULL AND " |
| + CarrierId.All.PRIVILEGE_ACCESS_RULE + " is NULL AND " |
| + CarrierId.All.APN + " is NULL", |
| /* selectionArgs */ new String[]{mccmnc}, |
| null); |
| return cursor; |
| } catch (Exception ex) { |
| loge("[getCursorForMccMnc]- ex: " + ex); |
| return null; |
| } |
| } |
| |
| private static boolean equals(String a, String b, boolean ignoreCase) { |
| if (a == null && b == null) return true; |
| if (a != null && b != null) { |
| return (ignoreCase) ? a.equalsIgnoreCase(b) : a.equals(b); |
| } |
| return false; |
| } |
| |
| private static void logd(String str) { |
| Rlog.d(LOG_TAG, str); |
| } |
| private static void loge(String str) { |
| Rlog.e(LOG_TAG, str); |
| } |
| |
| private static void logd(String str, int phoneId) { |
| Rlog.d(LOG_TAG + "[" + phoneId + "]", str); |
| } |
| |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); |
| ipw.println("mCarrierResolverLocalLogs:"); |
| ipw.increaseIndent(); |
| mCarrierIdLocalLog.dump(fd, pw, args); |
| ipw.decreaseIndent(); |
| |
| ipw.println("mCarrierId: " + mCarrierId); |
| ipw.println("mSpecificCarrierId: " + mSpecificCarrierId); |
| ipw.println("mMnoCarrierId: " + mMnoCarrierId); |
| ipw.println("mCarrierName: " + mCarrierName); |
| ipw.println("mSpecificCarrierName: " + mSpecificCarrierName); |
| ipw.println("carrier_list_version: " + getCarrierListVersion()); |
| |
| ipw.println("mCarrierMatchingRules on mccmnc: " |
| + mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId())); |
| ipw.increaseIndent(); |
| for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) { |
| ipw.println(rule.toString()); |
| } |
| ipw.decreaseIndent(); |
| |
| ipw.println("mSpn: " + mSpn); |
| ipw.println("mPreferApn: " + mPreferApn); |
| ipw.flush(); |
| } |
| } |