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