• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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