1 /* 2 * Copyright (C) 2008 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 17 package com.android.internal.telephony.uicc; 18 19 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_TEST_CSIM; 20 21 import java.io.FileDescriptor; 22 import java.io.PrintWriter; 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.Locale; 26 import android.content.Context; 27 import android.os.AsyncResult; 28 import android.os.Message; 29 import android.os.SystemProperties; 30 import android.telephony.SubscriptionInfo; 31 import android.telephony.SubscriptionManager; 32 import android.telephony.Rlog; 33 import android.text.TextUtils; 34 import android.util.Log; 35 import android.content.res.Resources; 36 37 import com.android.internal.telephony.CommandsInterface; 38 import com.android.internal.telephony.GsmAlphabet; 39 import com.android.internal.telephony.MccTable; 40 import com.android.internal.telephony.SubscriptionController; 41 42 import com.android.internal.telephony.cdma.sms.UserData; 43 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; 44 import com.android.internal.util.BitwiseInputStream; 45 46 /** 47 * {@hide} 48 */ 49 public class RuimRecords extends IccRecords { 50 static final String LOG_TAG = "RuimRecords"; 51 52 private boolean mOtaCommited=false; 53 54 // ***** Instance Variables 55 56 private String mMyMobileNumber; 57 private String mMin2Min1; 58 59 private String mPrlVersion; 60 // From CSIM application 61 private byte[] mEFpl = null; 62 private byte[] mEFli = null; 63 boolean mCsimSpnDisplayCondition = false; 64 private String mMdn; 65 private String mMin; 66 private String mHomeSystemId; 67 private String mHomeNetworkId; 68 private String mNai; 69 70 @Override toString()71 public String toString() { 72 return "RuimRecords: " + super.toString() 73 + " m_ota_commited" + mOtaCommited 74 + " mMyMobileNumber=" + "xxxx" 75 + " mMin2Min1=" + mMin2Min1 76 + " mPrlVersion=" + mPrlVersion 77 + " mEFpl=" + mEFpl 78 + " mEFli=" + mEFli 79 + " mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition 80 + " mMdn=" + mMdn 81 + " mMin=" + mMin 82 + " mHomeSystemId=" + mHomeSystemId 83 + " mHomeNetworkId=" + mHomeNetworkId; 84 } 85 86 // ***** Event Constants 87 private static final int EVENT_GET_IMSI_DONE = 3; 88 private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4; 89 private static final int EVENT_GET_ICCID_DONE = 5; 90 private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10; 91 private static final int EVENT_UPDATE_DONE = 14; 92 private static final int EVENT_GET_SST_DONE = 17; 93 private static final int EVENT_GET_ALL_SMS_DONE = 18; 94 private static final int EVENT_MARK_SMS_READ_DONE = 19; 95 96 private static final int EVENT_SMS_ON_RUIM = 21; 97 private static final int EVENT_GET_SMS_DONE = 22; 98 99 private static final int EVENT_RUIM_REFRESH = 31; 100 RuimRecords(UiccCardApplication app, Context c, CommandsInterface ci)101 public RuimRecords(UiccCardApplication app, Context c, CommandsInterface ci) { 102 super(app, c, ci); 103 104 mAdnCache = new AdnRecordCache(mFh); 105 106 mRecordsRequested = false; // No load request is made till SIM ready 107 108 // recordsToLoad is set to 0 because no requests are made yet 109 mRecordsToLoad = 0; 110 111 // NOTE the EVENT_SMS_ON_RUIM is not registered 112 mCi.registerForIccRefresh(this, EVENT_RUIM_REFRESH, null); 113 114 // Start off by setting empty state 115 resetRecords(); 116 117 mParentApp.registerForReady(this, EVENT_APP_READY, null); 118 if (DBG) log("RuimRecords X ctor this=" + this); 119 } 120 121 @Override dispose()122 public void dispose() { 123 if (DBG) log("Disposing RuimRecords " + this); 124 //Unregister for all events 125 mCi.unregisterForIccRefresh(this); 126 mParentApp.unregisterForReady(this); 127 resetRecords(); 128 super.dispose(); 129 } 130 131 @Override finalize()132 protected void finalize() { 133 if(DBG) log("RuimRecords finalized"); 134 } 135 resetRecords()136 protected void resetRecords() { 137 mMncLength = UNINITIALIZED; 138 log("setting0 mMncLength" + mMncLength); 139 mIccId = null; 140 mFullIccId = null; 141 142 mAdnCache.reset(); 143 144 // Don't clean up PROPERTY_ICC_OPERATOR_ISO_COUNTRY and 145 // PROPERTY_ICC_OPERATOR_NUMERIC here. Since not all CDMA 146 // devices have RUIM, these properties should keep the original 147 // values, e.g. build time settings, when there is no RUIM but 148 // set new values when RUIM is available and loaded. 149 150 // recordsRequested is set to false indicating that the SIM 151 // read requests made so far are not valid. This is set to 152 // true only when fresh set of read requests are made. 153 mRecordsRequested = false; 154 } 155 156 @Override getIMSI()157 public String getIMSI() { 158 return mImsi; 159 } 160 getMdnNumber()161 public String getMdnNumber() { 162 return mMyMobileNumber; 163 } 164 getCdmaMin()165 public String getCdmaMin() { 166 return mMin2Min1; 167 } 168 169 /** Returns null if RUIM is not yet ready */ getPrlVersion()170 public String getPrlVersion() { 171 return mPrlVersion; 172 } 173 174 @Override 175 /** Returns null if RUIM is not yet ready */ getNAI()176 public String getNAI() { 177 return mNai; 178 } 179 180 @Override setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete)181 public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete){ 182 // In CDMA this is Operator/OEM dependent 183 AsyncResult.forMessage((onComplete)).exception = 184 new IccException("setVoiceMailNumber not implemented"); 185 onComplete.sendToTarget(); 186 loge("method setVoiceMailNumber is not implemented"); 187 } 188 189 /** 190 * Called by CCAT Service when REFRESH is received. 191 * @param fileChanged indicates whether any files changed 192 * @param fileList if non-null, a list of EF files that changed 193 */ 194 @Override onRefresh(boolean fileChanged, int[] fileList)195 public void onRefresh(boolean fileChanged, int[] fileList) { 196 if (fileChanged) { 197 // A future optimization would be to inspect fileList and 198 // only reload those files that we care about. For now, 199 // just re-fetch all RUIM records that we cache. 200 fetchRuimRecords(); 201 } 202 } 203 adjstMinDigits(int digits)204 private int adjstMinDigits (int digits) { 205 // Per C.S0005 section 2.3.1. 206 digits += 111; 207 digits = (digits % 10 == 0)?(digits - 10):digits; 208 digits = ((digits / 10) % 10 == 0)?(digits - 100):digits; 209 digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits; 210 return digits; 211 } 212 213 /** 214 * Returns the 5 or 6 digit MCC/MNC of the operator that 215 * provided the RUIM card. Returns null of RUIM is not yet ready 216 */ getRUIMOperatorNumeric()217 public String getRUIMOperatorNumeric() { 218 if (mImsi == null) { 219 return null; 220 } 221 222 if (mMncLength != UNINITIALIZED && mMncLength != UNKNOWN) { 223 // Length = length of MCC + length of MNC 224 // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3) 225 return mImsi.substring(0, 3 + mMncLength); 226 } 227 228 // Guess the MNC length based on the MCC if we don't 229 // have a valid value in ef[ad] 230 231 int mcc = Integer.parseInt(mImsi.substring(0,3)); 232 return mImsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc)); 233 } 234 235 // Refer to ETSI TS 102.221 236 private class EfPlLoaded implements IccRecordLoaded { 237 @Override getEfName()238 public String getEfName() { 239 return "EF_PL"; 240 } 241 242 @Override onRecordLoaded(AsyncResult ar)243 public void onRecordLoaded(AsyncResult ar) { 244 mEFpl = (byte[]) ar.result; 245 if (DBG) log("EF_PL=" + IccUtils.bytesToHexString(mEFpl)); 246 } 247 } 248 249 // Refer to C.S0065 5.2.26 250 private class EfCsimLiLoaded implements IccRecordLoaded { 251 @Override getEfName()252 public String getEfName() { 253 return "EF_CSIM_LI"; 254 } 255 256 @Override onRecordLoaded(AsyncResult ar)257 public void onRecordLoaded(AsyncResult ar) { 258 mEFli = (byte[]) ar.result; 259 // convert csim efli data to iso 639 format 260 for (int i = 0; i < mEFli.length; i+=2) { 261 switch(mEFli[i+1]) { 262 case 0x01: mEFli[i] = 'e'; mEFli[i+1] = 'n';break; 263 case 0x02: mEFli[i] = 'f'; mEFli[i+1] = 'r';break; 264 case 0x03: mEFli[i] = 'e'; mEFli[i+1] = 's';break; 265 case 0x04: mEFli[i] = 'j'; mEFli[i+1] = 'a';break; 266 case 0x05: mEFli[i] = 'k'; mEFli[i+1] = 'o';break; 267 case 0x06: mEFli[i] = 'z'; mEFli[i+1] = 'h';break; 268 case 0x07: mEFli[i] = 'h'; mEFli[i+1] = 'e';break; 269 default: mEFli[i] = ' '; mEFli[i+1] = ' '; 270 } 271 } 272 273 if (DBG) log("EF_LI=" + IccUtils.bytesToHexString(mEFli)); 274 } 275 } 276 277 // Refer to C.S0065 5.2.32 278 private class EfCsimSpnLoaded implements IccRecordLoaded { 279 @Override getEfName()280 public String getEfName() { 281 return "EF_CSIM_SPN"; 282 } 283 284 @Override onRecordLoaded(AsyncResult ar)285 public void onRecordLoaded(AsyncResult ar) { 286 byte[] data = (byte[]) ar.result; 287 if (DBG) log("CSIM_SPN=" + 288 IccUtils.bytesToHexString(data)); 289 290 // C.S0065 for EF_SPN decoding 291 mCsimSpnDisplayCondition = ((0x01 & data[0]) != 0); 292 293 int encoding = data[1]; 294 int language = data[2]; 295 byte[] spnData = new byte[32]; 296 int len = ((data.length - 3) < 32) ? (data.length - 3) : 32; 297 System.arraycopy(data, 3, spnData, 0, len); 298 299 int numBytes; 300 for (numBytes = 0; numBytes < spnData.length; numBytes++) { 301 if ((spnData[numBytes] & 0xFF) == 0xFF) break; 302 } 303 304 if (numBytes == 0) { 305 setServiceProviderName(""); 306 return; 307 } 308 try { 309 switch (encoding) { 310 case UserData.ENCODING_OCTET: 311 case UserData.ENCODING_LATIN: 312 setServiceProviderName(new String(spnData, 0, numBytes, "ISO-8859-1")); 313 break; 314 case UserData.ENCODING_IA5: 315 case UserData.ENCODING_GSM_7BIT_ALPHABET: 316 setServiceProviderName( 317 GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes*8)/7)); 318 break; 319 case UserData.ENCODING_7BIT_ASCII: 320 String spn = new String(spnData, 0, numBytes, "US-ASCII"); 321 // To address issues with incorrect encoding scheme 322 // programmed in some commercial CSIM cards, the decoded 323 // SPN is checked to have characters in printable ASCII 324 // range. If not, they are decoded with 325 // ENCODING_GSM_7BIT_ALPHABET scheme. 326 if (TextUtils.isPrintableAsciiOnly(spn)) { 327 setServiceProviderName(spn); 328 } else { 329 if (DBG) log("Some corruption in SPN decoding = " + spn); 330 if (DBG) log("Using ENCODING_GSM_7BIT_ALPHABET scheme..."); 331 setServiceProviderName( 332 GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes * 8) / 7)); 333 } 334 break; 335 case UserData.ENCODING_UNICODE_16: 336 setServiceProviderName(new String(spnData, 0, numBytes, "utf-16")); 337 break; 338 default: 339 log("SPN encoding not supported"); 340 } 341 } catch(Exception e) { 342 log("spn decode error: " + e); 343 } 344 if (DBG) log("spn=" + getServiceProviderName()); 345 if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition); 346 mTelephonyManager.setSimOperatorNameForPhone( 347 mParentApp.getPhoneId(), getServiceProviderName()); 348 } 349 } 350 351 private class EfCsimMdnLoaded implements IccRecordLoaded { 352 @Override getEfName()353 public String getEfName() { 354 return "EF_CSIM_MDN"; 355 } 356 357 @Override onRecordLoaded(AsyncResult ar)358 public void onRecordLoaded(AsyncResult ar) { 359 byte[] data = (byte[]) ar.result; 360 if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data)); 361 // Refer to C.S0065 5.2.35 362 int mdnDigitsNum = 0x0F & data[0]; 363 mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum); 364 if (DBG) log("CSIM MDN=" + mMdn); 365 } 366 } 367 368 private class EfCsimImsimLoaded implements IccRecordLoaded { 369 @Override getEfName()370 public String getEfName() { 371 return "EF_CSIM_IMSIM"; 372 } 373 374 @Override onRecordLoaded(AsyncResult ar)375 public void onRecordLoaded(AsyncResult ar) { 376 byte[] data = (byte[]) ar.result; 377 if (VDBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data)); 378 // C.S0065 section 5.2.2 for IMSI_M encoding 379 // C.S0005 section 2.3.1 for MIN encoding in IMSI_M. 380 boolean provisioned = ((data[7] & 0x80) == 0x80); 381 382 if (provisioned) { 383 int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]); 384 int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6; 385 int digit7 = 0x0F & (data[4] >> 2); 386 if (digit7 > 0x09) digit7 = 0; 387 int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]); 388 first3digits = adjstMinDigits(first3digits); 389 second3digits = adjstMinDigits(second3digits); 390 last3digits = adjstMinDigits(last3digits); 391 392 StringBuilder builder = new StringBuilder(); 393 builder.append(String.format(Locale.US, "%03d", first3digits)); 394 builder.append(String.format(Locale.US, "%03d", second3digits)); 395 builder.append(String.format(Locale.US, "%d", digit7)); 396 builder.append(String.format(Locale.US, "%03d", last3digits)); 397 mMin = builder.toString(); 398 if (DBG) log("min present=" + mMin); 399 } else { 400 if (DBG) log("min not present"); 401 } 402 } 403 } 404 405 private class EfCsimCdmaHomeLoaded implements IccRecordLoaded { 406 @Override getEfName()407 public String getEfName() { 408 return "EF_CSIM_CDMAHOME"; 409 } 410 411 @Override onRecordLoaded(AsyncResult ar)412 public void onRecordLoaded(AsyncResult ar) { 413 // Per C.S0065 section 5.2.8 414 ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result; 415 if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size()); 416 if (dataList.isEmpty()) { 417 return; 418 } 419 StringBuilder sidBuf = new StringBuilder(); 420 StringBuilder nidBuf = new StringBuilder(); 421 422 for (byte[] data : dataList) { 423 if (data.length == 5) { 424 int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF); 425 int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF); 426 sidBuf.append(sid).append(','); 427 nidBuf.append(nid).append(','); 428 } 429 } 430 // remove trailing "," 431 sidBuf.setLength(sidBuf.length()-1); 432 nidBuf.setLength(nidBuf.length()-1); 433 434 mHomeSystemId = sidBuf.toString(); 435 mHomeNetworkId = nidBuf.toString(); 436 } 437 } 438 439 private class EfCsimEprlLoaded implements IccRecordLoaded { 440 @Override getEfName()441 public String getEfName() { 442 return "EF_CSIM_EPRL"; 443 } 444 @Override onRecordLoaded(AsyncResult ar)445 public void onRecordLoaded(AsyncResult ar) { 446 onGetCSimEprlDone(ar); 447 } 448 } 449 onGetCSimEprlDone(AsyncResult ar)450 private void onGetCSimEprlDone(AsyncResult ar) { 451 // C.S0065 section 5.2.57 for EFeprl encoding 452 // C.S0016 section 3.5.5 for PRL format. 453 byte[] data = (byte[]) ar.result; 454 if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data)); 455 456 // Only need the first 4 bytes of record 457 if (data.length > 3) { 458 int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF); 459 mPrlVersion = Integer.toString(prlId); 460 } 461 if (DBG) log("CSIM PRL version=" + mPrlVersion); 462 } 463 464 private class EfCsimMipUppLoaded implements IccRecordLoaded { 465 @Override getEfName()466 public String getEfName() { 467 return "EF_CSIM_MIPUPP"; 468 } 469 checkLengthLegal(int length, int expectLength)470 boolean checkLengthLegal(int length, int expectLength) { 471 if(length < expectLength) { 472 Log.e(LOG_TAG, "CSIM MIPUPP format error, length = " + length + 473 "expected length at least =" + expectLength); 474 return false; 475 } else { 476 return true; 477 } 478 } 479 480 @Override onRecordLoaded(AsyncResult ar)481 public void onRecordLoaded(AsyncResult ar) { 482 // 3GPP2 C.S0065 section 5.2.24 483 byte[] data = (byte[]) ar.result; 484 485 if(data.length < 1) { 486 Log.e(LOG_TAG,"MIPUPP read error"); 487 return; 488 } 489 490 BitwiseInputStream bitStream = new BitwiseInputStream(data); 491 try { 492 int mipUppLength = bitStream.read(8); 493 //transfer length from byte to bit 494 mipUppLength = (mipUppLength << 3); 495 496 if (!checkLengthLegal(mipUppLength, 1)) { 497 return; 498 } 499 //parse the MIPUPP body 3GPP2 C.S0016-C 3.5.8.6 500 int retryInfoInclude = bitStream.read(1); 501 mipUppLength--; 502 503 if(retryInfoInclude == 1) { 504 if (!checkLengthLegal(mipUppLength, 11)) { 505 return; 506 } 507 bitStream.skip(11); //not used now 508 //transfer length from byte to bit 509 mipUppLength -= 11; 510 } 511 512 if (!checkLengthLegal(mipUppLength, 4)) { 513 return; 514 } 515 int numNai = bitStream.read(4); 516 mipUppLength -= 4; 517 518 //start parse NAI body 519 for(int index = 0; index < numNai; index++) { 520 if (!checkLengthLegal(mipUppLength, 4)) { 521 return; 522 } 523 int naiEntryIndex = bitStream.read(4); 524 mipUppLength -= 4; 525 526 if (!checkLengthLegal(mipUppLength, 8)) { 527 return; 528 } 529 int naiLength = bitStream.read(8); 530 mipUppLength -= 8; 531 532 if(naiEntryIndex == 0) { 533 //we find the one! 534 if (!checkLengthLegal(mipUppLength, naiLength << 3)) { 535 return; 536 } 537 char naiCharArray[] = new char[naiLength]; 538 for(int index1 = 0; index1 < naiLength; index1++) { 539 naiCharArray[index1] = (char)(bitStream.read(8) & 0xFF); 540 } 541 mNai = new String(naiCharArray); 542 if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { 543 Log.v(LOG_TAG,"MIPUPP Nai = " + mNai); 544 } 545 return; //need not parsing further 546 } else { 547 //ignore this NAI body 548 if (!checkLengthLegal(mipUppLength, (naiLength << 3) + 102)) { 549 return; 550 } 551 bitStream.skip((naiLength << 3) + 101);//not used 552 int mnAaaSpiIndicator = bitStream.read(1); 553 mipUppLength -= ((naiLength << 3) + 102); 554 555 if(mnAaaSpiIndicator == 1) { 556 if (!checkLengthLegal(mipUppLength, 32)) { 557 return; 558 } 559 bitStream.skip(32); //not used 560 mipUppLength -= 32; 561 } 562 563 //MN-HA_AUTH_ALGORITHM 564 if (!checkLengthLegal(mipUppLength, 5)) { 565 return; 566 } 567 bitStream.skip(4); 568 mipUppLength -= 4; 569 int mnHaSpiIndicator = bitStream.read(1); 570 mipUppLength--; 571 572 if(mnHaSpiIndicator == 1) { 573 if (!checkLengthLegal(mipUppLength, 32)) { 574 return; 575 } 576 bitStream.skip(32); 577 mipUppLength -= 32; 578 } 579 } 580 } 581 } catch(Exception e) { 582 Log.e(LOG_TAG,"MIPUPP read Exception error!"); 583 return; 584 } 585 } 586 } 587 588 @Override handleMessage(Message msg)589 public void handleMessage(Message msg) { 590 AsyncResult ar; 591 592 byte data[]; 593 594 boolean isRecordLoadResponse = false; 595 596 if (mDestroyed.get()) { 597 loge("Received message " + msg + 598 "[" + msg.what + "] while being destroyed. Ignoring."); 599 return; 600 } 601 602 try { switch (msg.what) { 603 case EVENT_APP_READY: 604 onReady(); 605 break; 606 607 case EVENT_GET_DEVICE_IDENTITY_DONE: 608 log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received"); 609 break; 610 611 /* IO events */ 612 case EVENT_GET_IMSI_DONE: 613 isRecordLoadResponse = true; 614 615 ar = (AsyncResult)msg.obj; 616 if (ar.exception != null) { 617 loge("Exception querying IMSI, Exception:" + ar.exception); 618 break; 619 } 620 621 mImsi = (String) ar.result; 622 623 // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more 624 // than 15 (and usually 15). 625 if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) { 626 loge("invalid IMSI " + mImsi); 627 mImsi = null; 628 } 629 630 // FIXME: CSIM IMSI may not contain the MNC. 631 if (false) { 632 log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx"); 633 634 String operatorNumeric = getRUIMOperatorNumeric(); 635 if (operatorNumeric != null) { 636 if (operatorNumeric.length() <= 6) { 637 log("update mccmnc=" + operatorNumeric); 638 MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false); 639 } 640 } 641 } else { 642 String operatorNumeric = getRUIMOperatorNumeric(); 643 log("NO update mccmnc=" + operatorNumeric); 644 } 645 646 break; 647 648 case EVENT_GET_CDMA_SUBSCRIPTION_DONE: 649 ar = (AsyncResult)msg.obj; 650 String localTemp[] = (String[])ar.result; 651 if (ar.exception != null) { 652 break; 653 } 654 655 mMyMobileNumber = localTemp[0]; 656 mMin2Min1 = localTemp[3]; 657 mPrlVersion = localTemp[4]; 658 659 log("MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1); 660 661 break; 662 663 case EVENT_GET_ICCID_DONE: 664 isRecordLoadResponse = true; 665 666 ar = (AsyncResult)msg.obj; 667 data = (byte[])ar.result; 668 669 if (ar.exception != null) { 670 break; 671 } 672 673 mIccId = IccUtils.bcdToString(data, 0, data.length); 674 mFullIccId = IccUtils.bchToString(data, 0, data.length); 675 676 log("iccid: " + SubscriptionInfo.givePrintableIccid(mFullIccId)); 677 678 break; 679 680 case EVENT_UPDATE_DONE: 681 ar = (AsyncResult)msg.obj; 682 if (ar.exception != null) { 683 Rlog.i(LOG_TAG, "RuimRecords update failed", ar.exception); 684 } 685 break; 686 687 case EVENT_GET_ALL_SMS_DONE: 688 case EVENT_MARK_SMS_READ_DONE: 689 case EVENT_SMS_ON_RUIM: 690 case EVENT_GET_SMS_DONE: 691 Rlog.w(LOG_TAG, "Event not supported: " + msg.what); 692 break; 693 694 // TODO: probably EF_CST should be read instead 695 case EVENT_GET_SST_DONE: 696 log("Event EVENT_GET_SST_DONE Received"); 697 break; 698 699 case EVENT_RUIM_REFRESH: 700 isRecordLoadResponse = false; 701 ar = (AsyncResult)msg.obj; 702 if (ar.exception == null) { 703 handleRuimRefresh((IccRefreshResponse)ar.result); 704 } 705 break; 706 707 default: 708 super.handleMessage(msg); // IccRecords handles generic record load responses 709 710 }}catch (RuntimeException exc) { 711 // I don't want these exceptions to be fatal 712 Rlog.w(LOG_TAG, "Exception parsing RUIM record", exc); 713 } finally { 714 // Count up record load responses even if they are fails 715 if (isRecordLoadResponse) { 716 onRecordLoaded(); 717 } 718 } 719 } 720 721 /** 722 * Returns an array of languages we have assets for. 723 * 724 * NOTE: This array will have duplicates. If this method will be caused 725 * frequently or in a tight loop, it can be rewritten for efficiency. 726 */ getAssetLanguages(Context ctx)727 private static String[] getAssetLanguages(Context ctx) { 728 final String[] locales = ctx.getAssets().getLocales(); 729 final String[] localeLangs = new String[locales.length]; 730 for (int i = 0; i < locales.length; ++i) { 731 final String localeStr = locales[i]; 732 final int separator = localeStr.indexOf('-'); 733 if (separator < 0) { 734 localeLangs[i] = localeStr; 735 } else { 736 localeLangs[i] = localeStr.substring(0, separator); 737 } 738 } 739 740 return localeLangs; 741 } 742 743 @Override onRecordLoaded()744 protected void onRecordLoaded() { 745 // One record loaded successfully or failed, In either case 746 // we need to update the recordsToLoad count 747 mRecordsToLoad -= 1; 748 if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested); 749 750 if (mRecordsToLoad == 0 && mRecordsRequested == true) { 751 onAllRecordsLoaded(); 752 } else if (mRecordsToLoad < 0) { 753 loge("recordsToLoad <0, programmer error suspected"); 754 mRecordsToLoad = 0; 755 } 756 } 757 758 @Override onAllRecordsLoaded()759 protected void onAllRecordsLoaded() { 760 if (DBG) log("record load complete"); 761 762 // Further records that can be inserted are Operator/OEM dependent 763 764 // FIXME: CSIM IMSI may not contain the MNC. 765 if (false) { 766 String operator = getRUIMOperatorNumeric(); 767 if (!TextUtils.isEmpty(operator)) { 768 log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" + 769 operator + "'"); 770 log("update icc_operator_numeric=" + operator); 771 mTelephonyManager.setSimOperatorNumericForPhone( 772 mParentApp.getPhoneId(), operator); 773 } else { 774 log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping"); 775 } 776 777 if (!TextUtils.isEmpty(mImsi)) { 778 log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + mImsi) : "")); 779 mTelephonyManager.setSimCountryIsoForPhone( 780 mParentApp.getPhoneId(), 781 MccTable.countryCodeForMcc( 782 Integer.parseInt(mImsi.substring(0,3)))); 783 } else { 784 log("onAllRecordsLoaded empty imsi skipping setting mcc"); 785 } 786 } 787 788 Resources resource = Resources.getSystem(); 789 if (resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) { 790 setSimLanguage(mEFli, mEFpl); 791 } 792 793 mRecordsLoadedRegistrants.notifyRegistrants( 794 new AsyncResult(null, null, null)); 795 796 // TODO: The below is hacky since the SubscriptionController may not be ready at this time. 797 if (!TextUtils.isEmpty(mMdn)) { 798 int phoneId = mParentApp.getUiccCard().getPhoneId(); 799 int[] subIds = SubscriptionController.getInstance().getSubId(phoneId); 800 if (subIds != null) { 801 SubscriptionManager.from(mContext).setDisplayNumber(mMdn, subIds[0]); 802 } else { 803 log("Cannot call setDisplayNumber: invalid subId"); 804 } 805 } 806 } 807 808 @Override onReady()809 public void onReady() { 810 fetchRuimRecords(); 811 812 mCi.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE)); 813 } 814 815 fetchRuimRecords()816 private void fetchRuimRecords() { 817 mRecordsRequested = true; 818 819 if (DBG) log("fetchRuimRecords " + mRecordsToLoad); 820 821 mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE)); 822 mRecordsToLoad++; 823 824 mFh.loadEFTransparent(EF_ICCID, 825 obtainMessage(EVENT_GET_ICCID_DONE)); 826 mRecordsToLoad++; 827 828 mFh.loadEFTransparent(EF_PL, 829 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded())); 830 mRecordsToLoad++; 831 832 mFh.loadEFTransparent(EF_CSIM_LI, 833 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded())); 834 mRecordsToLoad++; 835 836 mFh.loadEFTransparent(EF_CSIM_SPN, 837 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded())); 838 mRecordsToLoad++; 839 840 mFh.loadEFLinearFixed(EF_CSIM_MDN, 1, 841 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMdnLoaded())); 842 mRecordsToLoad++; 843 844 mFh.loadEFTransparent(EF_CSIM_IMSIM, 845 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimImsimLoaded())); 846 mRecordsToLoad++; 847 848 mFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME, 849 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimCdmaHomeLoaded())); 850 mRecordsToLoad++; 851 852 // Entire PRL could be huge. We are only interested in 853 // the first 4 bytes of the record. 854 mFh.loadEFTransparent(EF_CSIM_EPRL, 4, 855 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimEprlLoaded())); 856 mRecordsToLoad++; 857 858 mFh.loadEFTransparent(EF_CSIM_MIPUPP, 859 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMipUppLoaded())); 860 mRecordsToLoad++; 861 862 if (DBG) log("fetchRuimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested); 863 // Further records that can be inserted are Operator/OEM dependent 864 } 865 866 /** 867 * {@inheritDoc} 868 * 869 * No Display rule for RUIMs yet. 870 */ 871 @Override getDisplayRule(String plmn)872 public int getDisplayRule(String plmn) { 873 // TODO together with spn 874 return 0; 875 } 876 877 @Override isProvisioned()878 public boolean isProvisioned() { 879 // If UICC card has CSIM app, look for MDN and MIN field 880 // to determine if the SIM is provisioned. Otherwise, 881 // consider the SIM is provisioned. (for case of ordinal 882 // USIM only UICC.) 883 // If PROPERTY_TEST_CSIM is defined, bypess provision check 884 // and consider the SIM is provisioned. 885 if (SystemProperties.getBoolean(PROPERTY_TEST_CSIM, false)) { 886 return true; 887 } 888 889 if (mParentApp == null) { 890 return false; 891 } 892 893 if (mParentApp.getType() == AppType.APPTYPE_CSIM && 894 ((mMdn == null) || (mMin == null))) { 895 return false; 896 } 897 return true; 898 } 899 900 @Override setVoiceMessageWaiting(int line, int countWaiting)901 public void setVoiceMessageWaiting(int line, int countWaiting) { 902 // Will be used in future to store voice mail count in UIM 903 // C.S0023-D_v1.0 does not have a file id in UIM for MWI 904 log("RuimRecords:setVoiceMessageWaiting - NOP for CDMA"); 905 } 906 907 @Override getVoiceMessageCount()908 public int getVoiceMessageCount() { 909 // Will be used in future to retrieve voice mail count for UIM 910 // C.S0023-D_v1.0 does not have a file id in UIM for MWI 911 log("RuimRecords:getVoiceMessageCount - NOP for CDMA"); 912 return 0; 913 } 914 handleRuimRefresh(IccRefreshResponse refreshResponse)915 private void handleRuimRefresh(IccRefreshResponse refreshResponse) { 916 if (refreshResponse == null) { 917 if (DBG) log("handleRuimRefresh received without input"); 918 return; 919 } 920 921 if (refreshResponse.aid != null && 922 !refreshResponse.aid.equals(mParentApp.getAid())) { 923 // This is for different app. Ignore. 924 return; 925 } 926 927 switch (refreshResponse.refreshResult) { 928 case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE: 929 if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED"); 930 mAdnCache.reset(); 931 fetchRuimRecords(); 932 break; 933 case IccRefreshResponse.REFRESH_RESULT_INIT: 934 if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT"); 935 // need to reload all files (that we care about) 936 onIccRefreshInit(); 937 break; 938 case IccRefreshResponse.REFRESH_RESULT_RESET: 939 // Refresh reset is handled by the UiccCard object. 940 if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET"); 941 break; 942 default: 943 // unknown refresh operation 944 if (DBG) log("handleRuimRefresh with unknown operation"); 945 break; 946 } 947 } 948 getMdn()949 public String getMdn() { 950 return mMdn; 951 } 952 getMin()953 public String getMin() { 954 return mMin; 955 } 956 getSid()957 public String getSid() { 958 return mHomeSystemId; 959 } 960 getNid()961 public String getNid() { 962 return mHomeNetworkId; 963 } 964 getCsimSpnDisplayCondition()965 public boolean getCsimSpnDisplayCondition() { 966 return mCsimSpnDisplayCondition; 967 } 968 @Override log(String s)969 protected void log(String s) { 970 Rlog.d(LOG_TAG, "[RuimRecords] " + s); 971 } 972 973 @Override loge(String s)974 protected void loge(String s) { 975 Rlog.e(LOG_TAG, "[RuimRecords] " + s); 976 } 977 978 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)979 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 980 pw.println("RuimRecords: " + this); 981 pw.println(" extends:"); 982 super.dump(fd, pw, args); 983 pw.println(" mOtaCommited=" + mOtaCommited); 984 pw.println(" mMyMobileNumber=" + mMyMobileNumber); 985 pw.println(" mMin2Min1=" + mMin2Min1); 986 pw.println(" mPrlVersion=" + mPrlVersion); 987 pw.println(" mEFpl[]=" + Arrays.toString(mEFpl)); 988 pw.println(" mEFli[]=" + Arrays.toString(mEFli)); 989 pw.println(" mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition); 990 pw.println(" mMdn=" + mMdn); 991 pw.println(" mMin=" + mMin); 992 pw.println(" mHomeSystemId=" + mHomeSystemId); 993 pw.println(" mHomeNetworkId=" + mHomeNetworkId); 994 pw.flush(); 995 } 996 } 997