1/*
2 * Copyright (C) 2006 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 java.util.ArrayList;
20
21import android.os.AsyncResult;
22import android.os.Handler;
23import android.os.Looper;
24import android.os.Message;
25import android.telephony.Rlog;
26
27import com.android.internal.telephony.uicc.IccConstants;
28
29public class AdnRecordLoader extends Handler {
30    final static String LOG_TAG = "AdnRecordLoader";
31    final static boolean VDBG = false;
32
33    //***** Instance Variables
34
35    private IccFileHandler mFh;
36    int mEf;
37    int mExtensionEF;
38    int mPendingExtLoads;
39    Message mUserResponse;
40    String mPin2;
41
42    // For "load one"
43    int mRecordNumber;
44
45    // for "load all"
46    ArrayList<AdnRecord> mAdns; // only valid after EVENT_ADN_LOAD_ALL_DONE
47
48    // Either an AdnRecord or a reference to adns depending
49    // if this is a load one or load all operation
50    Object mResult;
51
52    //***** Event Constants
53
54    static final int EVENT_ADN_LOAD_DONE = 1;
55    static final int EVENT_EXT_RECORD_LOAD_DONE = 2;
56    static final int EVENT_ADN_LOAD_ALL_DONE = 3;
57    static final int EVENT_EF_LINEAR_RECORD_SIZE_DONE = 4;
58    static final int EVENT_UPDATE_RECORD_DONE = 5;
59
60    //***** Constructor
61
62    AdnRecordLoader(IccFileHandler fh) {
63        // The telephony unit-test cases may create AdnRecords
64        // in secondary threads
65        super(Looper.getMainLooper());
66        mFh = fh;
67    }
68
69    private String getEFPath(int efid) {
70        if (efid == IccConstants.EF_ADN) {
71            return IccConstants.MF_SIM + IccConstants.DF_TELECOM;
72        }
73
74        return null;
75    }
76
77    /**
78     * Resulting AdnRecord is placed in response.obj.result
79     * or response.obj.exception is set
80     */
81    public void
82    loadFromEF(int ef, int extensionEF, int recordNumber,
83                Message response) {
84        mEf = ef;
85        mExtensionEF = extensionEF;
86        mRecordNumber = recordNumber;
87        mUserResponse = response;
88
89       mFh.loadEFLinearFixed(
90               ef, getEFPath(ef), recordNumber,
91               obtainMessage(EVENT_ADN_LOAD_DONE));
92    }
93
94
95    /**
96     * Resulting ArrayList&lt;adnRecord> is placed in response.obj.result
97     * or response.obj.exception is set
98     */
99    public void
100    loadAllFromEF(int ef, int extensionEF,
101                Message response) {
102        mEf = ef;
103        mExtensionEF = extensionEF;
104        mUserResponse = response;
105
106        /* If we are loading from EF_ADN, specifically
107         * specify the path as well, since, on some cards,
108         * the fileid is not unique.
109         */
110        mFh.loadEFLinearFixedAll(
111                ef, getEFPath(ef),
112                obtainMessage(EVENT_ADN_LOAD_ALL_DONE));
113    }
114
115    /**
116     * Write adn to a EF SIM record
117     * It will get the record size of EF record and compose hex adn array
118     * then write the hex array to EF record
119     *
120     * @param adn is set with alphaTag and phone number
121     * @param ef EF fileid
122     * @param extensionEF extension EF fileid
123     * @param recordNumber 1-based record index
124     * @param pin2 for CHV2 operations, must be null if pin2 is not needed
125     * @param response will be sent to its handler when completed
126     */
127    public void
128    updateEF(AdnRecord adn, int ef, int extensionEF, int recordNumber,
129            String pin2, Message response) {
130        mEf = ef;
131        mExtensionEF = extensionEF;
132        mRecordNumber = recordNumber;
133        mUserResponse = response;
134        mPin2 = pin2;
135
136        mFh.getEFLinearRecordSize( ef, getEFPath(ef),
137                obtainMessage(EVENT_EF_LINEAR_RECORD_SIZE_DONE, adn));
138     }
139
140    //***** Overridden from Handler
141
142    @Override
143    public void
144    handleMessage(Message msg) {
145        AsyncResult ar;
146        byte data[];
147        AdnRecord adn;
148
149        try {
150            switch (msg.what) {
151                case EVENT_EF_LINEAR_RECORD_SIZE_DONE:
152                    ar = (AsyncResult)(msg.obj);
153                    adn = (AdnRecord)(ar.userObj);
154
155                    if (ar.exception != null) {
156                        throw new RuntimeException("get EF record size failed",
157                                ar.exception);
158                    }
159
160                    int[] recordSize = (int[])ar.result;
161                    // recordSize is int[3] array
162                    // int[0]  is the record length
163                    // int[1]  is the total length of the EF file
164                    // int[2]  is the number of records in the EF file
165                    // So int[0] * int[2] = int[1]
166                   if (recordSize.length != 3 || mRecordNumber > recordSize[2]) {
167                        throw new RuntimeException("get wrong EF record size format",
168                                ar.exception);
169                    }
170
171                    data = adn.buildAdnString(recordSize[0]);
172
173                    if(data == null) {
174                        throw new RuntimeException("wrong ADN format",
175                                ar.exception);
176                    }
177
178
179                    mFh.updateEFLinearFixed(mEf, getEFPath(mEf), mRecordNumber,
180                            data, mPin2, obtainMessage(EVENT_UPDATE_RECORD_DONE));
181
182                    mPendingExtLoads = 1;
183
184                    break;
185                case EVENT_UPDATE_RECORD_DONE:
186                    ar = (AsyncResult)(msg.obj);
187                    if (ar.exception != null) {
188                        throw new RuntimeException("update EF adn record failed",
189                                ar.exception);
190                    }
191                    mPendingExtLoads = 0;
192                    mResult = null;
193                    break;
194                case EVENT_ADN_LOAD_DONE:
195                    ar = (AsyncResult)(msg.obj);
196                    data = (byte[])(ar.result);
197
198                    if (ar.exception != null) {
199                        throw new RuntimeException("load failed", ar.exception);
200                    }
201
202                    if (VDBG) {
203                        Rlog.d(LOG_TAG,"ADN EF: 0x"
204                            + Integer.toHexString(mEf)
205                            + ":" + mRecordNumber
206                            + "\n" + IccUtils.bytesToHexString(data));
207                    }
208
209                    adn = new AdnRecord(mEf, mRecordNumber, data);
210                    mResult = adn;
211
212                    if (adn.hasExtendedRecord()) {
213                        // If we have a valid value in the ext record field,
214                        // we're not done yet: we need to read the corresponding
215                        // ext record and append it
216
217                        mPendingExtLoads = 1;
218
219                        mFh.loadEFLinearFixed(
220                            mExtensionEF, adn.mExtRecord,
221                            obtainMessage(EVENT_EXT_RECORD_LOAD_DONE, adn));
222                    }
223                break;
224
225                case EVENT_EXT_RECORD_LOAD_DONE:
226                    ar = (AsyncResult)(msg.obj);
227                    data = (byte[])(ar.result);
228                    adn = (AdnRecord)(ar.userObj);
229
230                    if (ar.exception == null) {
231                        Rlog.d(LOG_TAG,"ADN extension EF: 0x"
232                                + Integer.toHexString(mExtensionEF)
233                                + ":" + adn.mExtRecord
234                                + "\n" + IccUtils.bytesToHexString(data));
235
236                        adn.appendExtRecord(data);
237                    }
238                    else {
239                        // If we can't get the rest of the number from EF_EXT1, rather than
240                        // providing the partial number, we clear the number since it's not
241                        // dialable anyway. Do not throw exception here otherwise the rest
242                        // of the good records will be dropped.
243
244                        Rlog.e(LOG_TAG, "Failed to read ext record. Clear the number now.");
245                        adn.setNumber("");
246                    }
247
248                    mPendingExtLoads--;
249                    // result should have been set in
250                    // EVENT_ADN_LOAD_DONE or EVENT_ADN_LOAD_ALL_DONE
251                break;
252
253                case EVENT_ADN_LOAD_ALL_DONE:
254                    ar = (AsyncResult)(msg.obj);
255                    ArrayList<byte[]> datas = (ArrayList<byte[]>)(ar.result);
256
257                    if (ar.exception != null) {
258                        throw new RuntimeException("load failed", ar.exception);
259                    }
260
261                    mAdns = new ArrayList<AdnRecord>(datas.size());
262                    mResult = mAdns;
263                    mPendingExtLoads = 0;
264
265                    for(int i = 0, s = datas.size() ; i < s ; i++) {
266                        adn = new AdnRecord(mEf, 1 + i, datas.get(i));
267                        mAdns.add(adn);
268
269                        if (adn.hasExtendedRecord()) {
270                            // If we have a valid value in the ext record field,
271                            // we're not done yet: we need to read the corresponding
272                            // ext record and append it
273
274                            mPendingExtLoads++;
275
276                            mFh.loadEFLinearFixed(
277                                mExtensionEF, adn.mExtRecord,
278                                obtainMessage(EVENT_EXT_RECORD_LOAD_DONE, adn));
279                        }
280                    }
281                break;
282            }
283        } catch (RuntimeException exc) {
284            if (mUserResponse != null) {
285                AsyncResult.forMessage(mUserResponse)
286                                .exception = exc;
287                mUserResponse.sendToTarget();
288                // Loading is all or nothing--either every load succeeds
289                // or we fail the whole thing.
290                mUserResponse = null;
291            }
292            return;
293        }
294
295        if (mUserResponse != null && mPendingExtLoads == 0) {
296            AsyncResult.forMessage(mUserResponse).result
297                = mResult;
298
299            mUserResponse.sendToTarget();
300            mUserResponse = null;
301        }
302    }
303}
304