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