1/* 2* Copyright (C) 2014 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 17package com.android.internal.telephony; 18 19import static android.Manifest.permission.READ_PHONE_STATE; 20 21import android.app.ActivityManagerNative; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.ContentResolver; 25import android.content.ContentValues; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.content.SharedPreferences; 29import android.os.AsyncResult; 30import android.os.Handler; 31import android.os.Message; 32import android.os.SystemProperties; 33import android.os.UserHandle; 34import android.preference.PreferenceManager; 35import android.provider.Settings; 36import android.telephony.Rlog; 37import android.telephony.SubscriptionManager; 38import android.telephony.SubscriptionInfo; 39import android.telephony.TelephonyManager; 40import com.android.internal.telephony.uicc.IccCardProxy; 41import com.android.internal.telephony.uicc.IccConstants; 42import com.android.internal.telephony.uicc.IccFileHandler; 43import com.android.internal.telephony.uicc.IccRecords; 44import com.android.internal.telephony.uicc.IccUtils; 45import android.text.TextUtils; 46 47import java.util.List; 48 49/** 50 *@hide 51 */ 52public class SubscriptionInfoUpdater extends Handler { 53 private static final String LOG_TAG = "SubscriptionInfoUpdater"; 54 private static final int PROJECT_SIM_NUM = TelephonyManager.getDefault().getPhoneCount(); 55 56 private static final int EVENT_SIM_LOCKED_QUERY_ICCID_DONE = 1; 57 private static final int EVENT_GET_NETWORK_SELECTION_MODE_DONE = 2; 58 private static final int EVENT_SIM_LOADED = 3; 59 private static final int EVENT_SIM_ABSENT = 4; 60 private static final int EVENT_SIM_LOCKED = 5; 61 62 private static final String ICCID_STRING_FOR_NO_SIM = ""; 63 /** 64 * int[] sInsertSimState maintains all slots' SIM inserted status currently, 65 * it may contain 4 kinds of values: 66 * SIM_NOT_INSERT : no SIM inserted in slot i now 67 * SIM_CHANGED : a valid SIM insert in slot i and is different SIM from last time 68 * it will later become SIM_NEW or SIM_REPOSITION during update procedure 69 * SIM_NOT_CHANGE : a valid SIM insert in slot i and is the same SIM as last time 70 * SIM_NEW : a valid SIM insert in slot i and is a new SIM 71 * SIM_REPOSITION : a valid SIM insert in slot i and is inserted in different slot last time 72 * positive integer #: index to distinguish SIM cards with the same IccId 73 */ 74 public static final int SIM_NOT_CHANGE = 0; 75 public static final int SIM_CHANGED = -1; 76 public static final int SIM_NEW = -2; 77 public static final int SIM_REPOSITION = -3; 78 public static final int SIM_NOT_INSERT = -99; 79 80 public static final int STATUS_NO_SIM_INSERTED = 0x00; 81 public static final int STATUS_SIM1_INSERTED = 0x01; 82 public static final int STATUS_SIM2_INSERTED = 0x02; 83 public static final int STATUS_SIM3_INSERTED = 0x04; 84 public static final int STATUS_SIM4_INSERTED = 0x08; 85 86 // Key used to read/write the current IMSI. Updated on SIM_STATE_CHANGED - LOADED. 87 public static final String CURR_SUBID = "curr_subid"; 88 89 private static Phone[] mPhone; 90 private static Context mContext = null; 91 private static String mIccId[] = new String[PROJECT_SIM_NUM]; 92 private static int[] mInsertSimState = new int[PROJECT_SIM_NUM]; 93 private SubscriptionManager mSubscriptionManager = null; 94 95 public SubscriptionInfoUpdater(Context context, Phone[] phoneProxy, CommandsInterface[] ci) { 96 logd("Constructor invoked"); 97 98 mContext = context; 99 mPhone = phoneProxy; 100 mSubscriptionManager = SubscriptionManager.from(mContext); 101 102 IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 103 mContext.registerReceiver(sReceiver, intentFilter); 104 intentFilter = new IntentFilter(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED); 105 mContext.registerReceiver(sReceiver, intentFilter); 106 } 107 108 private final BroadcastReceiver sReceiver = new BroadcastReceiver() { 109 @Override 110 public void onReceive(Context context, Intent intent) { 111 logd("[Receiver]+"); 112 String action = intent.getAction(); 113 logd("Action: " + action); 114 115 if (!action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED) && 116 !action.equals(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED)) { 117 return; 118 } 119 120 int slotId = intent.getIntExtra(PhoneConstants.PHONE_KEY, 121 SubscriptionManager.INVALID_SIM_SLOT_INDEX); 122 logd("slotId: " + slotId); 123 if (slotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 124 return; 125 } 126 127 String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); 128 logd("simStatus: " + simStatus); 129 130 if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { 131 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) { 132 sendMessage(obtainMessage(EVENT_SIM_ABSENT, slotId, -1)); 133 } else { 134 logd("Ignoring simStatus: " + simStatus); 135 } 136 } else if (action.equals(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED)) { 137 if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(simStatus)) { 138 String reason = intent.getStringExtra( 139 IccCardConstants.INTENT_KEY_LOCKED_REASON); 140 sendMessage(obtainMessage(EVENT_SIM_LOCKED, slotId, -1, reason)); 141 } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) { 142 sendMessage(obtainMessage(EVENT_SIM_LOADED, slotId, -1)); 143 } else { 144 logd("Ignoring simStatus: " + simStatus); 145 } 146 } 147 logd("[Receiver]-"); 148 } 149 }; 150 151 private boolean isAllIccIdQueryDone() { 152 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 153 if (mIccId[i] == null) { 154 logd("Wait for SIM" + (i + 1) + " IccId"); 155 return false; 156 } 157 } 158 logd("All IccIds query complete"); 159 160 return true; 161 } 162 163 public void setDisplayNameForNewSub(String newSubName, int subId, int newNameSource) { 164 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId); 165 if (subInfo != null) { 166 // overwrite SIM display name if it is not assigned by user 167 int oldNameSource = subInfo.getNameSource(); 168 CharSequence oldSubName = subInfo.getDisplayName(); 169 logd("[setDisplayNameForNewSub] subId = " + subInfo.getSubscriptionId() 170 + ", oldSimName = " + oldSubName + ", oldNameSource = " + oldNameSource 171 + ", newSubName = " + newSubName + ", newNameSource = " + newNameSource); 172 if (oldSubName == null || 173 (oldNameSource == 174 SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE && newSubName != null) || 175 (oldNameSource == SubscriptionManager.NAME_SOURCE_SIM_SOURCE && newSubName != null 176 && !newSubName.equals(oldSubName))) { 177 mSubscriptionManager.setDisplayName(newSubName, subInfo.getSubscriptionId(), 178 newNameSource); 179 } 180 } else { 181 logd("SUB" + (subId + 1) + " SubInfo not created yet"); 182 } 183 } 184 185 @Override 186 public void handleMessage(Message msg) { 187 switch (msg.what) { 188 case EVENT_SIM_LOCKED_QUERY_ICCID_DONE: { 189 AsyncResult ar = (AsyncResult)msg.obj; 190 QueryIccIdUserObj uObj = (QueryIccIdUserObj) ar.userObj; 191 int slotId = uObj.slotId; 192 logd("handleMessage : <EVENT_SIM_LOCKED_QUERY_ICCID_DONE> SIM" + (slotId + 1)); 193 if (ar.exception == null) { 194 if (ar.result != null) { 195 byte[] data = (byte[])ar.result; 196 mIccId[slotId] = IccUtils.bcdToString(data, 0, data.length); 197 } else { 198 logd("Null ar"); 199 mIccId[slotId] = ICCID_STRING_FOR_NO_SIM; 200 } 201 } else { 202 mIccId[slotId] = ICCID_STRING_FOR_NO_SIM; 203 logd("Query IccId fail: " + ar.exception); 204 } 205 logd("sIccId[" + slotId + "] = " + mIccId[slotId]); 206 if (isAllIccIdQueryDone()) { 207 updateSubscriptionInfoByIccId(); 208 } 209 broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED, 210 uObj.reason); 211 break; 212 } 213 214 case EVENT_GET_NETWORK_SELECTION_MODE_DONE: { 215 AsyncResult ar = (AsyncResult)msg.obj; 216 Integer slotId = (Integer)ar.userObj; 217 if (ar.exception == null && ar.result != null) { 218 int[] modes = (int[])ar.result; 219 if (modes[0] == 1) { // Manual mode. 220 mPhone[slotId].setNetworkSelectionModeAutomatic(null); 221 } 222 } else { 223 logd("EVENT_GET_NETWORK_SELECTION_MODE_DONE: error getting network mode."); 224 } 225 break; 226 } 227 228 case EVENT_SIM_LOADED: 229 handleSimLoaded(msg.arg1); 230 break; 231 232 case EVENT_SIM_ABSENT: 233 handleSimAbsent(msg.arg1); 234 break; 235 236 case EVENT_SIM_LOCKED: 237 handleSimLocked(msg.arg1, (String) msg.obj); 238 break; 239 240 default: 241 logd("Unknown msg:" + msg.what); 242 } 243 } 244 245 private static class QueryIccIdUserObj { 246 public String reason; 247 public int slotId; 248 249 QueryIccIdUserObj(String reason, int slotId) { 250 this.reason = reason; 251 this.slotId = slotId; 252 } 253 }; 254 255 private void handleSimLocked(int slotId, String reason) { 256 if (mIccId[slotId] != null && mIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) { 257 logd("SIM" + (slotId + 1) + " hot plug in"); 258 mIccId[slotId] = null; 259 } 260 261 262 IccFileHandler fileHandler = mPhone[slotId].getIccCard() == null ? null : 263 mPhone[slotId].getIccCard().getIccFileHandler(); 264 265 if (fileHandler != null) { 266 String iccId = mIccId[slotId]; 267 if (iccId == null) { 268 logd("Querying IccId"); 269 fileHandler.loadEFTransparent(IccConstants.EF_ICCID, 270 obtainMessage(EVENT_SIM_LOCKED_QUERY_ICCID_DONE, 271 new QueryIccIdUserObj(reason, slotId))); 272 } else { 273 logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId); 274 } 275 } else { 276 logd("sFh[" + slotId + "] is null, ignore"); 277 } 278 } 279 280 private void handleSimLoaded(int slotId) { 281 logd("handleSimStateLoadedInternal: slotId: " + slotId); 282 283 // The SIM should be loaded at this state, but it is possible in cases such as SIM being 284 // removed or a refresh RESET that the IccRecords could be null. The right behavior is to 285 // not broadcast the SIM loaded. 286 IccRecords records = mPhone[slotId].getIccCard().getIccRecords(); 287 if (records == null) { // Possibly a race condition. 288 logd("onRecieve: IccRecords null"); 289 return; 290 } 291 if (records.getIccId() == null) { 292 logd("onRecieve: IccID null"); 293 return; 294 } 295 mIccId[slotId] = records.getIccId(); 296 297 if (isAllIccIdQueryDone()) { 298 updateSubscriptionInfoByIccId(); 299 } 300 301 int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; 302 int[] subIds = SubscriptionController.getInstance().getSubId(slotId); 303 if (subIds != null) { // Why an array? 304 subId = subIds[0]; 305 } 306 307 if (SubscriptionManager.isValidSubscriptionId(subId)) { 308 String operator = records.getOperatorNumeric(); 309 if (operator != null) { 310 if (subId == SubscriptionController.getInstance().getDefaultSubId()) { 311 MccTable.updateMccMncConfiguration(mContext, operator, false); 312 } 313 SubscriptionController.getInstance().setMccMnc(operator,subId); 314 } else { 315 logd("EVENT_RECORDS_LOADED Operator name is null"); 316 } 317 TelephonyManager tm = TelephonyManager.getDefault(); 318 String msisdn = tm.getLine1NumberForSubscriber(subId); 319 ContentResolver contentResolver = mContext.getContentResolver(); 320 321 if (msisdn != null) { 322 ContentValues number = new ContentValues(1); 323 number.put(SubscriptionManager.NUMBER, msisdn); 324 contentResolver.update(SubscriptionManager.CONTENT_URI, number, 325 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" 326 + Long.toString(subId), null); 327 } 328 329 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId); 330 String nameToSet; 331 String CarrierName = tm.getSimOperatorNumericForSubscription(subId); 332 logd("CarrierName = " + CarrierName); 333 String simCarrierName = tm.getSimOperatorNameForSubscription(subId); 334 ContentValues name = new ContentValues(1); 335 336 if (subInfo != null && subInfo.getNameSource() != 337 SubscriptionManager.NAME_SOURCE_USER_INPUT) { 338 if (!TextUtils.isEmpty(simCarrierName)) { 339 nameToSet = simCarrierName; 340 } else { 341 nameToSet = "CARD " + Integer.toString(slotId + 1); 342 } 343 name.put(SubscriptionManager.DISPLAY_NAME, nameToSet); 344 logd("sim name = " + nameToSet); 345 } 346 name.put(SubscriptionManager.CARRIER_NAME, 347 !TextUtils.isEmpty(simCarrierName) ? simCarrierName : 348 mContext.getString(com.android.internal.R.string.unknownName)); 349 contentResolver.update(SubscriptionManager.CONTENT_URI, name, 350 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID 351 + "=" + Long.toString(subId), null); 352 logd("carrier name = " + simCarrierName); 353 354 /* Update preferred network type and network selection mode on SIM change. 355 * Storing last subId in SharedPreference for now to detect SIM change. */ 356 SharedPreferences sp = 357 PreferenceManager.getDefaultSharedPreferences(mContext); 358 int storedSubId = sp.getInt(CURR_SUBID + slotId, -1); 359 360 if (storedSubId != subId) { 361 int networkType = RILConstants.PREFERRED_NETWORK_MODE; 362 363 // Set the modem network mode 364 mPhone[slotId].setPreferredNetworkType(networkType, null); 365 Settings.Global.putInt(mPhone[slotId].getContext().getContentResolver(), 366 Settings.Global.PREFERRED_NETWORK_MODE + subId, 367 networkType); 368 369 // Only support automatic selection mode on SIM change. 370 mPhone[slotId].getNetworkSelectionMode( 371 obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE, new Integer(slotId))); 372 373 // Update stored subId 374 SharedPreferences.Editor editor = sp.edit(); 375 editor.putInt(CURR_SUBID + slotId, subId); 376 editor.apply(); 377 } 378 } else { 379 logd("Invalid subId, could not update ContentResolver"); 380 } 381 382 broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null); 383 } 384 385 private void handleSimAbsent(int slotId) { 386 if (mIccId[slotId] != null && !mIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) { 387 logd("SIM" + (slotId + 1) + " hot plug out"); 388 } 389 mIccId[slotId] = ICCID_STRING_FOR_NO_SIM; 390 if (isAllIccIdQueryDone()) { 391 updateSubscriptionInfoByIccId(); 392 } 393 } 394 395 /** 396 * TODO: Simplify more, as no one is interested in what happened 397 * only what the current list contains. 398 */ 399 synchronized private void updateSubscriptionInfoByIccId() { 400 logd("updateSubscriptionInfoByIccId:+ Start"); 401 402 mSubscriptionManager.clearSubscriptionInfo(); 403 404 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 405 mInsertSimState[i] = SIM_NOT_CHANGE; 406 } 407 408 int insertedSimCount = PROJECT_SIM_NUM; 409 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 410 if (ICCID_STRING_FOR_NO_SIM.equals(mIccId[i])) { 411 insertedSimCount--; 412 mInsertSimState[i] = SIM_NOT_INSERT; 413 } 414 } 415 logd("insertedSimCount = " + insertedSimCount); 416 417 int index = 0; 418 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 419 if (mInsertSimState[i] == SIM_NOT_INSERT) { 420 continue; 421 } 422 index = 2; 423 for (int j = i + 1; j < PROJECT_SIM_NUM; j++) { 424 if (mInsertSimState[j] == SIM_NOT_CHANGE && mIccId[i].equals(mIccId[j])) { 425 mInsertSimState[i] = 1; 426 mInsertSimState[j] = index; 427 index++; 428 } 429 } 430 } 431 432 ContentResolver contentResolver = mContext.getContentResolver(); 433 String[] oldIccId = new String[PROJECT_SIM_NUM]; 434 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 435 oldIccId[i] = null; 436 List<SubscriptionInfo> oldSubInfo = 437 SubscriptionController.getInstance().getSubInfoUsingSlotIdWithCheck(i, false); 438 if (oldSubInfo != null) { 439 oldIccId[i] = oldSubInfo.get(0).getIccId(); 440 logd("updateSubscriptionInfoByIccId: oldSubId = " 441 + oldSubInfo.get(0).getSubscriptionId()); 442 if (mInsertSimState[i] == SIM_NOT_CHANGE && !mIccId[i].equals(oldIccId[i])) { 443 mInsertSimState[i] = SIM_CHANGED; 444 } 445 if (mInsertSimState[i] != SIM_NOT_CHANGE) { 446 ContentValues value = new ContentValues(1); 447 value.put(SubscriptionManager.SIM_SLOT_INDEX, 448 SubscriptionManager.INVALID_SIM_SLOT_INDEX); 449 contentResolver.update(SubscriptionManager.CONTENT_URI, value, 450 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" 451 + Integer.toString(oldSubInfo.get(0).getSubscriptionId()), null); 452 } 453 } else { 454 if (mInsertSimState[i] == SIM_NOT_CHANGE) { 455 // no SIM inserted last time, but there is one SIM inserted now 456 mInsertSimState[i] = SIM_CHANGED; 457 } 458 oldIccId[i] = ICCID_STRING_FOR_NO_SIM; 459 logd("updateSubscriptionInfoByIccId: No SIM in slot " + i + " last time"); 460 } 461 } 462 463 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 464 logd("updateSubscriptionInfoByIccId: oldIccId[" + i + "] = " + oldIccId[i] + 465 ", sIccId[" + i + "] = " + mIccId[i]); 466 } 467 468 //check if the inserted SIM is new SIM 469 int nNewCardCount = 0; 470 int nNewSimStatus = 0; 471 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 472 if (mInsertSimState[i] == SIM_NOT_INSERT) { 473 logd("updateSubscriptionInfoByIccId: No SIM inserted in slot " + i + " this time"); 474 } else { 475 if (mInsertSimState[i] > 0) { 476 //some special SIMs may have the same IccIds, add suffix to distinguish them 477 //FIXME: addSubInfoRecord can return an error. 478 mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i] 479 + Integer.toString(mInsertSimState[i]), i); 480 logd("SUB" + (i + 1) + " has invalid IccId"); 481 } else /*if (sInsertSimState[i] != SIM_NOT_INSERT)*/ { 482 mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i], i); 483 } 484 if (isNewSim(mIccId[i], oldIccId)) { 485 nNewCardCount++; 486 switch (i) { 487 case PhoneConstants.SUB1: 488 nNewSimStatus |= STATUS_SIM1_INSERTED; 489 break; 490 case PhoneConstants.SUB2: 491 nNewSimStatus |= STATUS_SIM2_INSERTED; 492 break; 493 case PhoneConstants.SUB3: 494 nNewSimStatus |= STATUS_SIM3_INSERTED; 495 break; 496 //case PhoneConstants.SUB3: 497 // nNewSimStatus |= STATUS_SIM4_INSERTED; 498 // break; 499 } 500 501 mInsertSimState[i] = SIM_NEW; 502 } 503 } 504 } 505 506 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 507 if (mInsertSimState[i] == SIM_CHANGED) { 508 mInsertSimState[i] = SIM_REPOSITION; 509 } 510 logd("updateSubscriptionInfoByIccId: sInsertSimState[" + i + "] = " 511 + mInsertSimState[i]); 512 } 513 514 List<SubscriptionInfo> subInfos = mSubscriptionManager.getActiveSubscriptionInfoList(); 515 int nSubCount = (subInfos == null) ? 0 : subInfos.size(); 516 logd("updateSubscriptionInfoByIccId: nSubCount = " + nSubCount); 517 for (int i=0; i < nSubCount; i++) { 518 SubscriptionInfo temp = subInfos.get(i); 519 520 String msisdn = TelephonyManager.getDefault().getLine1NumberForSubscriber( 521 temp.getSubscriptionId()); 522 523 if (msisdn != null) { 524 ContentValues value = new ContentValues(1); 525 value.put(SubscriptionManager.NUMBER, msisdn); 526 contentResolver.update(SubscriptionManager.CONTENT_URI, value, 527 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" 528 + Integer.toString(temp.getSubscriptionId()), null); 529 } 530 } 531 532 SubscriptionController.getInstance().notifySubscriptionInfoChanged(); 533 logd("updateSubscriptionInfoByIccId:- SsubscriptionInfo update complete"); 534 } 535 536 private boolean isNewSim(String iccId, String[] oldIccId) { 537 boolean newSim = true; 538 for(int i = 0; i < PROJECT_SIM_NUM; i++) { 539 if(iccId.equals(oldIccId[i])) { 540 newSim = false; 541 break; 542 } 543 } 544 logd("newSim = " + newSim); 545 546 return newSim; 547 } 548 549 private void broadcastSimStateChanged(int slotId, String state, String reason) { 550 Intent i = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 551 // TODO - we'd like this intent to have a single snapshot of all sim state, 552 // but until then this should not use REPLACE_PENDING or we may lose 553 // information 554 // i.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING 555 // | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 556 i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 557 i.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone"); 558 i.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, state); 559 i.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason); 560 SubscriptionManager.putPhoneIdAndSubIdExtra(i, slotId); 561 logd("Broadcasting intent ACTION_SIM_STATE_CHANGED " + 562 IccCardConstants.INTENT_VALUE_ICC_LOADED + " reason " + null + 563 " for mCardIndex : " + slotId); 564 ActivityManagerNative.broadcastStickyIntent(i, READ_PHONE_STATE, 565 UserHandle.USER_ALL); 566 } 567 568 public void dispose() { 569 logd("[dispose]"); 570 mContext.unregisterReceiver(sReceiver); 571 } 572 573 private void logd(String message) { 574 Rlog.d(LOG_TAG, message); 575 } 576} 577