1 /* 2 * Copyright (C) 2009 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.gsm; 18 19 import android.os.AsyncResult; 20 import android.os.Handler; 21 import android.os.Message; 22 import android.util.Log; 23 24 import com.android.internal.telephony.AdnRecord; 25 import com.android.internal.telephony.AdnRecordCache; 26 import com.android.internal.telephony.IccConstants; 27 import com.android.internal.telephony.IccUtils; 28 import com.android.internal.telephony.PhoneBase; 29 30 import org.apache.harmony.luni.lang.reflect.ListOfTypes; 31 32 import java.util.ArrayList; 33 import java.util.HashMap; 34 import java.util.Map; 35 36 /** 37 * This class implements reading and parsing USIM records. 38 * Refer to Spec 3GPP TS 31.102 for more details. 39 * 40 * {@hide} 41 */ 42 public class UsimPhoneBookManager extends Handler implements IccConstants { 43 private static final String LOG_TAG = "GSM"; 44 private static final boolean DBG = true; 45 private PbrFile mPbrFile; 46 private Boolean mIsPbrPresent; 47 private PhoneBase mPhone; 48 private AdnRecordCache mAdnCache; 49 private Object mLock = new Object(); 50 private ArrayList<AdnRecord> mPhoneBookRecords; 51 private boolean mEmailPresentInIap = false; 52 private int mEmailTagNumberInIap = 0; 53 private ArrayList<byte[]> mIapFileRecord; 54 private ArrayList<byte[]> mEmailFileRecord; 55 private Map<Integer, ArrayList<String>> mEmailsForAdnRec; 56 57 private static final int EVENT_PBR_LOAD_DONE = 1; 58 private static final int EVENT_USIM_ADN_LOAD_DONE = 2; 59 private static final int EVENT_IAP_LOAD_DONE = 3; 60 private static final int EVENT_EMAIL_LOAD_DONE = 4; 61 62 private static final int USIM_TYPE1_TAG = 0xA8; 63 private static final int USIM_TYPE2_TAG = 0xA9; 64 private static final int USIM_TYPE3_TAG = 0xAA; 65 private static final int USIM_EFADN_TAG = 0xC0; 66 private static final int USIM_EFIAP_TAG = 0xC1; 67 private static final int USIM_EFEXT1_TAG = 0xC2; 68 private static final int USIM_EFSNE_TAG = 0xC3; 69 private static final int USIM_EFANR_TAG = 0xC4; 70 private static final int USIM_EFPBC_TAG = 0xC5; 71 private static final int USIM_EFGRP_TAG = 0xC6; 72 private static final int USIM_EFAAS_TAG = 0xC7; 73 private static final int USIM_EFGSD_TAG = 0xC8; 74 private static final int USIM_EFUID_TAG = 0xC9; 75 private static final int USIM_EFEMAIL_TAG = 0xCA; 76 private static final int USIM_EFCCP1_TAG = 0xCB; 77 UsimPhoneBookManager(PhoneBase phone, AdnRecordCache cache)78 public UsimPhoneBookManager(PhoneBase phone, AdnRecordCache cache) { 79 mPhone = phone; 80 mPhoneBookRecords = new ArrayList<AdnRecord>(); 81 mPbrFile = null; 82 // We assume its present, after the first read this is updated. 83 // So we don't have to read from UICC if its not present on subsequent reads. 84 mIsPbrPresent = true; 85 mAdnCache = cache; 86 } 87 reset()88 public void reset() { 89 mPhoneBookRecords.clear(); 90 mIapFileRecord = null; 91 mEmailFileRecord = null; 92 mPbrFile = null; 93 mIsPbrPresent = true; 94 } 95 loadEfFilesFromUsim()96 public ArrayList<AdnRecord> loadEfFilesFromUsim() { 97 synchronized (mLock) { 98 if (!mPhoneBookRecords.isEmpty()) return mPhoneBookRecords; 99 if (!mIsPbrPresent) return null; 100 101 // Check if the PBR file is present in the cache, if not read it 102 // from the USIM. 103 if (mPbrFile == null) { 104 readPbrFileAndWait(); 105 } 106 107 if (mPbrFile == null) return null; 108 109 int numRecs = mPbrFile.mFileIds.size(); 110 for (int i = 0; i < numRecs; i++) { 111 readAdnFileAndWait(i); 112 readEmailFileAndWait(i); 113 } 114 // All EF files are loaded, post the response. 115 } 116 return mPhoneBookRecords; 117 } 118 readPbrFileAndWait()119 private void readPbrFileAndWait() { 120 mPhone.getIccFileHandler().loadEFLinearFixedAll(EF_PBR, obtainMessage(EVENT_PBR_LOAD_DONE)); 121 try { 122 mLock.wait(); 123 } catch (InterruptedException e) { 124 Log.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait"); 125 } 126 } 127 readEmailFileAndWait(int recNum)128 private void readEmailFileAndWait(int recNum) { 129 Map <Integer,Integer> fileIds; 130 fileIds = mPbrFile.mFileIds.get(recNum); 131 if (fileIds == null) return; 132 133 if (fileIds.containsKey(USIM_EFEMAIL_TAG)) { 134 int efid = fileIds.get(USIM_EFEMAIL_TAG); 135 // Check if the EFEmail is a Type 1 file or a type 2 file. 136 // If mEmailPresentInIap is true, its a type 2 file. 137 // So we read the IAP file and then read the email records. 138 // instead of reading directly. 139 if (mEmailPresentInIap) { 140 readIapFileAndWait(fileIds.get(USIM_EFIAP_TAG)); 141 if (mIapFileRecord == null) { 142 Log.e(LOG_TAG, "Error: IAP file is empty"); 143 return; 144 } 145 } 146 // Read the EFEmail file. 147 mPhone.getIccFileHandler().loadEFLinearFixedAll(fileIds.get(USIM_EFEMAIL_TAG), 148 obtainMessage(EVENT_EMAIL_LOAD_DONE)); 149 try { 150 mLock.wait(); 151 } catch (InterruptedException e) { 152 Log.e(LOG_TAG, "Interrupted Exception in readEmailFileAndWait"); 153 } 154 155 if (mEmailFileRecord == null) { 156 Log.e(LOG_TAG, "Error: Email file is empty"); 157 return; 158 } 159 updatePhoneAdnRecord(); 160 } 161 162 } 163 readIapFileAndWait(int efid)164 private void readIapFileAndWait(int efid) { 165 mPhone.getIccFileHandler().loadEFLinearFixedAll(efid, obtainMessage(EVENT_IAP_LOAD_DONE)); 166 try { 167 mLock.wait(); 168 } catch (InterruptedException e) { 169 Log.e(LOG_TAG, "Interrupted Exception in readIapFileAndWait"); 170 } 171 } 172 updatePhoneAdnRecord()173 private void updatePhoneAdnRecord() { 174 if (mEmailFileRecord == null) return; 175 int numAdnRecs = mPhoneBookRecords.size(); 176 if (mIapFileRecord != null) { 177 // The number of records in the IAP file is same as the number of records in ADN file. 178 // The order of the pointers in an EFIAP shall be the same as the order of file IDs 179 // that appear in the TLV object indicated by Tag 'A9' in the reference file record. 180 // i.e value of mEmailTagNumberInIap 181 182 for (int i = 0; i < numAdnRecs; i++) { 183 byte[] record = null; 184 try { 185 record = mIapFileRecord.get(i); 186 } catch (IndexOutOfBoundsException e) { 187 Log.e(LOG_TAG, "Error: Improper ICC card: No IAP record for ADN, continuing"); 188 break; 189 } 190 int recNum = record[mEmailTagNumberInIap]; 191 192 if (recNum != -1) { 193 String[] emails = new String[1]; 194 // SIM record numbers are 1 based 195 emails[0] = readEmailRecord(recNum - 1); 196 AdnRecord rec = mPhoneBookRecords.get(i); 197 if (rec != null) { 198 rec.setEmails(emails); 199 } else { 200 // might be a record with only email 201 rec = new AdnRecord("", "", emails); 202 } 203 mPhoneBookRecords.set(i, rec); 204 } 205 } 206 } 207 208 // ICC cards can be made such that they have an IAP file but all 209 // records are empty. So we read both type 1 and type 2 file 210 // email records, just to be sure. 211 212 int len = mPhoneBookRecords.size(); 213 // Type 1 file, the number of records is the same as the number of 214 // records in the ADN file. 215 if (mEmailsForAdnRec == null) { 216 parseType1EmailFile(len); 217 } 218 for (int i = 0; i < numAdnRecs; i++) { 219 ArrayList<String> emailList = null; 220 try { 221 emailList = mEmailsForAdnRec.get(i); 222 } catch (IndexOutOfBoundsException e) { 223 break; 224 } 225 if (emailList == null) continue; 226 227 AdnRecord rec = mPhoneBookRecords.get(i); 228 229 String[] emails = new String[emailList.size()]; 230 System.arraycopy(emailList.toArray(), 0, emails, 0, emailList.size()); 231 rec.setEmails(emails); 232 mPhoneBookRecords.set(i, rec); 233 } 234 } 235 parseType1EmailFile(int numRecs)236 void parseType1EmailFile(int numRecs) { 237 mEmailsForAdnRec = new HashMap<Integer, ArrayList<String>>(); 238 byte[] emailRec = null; 239 for (int i = 0; i < numRecs; i++) { 240 try { 241 emailRec = mEmailFileRecord.get(i); 242 } catch (IndexOutOfBoundsException e) { 243 Log.e(LOG_TAG, "Error: Improper ICC card: No email record for ADN, continuing"); 244 break; 245 } 246 int adnRecNum = emailRec[emailRec.length - 1]; 247 248 if (adnRecNum == -1) { 249 continue; 250 } 251 252 String email = readEmailRecord(i); 253 254 if (email == null || email.equals("")) { 255 continue; 256 } 257 258 // SIM record numbers are 1 based. 259 ArrayList<String> val = mEmailsForAdnRec.get(adnRecNum - 1); 260 if (val == null) { 261 val = new ArrayList<String>(); 262 } 263 val.add(email); 264 // SIM record numbers are 1 based. 265 mEmailsForAdnRec.put(adnRecNum - 1, val); 266 } 267 } 268 readEmailRecord(int recNum)269 private String readEmailRecord(int recNum) { 270 byte[] emailRec = null; 271 try { 272 emailRec = mEmailFileRecord.get(recNum); 273 } catch (IndexOutOfBoundsException e) { 274 return null; 275 } 276 277 // The length of the record is X+2 byte, where X bytes is the email address 278 String email = IccUtils.adnStringFieldToString(emailRec, 0, emailRec.length - 2); 279 return email; 280 } 281 readAdnFileAndWait(int recNum)282 private void readAdnFileAndWait(int recNum) { 283 Map <Integer,Integer> fileIds; 284 fileIds = mPbrFile.mFileIds.get(recNum); 285 if (fileIds == null) return; 286 287 mAdnCache.requestLoadAllAdnLike(fileIds.get(USIM_EFADN_TAG), 288 fileIds.get(USIM_EFEXT1_TAG), obtainMessage(EVENT_USIM_ADN_LOAD_DONE)); 289 try { 290 mLock.wait(); 291 } catch (InterruptedException e) { 292 Log.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait"); 293 } 294 } 295 createPbrFile(ArrayList<byte[]> records)296 private void createPbrFile(ArrayList<byte[]> records) { 297 if (records == null) { 298 mPbrFile = null; 299 mIsPbrPresent = false; 300 return; 301 } 302 mPbrFile = new PbrFile(records); 303 } 304 305 @Override handleMessage(Message msg)306 public void handleMessage(Message msg) { 307 AsyncResult ar; 308 309 switch(msg.what) { 310 case EVENT_PBR_LOAD_DONE: 311 ar = (AsyncResult) msg.obj; 312 if (ar.exception == null) { 313 createPbrFile((ArrayList<byte[]>)ar.result); 314 } 315 synchronized (mLock) { 316 mLock.notify(); 317 } 318 break; 319 case EVENT_USIM_ADN_LOAD_DONE: 320 log("Loading USIM ADN records done"); 321 ar = (AsyncResult) msg.obj; 322 if (ar.exception == null) { 323 mPhoneBookRecords.addAll((ArrayList<AdnRecord>)ar.result); 324 } 325 synchronized (mLock) { 326 mLock.notify(); 327 } 328 break; 329 case EVENT_IAP_LOAD_DONE: 330 log("Loading USIM IAP records done"); 331 ar = (AsyncResult) msg.obj; 332 if (ar.exception == null) { 333 mIapFileRecord = ((ArrayList<byte[]>)ar.result); 334 } 335 synchronized (mLock) { 336 mLock.notify(); 337 } 338 break; 339 case EVENT_EMAIL_LOAD_DONE: 340 log("Loading USIM Email records done"); 341 ar = (AsyncResult) msg.obj; 342 if (ar.exception == null) { 343 mEmailFileRecord = ((ArrayList<byte[]>)ar.result); 344 } 345 346 synchronized (mLock) { 347 mLock.notify(); 348 } 349 break; 350 } 351 } 352 353 private class PbrFile { 354 // RecNum <EF Tag, efid> 355 HashMap<Integer,Map<Integer,Integer>> mFileIds; 356 PbrFile(ArrayList<byte[]> records)357 PbrFile(ArrayList<byte[]> records) { 358 mFileIds = new HashMap<Integer, Map<Integer, Integer>>(); 359 SimTlv recTlv; 360 int recNum = 0; 361 for (byte[] record: records) { 362 recTlv = new SimTlv(record, 0, record.length); 363 parseTag(recTlv, recNum); 364 recNum ++; 365 } 366 } 367 parseTag(SimTlv tlv, int recNum)368 void parseTag(SimTlv tlv, int recNum) { 369 SimTlv tlvEf; 370 int tag; 371 byte[] data; 372 Map<Integer, Integer> val = new HashMap<Integer, Integer>(); 373 do { 374 tag = tlv.getTag(); 375 switch(tag) { 376 case USIM_TYPE1_TAG: // A8 377 case USIM_TYPE3_TAG: // AA 378 case USIM_TYPE2_TAG: // A9 379 data = tlv.getData(); 380 tlvEf = new SimTlv(data, 0, data.length); 381 parseEf(tlvEf, val, tag); 382 break; 383 } 384 } while (tlv.nextObject()); 385 mFileIds.put(recNum, val); 386 } 387 parseEf(SimTlv tlv, Map<Integer, Integer> val, int parentTag)388 void parseEf(SimTlv tlv, Map<Integer, Integer> val, int parentTag) { 389 int tag; 390 byte[] data; 391 int tagNumberWithinParentTag = 0; 392 do { 393 tag = tlv.getTag(); 394 if (parentTag == USIM_TYPE2_TAG && tag == USIM_EFEMAIL_TAG) { 395 mEmailPresentInIap = true; 396 mEmailTagNumberInIap = tagNumberWithinParentTag; 397 } 398 switch(tag) { 399 case USIM_EFEMAIL_TAG: 400 case USIM_EFADN_TAG: 401 case USIM_EFEXT1_TAG: 402 case USIM_EFANR_TAG: 403 case USIM_EFPBC_TAG: 404 case USIM_EFGRP_TAG: 405 case USIM_EFAAS_TAG: 406 case USIM_EFGSD_TAG: 407 case USIM_EFUID_TAG: 408 case USIM_EFCCP1_TAG: 409 case USIM_EFIAP_TAG: 410 case USIM_EFSNE_TAG: 411 data = tlv.getData(); 412 int efid = data[0] << 8 | data[1]; 413 val.put(tag, efid); 414 break; 415 } 416 tagNumberWithinParentTag ++; 417 } while(tlv.nextObject()); 418 } 419 } 420 log(String msg)421 private void log(String msg) { 422 if(DBG) Log.d(LOG_TAG, msg); 423 } 424 } 425