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.Message; 23 import android.util.SparseArray; 24 25 import com.android.internal.telephony.gsm.UsimPhoneBookManager; 26 27 import java.util.ArrayList; 28 import java.util.Iterator; 29 30 /** 31 * {@hide} 32 */ 33 public class AdnRecordCache extends Handler implements IccConstants { 34 //***** Instance Variables 35 36 @UnsupportedAppUsage 37 private IccFileHandler mFh; 38 @UnsupportedAppUsage 39 private UsimPhoneBookManager mUsimPhoneBookManager; 40 41 // Indexed by EF ID 42 SparseArray<ArrayList<AdnRecord>> mAdnLikeFiles 43 = new SparseArray<ArrayList<AdnRecord>>(); 44 45 // People waiting for ADN-like files to be loaded 46 @UnsupportedAppUsage 47 SparseArray<ArrayList<Message>> mAdnLikeWaiters 48 = new SparseArray<ArrayList<Message>>(); 49 50 // People waiting for adn record to be updated 51 @UnsupportedAppUsage 52 SparseArray<Message> mUserWriteResponse = new SparseArray<Message>(); 53 54 //***** Event Constants 55 56 static final int EVENT_LOAD_ALL_ADN_LIKE_DONE = 1; 57 static final int EVENT_UPDATE_ADN_DONE = 2; 58 59 //***** Constructor 60 61 62 AdnRecordCache(IccFileHandler fh)63 AdnRecordCache(IccFileHandler fh) { 64 mFh = fh; 65 mUsimPhoneBookManager = new UsimPhoneBookManager(mFh, this); 66 } 67 68 //***** Called from SIMRecords 69 70 /** 71 * Called from SIMRecords.onRadioNotAvailable and SIMRecords.handleSimRefresh. 72 */ 73 @UnsupportedAppUsage reset()74 public void reset() { 75 mAdnLikeFiles.clear(); 76 mUsimPhoneBookManager.reset(); 77 78 clearWaiters(); 79 clearUserWriters(); 80 81 } 82 clearWaiters()83 private void clearWaiters() { 84 int size = mAdnLikeWaiters.size(); 85 for (int i = 0; i < size; i++) { 86 ArrayList<Message> waiters = mAdnLikeWaiters.valueAt(i); 87 AsyncResult ar = new AsyncResult(null, null, new RuntimeException("AdnCache reset")); 88 notifyWaiters(waiters, ar); 89 } 90 mAdnLikeWaiters.clear(); 91 } 92 clearUserWriters()93 private void clearUserWriters() { 94 int size = mUserWriteResponse.size(); 95 for (int i = 0; i < size; i++) { 96 sendErrorResponse(mUserWriteResponse.valueAt(i), "AdnCace reset"); 97 } 98 mUserWriteResponse.clear(); 99 } 100 101 /** 102 * @return List of AdnRecords for efid if we've already loaded them this 103 * radio session, or null if we haven't 104 */ 105 @UnsupportedAppUsage 106 public ArrayList<AdnRecord> getRecordsIfLoaded(int efid)107 getRecordsIfLoaded(int efid) { 108 return mAdnLikeFiles.get(efid); 109 } 110 111 /** 112 * Returns extension ef associated with ADN-like EF or -1 if 113 * we don't know. 114 * 115 * See 3GPP TS 51.011 for this mapping 116 */ 117 @UnsupportedAppUsage extensionEfForEf(int efid)118 public int extensionEfForEf(int efid) { 119 switch (efid) { 120 case EF_MBDN: return EF_EXT6; 121 case EF_ADN: return EF_EXT1; 122 case EF_SDN: return EF_EXT3; 123 case EF_FDN: return EF_EXT2; 124 case EF_MSISDN: return EF_EXT1; 125 case EF_PBR: return 0; // The EF PBR doesn't have an extension record 126 default: return -1; 127 } 128 } 129 130 @UnsupportedAppUsage sendErrorResponse(Message response, String errString)131 private void sendErrorResponse(Message response, String errString) { 132 if (response != null) { 133 Exception e = new RuntimeException(errString); 134 AsyncResult.forMessage(response).exception = e; 135 response.sendToTarget(); 136 } 137 } 138 139 /** 140 * Update an ADN-like record in EF by record index 141 * 142 * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN 143 * @param adn is the new adn to be stored 144 * @param recordIndex is the 1-based adn record index 145 * @param pin2 is required to update EF_FDN, otherwise must be null 146 * @param response message to be posted when done 147 * response.exception hold the exception in error 148 */ 149 @UnsupportedAppUsage updateAdnByIndex(int efid, AdnRecord adn, int recordIndex, String pin2, Message response)150 public void updateAdnByIndex(int efid, AdnRecord adn, int recordIndex, String pin2, 151 Message response) { 152 153 int extensionEF = extensionEfForEf(efid); 154 if (extensionEF < 0) { 155 sendErrorResponse(response, "EF is not known ADN-like EF:0x" + 156 Integer.toHexString(efid).toUpperCase()); 157 return; 158 } 159 160 Message pendingResponse = mUserWriteResponse.get(efid); 161 if (pendingResponse != null) { 162 sendErrorResponse(response, "Have pending update for EF:0x" + 163 Integer.toHexString(efid).toUpperCase()); 164 return; 165 } 166 167 mUserWriteResponse.put(efid, response); 168 169 new AdnRecordLoader(mFh).updateEF(adn, efid, extensionEF, 170 recordIndex, pin2, 171 obtainMessage(EVENT_UPDATE_ADN_DONE, efid, recordIndex, adn)); 172 } 173 174 /** 175 * Replace oldAdn with newAdn in ADN-like record in EF 176 * 177 * The ADN-like records must be read through requestLoadAllAdnLike() before 178 * 179 * @param efid must be one of EF_ADN, EF_FDN, and EF_SDN 180 * @param oldAdn is the adn to be replaced 181 * If oldAdn.isEmpty() is ture, it insert the newAdn 182 * @param newAdn is the adn to be stored 183 * If newAdn.isEmpty() is true, it delete the oldAdn 184 * @param pin2 is required to update EF_FDN, otherwise must be null 185 * @param response message to be posted when done 186 * response.exception hold the exception in error 187 */ updateAdnBySearch(int efid, AdnRecord oldAdn, AdnRecord newAdn, String pin2, Message response)188 public void updateAdnBySearch(int efid, AdnRecord oldAdn, AdnRecord newAdn, 189 String pin2, Message response) { 190 191 int extensionEF; 192 extensionEF = extensionEfForEf(efid); 193 194 if (extensionEF < 0) { 195 sendErrorResponse(response, "EF is not known ADN-like EF:0x" + 196 Integer.toHexString(efid).toUpperCase()); 197 return; 198 } 199 200 ArrayList<AdnRecord> oldAdnList; 201 202 if (efid == EF_PBR) { 203 oldAdnList = mUsimPhoneBookManager.loadEfFilesFromUsim(); 204 } else { 205 oldAdnList = getRecordsIfLoaded(efid); 206 } 207 208 if (oldAdnList == null) { 209 sendErrorResponse(response, "Adn list not exist for EF:0x" + 210 Integer.toHexString(efid).toUpperCase()); 211 return; 212 } 213 214 int index = -1; 215 int count = 1; 216 for (Iterator<AdnRecord> it = oldAdnList.iterator(); it.hasNext(); ) { 217 if (oldAdn.isEqual(it.next())) { 218 index = count; 219 break; 220 } 221 count++; 222 } 223 224 if (index == -1) { 225 sendErrorResponse(response, "Adn record don't exist for " + oldAdn); 226 return; 227 } 228 229 if (efid == EF_PBR) { 230 AdnRecord foundAdn = oldAdnList.get(index-1); 231 efid = foundAdn.mEfid; 232 extensionEF = foundAdn.mExtRecord; 233 index = foundAdn.mRecordNumber; 234 235 newAdn.mEfid = efid; 236 newAdn.mExtRecord = extensionEF; 237 newAdn.mRecordNumber = index; 238 } 239 240 Message pendingResponse = mUserWriteResponse.get(efid); 241 242 if (pendingResponse != null) { 243 sendErrorResponse(response, "Have pending update for EF:0x" + 244 Integer.toHexString(efid).toUpperCase()); 245 return; 246 } 247 248 mUserWriteResponse.put(efid, response); 249 250 new AdnRecordLoader(mFh).updateEF(newAdn, efid, extensionEF, 251 index, pin2, 252 obtainMessage(EVENT_UPDATE_ADN_DONE, efid, index, newAdn)); 253 } 254 255 256 /** 257 * Responds with exception (in response) if efid is not a known ADN-like 258 * record 259 */ 260 public void requestLoadAllAdnLike(int efid, int extensionEf, Message response)261 requestLoadAllAdnLike (int efid, int extensionEf, Message response) { 262 ArrayList<Message> waiters; 263 ArrayList<AdnRecord> result; 264 265 if (efid == EF_PBR) { 266 result = mUsimPhoneBookManager.loadEfFilesFromUsim(); 267 } else { 268 result = getRecordsIfLoaded(efid); 269 } 270 271 // Have we already loaded this efid? 272 if (result != null) { 273 if (response != null) { 274 AsyncResult.forMessage(response).result = result; 275 response.sendToTarget(); 276 } 277 278 return; 279 } 280 281 // Have we already *started* loading this efid? 282 283 waiters = mAdnLikeWaiters.get(efid); 284 285 if (waiters != null) { 286 // There's a pending request for this EF already 287 // just add ourselves to it 288 289 waiters.add(response); 290 return; 291 } 292 293 // Start loading efid 294 295 waiters = new ArrayList<Message>(); 296 waiters.add(response); 297 298 mAdnLikeWaiters.put(efid, waiters); 299 300 301 if (extensionEf < 0) { 302 // respond with error if not known ADN-like record 303 304 if (response != null) { 305 AsyncResult.forMessage(response).exception 306 = new RuntimeException("EF is not known ADN-like EF:0x" + 307 Integer.toHexString(efid).toUpperCase()); 308 response.sendToTarget(); 309 } 310 311 return; 312 } 313 314 new AdnRecordLoader(mFh).loadAllFromEF(efid, extensionEf, 315 obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0)); 316 } 317 318 //***** Private methods 319 320 private void notifyWaiters(ArrayList<Message> waiters, AsyncResult ar)321 notifyWaiters(ArrayList<Message> waiters, AsyncResult ar) { 322 323 if (waiters == null) { 324 return; 325 } 326 327 for (int i = 0, s = waiters.size() ; i < s ; i++) { 328 Message waiter = waiters.get(i); 329 330 AsyncResult.forMessage(waiter, ar.result, ar.exception); 331 waiter.sendToTarget(); 332 } 333 } 334 335 //***** Overridden from Handler 336 337 @Override 338 public void handleMessage(Message msg)339 handleMessage(Message msg) { 340 AsyncResult ar; 341 int efid; 342 343 switch(msg.what) { 344 case EVENT_LOAD_ALL_ADN_LIKE_DONE: 345 /* arg1 is efid, obj.result is ArrayList<AdnRecord>*/ 346 ar = (AsyncResult) msg.obj; 347 efid = msg.arg1; 348 ArrayList<Message> waiters; 349 350 waiters = mAdnLikeWaiters.get(efid); 351 mAdnLikeWaiters.delete(efid); 352 353 if (ar.exception == null) { 354 mAdnLikeFiles.put(efid, (ArrayList<AdnRecord>) ar.result); 355 } 356 notifyWaiters(waiters, ar); 357 break; 358 case EVENT_UPDATE_ADN_DONE: 359 ar = (AsyncResult)msg.obj; 360 efid = msg.arg1; 361 int index = msg.arg2; 362 AdnRecord adn = (AdnRecord) (ar.userObj); 363 364 if (ar.exception == null) { 365 mAdnLikeFiles.get(efid).set(index - 1, adn); 366 mUsimPhoneBookManager.invalidateCache(); 367 } 368 369 Message response = mUserWriteResponse.get(efid); 370 mUserWriteResponse.delete(efid); 371 372 // response may be cleared when simrecord is reset, 373 // so we should check if it is null. 374 if (response != null) { 375 AsyncResult.forMessage(response, null, ar.exception); 376 response.sendToTarget(); 377 } 378 break; 379 } 380 } 381 } 382