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;
18
19import android.os.AsyncResult;
20import android.os.Handler;
21import android.os.Message;
22import android.util.Log;
23import android.util.SparseArray;
24
25import com.android.internal.telephony.gsm.UsimPhoneBookManager;
26
27import java.util.ArrayList;
28import java.util.Iterator;
29
30/**
31 * {@hide}
32 */
33public final class AdnRecordCache extends Handler implements IccConstants {
34    //***** Instance Variables
35
36    private IccFileHandler mFh;
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(IccFileHandler fh) {
60        mFh = fh;
61        mUsimPhoneBookManager = new UsimPhoneBookManager(mFh, 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(mFh).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(mFh).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(mFh).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