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