AdnRecordCache.java revision cbaa45bbf2cab852b6c9c3a887e9f803d4e857ea
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
17package com.android.internal.telephony.uicc;
18
19import android.os.AsyncResult;
20import android.os.Handler;
21import android.os.Message;
22import android.util.SparseArray;
23
24import com.android.internal.telephony.gsm.UsimPhoneBookManager;
25
26import java.util.ArrayList;
27import java.util.Iterator;
28
29/**
30 * {@hide}
31 */
32public 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>> adnLikeFiles
40        = new SparseArray<ArrayList<AdnRecord>>();
41
42    // People waiting for ADN-like files to be loaded
43    SparseArray<ArrayList<Message>> adnLikeWaiters
44        = new SparseArray<ArrayList<Message>>();
45
46    // People waiting for adn record to be updated
47    SparseArray<Message> userWriteResponse = 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        adnLikeFiles.clear();
70        mUsimPhoneBookManager.reset();
71
72        clearWaiters();
73        clearUserWriters();
74
75    }
76
77    private void clearWaiters() {
78        int size = adnLikeWaiters.size();
79        for (int i = 0; i < size; i++) {
80            ArrayList<Message> waiters = adnLikeWaiters.valueAt(i);
81            AsyncResult ar = new AsyncResult(null, null, new RuntimeException("AdnCache reset"));
82            notifyWaiters(waiters, ar);
83        }
84        adnLikeWaiters.clear();
85    }
86
87    private void clearUserWriters() {
88        int size = userWriteResponse.size();
89        for (int i = 0; i < size; i++) {
90            sendErrorResponse(userWriteResponse.valueAt(i), "AdnCace reset");
91        }
92        userWriteResponse.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 adnLikeFiles.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 = userWriteResponse.get(efid);
150        if (pendingResponse != null) {
151            sendErrorResponse(response, "Have pending update for EF:" + efid);
152            return;
153        }
154
155        userWriteResponse.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.efid;
218            extensionEF = foundAdn.extRecord;
219            index = foundAdn.recordNumber;
220
221            newAdn.efid = efid;
222            newAdn.extRecord = extensionEF;
223            newAdn.recordNumber = index;
224        }
225
226        Message pendingResponse = userWriteResponse.get(efid);
227
228        if (pendingResponse != null) {
229            sendErrorResponse(response, "Have pending update for EF:" + efid);
230            return;
231        }
232
233        userWriteResponse.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 = adnLikeWaiters.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        adnLikeWaiters.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 = 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