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<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