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