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