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