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