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