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