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;
18
19import android.os.*;
20import android.util.Log;
21import java.util.ArrayList;
22
23/**
24 * {@hide}
25 */
26public abstract class IccFileHandler extends Handler implements IccConstants {
27
28    //from TS 11.11 9.1 or elsewhere
29    static protected final int COMMAND_READ_BINARY = 0xb0;
30    static protected final int COMMAND_UPDATE_BINARY = 0xd6;
31    static protected final int COMMAND_READ_RECORD = 0xb2;
32    static protected final int COMMAND_UPDATE_RECORD = 0xdc;
33    static protected final int COMMAND_SEEK = 0xa2;
34    static protected final int COMMAND_GET_RESPONSE = 0xc0;
35
36    // from TS 11.11 9.2.5
37    static protected final int READ_RECORD_MODE_ABSOLUTE = 4;
38
39    //***** types of files  TS 11.11 9.3
40    static protected final int EF_TYPE_TRANSPARENT = 0;
41    static protected final int EF_TYPE_LINEAR_FIXED = 1;
42    static protected final int EF_TYPE_CYCLIC = 3;
43
44    //***** types of files  TS 11.11 9.3
45    static protected final int TYPE_RFU = 0;
46    static protected final int TYPE_MF  = 1;
47    static protected final int TYPE_DF  = 2;
48    static protected final int TYPE_EF  = 4;
49
50    // size of GET_RESPONSE for EF's
51    static protected final int GET_RESPONSE_EF_SIZE_BYTES = 15;
52    static protected final int GET_RESPONSE_EF_IMG_SIZE_BYTES = 10;
53
54    // Byte order received in response to COMMAND_GET_RESPONSE
55    // Refer TS 51.011 Section 9.2.1
56    static protected final int RESPONSE_DATA_RFU_1 = 0;
57    static protected final int RESPONSE_DATA_RFU_2 = 1;
58
59    static protected final int RESPONSE_DATA_FILE_SIZE_1 = 2;
60    static protected final int RESPONSE_DATA_FILE_SIZE_2 = 3;
61
62    static protected final int RESPONSE_DATA_FILE_ID_1 = 4;
63    static protected final int RESPONSE_DATA_FILE_ID_2 = 5;
64    static protected final int RESPONSE_DATA_FILE_TYPE = 6;
65    static protected final int RESPONSE_DATA_RFU_3 = 7;
66    static protected final int RESPONSE_DATA_ACCESS_CONDITION_1 = 8;
67    static protected final int RESPONSE_DATA_ACCESS_CONDITION_2 = 9;
68    static protected final int RESPONSE_DATA_ACCESS_CONDITION_3 = 10;
69    static protected final int RESPONSE_DATA_FILE_STATUS = 11;
70    static protected final int RESPONSE_DATA_LENGTH = 12;
71    static protected final int RESPONSE_DATA_STRUCTURE = 13;
72    static protected final int RESPONSE_DATA_RECORD_LENGTH = 14;
73
74
75    //***** Events
76
77    /** Finished retrieving size of transparent EF; start loading. */
78    static protected final int EVENT_GET_BINARY_SIZE_DONE = 4;
79    /** Finished loading contents of transparent EF; post result. */
80    static protected final int EVENT_READ_BINARY_DONE = 5;
81    /** Finished retrieving size of records for linear-fixed EF; now load. */
82    static protected final int EVENT_GET_RECORD_SIZE_DONE = 6;
83    /** Finished loading single record from a linear-fixed EF; post result. */
84    static protected final int EVENT_READ_RECORD_DONE = 7;
85    /** Finished retrieving record size; post result. */
86    static protected final int EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE = 8;
87    /** Finished retrieving image instance record; post result. */
88    static protected final int EVENT_READ_IMG_DONE = 9;
89    /** Finished retrieving icon data; post result. */
90    static protected final int EVENT_READ_ICON_DONE = 10;
91
92     // member variables
93    protected final CommandsInterface mCi;
94    protected final UiccCardApplication mParentApp;
95    protected final String mAid;
96
97    static class LoadLinearFixedContext {
98
99        int efid;
100        int recordNum, recordSize, countRecords;
101        boolean loadAll;
102
103        Message onLoaded;
104
105        ArrayList<byte[]> results;
106
107        LoadLinearFixedContext(int efid, int recordNum, Message onLoaded) {
108            this.efid = efid;
109            this.recordNum = recordNum;
110            this.onLoaded = onLoaded;
111            this.loadAll = false;
112        }
113
114        LoadLinearFixedContext(int efid, Message onLoaded) {
115            this.efid = efid;
116            this.recordNum = 1;
117            this.loadAll = true;
118            this.onLoaded = onLoaded;
119        }
120    }
121
122    /**
123     * Default constructor
124     */
125    protected IccFileHandler(UiccCardApplication app, String aid, CommandsInterface ci) {
126        mParentApp = app;
127        mAid = aid;
128        mCi = ci;
129    }
130
131    public void dispose() {
132    }
133
134    //***** Public Methods
135
136    /**
137     * Load a record from a SIM Linear Fixed EF
138     *
139     * @param fileid EF id
140     * @param recordNum 1-based (not 0-based) record number
141     * @param onLoaded
142     *
143     * ((AsyncResult)(onLoaded.obj)).result is the byte[]
144     *
145     */
146    public void loadEFLinearFixed(int fileid, int recordNum, Message onLoaded) {
147        Message response
148            = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
149                        new LoadLinearFixedContext(fileid, recordNum, onLoaded));
150
151        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
152                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
153    }
154
155    /**
156     * Load a image instance record from a SIM Linear Fixed EF-IMG
157     *
158     * @param recordNum 1-based (not 0-based) record number
159     * @param onLoaded
160     *
161     * ((AsyncResult)(onLoaded.obj)).result is the byte[]
162     *
163     */
164    public void loadEFImgLinearFixed(int recordNum, Message onLoaded) {
165        Message response = obtainMessage(EVENT_READ_IMG_DONE,
166                new LoadLinearFixedContext(IccConstants.EF_IMG, recordNum,
167                        onLoaded));
168
169        // TODO(): Verify when path changes are done.
170        mCi.iccIOForApp(COMMAND_GET_RESPONSE, IccConstants.EF_IMG, "img",
171                recordNum, READ_RECORD_MODE_ABSOLUTE,
172                GET_RESPONSE_EF_IMG_SIZE_BYTES, null, null, mAid, response);
173    }
174
175    /**
176     * get record size for a linear fixed EF
177     *
178     * @param fileid EF id
179     * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[]
180     *        int[0] is the record length int[1] is the total length of the EF
181     *        file int[3] is the number of records in the EF file So int[0] *
182     *        int[3] = int[1]
183     */
184    public void getEFLinearRecordSize(int fileid, Message onLoaded) {
185        Message response
186                = obtainMessage(EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE,
187                        new LoadLinearFixedContext(fileid, onLoaded));
188        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
189                    0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
190    }
191
192    /**
193     * Load all records from a SIM Linear Fixed EF
194     *
195     * @param fileid EF id
196     * @param onLoaded
197     *
198     * ((AsyncResult)(onLoaded.obj)).result is an ArrayList<byte[]>
199     *
200     */
201    public void loadEFLinearFixedAll(int fileid, Message onLoaded) {
202        Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
203                        new LoadLinearFixedContext(fileid,onLoaded));
204
205        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
206                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
207    }
208
209    /**
210     * Load a SIM Transparent EF
211     *
212     * @param fileid EF id
213     * @param onLoaded
214     *
215     * ((AsyncResult)(onLoaded.obj)).result is the byte[]
216     *
217     */
218
219    public void loadEFTransparent(int fileid, Message onLoaded) {
220        Message response = obtainMessage(EVENT_GET_BINARY_SIZE_DONE,
221                        fileid, 0, onLoaded);
222
223        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
224                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
225    }
226
227    /**
228     * Load first @size bytes from SIM Transparent EF
229     *
230     * @param fileid EF id
231     * @param size
232     * @param onLoaded
233     *
234     * ((AsyncResult)(onLoaded.obj)).result is the byte[]
235     *
236     */
237    public void loadEFTransparent(int fileid, int size, Message onLoaded) {
238        Message response = obtainMessage(EVENT_READ_BINARY_DONE,
239                        fileid, 0, onLoaded);
240
241        mCi.iccIOForApp(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
242                        0, 0, size, null, null, mAid, response);
243    }
244
245    /**
246     * Load a SIM Transparent EF-IMG. Used right after loadEFImgLinearFixed to
247     * retrive STK's icon data.
248     *
249     * @param fileid EF id
250     * @param onLoaded
251     *
252     * ((AsyncResult)(onLoaded.obj)).result is the byte[]
253     *
254     */
255    public void loadEFImgTransparent(int fileid, int highOffset, int lowOffset,
256            int length, Message onLoaded) {
257        Message response = obtainMessage(EVENT_READ_ICON_DONE, fileid, 0,
258                onLoaded);
259
260        mCi.iccIOForApp(COMMAND_READ_BINARY, fileid, "img", highOffset, lowOffset,
261                length, null, null, mAid, response);
262    }
263
264    /**
265     * Update a record in a linear fixed EF
266     * @param fileid EF id
267     * @param recordNum 1-based (not 0-based) record number
268     * @param data must be exactly as long as the record in the EF
269     * @param pin2 for CHV2 operations, otherwist must be null
270     * @param onComplete onComplete.obj will be an AsyncResult
271     *                   onComplete.obj.userObj will be a IccIoResult on success
272     */
273    public void updateEFLinearFixed(int fileid, int recordNum, byte[] data,
274            String pin2, Message onComplete) {
275        mCi.iccIOForApp(COMMAND_UPDATE_RECORD, fileid, getEFPath(fileid),
276                        recordNum, READ_RECORD_MODE_ABSOLUTE, data.length,
277                        IccUtils.bytesToHexString(data), pin2, mAid, onComplete);
278    }
279
280    /**
281     * Update a transparent EF
282     * @param fileid EF id
283     * @param data must be exactly as long as the EF
284     */
285    public void updateEFTransparent(int fileid, byte[] data, Message onComplete) {
286        mCi.iccIOForApp(COMMAND_UPDATE_BINARY, fileid, getEFPath(fileid),
287                        0, 0, data.length,
288                        IccUtils.bytesToHexString(data), null, mAid, onComplete);
289    }
290
291
292    //***** Abstract Methods
293
294
295    //***** Private Methods
296
297    private void sendResult(Message response, Object result, Throwable ex) {
298        if (response == null) {
299            return;
300        }
301
302        AsyncResult.forMessage(response, result, ex);
303
304        response.sendToTarget();
305    }
306
307    //***** Overridden from Handler
308
309    public void handleMessage(Message msg) {
310        AsyncResult ar;
311        IccIoResult result;
312        Message response = null;
313        String str;
314        LoadLinearFixedContext lc;
315
316        IccException iccException;
317        byte data[];
318        int size;
319        int fileid;
320        int recordNum;
321        int recordSize[];
322
323        try {
324            switch (msg.what) {
325            case EVENT_READ_IMG_DONE:
326                ar = (AsyncResult) msg.obj;
327                lc = (LoadLinearFixedContext) ar.userObj;
328                result = (IccIoResult) ar.result;
329                response = lc.onLoaded;
330
331                iccException = result.getException();
332                if (iccException != null) {
333                    sendResult(response, result.payload, ar.exception);
334                }
335                break;
336            case EVENT_READ_ICON_DONE:
337                ar = (AsyncResult) msg.obj;
338                response = (Message) ar.userObj;
339                result = (IccIoResult) ar.result;
340
341                iccException = result.getException();
342                if (iccException != null) {
343                    sendResult(response, result.payload, ar.exception);
344                }
345                break;
346            case EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE:
347                ar = (AsyncResult)msg.obj;
348                lc = (LoadLinearFixedContext) ar.userObj;
349                result = (IccIoResult) ar.result;
350                response = lc.onLoaded;
351
352                if (ar.exception != null) {
353                    sendResult(response, null, ar.exception);
354                    break;
355                }
356
357                iccException = result.getException();
358                if (iccException != null) {
359                    sendResult(response, null, iccException);
360                    break;
361                }
362
363                data = result.payload;
364
365                if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE] ||
366                    EF_TYPE_LINEAR_FIXED != data[RESPONSE_DATA_STRUCTURE]) {
367                    throw new IccFileTypeMismatch();
368                }
369
370                recordSize = new int[3];
371                recordSize[0] = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF;
372                recordSize[1] = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8)
373                       + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff);
374                recordSize[2] = recordSize[1] / recordSize[0];
375
376                sendResult(response, recordSize, null);
377                break;
378             case EVENT_GET_RECORD_SIZE_DONE:
379                ar = (AsyncResult)msg.obj;
380                lc = (LoadLinearFixedContext) ar.userObj;
381                result = (IccIoResult) ar.result;
382                response = lc.onLoaded;
383
384                if (ar.exception != null) {
385                    sendResult(response, null, ar.exception);
386                    break;
387                }
388
389                iccException = result.getException();
390
391                if (iccException != null) {
392                    sendResult(response, null, iccException);
393                    break;
394                }
395
396                data = result.payload;
397                fileid = lc.efid;
398                recordNum = lc.recordNum;
399
400                if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) {
401                    throw new IccFileTypeMismatch();
402                }
403
404                if (EF_TYPE_LINEAR_FIXED != data[RESPONSE_DATA_STRUCTURE]) {
405                    throw new IccFileTypeMismatch();
406                }
407
408                lc.recordSize = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF;
409
410                size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8)
411                       + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff);
412
413                lc.countRecords = size / lc.recordSize;
414
415                 if (lc.loadAll) {
416                     lc.results = new ArrayList<byte[]>(lc.countRecords);
417                 }
418
419                 mCi.iccIOForApp(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
420                         lc.recordNum,
421                         READ_RECORD_MODE_ABSOLUTE,
422                         lc.recordSize, null, null, mAid,
423                         obtainMessage(EVENT_READ_RECORD_DONE, lc));
424                 break;
425            case EVENT_GET_BINARY_SIZE_DONE:
426                ar = (AsyncResult)msg.obj;
427                response = (Message) ar.userObj;
428                result = (IccIoResult) ar.result;
429
430                if (ar.exception != null) {
431                    sendResult(response, null, ar.exception);
432                    break;
433                }
434
435                iccException = result.getException();
436
437                if (iccException != null) {
438                    sendResult(response, null, iccException);
439                    break;
440                }
441
442                data = result.payload;
443
444                fileid = msg.arg1;
445
446                if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) {
447                    throw new IccFileTypeMismatch();
448                }
449
450                if (EF_TYPE_TRANSPARENT != data[RESPONSE_DATA_STRUCTURE]) {
451                    throw new IccFileTypeMismatch();
452                }
453
454                size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8)
455                       + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff);
456
457                mCi.iccIOForApp(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
458                                0, 0, size, null, null, mAid,
459                                obtainMessage(EVENT_READ_BINARY_DONE,
460                                              fileid, 0, response));
461            break;
462
463            case EVENT_READ_RECORD_DONE:
464
465                ar = (AsyncResult)msg.obj;
466                lc = (LoadLinearFixedContext) ar.userObj;
467                result = (IccIoResult) ar.result;
468                response = lc.onLoaded;
469
470                if (ar.exception != null) {
471                    sendResult(response, null, ar.exception);
472                    break;
473                }
474
475                iccException = result.getException();
476
477                if (iccException != null) {
478                    sendResult(response, null, iccException);
479                    break;
480                }
481
482                if (!lc.loadAll) {
483                    sendResult(response, result.payload, null);
484                } else {
485                    lc.results.add(result.payload);
486
487                    lc.recordNum++;
488
489                    if (lc.recordNum > lc.countRecords) {
490                        sendResult(response, lc.results, null);
491                    } else {
492                        mCi.iccIOForApp(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
493                                    lc.recordNum,
494                                    READ_RECORD_MODE_ABSOLUTE,
495                                    lc.recordSize, null, null, mAid,
496                                    obtainMessage(EVENT_READ_RECORD_DONE, lc));
497                    }
498                }
499
500            break;
501
502            case EVENT_READ_BINARY_DONE:
503                ar = (AsyncResult)msg.obj;
504                response = (Message) ar.userObj;
505                result = (IccIoResult) ar.result;
506
507                if (ar.exception != null) {
508                    sendResult(response, null, ar.exception);
509                    break;
510                }
511
512                iccException = result.getException();
513
514                if (iccException != null) {
515                    sendResult(response, null, iccException);
516                    break;
517                }
518
519                sendResult(response, result.payload, null);
520            break;
521
522        }} catch (Exception exc) {
523            if (response != null) {
524                sendResult(response, null, exc);
525            } else {
526                loge("uncaught exception" + exc);
527            }
528        }
529    }
530
531    /**
532     * Returns the root path of the EF file.
533     * i.e returns MasterFile + DFfile as a string.
534     * Ex: For EF_ADN on a SIM, it will return "3F007F10"
535     * This function handles only EFids that are common to
536     * RUIM, SIM, USIM and other types of Icc cards.
537     *
538     * @param efId
539     * @return root path of the file.
540     */
541    protected String getCommonIccEFPath(int efid) {
542        switch(efid) {
543        case EF_ADN:
544        case EF_FDN:
545        case EF_MSISDN:
546        case EF_SDN:
547        case EF_EXT1:
548        case EF_EXT2:
549        case EF_EXT3:
550            return MF_SIM + DF_TELECOM;
551
552        case EF_ICCID:
553        case EF_PL:
554            return MF_SIM;
555        case EF_PBR:
556            // we only support global phonebook.
557            return MF_SIM + DF_TELECOM + DF_PHONEBOOK;
558        case EF_IMG:
559            return MF_SIM + DF_TELECOM + DF_GRAPHICS;
560        }
561        return null;
562    }
563
564    protected abstract String getEFPath(int efid);
565    protected abstract void logd(String s);
566
567    protected abstract void loge(String s);
568
569}
570