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