Home | History | Annotate | Download | only in uicc
      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 final 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 
     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      */
     68     public void reset() {
     69         mAdnLikeFiles.clear();
     70         mUsimPhoneBookManager.reset();
     71 
     72         clearWaiters();
     73         clearUserWriters();
     74 
     75     }
     76 
     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 
     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>
    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      */
    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 
    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      */
    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:" + efid);
    146             return;
    147         }
    148 
    149         Message pendingResponse = mUserWriteResponse.get(efid);
    150         if (pendingResponse != null) {
    151             sendErrorResponse(response, "Have pending update for EF:" + efid);
    152             return;
    153         }
    154 
    155         mUserWriteResponse.put(efid, response);
    156 
    157         new AdnRecordLoader(mFh).updateEF(adn, efid, extensionEF,
    158                 recordIndex, pin2,
    159                 obtainMessage(EVENT_UPDATE_ADN_DONE, efid, recordIndex, adn));
    160     }
    161 
    162     /**
    163      * Replace oldAdn with newAdn in ADN-like record in EF
    164      *
    165      * The ADN-like records must be read through requestLoadAllAdnLike() before
    166      *
    167      * @param efid must be one of EF_ADN, EF_FDN, and EF_SDN
    168      * @param oldAdn is the adn to be replaced
    169      *        If oldAdn.isEmpty() is ture, it insert the newAdn
    170      * @param newAdn is the adn to be stored
    171      *        If newAdn.isEmpty() is true, it delete the oldAdn
    172      * @param pin2 is required to update EF_FDN, otherwise must be null
    173      * @param response message to be posted when done
    174      *        response.exception hold the exception in error
    175      */
    176     public void updateAdnBySearch(int efid, AdnRecord oldAdn, AdnRecord newAdn,
    177             String pin2, Message response) {
    178 
    179         int extensionEF;
    180         extensionEF = extensionEfForEf(efid);
    181 
    182         if (extensionEF < 0) {
    183             sendErrorResponse(response, "EF is not known ADN-like EF:" + efid);
    184             return;
    185         }
    186 
    187         ArrayList<AdnRecord>  oldAdnList;
    188 
    189         if (efid == EF_PBR) {
    190             oldAdnList = mUsimPhoneBookManager.loadEfFilesFromUsim();
    191         } else {
    192             oldAdnList = getRecordsIfLoaded(efid);
    193         }
    194 
    195         if (oldAdnList == null) {
    196             sendErrorResponse(response, "Adn list not exist for EF:" + efid);
    197             return;
    198         }
    199 
    200         int index = -1;
    201         int count = 1;
    202         for (Iterator<AdnRecord> it = oldAdnList.iterator(); it.hasNext(); ) {
    203             if (oldAdn.isEqual(it.next())) {
    204                 index = count;
    205                 break;
    206             }
    207             count++;
    208         }
    209 
    210         if (index == -1) {
    211             sendErrorResponse(response, "Adn record don't exist for " + oldAdn);
    212             return;
    213         }
    214 
    215         if (efid == EF_PBR) {
    216             AdnRecord foundAdn = oldAdnList.get(index-1);
    217             efid = foundAdn.mEfid;
    218             extensionEF = foundAdn.mExtRecord;
    219             index = foundAdn.mRecordNumber;
    220 
    221             newAdn.mEfid = efid;
    222             newAdn.mExtRecord = extensionEF;
    223             newAdn.mRecordNumber = index;
    224         }
    225 
    226         Message pendingResponse = mUserWriteResponse.get(efid);
    227 
    228         if (pendingResponse != null) {
    229             sendErrorResponse(response, "Have pending update for EF:" + efid);
    230             return;
    231         }
    232 
    233         mUserWriteResponse.put(efid, response);
    234 
    235         new AdnRecordLoader(mFh).updateEF(newAdn, efid, extensionEF,
    236                 index, pin2,
    237                 obtainMessage(EVENT_UPDATE_ADN_DONE, efid, index, newAdn));
    238     }
    239 
    240 
    241     /**
    242      * Responds with exception (in response) if efid is not a known ADN-like
    243      * record
    244      */
    245     public void
    246     requestLoadAllAdnLike (int efid, int extensionEf, Message response) {
    247         ArrayList<Message> waiters;
    248         ArrayList<AdnRecord> result;
    249 
    250         if (efid == EF_PBR) {
    251             result = mUsimPhoneBookManager.loadEfFilesFromUsim();
    252         } else {
    253             result = getRecordsIfLoaded(efid);
    254         }
    255 
    256         // Have we already loaded this efid?
    257         if (result != null) {
    258             if (response != null) {
    259                 AsyncResult.forMessage(response).result = result;
    260                 response.sendToTarget();
    261             }
    262 
    263             return;
    264         }
    265 
    266         // Have we already *started* loading this efid?
    267 
    268         waiters = mAdnLikeWaiters.get(efid);
    269 
    270         if (waiters != null) {
    271             // There's a pending request for this EF already
    272             // just add ourselves to it
    273 
    274             waiters.add(response);
    275             return;
    276         }
    277 
    278         // Start loading efid
    279 
    280         waiters = new ArrayList<Message>();
    281         waiters.add(response);
    282 
    283         mAdnLikeWaiters.put(efid, waiters);
    284 
    285 
    286         if (extensionEf < 0) {
    287             // respond with error if not known ADN-like record
    288 
    289             if (response != null) {
    290                 AsyncResult.forMessage(response).exception
    291                     = new RuntimeException("EF is not known ADN-like EF:" + efid);
    292                 response.sendToTarget();
    293             }
    294 
    295             return;
    296         }
    297 
    298         new AdnRecordLoader(mFh).loadAllFromEF(efid, extensionEf,
    299             obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0));
    300     }
    301 
    302     //***** Private methods
    303 
    304     private void
    305     notifyWaiters(ArrayList<Message> waiters, AsyncResult ar) {
    306 
    307         if (waiters == null) {
    308             return;
    309         }
    310 
    311         for (int i = 0, s = waiters.size() ; i < s ; i++) {
    312             Message waiter = waiters.get(i);
    313 
    314             AsyncResult.forMessage(waiter, ar.result, ar.exception);
    315             waiter.sendToTarget();
    316         }
    317     }
    318 
    319     //***** Overridden from Handler
    320 
    321     @Override
    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 = mAdnLikeWaiters.get(efid);
    335                 mAdnLikeWaiters.delete(efid);
    336 
    337                 if (ar.exception == null) {
    338                     mAdnLikeFiles.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                     mAdnLikeFiles.get(efid).set(index - 1, adn);
    350                     mUsimPhoneBookManager.invalidateCache();
    351                 }
    352 
    353                 Message response = mUserWriteResponse.get(efid);
    354                 mUserWriteResponse.delete(efid);
    355 
    356                 AsyncResult.forMessage(response, null, ar.exception);
    357                 response.sendToTarget();
    358                 break;
    359         }
    360 
    361     }
    362 
    363 
    364 }
    365