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