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