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 
17 package com.android.internal.telephony.uicc;
18 
19 import java.util.ArrayList;
20 
21 import android.os.AsyncResult;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.Message;
25 import android.telephony.Rlog;
26 
27 import com.android.internal.telephony.uicc.IccConstants;
28 
29 public 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 
AdnRecordLoader(IccFileHandler fh)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 
getEFPath(int efid)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
loadFromEF(int ef, int extensionEF, int recordNumber, Message response)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
loadAllFromEF(int ef, int extensionEF, Message response)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
updateEF(AdnRecord adn, int ef, int extensionEF, int recordNumber, String pin2, Message response)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
handleMessage(Message msg)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                         throw new RuntimeException("load failed", ar.exception);
232                     }
233 
234                     Rlog.d(LOG_TAG,"ADN extension EF: 0x"
235                         + Integer.toHexString(mExtensionEF)
236                         + ":" + adn.mExtRecord
237                         + "\n" + IccUtils.bytesToHexString(data));
238 
239                     adn.appendExtRecord(data);
240 
241                     mPendingExtLoads--;
242                     // result should have been set in
243                     // EVENT_ADN_LOAD_DONE or EVENT_ADN_LOAD_ALL_DONE
244                 break;
245 
246                 case EVENT_ADN_LOAD_ALL_DONE:
247                     ar = (AsyncResult)(msg.obj);
248                     ArrayList<byte[]> datas = (ArrayList<byte[]>)(ar.result);
249 
250                     if (ar.exception != null) {
251                         throw new RuntimeException("load failed", ar.exception);
252                     }
253 
254                     mAdns = new ArrayList<AdnRecord>(datas.size());
255                     mResult = mAdns;
256                     mPendingExtLoads = 0;
257 
258                     for(int i = 0, s = datas.size() ; i < s ; i++) {
259                         adn = new AdnRecord(mEf, 1 + i, datas.get(i));
260                         mAdns.add(adn);
261 
262                         if (adn.hasExtendedRecord()) {
263                             // If we have a valid value in the ext record field,
264                             // we're not done yet: we need to read the corresponding
265                             // ext record and append it
266 
267                             mPendingExtLoads++;
268 
269                             mFh.loadEFLinearFixed(
270                                 mExtensionEF, adn.mExtRecord,
271                                 obtainMessage(EVENT_EXT_RECORD_LOAD_DONE, adn));
272                         }
273                     }
274                 break;
275             }
276         } catch (RuntimeException exc) {
277             if (mUserResponse != null) {
278                 AsyncResult.forMessage(mUserResponse)
279                                 .exception = exc;
280                 mUserResponse.sendToTarget();
281                 // Loading is all or nothing--either every load succeeds
282                 // or we fail the whole thing.
283                 mUserResponse = null;
284             }
285             return;
286         }
287 
288         if (mUserResponse != null && mPendingExtLoads == 0) {
289             AsyncResult.forMessage(mUserResponse).result
290                 = mResult;
291 
292             mUserResponse.sendToTarget();
293             mUserResponse = null;
294         }
295     }
296 
297 
298 }
299