1 /* 2 * Copyright (C) 2011-2014 MediaTek Inc. 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 17 package com.android.internal.telephony; 18 19 import static android.Manifest.permission.READ_PHONE_STATE; 20 import android.app.ActivityManagerNative; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.ContentResolver; 24 import android.content.ContentValues; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.os.AsyncResult; 28 import android.os.Handler; 29 import android.os.Message; 30 import android.os.UserHandle; 31 import android.telephony.Rlog; 32 import android.telephony.SubscriptionManager; 33 import android.telephony.SubInfoRecord; 34 import android.telephony.TelephonyManager; 35 import com.android.internal.telephony.CommandsInterface; 36 import com.android.internal.telephony.IccCardConstants; 37 import com.android.internal.telephony.Phone; 38 import com.android.internal.telephony.PhoneConstants; 39 import com.android.internal.telephony.PhoneProxy; 40 import com.android.internal.telephony.TelephonyIntents; 41 import com.android.internal.telephony.uicc.IccConstants; 42 import com.android.internal.telephony.uicc.IccFileHandler; 43 import com.android.internal.telephony.uicc.IccUtils; 44 import com.android.internal.telephony.uicc.SpnOverride; 45 46 import java.util.List; 47 48 /** 49 *@hide 50 */ 51 public class SubInfoRecordUpdater extends Handler { 52 private static final String LOG_TAG = "SUB"; 53 private static final int PROJECT_SIM_NUM = TelephonyManager.getDefault().getPhoneCount(); 54 private static final int EVENT_OFFSET = 8; 55 private static final int EVENT_QUERY_ICCID_DONE = 1; 56 private static final String ICCID_STRING_FOR_NO_SIM = ""; 57 /** 58 * int[] sInsertSimState maintains all slots' SIM inserted status currently, 59 * it may contain 4 kinds of values: 60 * SIM_NOT_INSERT : no SIM inserted in slot i now 61 * SIM_CHANGED : a valid SIM insert in slot i and is different SIM from last time 62 * it will later become SIM_NEW or SIM_REPOSITION during update procedure 63 * SIM_NOT_CHANGE : a valid SIM insert in slot i and is the same SIM as last time 64 * SIM_NEW : a valid SIM insert in slot i and is a new SIM 65 * SIM_REPOSITION : a valid SIM insert in slot i and is inserted in different slot last time 66 * positive integer #: index to distinguish SIM cards with the same IccId 67 */ 68 public static final int SIM_NOT_CHANGE = 0; 69 public static final int SIM_CHANGED = -1; 70 public static final int SIM_NEW = -2; 71 public static final int SIM_REPOSITION = -3; 72 public static final int SIM_NOT_INSERT = -99; 73 74 public static final int STATUS_NO_SIM_INSERTED = 0x00; 75 public static final int STATUS_SIM1_INSERTED = 0x01; 76 public static final int STATUS_SIM2_INSERTED = 0x02; 77 public static final int STATUS_SIM3_INSERTED = 0x04; 78 public static final int STATUS_SIM4_INSERTED = 0x08; 79 80 private static Phone[] sPhone; 81 private static Context sContext = null; 82 private static IccFileHandler[] sFh = new IccFileHandler[PROJECT_SIM_NUM]; 83 private static String sIccId[] = new String[PROJECT_SIM_NUM]; 84 private static int[] sInsertSimState = new int[PROJECT_SIM_NUM]; 85 private static TelephonyManager sTelephonyMgr = null; 86 // To prevent repeatedly update flow every time receiver SIM_STATE_CHANGE 87 private static boolean sNeedUpdate = true; 88 89 public SubInfoRecordUpdater(Context context, Phone[] phoneProxy, CommandsInterface[] ci) { 90 logd("Constructor invoked"); 91 92 sContext = context; 93 sPhone = phoneProxy; 94 IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 95 sContext.registerReceiver(sReceiver, intentFilter); 96 } 97 98 private static int encodeEventId(int event, int slotId) { 99 return event << (slotId * EVENT_OFFSET); 100 } 101 102 private final BroadcastReceiver sReceiver = new BroadcastReceiver() { 103 @Override 104 public void onReceive(Context context, Intent intent) { 105 logd("[Receiver]+"); 106 String action = intent.getAction(); 107 int slotId; 108 logd("Action: " + action); 109 if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { 110 String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); 111 slotId = intent.getIntExtra(PhoneConstants.SLOT_KEY, 112 SubscriptionManager.INVALID_SLOT_ID); 113 logd("slotId: " + slotId + " simStatus: " + simStatus); 114 if (slotId == SubscriptionManager.INVALID_SLOT_ID) { 115 return; 116 } 117 if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(simStatus) 118 || IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(simStatus)) { 119 if (sIccId[slotId] != null && sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) { 120 logd("SIM" + (slotId + 1) + " hot plug in"); 121 sIccId[slotId] = null; 122 sNeedUpdate = true; 123 } 124 queryIccId(slotId); 125 } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) { 126 queryIccId(slotId); 127 if (sTelephonyMgr == null) { 128 sTelephonyMgr = TelephonyManager.from(sContext); 129 } 130 131 long subId = intent.getLongExtra(PhoneConstants.SUBSCRIPTION_KEY, 132 SubscriptionManager.INVALID_SUB_ID); 133 134 if (SubscriptionManager.isValidSubId(subId)) { 135 String msisdn = TelephonyManager.getDefault().getLine1NumberForSubscriber(subId); 136 ContentResolver contentResolver = sContext.getContentResolver(); 137 138 if (msisdn != null) { 139 ContentValues number = new ContentValues(1); 140 number.put(SubscriptionManager.NUMBER, msisdn); 141 contentResolver.update(SubscriptionManager.CONTENT_URI, number, 142 SubscriptionManager._ID + "=" + Long.toString(subId), null); 143 } 144 145 SubInfoRecord subInfo = 146 SubscriptionManager.getSubInfoForSubscriber(subId); 147 148 if (subInfo != null 149 && subInfo.nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) { 150 SpnOverride mSpnOverride = new SpnOverride(); 151 String nameToSet; 152 String CarrierName = 153 TelephonyManager.getDefault().getSimOperator(subId); 154 logd("CarrierName = " + CarrierName); 155 156 if (mSpnOverride.containsCarrier(CarrierName)) { 157 nameToSet = mSpnOverride.getSpn(CarrierName) + " 0" 158 + Integer.toString(slotId + 1); 159 logd("Found, name = " + nameToSet); 160 } else { 161 nameToSet = "SUB 0" + Integer.toString(slotId + 1); 162 logd("Not found, name = " + nameToSet); 163 } 164 165 ContentValues name = new ContentValues(1); 166 name.put(SubscriptionManager.DISPLAY_NAME, nameToSet); 167 contentResolver.update(SubscriptionManager.CONTENT_URI, name, 168 SubscriptionManager._ID + "=" + Long.toString(subId), null); 169 } 170 } else { 171 logd("[Receiver] Invalid subId, could not update ContentResolver"); 172 } 173 } else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) { 174 if (sIccId[slotId] != null && !sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) { 175 logd("SIM" + (slotId + 1) + " hot plug out"); 176 sNeedUpdate = true; 177 } 178 sFh[slotId] = null; 179 sIccId[slotId] = ICCID_STRING_FOR_NO_SIM; 180 if (isAllIccIdQueryDone() && sNeedUpdate) { 181 updateSimInfoByIccId(); 182 } 183 } 184 } 185 logd("[Receiver]-"); 186 } 187 }; 188 189 private boolean isAllIccIdQueryDone() { 190 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 191 if (sIccId[i] == null) { 192 logd("Wait for SIM" + (i + 1) + " IccId"); 193 return false; 194 } 195 } 196 logd("All IccIds query complete"); 197 198 return true; 199 } 200 201 public static void setDisplayNameForNewSub(String newSubName, int subId, int newNameSource) { 202 SubInfoRecord subInfo = SubscriptionManager.getSubInfoForSubscriber(subId); 203 if (subInfo != null) { 204 // overwrite SIM display name if it is not assigned by user 205 int oldNameSource = subInfo.nameSource; 206 String oldSubName = subInfo.displayName; 207 logd("[setDisplayNameForNewSub] mSubInfoIdx = " + subInfo.subId + ", oldSimName = " 208 + oldSubName + ", oldNameSource = " + oldNameSource + ", newSubName = " 209 + newSubName + ", newNameSource = " + newNameSource); 210 if (oldSubName == null || 211 (oldNameSource == SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE && newSubName != null) || 212 (oldNameSource == SubscriptionManager.NAME_SOURCE_SIM_SOURCE && newSubName != null 213 && !newSubName.equals(oldSubName))) { 214 SubscriptionManager.setDisplayName(newSubName, 215 subInfo.subId, newNameSource); 216 } 217 } else { 218 logd("SUB" + (subId + 1) + " SubInfo not created yet"); 219 } 220 } 221 222 @Override 223 public void handleMessage(Message msg) { 224 AsyncResult ar = (AsyncResult)msg.obj; 225 int msgNum = msg.what; 226 int slotId; 227 for (slotId = PhoneConstants.SUB1; slotId <= PhoneConstants.SUB3; slotId++) { 228 int pivot = 1 << (slotId * EVENT_OFFSET); 229 if (msgNum >= pivot) { 230 continue; 231 } else { 232 break; 233 } 234 } 235 slotId--; 236 int event = msgNum >> (slotId * EVENT_OFFSET); 237 switch (event) { 238 case EVENT_QUERY_ICCID_DONE: 239 logd("handleMessage : <EVENT_QUERY_ICCID_DONE> SIM" + (slotId + 1)); 240 if (ar.exception == null) { 241 if (ar.result != null) { 242 byte[] data = (byte[])ar.result; 243 sIccId[slotId] = IccUtils.bcdToString(data, 0, data.length); 244 } else { 245 logd("Null ar"); 246 sIccId[slotId] = ICCID_STRING_FOR_NO_SIM; 247 } 248 } else { 249 sIccId[slotId] = ICCID_STRING_FOR_NO_SIM; 250 logd("Query IccId fail: " + ar.exception); 251 } 252 logd("sIccId[" + slotId + "] = " + sIccId[slotId]); 253 if (isAllIccIdQueryDone() && sNeedUpdate) { 254 updateSimInfoByIccId(); 255 } 256 break; 257 default: 258 logd("Unknown msg:" + msg.what); 259 } 260 } 261 262 private void queryIccId(int slotId) { 263 logd("queryIccId: slotid=" + slotId); 264 if (sFh[slotId] == null) { 265 logd("Getting IccFileHandler"); 266 sFh[slotId] = ((PhoneProxy)sPhone[slotId]).getIccFileHandler(); 267 } 268 if (sFh[slotId] != null) { 269 String iccId = sIccId[slotId]; 270 if (iccId == null) { 271 logd("Querying IccId"); 272 sFh[slotId].loadEFTransparent(IccConstants.EF_ICCID, obtainMessage(encodeEventId(EVENT_QUERY_ICCID_DONE, slotId))); 273 } else { 274 logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId); 275 } 276 } else { 277 logd("sFh[" + slotId + "] is null, ignore"); 278 } 279 } 280 281 synchronized public void updateSimInfoByIccId() { 282 logd("[updateSimInfoByIccId]+ Start"); 283 sNeedUpdate = false; 284 285 SubscriptionManager.clearSubInfo(); 286 287 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 288 sInsertSimState[i] = SIM_NOT_CHANGE; 289 } 290 291 int insertedSimCount = PROJECT_SIM_NUM; 292 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 293 if (ICCID_STRING_FOR_NO_SIM.equals(sIccId[i])) { 294 insertedSimCount--; 295 sInsertSimState[i] = SIM_NOT_INSERT; 296 } 297 } 298 logd("insertedSimCount = " + insertedSimCount); 299 300 int index = 0; 301 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 302 if (sInsertSimState[i] == SIM_NOT_INSERT) { 303 continue; 304 } 305 index = 2; 306 for (int j = i + 1; j < PROJECT_SIM_NUM; j++) { 307 if (sInsertSimState[j] == SIM_NOT_CHANGE && sIccId[i].equals(sIccId[j])) { 308 sInsertSimState[i] = 1; 309 sInsertSimState[j] = index; 310 index++; 311 } 312 } 313 } 314 315 ContentResolver contentResolver = sContext.getContentResolver(); 316 String[] oldIccId = new String[PROJECT_SIM_NUM]; 317 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 318 oldIccId[i] = null; 319 List<SubInfoRecord> oldSubInfo = 320 SubscriptionController.getInstance().getSubInfoUsingSlotIdWithCheck(i, false); 321 if (oldSubInfo != null) { 322 oldIccId[i] = oldSubInfo.get(0).iccId; 323 logd("oldSubId = " + oldSubInfo.get(0).subId); 324 if (sInsertSimState[i] == SIM_NOT_CHANGE && !sIccId[i].equals(oldIccId[i])) { 325 sInsertSimState[i] = SIM_CHANGED; 326 } 327 if (sInsertSimState[i] != SIM_NOT_CHANGE) { 328 ContentValues value = new ContentValues(1); 329 value.put(SubscriptionManager.SIM_ID, SubscriptionManager.INVALID_SLOT_ID); 330 contentResolver.update(SubscriptionManager.CONTENT_URI, value, 331 SubscriptionManager._ID + "=" 332 + Long.toString(oldSubInfo.get(0).subId), null); 333 } 334 } else { 335 if (sInsertSimState[i] == SIM_NOT_CHANGE) { 336 // no SIM inserted last time, but there is one SIM inserted now 337 sInsertSimState[i] = SIM_CHANGED; 338 } 339 oldIccId[i] = ICCID_STRING_FOR_NO_SIM; 340 logd("No SIM in slot " + i + " last time"); 341 } 342 } 343 344 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 345 logd("oldIccId[" + i + "] = " + oldIccId[i] + ", sIccId[" + i + "] = " + sIccId[i]); 346 } 347 348 //check if the inserted SIM is new SIM 349 int nNewCardCount = 0; 350 int nNewSimStatus = 0; 351 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 352 if (sInsertSimState[i] == SIM_NOT_INSERT) { 353 logd("No SIM inserted in slot " + i + " this time"); 354 } else { 355 if (sInsertSimState[i] > 0) { 356 //some special SIMs may have the same IccIds, add suffix to distinguish them 357 //FIXME: addSubInfoRecord can return an error. 358 SubscriptionManager.addSubInfoRecord(sIccId[i] 359 + Integer.toString(sInsertSimState[i]), i); 360 logd("SUB" + (i + 1) + " has invalid IccId"); 361 } else /*if (sInsertSimState[i] != SIM_NOT_INSERT)*/ { 362 SubscriptionManager.addSubInfoRecord(sIccId[i], i); 363 } 364 if (isNewSim(sIccId[i], oldIccId)) { 365 nNewCardCount++; 366 switch (i) { 367 case PhoneConstants.SUB1: 368 nNewSimStatus |= STATUS_SIM1_INSERTED; 369 break; 370 case PhoneConstants.SUB2: 371 nNewSimStatus |= STATUS_SIM2_INSERTED; 372 break; 373 case PhoneConstants.SUB3: 374 nNewSimStatus |= STATUS_SIM3_INSERTED; 375 break; 376 //case PhoneConstants.SUB3: 377 // nNewSimStatus |= STATUS_SIM4_INSERTED; 378 // break; 379 } 380 381 sInsertSimState[i] = SIM_NEW; 382 } 383 } 384 } 385 386 for (int i = 0; i < PROJECT_SIM_NUM; i++) { 387 if (sInsertSimState[i] == SIM_CHANGED) { 388 sInsertSimState[i] = SIM_REPOSITION; 389 } 390 logd("sInsertSimState[" + i + "] = " + sInsertSimState[i]); 391 } 392 393 List<SubInfoRecord> subInfos = SubscriptionManager.getActiveSubInfoList(); 394 int nSubCount = (subInfos == null) ? 0 : subInfos.size(); 395 logd("nSubCount = " + nSubCount); 396 for (int i=0; i<nSubCount; i++) { 397 SubInfoRecord temp = subInfos.get(i); 398 399 String msisdn = TelephonyManager.getDefault().getLine1NumberForSubscriber(temp.subId); 400 401 if (msisdn != null) { 402 ContentValues value = new ContentValues(1); 403 value.put(SubscriptionManager.NUMBER, msisdn); 404 contentResolver.update(SubscriptionManager.CONTENT_URI, value, 405 SubscriptionManager._ID + "=" + Long.toString(temp.subId), null); 406 } 407 } 408 409 // true if any slot has no SIM this time, but has SIM last time 410 boolean hasSimRemoved = false; 411 for (int i=0; i < PROJECT_SIM_NUM; i++) { 412 if (sIccId[i] != null && sIccId[i].equals(ICCID_STRING_FOR_NO_SIM) 413 && !oldIccId[i].equals("")) { 414 hasSimRemoved = true; 415 break; 416 } 417 } 418 419 if (nNewCardCount == 0) { 420 int i; 421 if (hasSimRemoved) { 422 // no new SIM, at least one SIM is removed, check if any SIM is repositioned first 423 for (i=0; i < PROJECT_SIM_NUM; i++) { 424 if (sInsertSimState[i] == SIM_REPOSITION) { 425 logd("No new SIM detected and SIM repositioned"); 426 setUpdatedData(SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM, 427 nSubCount, nNewSimStatus); 428 break; 429 } 430 } 431 if (i == PROJECT_SIM_NUM) { 432 // no new SIM, no SIM is repositioned => at least one SIM is removed 433 logd("No new SIM detected and SIM removed"); 434 setUpdatedData(SubscriptionManager.EXTRA_VALUE_REMOVE_SIM, 435 nSubCount, nNewSimStatus); 436 } 437 } else { 438 // no SIM is removed, no new SIM, just check if any SIM is repositioned 439 for (i=0; i< PROJECT_SIM_NUM; i++) { 440 if (sInsertSimState[i] == SIM_REPOSITION) { 441 logd("No new SIM detected and SIM repositioned"); 442 setUpdatedData(SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM, 443 nSubCount, nNewSimStatus); 444 break; 445 } 446 } 447 if (i == PROJECT_SIM_NUM) { 448 // all status remain unchanged 449 logd("[updateSimInfoByIccId] All SIM inserted into the same slot"); 450 setUpdatedData(SubscriptionManager.EXTRA_VALUE_NOCHANGE, 451 nSubCount, nNewSimStatus); 452 } 453 } 454 } else { 455 logd("New SIM detected"); 456 setUpdatedData(SubscriptionManager.EXTRA_VALUE_NEW_SIM, nSubCount, nNewSimStatus); 457 } 458 459 logd("[updateSimInfoByIccId]- SimInfo update complete"); 460 } 461 462 private static void setUpdatedData(int detectedType, int subCount, int newSimStatus) { 463 464 Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED); 465 466 logd("[setUpdatedData]+ "); 467 468 if (detectedType == SubscriptionManager.EXTRA_VALUE_NEW_SIM ) { 469 intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS, 470 SubscriptionManager.EXTRA_VALUE_NEW_SIM); 471 intent.putExtra(SubscriptionManager.INTENT_KEY_SIM_COUNT, subCount); 472 intent.putExtra(SubscriptionManager.INTENT_KEY_NEW_SIM_SLOT, newSimStatus); 473 } else if (detectedType == SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM) { 474 intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS, 475 SubscriptionManager.EXTRA_VALUE_REPOSITION_SIM); 476 intent.putExtra(SubscriptionManager.INTENT_KEY_SIM_COUNT, subCount); 477 } else if (detectedType == SubscriptionManager.EXTRA_VALUE_REMOVE_SIM) { 478 intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS, 479 SubscriptionManager.EXTRA_VALUE_REMOVE_SIM); 480 intent.putExtra(SubscriptionManager.INTENT_KEY_SIM_COUNT, subCount); 481 } else if (detectedType == SubscriptionManager.EXTRA_VALUE_NOCHANGE) { 482 intent.putExtra(SubscriptionManager.INTENT_KEY_DETECT_STATUS, 483 SubscriptionManager.EXTRA_VALUE_NOCHANGE); 484 } 485 486 logd("broadcast intent ACTION_SUBINFO_RECORD_UPDATED : [" + detectedType + ", " 487 + subCount + ", " + newSimStatus+ "]"); 488 ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE, UserHandle.USER_ALL); 489 logd("[setUpdatedData]- "); 490 } 491 492 private static boolean isNewSim(String iccId, String[] oldIccId) { 493 boolean newSim = true; 494 for(int i = 0; i < PROJECT_SIM_NUM; i++) { 495 if(iccId.equals(oldIccId[i])) { 496 newSim = false; 497 break; 498 } 499 } 500 logd("newSim = " + newSim); 501 502 return newSim; 503 } 504 505 public void dispose() { 506 logd("[dispose]"); 507 sContext.unregisterReceiver(sReceiver); 508 } 509 510 private static void logd(String message) { 511 Rlog.d(LOG_TAG, "[SubInfoRecordUpdater]" + message); 512 } 513 } 514 515