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