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