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