• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.internal.telephony;
17 
18 import static android.provider.Telephony.CarrierId;
19 
20 import android.content.ContentValues;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.database.ContentObserver;
24 import android.database.Cursor;
25 import android.net.Uri;
26 import android.os.Handler;
27 import android.os.Message;
28 import android.provider.Telephony;
29 import android.telephony.Rlog;
30 import android.telephony.SubscriptionManager;
31 import android.telephony.TelephonyManager;
32 import android.text.TextUtils;
33 import android.util.LocalLog;
34 import android.util.Log;
35 
36 import com.android.internal.telephony.metrics.TelephonyMetrics;
37 import com.android.internal.telephony.uicc.IccRecords;
38 import com.android.internal.telephony.uicc.UiccController;
39 import com.android.internal.telephony.uicc.UiccProfile;
40 import com.android.internal.util.IndentingPrintWriter;
41 
42 import java.io.FileDescriptor;
43 import java.io.PrintWriter;
44 import java.util.ArrayList;
45 import java.util.List;
46 import java.util.concurrent.atomic.AtomicInteger;
47 
48 /**
49  * CarrierIdentifier identifies the subscription carrier and returns a canonical carrier Id
50  * and a user friendly carrier name. CarrierIdentifier reads subscription info and check against
51  * all carrier matching rules stored in CarrierIdProvider. It is msim aware, each phone has a
52  * dedicated CarrierIdentifier.
53  */
54 public class CarrierIdentifier extends Handler {
55     private static final String LOG_TAG = CarrierIdentifier.class.getSimpleName();
56     private static final boolean DBG = true;
57     private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
58 
59     // events to trigger carrier identification
60     private static final int SIM_LOAD_EVENT             = 1;
61     private static final int SIM_ABSENT_EVENT           = 2;
62     private static final int SPN_OVERRIDE_EVENT         = 3;
63     private static final int ICC_CHANGED_EVENT          = 4;
64     private static final int PREFER_APN_UPDATE_EVENT    = 5;
65     private static final int CARRIER_ID_DB_UPDATE_EVENT = 6;
66 
67     private static final Uri CONTENT_URL_PREFER_APN = Uri.withAppendedPath(
68             Telephony.Carriers.CONTENT_URI, "preferapn");
69     private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
70 
71     // cached matching rules based mccmnc to speed up resolution
72     private List<CarrierMatchingRule> mCarrierMatchingRulesOnMccMnc = new ArrayList<>();
73     // cached carrier Id
74     private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
75     // cached carrier name
76     private String mCarrierName;
77     // cached preferapn name
78     private String mPreferApn;
79     // cached service provider name. telephonyManager API returns empty string as default value.
80     // some carriers need to target devices with Empty SPN. In that case, carrier matching rule
81     // should specify "" spn explicitly.
82     private String mSpn = "";
83 
84     private Context mContext;
85     private Phone mPhone;
86     private IccRecords mIccRecords;
87     private UiccProfile mUiccProfile;
88     private final LocalLog mCarrierIdLocalLog = new LocalLog(20);
89     private final TelephonyManager mTelephonyMgr;
90     private final SubscriptionsChangedListener mOnSubscriptionsChangedListener =
91             new SubscriptionsChangedListener();
92 
93     private final ContentObserver mContentObserver = new ContentObserver(this) {
94         @Override
95         public void onChange(boolean selfChange, Uri uri) {
96             if (CONTENT_URL_PREFER_APN.equals(uri.getLastPathSegment())) {
97                 logd("onChange URI: " + uri);
98                 sendEmptyMessage(PREFER_APN_UPDATE_EVENT);
99             } else if (CarrierId.All.CONTENT_URI.equals(uri)) {
100                 logd("onChange URI: " + uri);
101                 sendEmptyMessage(CARRIER_ID_DB_UPDATE_EVENT);
102             }
103         }
104     };
105 
106     private class SubscriptionsChangedListener
107             extends SubscriptionManager.OnSubscriptionsChangedListener {
108         final AtomicInteger mPreviousSubId =
109                 new AtomicInteger(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
110         /**
111          * Callback invoked when there is any change to any SubscriptionInfo. Typically
112          * this method would invoke {@link SubscriptionManager#getActiveSubscriptionInfoList}
113          */
114         @Override
onSubscriptionsChanged()115         public void onSubscriptionsChanged() {
116             int subId = mPhone.getSubId();
117             if (mPreviousSubId.getAndSet(subId) != subId) {
118                 if (DBG) {
119                     logd("SubscriptionListener.onSubscriptionInfoChanged subId: "
120                             + mPreviousSubId);
121                 }
122                 if (SubscriptionManager.isValidSubscriptionId(subId)) {
123                     sendEmptyMessage(SIM_LOAD_EVENT);
124                 } else {
125                     sendEmptyMessage(SIM_ABSENT_EVENT);
126                 }
127             }
128         }
129     }
130 
CarrierIdentifier(Phone phone)131     public CarrierIdentifier(Phone phone) {
132         logd("Creating CarrierIdentifier[" + phone.getPhoneId() + "]");
133         mContext = phone.getContext();
134         mPhone = phone;
135         mTelephonyMgr = TelephonyManager.from(mContext);
136 
137         // register events
138         mContext.getContentResolver().registerContentObserver(CONTENT_URL_PREFER_APN, false,
139                 mContentObserver);
140         mContext.getContentResolver().registerContentObserver(
141                 CarrierId.All.CONTENT_URI, false, mContentObserver);
142         SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
143                 mOnSubscriptionsChangedListener);
144         UiccController.getInstance().registerForIccChanged(this, ICC_CHANGED_EVENT, null);
145     }
146 
147     /**
148      * Entry point for the carrier identification.
149      *
150      *    1. SIM_LOAD_EVENT
151      *        This indicates that all SIM records has been loaded and its first entry point for the
152      *        carrier identification. Note, there are other attributes could be changed on the fly
153      *        like APN and SPN. We cached all carrier matching rules based on MCCMNC to speed
154      *        up carrier resolution on following trigger events.
155      *
156      *    2. PREFER_APN_UPDATE_EVENT
157      *        This indicates prefer apn has been changed. It could be triggered when user modified
158      *        APN settings or when default data connection first establishes on the current carrier.
159      *        We follow up on this by querying prefer apn sqlite and re-issue carrier identification
160      *        with the updated prefer apn name.
161      *
162      *    3. SPN_OVERRIDE_EVENT
163      *        This indicates that SPN value as been changed. It could be triggered from EF_SPN
164      *        record loading, carrier config override
165      *        {@link android.telephony.CarrierConfigManager#KEY_CARRIER_NAME_STRING}
166      *        or carrier app override {@link TelephonyManager#setOperatorBrandOverride(String)}.
167      *        we follow up this by checking the cached mSPN against the latest value and issue
168      *        carrier identification only if spn changes.
169      *
170      *    4. CARRIER_ID_DB_UPDATE_EVENT
171      *        This indicates that carrierIdentification database which stores all matching rules
172      *        has been updated. It could be triggered from OTA or assets update.
173      */
174     @Override
handleMessage(Message msg)175     public void handleMessage(Message msg) {
176         if (VDBG) logd("handleMessage: " + msg.what);
177         switch (msg.what) {
178             case SIM_LOAD_EVENT:
179             case CARRIER_ID_DB_UPDATE_EVENT:
180                 mSpn = mTelephonyMgr.getSimOperatorNameForPhone(mPhone.getPhoneId());
181                 mPreferApn = getPreferApn();
182                 loadCarrierMatchingRulesOnMccMnc();
183                 break;
184             case SIM_ABSENT_EVENT:
185                 mCarrierMatchingRulesOnMccMnc.clear();
186                 mSpn = null;
187                 mPreferApn = null;
188                 updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null);
189                 break;
190             case PREFER_APN_UPDATE_EVENT:
191                 String preferApn = getPreferApn();
192                 if (!equals(mPreferApn, preferApn, true)) {
193                     logd("[updatePreferApn] from:" + mPreferApn + " to:" + preferApn);
194                     mPreferApn = preferApn;
195                     matchCarrier();
196                 }
197                 break;
198             case SPN_OVERRIDE_EVENT:
199                 String spn = mTelephonyMgr.getSimOperatorNameForPhone(mPhone.getPhoneId());
200                 if (!equals(mSpn, spn, true)) {
201                     logd("[updateSpn] from:" + mSpn + " to:" + spn);
202                     mSpn = spn;
203                     matchCarrier();
204                 }
205                 break;
206             case ICC_CHANGED_EVENT:
207                 // all records used for carrier identification are from SimRecord
208                 final IccRecords newIccRecords = UiccController.getInstance().getIccRecords(
209                         mPhone.getPhoneId(), UiccController.APP_FAM_3GPP);
210                 if (mIccRecords != newIccRecords) {
211                     if (mIccRecords != null) {
212                         logd("Removing stale icc objects.");
213                         mIccRecords.unregisterForRecordsLoaded(this);
214                         mIccRecords.unregisterForRecordsOverride(this);
215                         mIccRecords = null;
216                     }
217                     if (newIccRecords != null) {
218                         logd("new Icc object");
219                         newIccRecords.registerForRecordsLoaded(this, SIM_LOAD_EVENT, null);
220                         newIccRecords.registerForRecordsOverride(this, SIM_LOAD_EVENT, null);
221                         mIccRecords = newIccRecords;
222                     }
223                 }
224                 // check UICC profile
225                 final UiccProfile uiccProfile = UiccController.getInstance()
226                         .getUiccProfileForPhone(mPhone.getPhoneId());
227                 if (mUiccProfile != uiccProfile) {
228                     if (mUiccProfile != null) {
229                         logd("unregister operatorBrandOverride");
230                         mUiccProfile.unregisterForOperatorBrandOverride(this);
231                         mUiccProfile = null;
232                     }
233                     if (uiccProfile != null) {
234                         logd("register operatorBrandOverride");
235                         uiccProfile.registerForOpertorBrandOverride(this, SPN_OVERRIDE_EVENT, null);
236                         mUiccProfile = uiccProfile;
237                     }
238                 }
239                 break;
240             default:
241                 loge("invalid msg: " + msg.what);
242                 break;
243         }
244     }
245 
loadCarrierMatchingRulesOnMccMnc()246     private void loadCarrierMatchingRulesOnMccMnc() {
247         try {
248             String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId());
249             Cursor cursor = mContext.getContentResolver().query(
250                     CarrierId.All.CONTENT_URI,
251                     /* projection */ null,
252                     /* selection */ CarrierId.All.MCCMNC + "=?",
253                     /* selectionArgs */ new String[]{mccmnc}, null);
254             try {
255                 if (cursor != null) {
256                     if (VDBG) {
257                         logd("[loadCarrierMatchingRules]- " + cursor.getCount()
258                                 + " Records(s) in DB" + " mccmnc: " + mccmnc);
259                     }
260                     mCarrierMatchingRulesOnMccMnc.clear();
261                     while (cursor.moveToNext()) {
262                         mCarrierMatchingRulesOnMccMnc.add(makeCarrierMatchingRule(cursor));
263                     }
264                     matchCarrier();
265                 }
266             } finally {
267                 if (cursor != null) {
268                     cursor.close();
269                 }
270             }
271         } catch (Exception ex) {
272             loge("[loadCarrierMatchingRules]- ex: " + ex);
273         }
274     }
275 
getPreferApn()276     private String getPreferApn() {
277         Cursor cursor = mContext.getContentResolver().query(
278                 Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapn/subId/"
279                 + mPhone.getSubId()), /* projection */ new String[]{Telephony.Carriers.APN},
280                 /* selection */ null, /* selectionArgs */ null, /* sortOrder */ null);
281         try {
282             if (cursor != null) {
283                 if (VDBG) {
284                     logd("[getPreferApn]- " + cursor.getCount() + " Records(s) in DB");
285                 }
286                 while (cursor.moveToNext()) {
287                     String apn = cursor.getString(cursor.getColumnIndexOrThrow(
288                             Telephony.Carriers.APN));
289                     logd("[getPreferApn]- " + apn);
290                     return apn;
291                 }
292             }
293         } catch (Exception ex) {
294             loge("[getPreferApn]- exception: " + ex);
295         } finally {
296             if (cursor != null) {
297                 cursor.close();
298             }
299         }
300         return null;
301     }
302 
updateCarrierIdAndName(int cid, String name)303     private void updateCarrierIdAndName(int cid, String name) {
304         boolean update = false;
305         if (!equals(name, mCarrierName, true)) {
306             logd("[updateCarrierName] from:" + mCarrierName + " to:" + name);
307             mCarrierName = name;
308             update = true;
309         }
310         if (cid != mCarrierId) {
311             logd("[updateCarrierId] from:" + mCarrierId + " to:" + cid);
312             mCarrierId = cid;
313             update = true;
314         }
315         if (update) {
316             mCarrierIdLocalLog.log("[updateCarrierIdAndName] cid:" + mCarrierId + " name:"
317                     + mCarrierName);
318             final Intent intent = new Intent(TelephonyManager
319                     .ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
320             intent.putExtra(TelephonyManager.EXTRA_CARRIER_ID, mCarrierId);
321             intent.putExtra(TelephonyManager.EXTRA_CARRIER_NAME, mCarrierName);
322             intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId());
323             mContext.sendBroadcast(intent);
324 
325             // update current subscriptions
326             ContentValues cv = new ContentValues();
327             cv.put(CarrierId.CARRIER_ID, mCarrierId);
328             cv.put(CarrierId.CARRIER_NAME, mCarrierName);
329             mContext.getContentResolver().update(
330                     Uri.withAppendedPath(CarrierId.CONTENT_URI,
331                     Integer.toString(mPhone.getSubId())), cv, null, null);
332         }
333     }
334 
makeCarrierMatchingRule(Cursor cursor)335     private CarrierMatchingRule makeCarrierMatchingRule(Cursor cursor) {
336         return new CarrierMatchingRule(
337                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.MCCMNC)),
338                 cursor.getString(cursor.getColumnIndexOrThrow(
339                         CarrierId.All.IMSI_PREFIX_XPATTERN)),
340                 cursor.getString(cursor.getColumnIndexOrThrow(
341                         CarrierId.All.ICCID_PREFIX)),
342                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID1)),
343                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID2)),
344                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.PLMN)),
345                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.SPN)),
346                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.APN)),
347                 cursor.getInt(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_ID)),
348                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_NAME)));
349     }
350 
351     /**
352      * carrier matching attributes with corresponding cid
353      */
354     private static class CarrierMatchingRule {
355         /**
356          * These scores provide the hierarchical relationship between the attributes, intended to
357          * resolve conflicts in a deterministic way. The scores are constructed such that a match
358          * from a higher tier will beat any subsequent match which does not match at that tier,
359          * so MCCMNC beats everything else. This avoids problems when two (or more) carriers rule
360          * matches as the score helps to find the best match uniquely. e.g.,
361          * rule 1 {mccmnc, imsi} rule 2 {mccmnc, imsi, gid1} and rule 3 {mccmnc, imsi, gid2} all
362          * matches with subscription data. rule 2 wins with the highest matching score.
363          */
364         private static final int SCORE_MCCMNC          = 1 << 7;
365         private static final int SCORE_IMSI_PREFIX     = 1 << 6;
366         private static final int SCORE_ICCID_PREFIX    = 1 << 5;
367         private static final int SCORE_GID1            = 1 << 4;
368         private static final int SCORE_GID2            = 1 << 3;
369         private static final int SCORE_PLMN            = 1 << 2;
370         private static final int SCORE_SPN             = 1 << 1;
371         private static final int SCORE_APN             = 1 << 0;
372 
373         private static final int SCORE_INVALID         = -1;
374 
375         // carrier matching attributes
376         private String mMccMnc;
377         private String mImsiPrefixPattern;
378         private String mIccidPrefix;
379         private String mGid1;
380         private String mGid2;
381         private String mPlmn;
382         private String mSpn;
383         private String mApn;
384 
385         // user-facing carrier name
386         private String mName;
387         // unique carrier id
388         private int mCid;
389 
390         private int mScore = 0;
391 
CarrierMatchingRule(String mccmnc, String imsiPrefixPattern, String iccidPrefix, String gid1, String gid2, String plmn, String spn, String apn, int cid, String name)392         CarrierMatchingRule(String mccmnc, String imsiPrefixPattern, String iccidPrefix,
393                 String gid1, String gid2, String plmn, String spn, String apn, int cid,
394                 String name) {
395             mMccMnc = mccmnc;
396             mImsiPrefixPattern = imsiPrefixPattern;
397             mIccidPrefix = iccidPrefix;
398             mGid1 = gid1;
399             mGid2 = gid2;
400             mPlmn = plmn;
401             mSpn = spn;
402             mApn = apn;
403             mCid = cid;
404             mName = name;
405         }
406 
407         // Calculate matching score. Values which aren't set in the rule are considered "wild".
408         // All values in the rule must match in order for the subscription to be considered part of
409         // the carrier. Otherwise, a invalid score -1 will be assigned. A match from a higher tier
410         // will beat any subsequent match which does not match at that tier. When there are multiple
411         // matches at the same tier, the match with highest score will be used.
match(CarrierMatchingRule subscriptionRule)412         public void match(CarrierMatchingRule subscriptionRule) {
413             mScore = 0;
414             if (mMccMnc != null) {
415                 if (!CarrierIdentifier.equals(subscriptionRule.mMccMnc, mMccMnc, false)) {
416                     mScore = SCORE_INVALID;
417                     return;
418                 }
419                 mScore += SCORE_MCCMNC;
420             }
421             if (mImsiPrefixPattern != null) {
422                 if (!imsiPrefixMatch(subscriptionRule.mImsiPrefixPattern, mImsiPrefixPattern)) {
423                     mScore = SCORE_INVALID;
424                     return;
425                 }
426                 mScore += SCORE_IMSI_PREFIX;
427             }
428             if (mIccidPrefix != null) {
429                 if (!iccidPrefixMatch(subscriptionRule.mIccidPrefix, mIccidPrefix)) {
430                     mScore = SCORE_INVALID;
431                     return;
432                 }
433                 mScore += SCORE_ICCID_PREFIX;
434             }
435             if (mGid1 != null) {
436                 // full string match. carrier matching should cover the corner case that gid1
437                 // with garbage tail due to SIM manufacture issues.
438                 if (!CarrierIdentifier.equals(subscriptionRule.mGid1, mGid1, true)) {
439                     mScore = SCORE_INVALID;
440                     return;
441                 }
442                 mScore += SCORE_GID1;
443             }
444             if (mGid2 != null) {
445                 // full string match. carrier matching should cover the corner case that gid2
446                 // with garbage tail due to SIM manufacture issues.
447                 if (!CarrierIdentifier.equals(subscriptionRule.mGid2, mGid2, true)) {
448                     mScore = SCORE_INVALID;
449                     return;
450                 }
451                 mScore += SCORE_GID2;
452             }
453             if (mPlmn != null) {
454                 if (!CarrierIdentifier.equals(subscriptionRule.mPlmn, mPlmn, true)) {
455                     mScore = SCORE_INVALID;
456                     return;
457                 }
458                 mScore += SCORE_PLMN;
459             }
460             if (mSpn != null) {
461                 if (!CarrierIdentifier.equals(subscriptionRule.mSpn, mSpn, true)) {
462                     mScore = SCORE_INVALID;
463                     return;
464                 }
465                 mScore += SCORE_SPN;
466             }
467             if (mApn != null) {
468                 if (!CarrierIdentifier.equals(subscriptionRule.mApn, mApn, true)) {
469                     mScore = SCORE_INVALID;
470                     return;
471                 }
472                 mScore += SCORE_APN;
473             }
474         }
475 
imsiPrefixMatch(String imsi, String prefixXPattern)476         private boolean imsiPrefixMatch(String imsi, String prefixXPattern) {
477             if (TextUtils.isEmpty(prefixXPattern)) return true;
478             if (TextUtils.isEmpty(imsi)) return false;
479             if (imsi.length() < prefixXPattern.length()) {
480                 return false;
481             }
482             for (int i = 0; i < prefixXPattern.length(); i++) {
483                 if ((prefixXPattern.charAt(i) != 'x') && (prefixXPattern.charAt(i) != 'X')
484                         && (prefixXPattern.charAt(i) != imsi.charAt(i))) {
485                     return false;
486                 }
487             }
488             return true;
489         }
490 
iccidPrefixMatch(String iccid, String prefix)491         private boolean iccidPrefixMatch(String iccid, String prefix) {
492             if (iccid == null || prefix == null) {
493                 return false;
494             }
495             return iccid.startsWith(prefix);
496         }
497 
toString()498         public String toString() {
499             return "[CarrierMatchingRule] -"
500                     + " mccmnc: " + mMccMnc
501                     + " gid1: " + mGid1
502                     + " gid2: " + mGid2
503                     + " plmn: " + mPlmn
504                     + " imsi_prefix: " + mImsiPrefixPattern
505                     + " iccid_prefix" + mIccidPrefix
506                     + " spn: " + mSpn
507                     + " apn: " + mApn
508                     + " name: " + mName
509                     + " cid: " + mCid
510                     + " score: " + mScore;
511         }
512     }
513 
514     /**
515      * find the best matching carrier from candidates with matched MCCMNC and notify
516      * all interested parties on carrier id change.
517      */
matchCarrier()518     private void matchCarrier() {
519         if (!SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
520             logd("[matchCarrier]" + "skip before sim records loaded");
521             return;
522         }
523         final String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId());
524         final String iccid = mPhone.getIccSerialNumber();
525         final String gid1 = mPhone.getGroupIdLevel1();
526         final String gid2 = mPhone.getGroupIdLevel2();
527         final String imsi = mPhone.getSubscriberId();
528         final String plmn = mPhone.getPlmn();
529         final String spn = mSpn;
530         final String apn = mPreferApn;
531 
532         if (VDBG) {
533             logd("[matchCarrier]"
534                     + " mnnmnc:" + mccmnc
535                     + " gid1: " + gid1
536                     + " gid2: " + gid2
537                     + " imsi: " + Rlog.pii(LOG_TAG, imsi)
538                     + " iccid: " + Rlog.pii(LOG_TAG, iccid)
539                     + " plmn: " + plmn
540                     + " spn: " + spn
541                     + " apn: " + apn);
542         }
543 
544         CarrierMatchingRule subscriptionRule = new CarrierMatchingRule(
545                 mccmnc, imsi, iccid, gid1, gid2, plmn,  spn, apn,
546                 TelephonyManager.UNKNOWN_CARRIER_ID, null);
547 
548         int maxScore = CarrierMatchingRule.SCORE_INVALID;
549         CarrierMatchingRule maxRule = null;
550 
551         for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) {
552             rule.match(subscriptionRule);
553             if (rule.mScore > maxScore) {
554                 maxScore = rule.mScore;
555                 maxRule = rule;
556             }
557         }
558 
559         if (maxScore == CarrierMatchingRule.SCORE_INVALID) {
560             logd("[matchCarrier - no match] cid: " + TelephonyManager.UNKNOWN_CARRIER_ID
561                     + " name: " + null);
562             updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null);
563         } else {
564             logd("[matchCarrier] cid: " + maxRule.mCid + " name: " + maxRule.mName);
565             updateCarrierIdAndName(maxRule.mCid, maxRule.mName);
566         }
567 
568         /*
569          * Write Carrier Identification Matching event, logging with the
570          * carrierId, mccmnc, gid1 and carrier list version to differentiate below cases of metrics:
571          * 1) unknown mccmnc - the Carrier Id provider contains no rule that matches the
572          * read mccmnc.
573          * 2) the Carrier Id provider contains some rule(s) that match the read mccmnc,
574          * but the read gid1 is not matched within the highest-scored rule.
575          * 3) successfully found a matched carrier id in the provider.
576          * 4) use carrier list version to compare the unknown carrier ratio between each version.
577          */
578         String unknownGid1ToLog = ((maxScore & CarrierMatchingRule.SCORE_GID1) == 0
579                 && !TextUtils.isEmpty(subscriptionRule.mGid1)) ? subscriptionRule.mGid1 : null;
580         String unknownMccmncToLog = ((maxScore == CarrierMatchingRule.SCORE_INVALID
581                 || (maxScore & CarrierMatchingRule.SCORE_GID1) == 0)
582                 && !TextUtils.isEmpty(subscriptionRule.mMccMnc)) ? subscriptionRule.mMccMnc : null;
583         TelephonyMetrics.getInstance().writeCarrierIdMatchingEvent(
584                 mPhone.getPhoneId(), getCarrierListVersion(), mCarrierId,
585                 unknownMccmncToLog, unknownGid1ToLog);
586     }
587 
getCarrierListVersion()588     public int getCarrierListVersion() {
589         final Cursor cursor = mContext.getContentResolver().query(
590                 Uri.withAppendedPath(CarrierId.All.CONTENT_URI,
591                 "get_version"), null, null, null);
592         cursor.moveToFirst();
593         return cursor.getInt(0);
594     }
595 
getCarrierId()596     public int getCarrierId() {
597         return mCarrierId;
598     }
599 
getCarrierName()600     public String getCarrierName() {
601         return mCarrierName;
602     }
603 
equals(String a, String b, boolean ignoreCase)604     private static boolean equals(String a, String b, boolean ignoreCase) {
605         if (a == null && b == null) return true;
606         if (a != null && b != null) {
607             return (ignoreCase) ? a.equalsIgnoreCase(b) : a.equals(b);
608         }
609         return false;
610     }
611 
logd(String str)612     private static void logd(String str) {
613         Rlog.d(LOG_TAG, str);
614     }
loge(String str)615     private static void loge(String str) {
616         Rlog.e(LOG_TAG, str);
617     }
dump(FileDescriptor fd, PrintWriter pw, String[] args)618     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
619         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
620         ipw.println("mCarrierIdLocalLogs:");
621         ipw.increaseIndent();
622         mCarrierIdLocalLog.dump(fd, pw, args);
623         ipw.decreaseIndent();
624 
625         ipw.println("mCarrierId: " + mCarrierId);
626         ipw.println("mCarrierName: " + mCarrierName);
627         ipw.println("version: " + getCarrierListVersion());
628 
629         ipw.println("mCarrierMatchingRules on mccmnc: "
630                 + mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId()));
631         ipw.increaseIndent();
632         for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) {
633             ipw.println(rule.toString());
634         }
635         ipw.decreaseIndent();
636 
637         ipw.println("mSpn: " + mSpn);
638         ipw.println("mPreferApn: " + mPreferApn);
639         ipw.flush();
640     }
641 }
642