• 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.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.content.BroadcastReceiver;
23 import android.content.ContentValues;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.database.ContentObserver;
28 import android.database.Cursor;
29 import android.net.Uri;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.provider.Telephony;
33 import android.service.carrier.CarrierIdentifier;
34 import android.telephony.CarrierConfigManager;
35 import android.telephony.PhoneStateListener;
36 import android.telephony.SubscriptionManager;
37 import android.telephony.TelephonyManager;
38 import android.text.TextUtils;
39 import android.util.LocalLog;
40 import android.util.Log;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.telephony.metrics.CarrierIdMatchStats;
44 import com.android.internal.telephony.metrics.TelephonyMetrics;
45 import com.android.internal.telephony.uicc.IccRecords;
46 import com.android.internal.telephony.uicc.UiccController;
47 import com.android.internal.telephony.util.TelephonyUtils;
48 import com.android.internal.util.IndentingPrintWriter;
49 import com.android.telephony.Rlog;
50 
51 import java.io.FileDescriptor;
52 import java.io.PrintWriter;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.List;
56 
57 /**
58  * CarrierResolver identifies the subscription carrier and returns a canonical carrier Id
59  * and a user friendly carrier name. CarrierResolver reads subscription info and check against
60  * all carrier matching rules stored in CarrierIdProvider. It is msim aware, each phone has a
61  * dedicated CarrierResolver.
62  */
63 public class CarrierResolver extends Handler {
64     private static final String LOG_TAG = CarrierResolver.class.getSimpleName();
65     private static final boolean DBG = true;
66     private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
67 
68     // events to trigger carrier identification
69     private static final int SIM_LOAD_EVENT             = 1;
70     private static final int ICC_CHANGED_EVENT          = 2;
71     private static final int PREFER_APN_UPDATE_EVENT    = 3;
72     private static final int CARRIER_ID_DB_UPDATE_EVENT = 4;
73 
74     private static final Uri CONTENT_URL_PREFER_APN = Uri.withAppendedPath(
75             Telephony.Carriers.CONTENT_URI, "preferapn");
76 
77     // Test purpose only.
78     private static final String TEST_ACTION = "com.android.internal.telephony"
79             + ".ACTION_TEST_OVERRIDE_CARRIER_ID";
80 
81     // cached version of the carrier list, so that we don't need to re-query it every time.
82     private Integer mCarrierListVersion;
83     // cached matching rules based mccmnc to speed up resolution
84     private List<CarrierMatchingRule> mCarrierMatchingRulesOnMccMnc = new ArrayList<>();
85     // cached carrier Id
86     private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
87     // cached specific carrier Id
88     private int mSpecificCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
89     // cached MNO carrier Id. mno carrier shares the same mccmnc as cid and can be solely
90     // identified by mccmnc only. If there is no such mno carrier, mno carrier id equals to
91     // the cid.
92     private int mMnoCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
93     // cached carrier name
94     private String mCarrierName;
95     private String mSpecificCarrierName;
96     // cached preferapn name
97     private String mPreferApn;
98     // override for testing purpose
99     private String mTestOverrideApn;
100     private String mTestOverrideCarrierPriviledgeRule;
101     // cached service provider name. telephonyManager API returns empty string as default value.
102     // some carriers need to target devices with Empty SPN. In that case, carrier matching rule
103     // should specify "" spn explicitly.
104     private String mSpn = "";
105 
106     private Context mContext;
107     private Phone mPhone;
108     private IccRecords mIccRecords;
109     private final LocalLog mCarrierIdLocalLog = new LocalLog(20);
110     private final TelephonyManager mTelephonyMgr;
111 
112     private final ContentObserver mContentObserver = new ContentObserver(this) {
113         @Override
114         public void onChange(boolean selfChange, Uri uri) {
115             if (Telephony.Carriers.CONTENT_URI.equals(uri)) {
116                 logd("onChange URI: " + uri);
117                 sendEmptyMessage(PREFER_APN_UPDATE_EVENT);
118             } else if (CarrierId.All.CONTENT_URI.equals(uri)) {
119                 logd("onChange URI: " + uri);
120                 sendEmptyMessage(CARRIER_ID_DB_UPDATE_EVENT);
121             }
122         }
123     };
124 
125     /**
126      * A broadcast receiver used for overriding carrier id for testing. There are six parameters,
127      * only override_carrier_id is required, the others are options.
128      *
129      * To override carrier id by adb command, e.g.:
130      * adb shell am broadcast -a com.android.internal.telephony.ACTION_TEST_OVERRIDE_CARRIER_ID \
131      * --ei override_carrier_id 1
132      * --ei override_specific_carrier_id 1
133      * --ei override_mno_carrier_id 1
134      * --es override_carrier_name test
135      * --es override_specific_carrier_name test
136      * --ei sub_id 1
137      */
138     private final BroadcastReceiver mCarrierIdTestReceiver = new BroadcastReceiver() {
139         @Override
140         public void onReceive(Context context, Intent intent) {
141             int phoneId = mPhone.getPhoneId();
142             int carrierId = intent.getIntExtra("override_carrier_id",
143                     TelephonyManager.UNKNOWN_CARRIER_ID);
144             int specificCarrierId = intent.getIntExtra("override_specific_carrier_id", carrierId);
145             int mnoCarrierId = intent.getIntExtra("override_mno_carrier_id", carrierId);
146             String carrierName = intent.getStringExtra("override_carrier_name");
147             String specificCarrierName = intent.getStringExtra("override_specific_carrier_name");
148             int subId = intent.getIntExtra("sub_id",
149                     SubscriptionManager.getDefaultSubscriptionId());
150 
151             if (carrierId <= 0) {
152                 logd("Override carrier id must be greater than 0.", phoneId);
153                 return;
154             } else if (subId != mPhone.getSubId()) {
155                 logd("Override carrier id failed. The sub id doesn't same as phone's sub id.",
156                         phoneId);
157                 return;
158             } else {
159                 logd("Override carrier id to: " + carrierId, phoneId);
160                 logd("Override specific carrier id to: " + specificCarrierId, phoneId);
161                 logd("Override mno carrier id to: " + mnoCarrierId, phoneId);
162                 logd("Override carrier name to: " + carrierName, phoneId);
163                 logd("Override specific carrier name to: " + specificCarrierName, phoneId);
164                 updateCarrierIdAndName(
165                     carrierId, carrierName != null ? carrierName : "",
166                     specificCarrierId, specificCarrierName != null ? carrierName : "",
167                     mnoCarrierId);
168             }
169         }
170     };
171 
CarrierResolver(Phone phone)172     public CarrierResolver(Phone phone) {
173         logd("Creating CarrierResolver[" + phone.getPhoneId() + "]");
174         mContext = phone.getContext();
175         mPhone = phone;
176         mTelephonyMgr = TelephonyManager.from(mContext);
177 
178         // register events
179         mContext.getContentResolver().registerContentObserver(CONTENT_URL_PREFER_APN, false,
180                 mContentObserver);
181         mContext.getContentResolver().registerContentObserver(
182                 CarrierId.All.CONTENT_URI, false, mContentObserver);
183         UiccController.getInstance().registerForIccChanged(this, ICC_CHANGED_EVENT, null);
184 
185         if (TelephonyUtils.IS_DEBUGGABLE) {
186             IntentFilter filter = new IntentFilter();
187             filter.addAction(TEST_ACTION);
188             mContext.registerReceiver(mCarrierIdTestReceiver, filter);
189         }
190     }
191 
192     /**
193      * This is triggered from SubscriptionInfoUpdater after sim state change.
194      * The sequence of sim loading would be
195      *  1. ACTION_SUBINFO_CONTENT_CHANGE
196      *  2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED
197      *  /ACTION_SIM_APPLICATION_STATE_CHANGED
198      *  3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED
199      *
200      *  For SIM refresh either reset or init refresh type, SubscriptionInfoUpdater will re-trigger
201      *  carrier identification with sim loaded state. Framework today silently handle single file
202      *  refresh type.
203      *  TODO: check fileId from single file refresh, if the refresh file is IMSI, gid1 or other
204      *  records which might change carrier id, framework should trigger sim loaded state just like
205      *  other refresh events: INIT or RESET and which will ultimately trigger carrier
206      *  re-identification.
207      */
resolveSubscriptionCarrierId(String simState)208     public void resolveSubscriptionCarrierId(String simState) {
209         logd("[resolveSubscriptionCarrierId] simState: " + simState);
210         switch (simState) {
211             case IccCardConstants.INTENT_VALUE_ICC_ABSENT:
212             case IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR:
213                 // only clear carrier id on absent to avoid transition to unknown carrier id during
214                 // intermediate states of sim refresh
215                 handleSimAbsent();
216                 break;
217             case IccCardConstants.INTENT_VALUE_ICC_LOADED:
218                 handleSimLoaded();
219                 break;
220         }
221     }
222 
handleSimLoaded()223     private void handleSimLoaded() {
224         if (mIccRecords != null) {
225             /**
226              * returns empty string to be consistent with
227              * {@link TelephonyManager#getSimOperatorName()}
228              */
229             mSpn = (mIccRecords.getServiceProviderName() == null) ? ""
230                     : mIccRecords.getServiceProviderName();
231         } else {
232             loge("mIccRecords is null on SIM_LOAD_EVENT, could not get SPN");
233         }
234         mPreferApn = getPreferApn();
235         loadCarrierMatchingRulesOnMccMnc(false /* update carrier config */);
236     }
237 
handleSimAbsent()238     private void handleSimAbsent() {
239         mCarrierMatchingRulesOnMccMnc.clear();
240         mSpn = null;
241         mPreferApn = null;
242         updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null,
243                 TelephonyManager.UNKNOWN_CARRIER_ID, null,
244                 TelephonyManager.UNKNOWN_CARRIER_ID);
245     }
246 
247     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
248         @Override
249         public void onCallStateChanged(int state, String ignored) {
250         }
251     };
252 
253     /**
254      * Entry point for the carrier identification.
255      *
256      *    1. SIM_LOAD_EVENT
257      *        This indicates that all SIM records has been loaded and its first entry point for the
258      *        carrier identification. Note, there are other attributes could be changed on the fly
259      *        like APN. We cached all carrier matching rules based on MCCMNC to speed
260      *        up carrier resolution on following trigger events.
261      *
262      *    2. PREFER_APN_UPDATE_EVENT
263      *        This indicates prefer apn has been changed. It could be triggered when user modified
264      *        APN settings or when default data connection first establishes on the current carrier.
265      *        We follow up on this by querying prefer apn sqlite and re-issue carrier identification
266      *        with the updated prefer apn name.
267      *
268      *    3. CARRIER_ID_DB_UPDATE_EVENT
269      *        This indicates that carrierIdentification database which stores all matching rules
270      *        has been updated. It could be triggered from OTA or assets update.
271      */
272     @Override
handleMessage(Message msg)273     public void handleMessage(Message msg) {
274         if (DBG) logd("handleMessage: " + msg.what);
275         switch (msg.what) {
276             case SIM_LOAD_EVENT:
277                 handleSimLoaded();
278                 break;
279             case CARRIER_ID_DB_UPDATE_EVENT:
280                 // clean the cached carrier list version, so that a new one will be queried.
281                 mCarrierListVersion = null;
282                 loadCarrierMatchingRulesOnMccMnc(true /* update carrier config*/);
283                 break;
284             case PREFER_APN_UPDATE_EVENT:
285                 String preferApn = getPreferApn();
286                 if (!equals(mPreferApn, preferApn, true)) {
287                     logd("[updatePreferApn] from:" + mPreferApn + " to:" + preferApn);
288                     mPreferApn = preferApn;
289                     matchSubscriptionCarrier(true /* update carrier config*/);
290                 }
291                 break;
292             case ICC_CHANGED_EVENT:
293                 // all records used for carrier identification are from SimRecord.
294                 final IccRecords newIccRecords = UiccController.getInstance().getIccRecords(
295                         mPhone.getPhoneId(), UiccController.APP_FAM_3GPP);
296                 if (mIccRecords != newIccRecords) {
297                     if (mIccRecords != null) {
298                         logd("Removing stale icc objects.");
299                         mIccRecords.unregisterForRecordsOverride(this);
300                         mIccRecords = null;
301                     }
302                     if (newIccRecords != null) {
303                         logd("new Icc object");
304                         newIccRecords.registerForRecordsOverride(this, SIM_LOAD_EVENT, null);
305                         mIccRecords = newIccRecords;
306                     }
307                 }
308                 break;
309             default:
310                 loge("invalid msg: " + msg.what);
311                 break;
312         }
313     }
314 
loadCarrierMatchingRulesOnMccMnc(boolean updateCarrierConfig)315     private void loadCarrierMatchingRulesOnMccMnc(boolean updateCarrierConfig) {
316         try {
317             String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId());
318             Cursor cursor = mContext.getContentResolver().query(
319                     CarrierId.All.CONTENT_URI,
320                     /* projection */ null,
321                     /* selection */ CarrierId.All.MCCMNC + "=?",
322                     /* selectionArgs */ new String[]{mccmnc}, null);
323             try {
324                 if (cursor != null) {
325                     if (VDBG) {
326                         logd("[loadCarrierMatchingRules]- " + cursor.getCount()
327                                 + " Records(s) in DB" + " mccmnc: " + mccmnc);
328                     }
329                     mCarrierMatchingRulesOnMccMnc.clear();
330                     while (cursor.moveToNext()) {
331                         mCarrierMatchingRulesOnMccMnc.add(makeCarrierMatchingRule(cursor));
332                     }
333                     matchSubscriptionCarrier(updateCarrierConfig);
334 
335                     // Generate metrics related to carrier ID table version.
336                     CarrierIdMatchStats.sendCarrierIdTableVersion(getCarrierListVersion());
337                 }
338             } finally {
339                 if (cursor != null) {
340                     cursor.close();
341                 }
342             }
343         } catch (Exception ex) {
344             loge("[loadCarrierMatchingRules]- ex: " + ex);
345         }
346     }
347 
getCarrierNameFromId(int cid)348     private String getCarrierNameFromId(int cid) {
349         try {
350             Cursor cursor = mContext.getContentResolver().query(
351                     CarrierId.All.CONTENT_URI,
352                     /* projection */ null,
353                     /* selection */ CarrierId.CARRIER_ID + "=?",
354                     /* selectionArgs */ new String[]{cid + ""}, null);
355             try {
356                 if (cursor != null) {
357                     if (VDBG) {
358                         logd("[getCarrierNameFromId]- " + cursor.getCount()
359                                 + " Records(s) in DB" + " cid: " + cid);
360                     }
361                     while (cursor.moveToNext()) {
362                         return cursor.getString(cursor.getColumnIndex(CarrierId.CARRIER_NAME));
363                     }
364                 }
365             } finally {
366                 if (cursor != null) {
367                     cursor.close();
368                 }
369             }
370         } catch (Exception ex) {
371             loge("[getCarrierNameFromId]- ex: " + ex);
372         }
373         return null;
374     }
375 
getCarrierMatchingRulesFromMccMnc( @onNull Context context, String mccmnc)376     private static List<CarrierMatchingRule> getCarrierMatchingRulesFromMccMnc(
377             @NonNull Context context, String mccmnc) {
378         List<CarrierMatchingRule> rules = new ArrayList<>();
379         try {
380             Cursor cursor = context.getContentResolver().query(
381                     CarrierId.All.CONTENT_URI,
382                     /* projection */ null,
383                     /* selection */ CarrierId.All.MCCMNC + "=?",
384                     /* selectionArgs */ new String[]{mccmnc}, null);
385             try {
386                 if (cursor != null) {
387                     if (VDBG) {
388                         logd("[loadCarrierMatchingRules]- " + cursor.getCount()
389                                 + " Records(s) in DB" + " mccmnc: " + mccmnc);
390                     }
391                     rules.clear();
392                     while (cursor.moveToNext()) {
393                         rules.add(makeCarrierMatchingRule(cursor));
394                     }
395                 }
396             } finally {
397                 if (cursor != null) {
398                     cursor.close();
399                 }
400             }
401         } catch (Exception ex) {
402             loge("[loadCarrierMatchingRules]- ex: " + ex);
403         }
404         return rules;
405     }
406 
getPreferApn()407     private String getPreferApn() {
408         // return test overrides if present
409         if (!TextUtils.isEmpty(mTestOverrideApn)) {
410             logd("[getPreferApn]- " + mTestOverrideApn + " test override");
411             return mTestOverrideApn;
412         }
413         Cursor cursor = mContext.getContentResolver().query(
414                 Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapn/subId/"
415                 + mPhone.getSubId()), /* projection */ new String[]{Telephony.Carriers.APN},
416                 /* selection */ null, /* selectionArgs */ null, /* sortOrder */ null);
417         try {
418             if (cursor != null) {
419                 if (VDBG) {
420                     logd("[getPreferApn]- " + cursor.getCount() + " Records(s) in DB");
421                 }
422                 while (cursor.moveToNext()) {
423                     String apn = cursor.getString(cursor.getColumnIndexOrThrow(
424                             Telephony.Carriers.APN));
425                     logd("[getPreferApn]- " + apn);
426                     return apn;
427                 }
428             }
429         } catch (Exception ex) {
430             loge("[getPreferApn]- exception: " + ex);
431         } finally {
432             if (cursor != null) {
433                 cursor.close();
434             }
435         }
436         return null;
437     }
438 
isPreferApnUserEdited(@onNull String preferApn)439     private boolean isPreferApnUserEdited(@NonNull String preferApn) {
440         try (Cursor cursor = mContext.getContentResolver().query(
441                 Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI,
442                         "preferapn/subId/" + mPhone.getSubId()),
443                 /* projection */ new String[]{Telephony.Carriers.EDITED_STATUS},
444                 /* selection */ Telephony.Carriers.APN + "=?",
445                 /* selectionArgs */ new String[]{preferApn}, /* sortOrder */ null) ) {
446             if (cursor != null && cursor.moveToFirst()) {
447                 return cursor.getInt(cursor.getColumnIndexOrThrow(
448                         Telephony.Carriers.EDITED_STATUS)) == Telephony.Carriers.USER_EDITED;
449             }
450         } catch (Exception ex) {
451             loge("[isPreferApnUserEdited]- exception: " + ex);
452         }
453         return false;
454     }
455 
setTestOverrideApn(String apn)456     public void setTestOverrideApn(String apn) {
457         logd("[setTestOverrideApn]: " + apn);
458         mTestOverrideApn = apn;
459     }
460 
setTestOverrideCarrierPriviledgeRule(String rule)461     public void setTestOverrideCarrierPriviledgeRule(String rule) {
462         logd("[setTestOverrideCarrierPriviledgeRule]: " + rule);
463         mTestOverrideCarrierPriviledgeRule = rule;
464     }
465 
updateCarrierIdAndName(int cid, String name, int specificCarrierId, String specificCarrierName, int mnoCid)466     private void updateCarrierIdAndName(int cid, String name,
467                                         int specificCarrierId, String specificCarrierName,
468                                         int mnoCid) {
469         boolean update = false;
470         if (specificCarrierId != mSpecificCarrierId) {
471             logd("[updateSpecificCarrierId] from:" + mSpecificCarrierId + " to:"
472                     + specificCarrierId);
473             mSpecificCarrierId = specificCarrierId;
474             update = true;
475         }
476         if (specificCarrierName != mSpecificCarrierName) {
477             logd("[updateSpecificCarrierName] from:" + mSpecificCarrierName + " to:"
478                     + specificCarrierName);
479             mSpecificCarrierName = specificCarrierName;
480             update = true;
481         }
482         if (update) {
483             mCarrierIdLocalLog.log("[updateSpecificCarrierIdAndName] cid:"
484                     + mSpecificCarrierId + " name:" + mSpecificCarrierName);
485             final Intent intent = new Intent(TelephonyManager
486                     .ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED);
487             intent.putExtra(TelephonyManager.EXTRA_SPECIFIC_CARRIER_ID, mSpecificCarrierId);
488             intent.putExtra(TelephonyManager.EXTRA_SPECIFIC_CARRIER_NAME, mSpecificCarrierName);
489             intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId());
490             mContext.sendBroadcast(intent);
491 
492             // notify content observers for specific carrier id change event.
493             ContentValues cv = new ContentValues();
494             cv.put(CarrierId.SPECIFIC_CARRIER_ID, mSpecificCarrierId);
495             cv.put(CarrierId.SPECIFIC_CARRIER_ID_NAME, mSpecificCarrierName);
496             mContext.getContentResolver().update(
497                     Telephony.CarrierId.getSpecificCarrierIdUriForSubscriptionId(mPhone.getSubId()),
498                     cv, null, null);
499         }
500 
501         update = false;
502         if (!equals(name, mCarrierName, true)) {
503             logd("[updateCarrierName] from:" + mCarrierName + " to:" + name);
504             mCarrierName = name;
505             update = true;
506         }
507         if (cid != mCarrierId) {
508             logd("[updateCarrierId] from:" + mCarrierId + " to:" + cid);
509             mCarrierId = cid;
510             update = true;
511         }
512         if (mnoCid != mMnoCarrierId) {
513             logd("[updateMnoCarrierId] from:" + mMnoCarrierId + " to:" + mnoCid);
514             mMnoCarrierId = mnoCid;
515             update = true;
516         }
517         if (update) {
518             mCarrierIdLocalLog.log("[updateCarrierIdAndName] cid:" + mCarrierId + " name:"
519                     + mCarrierName + " mnoCid:" + mMnoCarrierId);
520             final Intent intent = new Intent(TelephonyManager
521                     .ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
522             intent.putExtra(TelephonyManager.EXTRA_CARRIER_ID, mCarrierId);
523             intent.putExtra(TelephonyManager.EXTRA_CARRIER_NAME, mCarrierName);
524             intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId());
525             mContext.sendBroadcast(intent);
526 
527             // notify content observers for carrier id change event
528             ContentValues cv = new ContentValues();
529             cv.put(CarrierId.CARRIER_ID, mCarrierId);
530             cv.put(CarrierId.CARRIER_NAME, mCarrierName);
531             mContext.getContentResolver().update(
532                     Telephony.CarrierId.getUriForSubscriptionId(mPhone.getSubId()), cv, null, null);
533         }
534         // during esim profile switch, there is no sim absent thus carrier id will persist and
535         // might not trigger an update if switch profiles for the same carrier. thus always update
536         // subscriptioninfo db to make sure we have correct carrier id set.
537         if (SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
538             // only persist carrier id to simInfo db when subId is valid.
539             SubscriptionController.getInstance().setCarrierId(mCarrierId, mPhone.getSubId());
540         }
541     }
542 
makeCarrierMatchingRule(Cursor cursor)543     private static CarrierMatchingRule makeCarrierMatchingRule(Cursor cursor) {
544         String certs = cursor.getString(
545                 cursor.getColumnIndexOrThrow(CarrierId.All.PRIVILEGE_ACCESS_RULE));
546         return new CarrierMatchingRule(
547                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.MCCMNC)),
548                 cursor.getString(cursor.getColumnIndexOrThrow(
549                         CarrierId.All.IMSI_PREFIX_XPATTERN)),
550                 cursor.getString(cursor.getColumnIndexOrThrow(
551                         CarrierId.All.ICCID_PREFIX)),
552                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID1)),
553                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID2)),
554                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.PLMN)),
555                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.SPN)),
556                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.APN)),
557                 (TextUtils.isEmpty(certs) ? null : new ArrayList<>(Arrays.asList(certs))),
558                 cursor.getInt(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_ID)),
559                 cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_NAME)),
560                 cursor.getInt(cursor.getColumnIndexOrThrow(CarrierId.PARENT_CARRIER_ID)));
561     }
562 
563     /**
564      * carrier matching attributes with corresponding cid
565      */
566     public static class CarrierMatchingRule {
567         /**
568          * These scores provide the hierarchical relationship between the attributes, intended to
569          * resolve conflicts in a deterministic way. The scores are constructed such that a match
570          * from a higher tier will beat any subsequent match which does not match at that tier,
571          * so MCCMNC beats everything else. This avoids problems when two (or more) carriers rule
572          * matches as the score helps to find the best match uniquely. e.g.,
573          * rule 1 {mccmnc, imsi} rule 2 {mccmnc, imsi, gid1} and rule 3 {mccmnc, imsi, gid2} all
574          * matches with subscription data. rule 2 wins with the highest matching score.
575          */
576         private static final int SCORE_MCCMNC                   = 1 << 8;
577         private static final int SCORE_IMSI_PREFIX              = 1 << 7;
578         private static final int SCORE_ICCID_PREFIX             = 1 << 6;
579         private static final int SCORE_GID1                     = 1 << 5;
580         private static final int SCORE_GID2                     = 1 << 4;
581         private static final int SCORE_PLMN                     = 1 << 3;
582         private static final int SCORE_PRIVILEGE_ACCESS_RULE    = 1 << 2;
583         private static final int SCORE_SPN                      = 1 << 1;
584         private static final int SCORE_APN                      = 1 << 0;
585 
586         private static final int SCORE_INVALID                  = -1;
587 
588         // carrier matching attributes
589         public final String mccMnc;
590         public final String imsiPrefixPattern;
591         public final String iccidPrefix;
592         public final String gid1;
593         public final String gid2;
594         public final String plmn;
595         public final String spn;
596         public final String apn;
597         // there can be multiple certs configured in the UICC
598         public final List<String> privilegeAccessRule;
599 
600         // user-facing carrier name
601         private String mName;
602         // unique carrier id
603         private int mCid;
604         // unique parent carrier id
605         private int mParentCid;
606 
607         private int mScore = 0;
608 
609         @VisibleForTesting
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)610         public CarrierMatchingRule(String mccmnc, String imsiPrefixPattern, String iccidPrefix,
611                 String gid1, String gid2, String plmn, String spn, String apn,
612                 List<String> privilegeAccessRule, int cid, String name, int parentCid) {
613             mccMnc = mccmnc;
614             this.imsiPrefixPattern = imsiPrefixPattern;
615             this.iccidPrefix = iccidPrefix;
616             this.gid1 = gid1;
617             this.gid2 = gid2;
618             this.plmn = plmn;
619             this.spn = spn;
620             this.apn = apn;
621             this.privilegeAccessRule = privilegeAccessRule;
622             mCid = cid;
623             mName = name;
624             mParentCid = parentCid;
625         }
626 
CarrierMatchingRule(CarrierMatchingRule rule)627         private CarrierMatchingRule(CarrierMatchingRule rule) {
628             mccMnc = rule.mccMnc;
629             imsiPrefixPattern = rule.imsiPrefixPattern;
630             iccidPrefix = rule.iccidPrefix;
631             gid1 = rule.gid1;
632             gid2 = rule.gid2;
633             plmn = rule.plmn;
634             spn = rule.spn;
635             apn = rule.apn;
636             privilegeAccessRule = rule.privilegeAccessRule;
637             mCid = rule.mCid;
638             mName = rule.mName;
639             mParentCid = rule.mParentCid;
640         }
641 
642         // Calculate matching score. Values which aren't set in the rule are considered "wild".
643         // All values in the rule must match in order for the subscription to be considered part of
644         // the carrier. Otherwise, a invalid score -1 will be assigned. A match from a higher tier
645         // will beat any subsequent match which does not match at that tier. When there are multiple
646         // matches at the same tier, the match with highest score will be used.
match(CarrierMatchingRule subscriptionRule)647         public void match(CarrierMatchingRule subscriptionRule) {
648             mScore = 0;
649             if (mccMnc != null) {
650                 if (!CarrierResolver.equals(subscriptionRule.mccMnc, mccMnc, false)) {
651                     mScore = SCORE_INVALID;
652                     return;
653                 }
654                 mScore += SCORE_MCCMNC;
655             }
656             if (imsiPrefixPattern != null) {
657                 if (!imsiPrefixMatch(subscriptionRule.imsiPrefixPattern, imsiPrefixPattern)) {
658                     mScore = SCORE_INVALID;
659                     return;
660                 }
661                 mScore += SCORE_IMSI_PREFIX;
662             }
663             if (iccidPrefix != null) {
664                 if (!iccidPrefixMatch(subscriptionRule.iccidPrefix, iccidPrefix)) {
665                     mScore = SCORE_INVALID;
666                     return;
667                 }
668                 mScore += SCORE_ICCID_PREFIX;
669             }
670             if (gid1 != null) {
671                 if (!gidMatch(subscriptionRule.gid1, gid1)) {
672                     mScore = SCORE_INVALID;
673                     return;
674                 }
675                 mScore += SCORE_GID1;
676             }
677             if (gid2 != null) {
678                 if (!gidMatch(subscriptionRule.gid2, gid2)) {
679                     mScore = SCORE_INVALID;
680                     return;
681                 }
682                 mScore += SCORE_GID2;
683             }
684             if (plmn != null) {
685                 if (!CarrierResolver.equals(subscriptionRule.plmn, plmn, true)) {
686                     mScore = SCORE_INVALID;
687                     return;
688                 }
689                 mScore += SCORE_PLMN;
690             }
691             if (spn != null) {
692                 if (!CarrierResolver.equals(subscriptionRule.spn, spn, true)) {
693                     mScore = SCORE_INVALID;
694                     return;
695                 }
696                 mScore += SCORE_SPN;
697             }
698 
699             if (privilegeAccessRule != null && !privilegeAccessRule.isEmpty()) {
700                 if (!carrierPrivilegeRulesMatch(subscriptionRule.privilegeAccessRule,
701                         privilegeAccessRule)) {
702                     mScore = SCORE_INVALID;
703                     return;
704                 }
705                 mScore += SCORE_PRIVILEGE_ACCESS_RULE;
706             }
707 
708             if (apn != null) {
709                 if (!CarrierResolver.equals(subscriptionRule.apn, apn, true)) {
710                     mScore = SCORE_INVALID;
711                     return;
712                 }
713                 mScore += SCORE_APN;
714             }
715         }
716 
imsiPrefixMatch(String imsi, String prefixXPattern)717         private boolean imsiPrefixMatch(String imsi, String prefixXPattern) {
718             if (TextUtils.isEmpty(prefixXPattern)) return true;
719             if (TextUtils.isEmpty(imsi)) return false;
720             if (imsi.length() < prefixXPattern.length()) {
721                 return false;
722             }
723             for (int i = 0; i < prefixXPattern.length(); i++) {
724                 if ((prefixXPattern.charAt(i) != 'x') && (prefixXPattern.charAt(i) != 'X')
725                         && (prefixXPattern.charAt(i) != imsi.charAt(i))) {
726                     return false;
727                 }
728             }
729             return true;
730         }
731 
iccidPrefixMatch(String iccid, String prefix)732         private boolean iccidPrefixMatch(String iccid, String prefix) {
733             if (iccid == null || prefix == null) {
734                 return false;
735             }
736             return iccid.startsWith(prefix);
737         }
738 
739         // We are doing prefix and case insensitive match.
740         // Ideally we should do full string match. However due to SIM manufacture issues
741         // gid from some SIM might has garbage tail.
gidMatch(String gidFromSim, String gid)742         private boolean gidMatch(String gidFromSim, String gid) {
743             return (gidFromSim != null) && gidFromSim.toLowerCase().startsWith(gid.toLowerCase());
744         }
745 
carrierPrivilegeRulesMatch(List<String> certsFromSubscription, List<String> certs)746         private boolean carrierPrivilegeRulesMatch(List<String> certsFromSubscription,
747                                                    List<String> certs) {
748             if (certsFromSubscription == null || certsFromSubscription.isEmpty()) {
749                 return false;
750             }
751             for (String cert : certs) {
752                 for (String certFromSubscription : certsFromSubscription) {
753                     if (!TextUtils.isEmpty(cert)
754                             && cert.equalsIgnoreCase(certFromSubscription)) {
755                         return true;
756                     }
757                 }
758             }
759             return false;
760         }
761 
toString()762         public String toString() {
763             return "[CarrierMatchingRule] -"
764                     + " mccmnc: " + mccMnc
765                     + " gid1: " + gid1
766                     + " gid2: " + gid2
767                     + " plmn: " + plmn
768                     + " imsi_prefix: " + imsiPrefixPattern
769                     + " iccid_prefix" + iccidPrefix
770                     + " spn: " + spn
771                     + " privilege_access_rule: " + privilegeAccessRule
772                     + " apn: " + apn
773                     + " name: " + mName
774                     + " cid: " + mCid
775                     + " score: " + mScore;
776         }
777     }
778 
getSubscriptionMatchingRule()779     private CarrierMatchingRule getSubscriptionMatchingRule() {
780         final String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId());
781         final String iccid = mPhone.getIccSerialNumber();
782         final String gid1 = mPhone.getGroupIdLevel1();
783         final String gid2 = mPhone.getGroupIdLevel2();
784         final String imsi = mPhone.getSubscriberId();
785         final String plmn = mPhone.getPlmn();
786         final String spn = mSpn;
787         final String apn = mPreferApn;
788         List<String> accessRules;
789         // check if test override present
790         if (!TextUtils.isEmpty(mTestOverrideCarrierPriviledgeRule)) {
791             accessRules = new ArrayList<>(Arrays.asList(mTestOverrideCarrierPriviledgeRule));
792         } else {
793             accessRules = mTelephonyMgr.createForSubscriptionId(mPhone.getSubId())
794                     .getCertsFromCarrierPrivilegeAccessRules();
795         }
796 
797         if (VDBG) {
798             logd("[matchSubscriptionCarrier]"
799                     + " mnnmnc:" + mccmnc
800                     + " gid1: " + gid1
801                     + " gid2: " + gid2
802                     + " imsi: " + Rlog.pii(LOG_TAG, imsi)
803                     + " iccid: " + Rlog.pii(LOG_TAG, iccid)
804                     + " plmn: " + plmn
805                     + " spn: " + spn
806                     + " apn: " + apn
807                     + " accessRules: " + ((accessRules != null) ? accessRules : null));
808         }
809         return new CarrierMatchingRule(
810                 mccmnc, imsi, iccid, gid1, gid2, plmn, spn, apn, accessRules,
811                 TelephonyManager.UNKNOWN_CARRIER_ID, null,
812                 TelephonyManager.UNKNOWN_CARRIER_ID);
813     }
814 
updateCarrierConfig()815     private void updateCarrierConfig() {
816         IccCard iccCard = mPhone.getIccCard();
817         IccCardConstants.State simState = IccCardConstants.State.UNKNOWN;
818         if (iccCard != null) {
819             simState = iccCard.getState();
820         }
821         CarrierConfigManager configManager = (CarrierConfigManager)
822                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
823         configManager.updateConfigForPhoneId(mPhone.getPhoneId(),
824                 UiccController.getIccStateIntentString(simState));
825     }
826 
827     /**
828      * find the best matching carrier from candidates with matched subscription MCCMNC.
829      */
matchSubscriptionCarrier(boolean updateCarrierConfig)830     private void matchSubscriptionCarrier(boolean updateCarrierConfig) {
831         if (!SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
832             logd("[matchSubscriptionCarrier]" + "skip before sim records loaded");
833             return;
834         }
835         int maxScore = CarrierMatchingRule.SCORE_INVALID;
836         /**
837          * For child-parent relationship. either child and parent have the same matching
838          * score, or child's matching score > parents' matching score.
839          */
840         CarrierMatchingRule maxRule = null;
841         CarrierMatchingRule maxRuleParent = null;
842         /**
843          * matching rule with mccmnc only. If mnoRule is found, then mno carrier id equals to the
844          * cid from mnoRule. otherwise, mno carrier id is same as cid.
845          */
846         CarrierMatchingRule mnoRule = null;
847         CarrierMatchingRule subscriptionRule = getSubscriptionMatchingRule();
848 
849         for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) {
850             rule.match(subscriptionRule);
851             if (rule.mScore > maxScore) {
852                 maxScore = rule.mScore;
853                 maxRule = rule;
854                 maxRuleParent = rule;
855             } else if (maxScore > CarrierMatchingRule.SCORE_INVALID && rule.mScore == maxScore) {
856                 // to handle the case that child parent has the same matching score, we need to
857                 // differentiate who is child who is parent.
858                 if (rule.mParentCid == maxRule.mCid) {
859                     maxRule = rule;
860                 } else if (maxRule.mParentCid == rule.mCid) {
861                     maxRuleParent = rule;
862                 }
863             }
864             if (rule.mScore == CarrierMatchingRule.SCORE_MCCMNC) {
865                 mnoRule = rule;
866             }
867         }
868         if (maxScore == CarrierMatchingRule.SCORE_INVALID) {
869             logd("[matchSubscriptionCarrier - no match] cid: " + TelephonyManager.UNKNOWN_CARRIER_ID
870                     + " name: " + null);
871             updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null,
872                     TelephonyManager.UNKNOWN_CARRIER_ID, null,
873                     TelephonyManager.UNKNOWN_CARRIER_ID);
874         } else {
875             // if there is a single matching result, check if this rule has parent cid assigned.
876             if ((maxRule == maxRuleParent)
877                     && maxRule.mParentCid != TelephonyManager.UNKNOWN_CARRIER_ID) {
878                 maxRuleParent = new CarrierMatchingRule(maxRule);
879                 maxRuleParent.mCid = maxRuleParent.mParentCid;
880                 maxRuleParent.mName = getCarrierNameFromId(maxRuleParent.mCid);
881             }
882             logd("[matchSubscriptionCarrier] specific cid: " + maxRule.mCid
883                     + " specific name: " + maxRule.mName +" cid: " + maxRuleParent.mCid
884                     + " name: " + maxRuleParent.mName);
885             updateCarrierIdAndName(maxRuleParent.mCid, maxRuleParent.mName,
886                     maxRule.mCid, maxRule.mName,
887                     (mnoRule == null) ? maxRule.mCid : mnoRule.mCid);
888 
889             if (updateCarrierConfig) {
890                 logd("[matchSubscriptionCarrier] - Calling updateCarrierConfig()");
891                 updateCarrierConfig();
892             }
893         }
894 
895         /*
896          * Write Carrier Identification Matching event, logging with the
897          * carrierId, mccmnc, gid1 and carrier list version to differentiate below cases of metrics:
898          * 1) unknown mccmnc - the Carrier Id provider contains no rule that matches the
899          * read mccmnc.
900          * 2) the Carrier Id provider contains some rule(s) that match the read mccmnc,
901          * but the read gid1 is not matched within the highest-scored rule.
902          * 3) successfully found a matched carrier id in the provider.
903          * 4) use carrier list version to compare the unknown carrier ratio between each version.
904          */
905         String unknownGid1ToLog = ((maxScore & CarrierMatchingRule.SCORE_GID1) == 0
906                 && !TextUtils.isEmpty(subscriptionRule.gid1)) ? subscriptionRule.gid1 : null;
907         String unknownMccmncToLog = ((maxScore == CarrierMatchingRule.SCORE_INVALID
908                 || (maxScore & CarrierMatchingRule.SCORE_GID1) == 0)
909                 && !TextUtils.isEmpty(subscriptionRule.mccMnc)) ? subscriptionRule.mccMnc : null;
910 
911         // pass subscription rule to metrics. scrub all possible PII before uploading.
912         // only log apn if not user edited.
913         String apn = (subscriptionRule.apn != null
914                 && !isPreferApnUserEdited(subscriptionRule.apn))
915                 ? subscriptionRule.apn : null;
916         // only log first 7 bits of iccid
917         String iccidPrefix = (subscriptionRule.iccidPrefix != null)
918                 && (subscriptionRule.iccidPrefix.length() >= 7)
919                 ? subscriptionRule.iccidPrefix.substring(0, 7) : subscriptionRule.iccidPrefix;
920         // only log first 8 bits of imsi
921         String imsiPrefix = (subscriptionRule.imsiPrefixPattern != null)
922                 && (subscriptionRule.imsiPrefixPattern.length() >= 8)
923                 ? subscriptionRule.imsiPrefixPattern.substring(0, 8)
924                 : subscriptionRule.imsiPrefixPattern;
925 
926         CarrierMatchingRule simInfo = new CarrierMatchingRule(
927                 subscriptionRule.mccMnc,
928                 imsiPrefix,
929                 iccidPrefix,
930                 subscriptionRule.gid1,
931                 subscriptionRule.gid2,
932                 subscriptionRule.plmn,
933                 subscriptionRule.spn,
934                 apn,
935                 subscriptionRule.privilegeAccessRule,
936                 -1, null, -1);
937 
938         TelephonyMetrics.getInstance().writeCarrierIdMatchingEvent(
939                 mPhone.getPhoneId(), getCarrierListVersion(), mCarrierId,
940                 unknownMccmncToLog, unknownGid1ToLog, simInfo);
941 
942         // Generate statsd metrics only when MCC/MNC is unknown or there is no match for GID1.
943         if (unknownMccmncToLog != null || unknownGid1ToLog != null) {
944             // Pass the PNN value to metrics only if the SPN is empty
945             String pnn = TextUtils.isEmpty(subscriptionRule.spn) ? subscriptionRule.plmn : "";
946             CarrierIdMatchStats.onCarrierIdMismatch(
947                     mCarrierId, unknownMccmncToLog, unknownGid1ToLog, subscriptionRule.spn, pnn);
948         }
949     }
950 
getCarrierListVersion()951     public int getCarrierListVersion() {
952         // Use the cached value if it exists, otherwise retrieve it.
953         if (mCarrierListVersion == null) {
954             final Cursor cursor = mContext.getContentResolver().query(
955                     Uri.withAppendedPath(CarrierId.All.CONTENT_URI,
956                     "get_version"), null, null, null);
957             cursor.moveToFirst();
958             mCarrierListVersion = cursor.getInt(0);
959         }
960         return mCarrierListVersion;
961     }
962 
getCarrierId()963     public int getCarrierId() {
964         return mCarrierId;
965     }
966     /**
967      * Returns fine-grained carrier id of the current subscription. Carrier ids with a valid parent
968      * id are specific carrier ids.
969      *
970      * A specific carrier ID can represent the fact that a carrier may be in effect an aggregation
971      * of other carriers (ie in an MVNO type scenario) where each of these specific carriers which
972      * are used to make up the actual carrier service may have different carrier configurations.
973      * A specific carrier ID could also be used, for example, in a scenario where a carrier requires
974      * different carrier configuration for different service offering such as a prepaid plan.
975      * e.g, {@link #getCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM, while
976      * {@link #getSpecificCarrierId()} can return Tracfone AT&T or Tracfone T-Mobile based on the
977      * IMSI from the current subscription.
978      *
979      * For carriers without any fine-grained carrier ids, return {@link #getCarrierId()}
980      */
getSpecificCarrierId()981     public int getSpecificCarrierId() {
982         return mSpecificCarrierId;
983     }
984 
getCarrierName()985     public String getCarrierName() {
986         return mCarrierName;
987     }
988 
getSpecificCarrierName()989     public String getSpecificCarrierName() {
990         return mSpecificCarrierName;
991     }
992 
getMnoCarrierId()993     public int getMnoCarrierId() {
994         return mMnoCarrierId;
995     }
996 
997     /**
998      * a util function to convert carrierIdentifier to the best matching carrier id.
999      *
1000      * @return the best matching carrier id.
1001      */
getCarrierIdFromIdentifier(@onNull Context context, @NonNull CarrierIdentifier carrierIdentifier)1002     public static int getCarrierIdFromIdentifier(@NonNull Context context,
1003                                                  @NonNull CarrierIdentifier carrierIdentifier) {
1004         final String mccmnc = carrierIdentifier.getMcc() + carrierIdentifier.getMnc();
1005         final String gid1 = carrierIdentifier.getGid1();
1006         final String gid2 = carrierIdentifier.getGid2();
1007         final String imsi = carrierIdentifier.getImsi();
1008         final String spn = carrierIdentifier.getSpn();
1009         if (VDBG) {
1010             logd("[getCarrierIdFromIdentifier]"
1011                     + " mnnmnc:" + mccmnc
1012                     + " gid1: " + gid1
1013                     + " gid2: " + gid2
1014                     + " imsi: " + Rlog.pii(LOG_TAG, imsi)
1015                     + " spn: " + spn);
1016         }
1017         // assign null to other fields which are not supported by carrierIdentifier.
1018         CarrierMatchingRule targetRule =
1019                 new CarrierMatchingRule(mccmnc, imsi, null, gid1, gid2, null,
1020                         spn, null, null,
1021                         TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION, null,
1022                         TelephonyManager.UNKNOWN_CARRIER_ID);
1023 
1024         int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
1025         int maxScore = CarrierMatchingRule.SCORE_INVALID;
1026         List<CarrierMatchingRule> rules = getCarrierMatchingRulesFromMccMnc(
1027                 context, targetRule.mccMnc);
1028         for (CarrierMatchingRule rule : rules) {
1029             rule.match(targetRule);
1030             if (rule.mScore > maxScore) {
1031                 maxScore = rule.mScore;
1032                 carrierId = rule.mCid;
1033             }
1034         }
1035         return carrierId;
1036     }
1037 
1038     /**
1039      * a util function to convert {mccmnc, mvno_type, mvno_data} to all matching carrier ids.
1040      *
1041      * @return a list of id with matching {mccmnc, mvno_type, mvno_data}
1042      */
getCarrierIdsFromApnQuery(@onNull Context context, String mccmnc, String mvnoCase, String mvnoData)1043     public static List<Integer> getCarrierIdsFromApnQuery(@NonNull Context context,
1044                                                           String mccmnc, String mvnoCase,
1045                                                           String mvnoData) {
1046         String selection = CarrierId.All.MCCMNC + "=" + mccmnc;
1047         // build the proper query
1048         if ("spn".equals(mvnoCase) && mvnoData != null) {
1049             selection += " AND " + CarrierId.All.SPN + "='" + mvnoData + "'";
1050         } else if ("imsi".equals(mvnoCase) && mvnoData != null) {
1051             selection += " AND " + CarrierId.All.IMSI_PREFIX_XPATTERN + "='" + mvnoData + "'";
1052         } else if ("gid1".equals(mvnoCase) && mvnoData != null) {
1053             selection += " AND " + CarrierId.All.GID1 + "='" + mvnoData + "'";
1054         } else if ("gid2".equals(mvnoCase) && mvnoData != null) {
1055             selection += " AND " + CarrierId.All.GID2 + "='" + mvnoData +"'";
1056         } else {
1057             logd("mvno case empty or other invalid values");
1058         }
1059 
1060         List<Integer> ids = new ArrayList<>();
1061         try {
1062             Cursor cursor = context.getContentResolver().query(
1063                     CarrierId.All.CONTENT_URI,
1064                     /* projection */ null,
1065                     /* selection */ selection,
1066                     /* selectionArgs */ null, null);
1067             try {
1068                 if (cursor != null) {
1069                     if (VDBG) {
1070                         logd("[getCarrierIdsFromApnQuery]- " + cursor.getCount()
1071                                 + " Records(s) in DB");
1072                     }
1073                     while (cursor.moveToNext()) {
1074                         int cid = cursor.getInt(cursor.getColumnIndex(CarrierId.CARRIER_ID));
1075                         if (!ids.contains(cid)) {
1076                             ids.add(cid);
1077                         }
1078                     }
1079                 }
1080             } finally {
1081                 if (cursor != null) {
1082                     cursor.close();
1083                 }
1084             }
1085         } catch (Exception ex) {
1086             loge("[getCarrierIdsFromApnQuery]- ex: " + ex);
1087         }
1088         logd(selection + " " + ids);
1089         return ids;
1090     }
1091 
1092     // static helper function to get carrier id from mccmnc
getCarrierIdFromMccMnc(@onNull Context context, String mccmnc)1093     public static int getCarrierIdFromMccMnc(@NonNull Context context, String mccmnc) {
1094         try (Cursor cursor = getCursorForMccMnc(context, mccmnc)) {
1095             if (cursor == null || !cursor.moveToNext()) return TelephonyManager.UNKNOWN_CARRIER_ID;
1096             if (VDBG) {
1097                 logd("[getCarrierIdFromMccMnc]- " + cursor.getCount()
1098                         + " Records(s) in DB" + " mccmnc: " + mccmnc);
1099             }
1100             return cursor.getInt(cursor.getColumnIndex(CarrierId.CARRIER_ID));
1101         } catch (Exception ex) {
1102             loge("[getCarrierIdFromMccMnc]- ex: " + ex);
1103         }
1104         return TelephonyManager.UNKNOWN_CARRIER_ID;
1105     }
1106 
1107     /**
1108      * Static helper function to get carrier name from mccmnc
1109      * @param context Context
1110      * @param mccmnc PLMN
1111      * @return Carrier name string given mccmnc/PLMN
1112      *
1113      * @hide
1114      */
1115     @Nullable
getCarrierNameFromMccMnc(@onNull Context context, String mccmnc)1116     public static String getCarrierNameFromMccMnc(@NonNull Context context, String mccmnc) {
1117         try (Cursor cursor = getCursorForMccMnc(context, mccmnc)) {
1118             if (cursor == null || !cursor.moveToNext()) return null;
1119             if (VDBG) {
1120                 logd("[getCarrierNameFromMccMnc]- " + cursor.getCount()
1121                         + " Records(s) in DB" + " mccmnc: " + mccmnc);
1122             }
1123             return cursor.getString(cursor.getColumnIndex(CarrierId.CARRIER_NAME));
1124         } catch (Exception ex) {
1125             loge("[getCarrierNameFromMccMnc]- ex: " + ex);
1126         }
1127         return null;
1128     }
1129 
1130     @Nullable
getCursorForMccMnc(@onNull Context context, String mccmnc)1131     private static Cursor getCursorForMccMnc(@NonNull Context context, String mccmnc) {
1132         try {
1133             Cursor cursor = context.getContentResolver().query(
1134                     CarrierId.All.CONTENT_URI,
1135                     /* projection */ null,
1136                     /* selection */ CarrierId.All.MCCMNC + "=? AND "
1137                             + CarrierId.All.GID1 + " is NULL AND "
1138                             + CarrierId.All.GID2 + " is NULL AND "
1139                             + CarrierId.All.IMSI_PREFIX_XPATTERN + " is NULL AND "
1140                             + CarrierId.All.SPN + " is NULL AND "
1141                             + CarrierId.All.ICCID_PREFIX + " is NULL AND "
1142                             + CarrierId.All.PLMN + " is NULL AND "
1143                             + CarrierId.All.PRIVILEGE_ACCESS_RULE + " is NULL AND "
1144                             + CarrierId.All.APN + " is NULL",
1145                     /* selectionArgs */ new String[]{mccmnc},
1146                     null);
1147             return cursor;
1148         } catch (Exception ex) {
1149             loge("[getCursorForMccMnc]- ex: " + ex);
1150             return null;
1151         }
1152     }
1153 
equals(String a, String b, boolean ignoreCase)1154     private static boolean equals(String a, String b, boolean ignoreCase) {
1155         if (a == null && b == null) return true;
1156         if (a != null && b != null) {
1157             return (ignoreCase) ? a.equalsIgnoreCase(b) : a.equals(b);
1158         }
1159         return false;
1160     }
1161 
logd(String str)1162     private static void logd(String str) {
1163         Rlog.d(LOG_TAG, str);
1164     }
loge(String str)1165     private static void loge(String str) {
1166         Rlog.e(LOG_TAG, str);
1167     }
1168 
logd(String str, int phoneId)1169     private static void logd(String str, int phoneId) {
1170         Rlog.d(LOG_TAG + "[" + phoneId + "]", str);
1171     }
1172 
dump(FileDescriptor fd, PrintWriter pw, String[] args)1173     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1174         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
1175         ipw.println("mCarrierResolverLocalLogs:");
1176         ipw.increaseIndent();
1177         mCarrierIdLocalLog.dump(fd, pw, args);
1178         ipw.decreaseIndent();
1179 
1180         ipw.println("mCarrierId: " + mCarrierId);
1181         ipw.println("mSpecificCarrierId: " + mSpecificCarrierId);
1182         ipw.println("mMnoCarrierId: " + mMnoCarrierId);
1183         ipw.println("mCarrierName: " + mCarrierName);
1184         ipw.println("mSpecificCarrierName: " + mSpecificCarrierName);
1185         ipw.println("carrier_list_version: " + getCarrierListVersion());
1186 
1187         ipw.println("mCarrierMatchingRules on mccmnc: "
1188                 + mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId()));
1189         ipw.increaseIndent();
1190         for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) {
1191             ipw.println(rule.toString());
1192         }
1193         ipw.decreaseIndent();
1194 
1195         ipw.println("mSpn: " + mSpn);
1196         ipw.println("mPreferApn: " + mPreferApn);
1197         ipw.flush();
1198     }
1199 }
1200