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