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