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

Commit 44521224 authored by chen xu's avatar chen xu
Browse files

refactor carrier resolver

1. use TelephonyManager API getCarrierPrivilegeRules
2. provide static util API to convert mccmnc, mvno_data, mvno_type to
carrier id. This will be consumed by APNSettings to handle backward
compatibility.
3. other refactor

Bug: 110559381
Test: unit test & manual test
Change-Id: If4a09552f4051ceb873814c276df43632f2dd359
parent 26c85382
Loading
Loading
Loading
Loading
+148 −36
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.internal.telephony;

import static android.provider.Telephony.CarrierId;

import android.annotation.NonNull;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -37,12 +38,12 @@ import android.util.Log;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccProfile;
import com.android.internal.util.IndentingPrintWriter;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@@ -87,7 +88,6 @@ public class CarrierResolver extends Handler {
    private Context mContext;
    private Phone mPhone;
    private IccRecords mIccRecords;
    private UiccProfile mUiccProfile;
    private final LocalLog mCarrierIdLocalLog = new LocalLog(20);
    private final TelephonyManager mTelephonyMgr;
    private final SubscriptionsChangedListener mOnSubscriptionsChangedListener =
@@ -192,7 +192,7 @@ public class CarrierResolver extends Handler {
                if (!equals(mPreferApn, preferApn, true)) {
                    logd("[updatePreferApn] from:" + mPreferApn + " to:" + preferApn);
                    mPreferApn = preferApn;
                    matchCarrier(getSubscriptionMatchingRule(), true);
                    matchSubscriptionCarrier();
                }
                break;
            case ICC_CHANGED_EVENT:
@@ -211,9 +211,6 @@ public class CarrierResolver extends Handler {
                        mIccRecords = newIccRecords;
                    }
                }
                // check UICC profile
                mUiccProfile = UiccController.getInstance()
                        .getUiccProfileForPhone(mPhone.getPhoneId());
                break;
            default:
                loge("invalid msg: " + msg.what);
@@ -239,7 +236,7 @@ public class CarrierResolver extends Handler {
                    while (cursor.moveToNext()) {
                        mCarrierMatchingRulesOnMccMnc.add(makeCarrierMatchingRule(cursor));
                    }
                    matchCarrier(getSubscriptionMatchingRule(), true);
                    matchSubscriptionCarrier();
                }
            } finally {
                if (cursor != null) {
@@ -251,6 +248,37 @@ public class CarrierResolver extends Handler {
        }
    }

    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() {
        Cursor cursor = mContext.getContentResolver().query(
                Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapn/subId/"
@@ -318,7 +346,9 @@ public class CarrierResolver extends Handler {
        }
    }

    private CarrierMatchingRule makeCarrierMatchingRule(Cursor cursor) {
    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(
@@ -330,7 +360,7 @@ public class CarrierResolver extends Handler {
                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.PLMN)),
                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.SPN)),
                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.APN)),
                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.PRIVILEGE_ACCESS_RULE)),
                (TextUtils.isEmpty(certs) ? null : new ArrayList<>(Arrays.asList(certs))),
                cursor.getInt(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_ID)),
                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_NAME)));
    }
@@ -338,7 +368,7 @@ public class CarrierResolver extends Handler {
    /**
     * carrier matching attributes with corresponding cid
     */
    private class CarrierMatchingRule {
    private 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
@@ -369,7 +399,8 @@ public class CarrierResolver extends Handler {
        private final String mPlmn;
        private final String mSpn;
        private final String mApn;
        private final String mPrivilegeAccessRule;
        // there can be multiple certs configured in the UICC
        private final List<String> mPrivilegeAccessRule;

        // user-facing carrier name
        private String mName;
@@ -380,7 +411,7 @@ public class CarrierResolver extends Handler {

        CarrierMatchingRule(String mccmnc, String imsiPrefixPattern, String iccidPrefix,
                String gid1, String gid2, String plmn, String spn, String apn,
                String privilegeAccessRule, int cid, String name) {
                List<String> privilegeAccessRule, int cid, String name) {
            mMccMnc = mccmnc;
            mImsiPrefixPattern = imsiPrefixPattern;
            mIccidPrefix = iccidPrefix;
@@ -455,8 +486,8 @@ public class CarrierResolver extends Handler {
                mScore += SCORE_SPN;
            }

            if (mPrivilegeAccessRule != null) {
                if (mUiccProfile == null || !mUiccProfile.hasCarrierPrivilegeRulesLoadedForCertHex(
            if (mPrivilegeAccessRule != null && !mPrivilegeAccessRule.isEmpty()) {
                if (!carrierPrivilegeRulesMatch(subscriptionRule.mPrivilegeAccessRule,
                        mPrivilegeAccessRule)) {
                    mScore = SCORE_INVALID;
                    return;
@@ -495,6 +526,22 @@ public class CarrierResolver extends Handler {
            return iccid.startsWith(prefix);
        }

        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: " + mMccMnc
@@ -521,9 +568,10 @@ public class CarrierResolver extends Handler {
        final String plmn = mPhone.getPlmn();
        final String spn = mSpn;
        final String apn = mPreferApn;
        final List<String> accessRules = mTelephonyMgr.getCertsFromCarrierPrivilegeAccessRules();

        if (VDBG) {
            logd("[matchCarrier]"
            logd("[matchSubscriptionCarrier]"
                    + " mnnmnc:" + mccmnc
                    + " gid1: " + gid1
                    + " gid2: " + gid2
@@ -531,23 +579,22 @@ public class CarrierResolver extends Handler {
                    + " iccid: " + Rlog.pii(LOG_TAG, iccid)
                    + " plmn: " + plmn
                    + " spn: " + spn
                    + " apn: " + apn);
                    + " apn: " + apn
                    + " accessRules: " + ((accessRules != null) ? accessRules : null));
        }
        return new CarrierMatchingRule(
                mccmnc, imsi, iccid, gid1, gid2, plmn, spn, apn, null
                /** fetching privilege access rule is handled by CarrierMatchingRule#match **/,
                mccmnc, imsi, iccid, gid1, gid2, plmn, spn, apn, accessRules,
                TelephonyManager.UNKNOWN_CARRIER_ID, null);
    }

    /**
     * find the best matching carrier from candidates with matched MCCMNC.
     * @param update if true, update cached mCarrierId and notify registrants on carrier id change.
     * find the best matching carrier from candidates with matched subscription MCCMNC.
     * @return the best matching carrier id.
     */
    private int matchCarrier(CarrierMatchingRule subscriptionRule, boolean update) {
    private int matchSubscriptionCarrier() {
        int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
        if (update && !SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
            logd("[matchCarrier]" + "skip before sim records loaded");
        if (!SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
            logd("[matchSubscriptionCarrier]" + "skip before sim records loaded");
            return carrierId;
        }
        int maxScore = CarrierMatchingRule.SCORE_INVALID;
@@ -557,6 +604,7 @@ public class CarrierResolver extends Handler {
         * cid from mnoRule. otherwise, mno carrier id is same as cid.
         */
        CarrierMatchingRule mnoRule = null;
        CarrierMatchingRule subscriptionRule = getSubscriptionMatchingRule();

        for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) {
            rule.match(subscriptionRule);
@@ -569,17 +617,13 @@ public class CarrierResolver extends Handler {
                mnoRule = rule;
            }
        }
        // skip updating the cached carrierId
        if (!update) {
            return carrierId;
        }
        if (maxScore == CarrierMatchingRule.SCORE_INVALID) {
            logd("[matchCarrier - no match] cid: " + TelephonyManager.UNKNOWN_CARRIER_ID
            logd("[matchSubscriptionCarrier - no match] cid: " + TelephonyManager.UNKNOWN_CARRIER_ID
                    + " name: " + null);
            updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID,
                    TelephonyManager.UNKNOWN_CARRIER_ID, null);
        } else {
            logd("[matchCarrier] cid: " + maxRule.mCid + " name: " + maxRule.mName);
            logd("[matchSubscriptionCarrier] cid: " + maxRule.mCid + " name: " + maxRule.mName);
            updateCarrierIdAndName(maxRule.mCid,
                    (mnoRule == null) ? maxRule.mCid : mnoRule.mCid, maxRule.mName);
        }
@@ -627,27 +671,95 @@ public class CarrierResolver extends Handler {

    /**
     * a util function to convert carrierIdentifier to the best matching carrier id.
     * If there is no exact match for MVNO, will fallback to match its MNO.
     *
     * @return the best matching carrier id.
     */
    public int getCarrierIdFromIdentifier(CarrierIdentifier carrierIdentifier) {
    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("[matchCarrier]"
            logd("[getCarrierIdFromIdentifier]"
                    + " mnnmnc:" + mccmnc
                    + " gid1: " + gid1
                    + " gid2: " + gid2
                    + " imsi: " + Rlog.pii(LOG_TAG, imsi)
                    + " spn: " + spn);
        }
        CarrierMatchingRule rule = new CarrierMatchingRule(mccmnc, imsi, null, gid1, gid2, null,
        // assign null to other fields which are not supported by carrierIdentifier.
        CarrierMatchingRule targetRule =
                new CarrierMatchingRule(mccmnc, imsi, null, gid1, gid2, null,
                spn, null, null, -1, null);
        // not trigger the updating logic for internal conversion.
        return matchCarrier(rule, false);

        int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
        int maxScore = CarrierMatchingRule.SCORE_INVALID;
        List<CarrierMatchingRule> rules = getCarrierMatchingRulesFromMccMnc(
                context, targetRule.mMccMnc);
        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;
    }

    private static boolean equals(String a, String b, boolean ignoreCase) {
+9 −10
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import com.android.internal.telephony.uicc.euicc.EuiccCard;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -1464,23 +1465,21 @@ public class UiccProfile extends IccCard {
    }

    /**
     * Match the input certificate to any loaded carrier privileges access rules.
     * Return a list of certs in hex string from loaded carrier privileges access rules.
     *
     * @param cert certificate in hex string
     * @return true if matching certificate is found. false otherwise.
     * @return a list of certificate in hex string. return {@code null} if there is no certs
     * or privilege rules are not loaded yet.
     */
    public boolean hasCarrierPrivilegeRulesLoadedForCertHex(String cert) {
        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
    public List<String> getCertsFromCarrierPrivilegeAccessRules() {
        final List<String> certs = new ArrayList<>();
        final UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
        if (carrierPrivilegeRules != null) {
            List<UiccAccessRule> accessRules = carrierPrivilegeRules.getAccessRules();
            for (UiccAccessRule accessRule : accessRules) {
                String certHexString = accessRule.getCertificateHexString();
                if (!TextUtils.isEmpty(certHexString) && certHexString.equalsIgnoreCase(cert)) {
                    return true;
                certs.add(accessRule.getCertificateHexString());
            }
        }
        }
        return false;
        return certs.isEmpty() ? null : certs;
    }

    /**
+3 −3
Original line number Diff line number Diff line
@@ -195,17 +195,17 @@ public class CarrierResolverTest extends TelephonyTest {
        waitForMs(200);

        CarrierIdentifier identifier = new CarrierIdentifier(null, null, null, null, null, null);
        int carrierid = mCarrierResolver.getCarrierIdFromIdentifier(identifier);
        int carrierid = mCarrierResolver.getCarrierIdFromIdentifier(mContext, identifier);
        assertEquals(CID_UNKNOWN, carrierid);

        identifier = new CarrierIdentifier(MCCMNC.substring(0, 3), MCCMNC.substring(3), null, null,
                null, null);
        carrierid = mCarrierResolver.getCarrierIdFromIdentifier(identifier);
        carrierid = mCarrierResolver.getCarrierIdFromIdentifier(mContext, identifier);
        assertEquals(CID_VZW, carrierid);

        identifier = new CarrierIdentifier(MCCMNC.substring(0, 3), MCCMNC.substring(3),  SPN_FI, null,
                null, null);
        carrierid = mCarrierResolver.getCarrierIdFromIdentifier(identifier);
        carrierid = mCarrierResolver.getCarrierIdFromIdentifier(mContext, identifier);
        assertEquals(CID_FI, carrierid);
    }