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    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        oldAdnList = getRecordsIfLoaded(efid);
190
191        if (oldAdnList == null) {
192            sendErrorResponse(response, "Adn list not exist for EF:" + efid);
193            return;
194        }
195
196        int index = -1;
197        int count = 1;
198        for (Iterator<AdnRecord> it = oldAdnList.iterator(); it.hasNext(); ) {
199            if (oldAdn.isEqual(it.next())) {
200                index = count;
201                break;
202            }
203            count++;
204        }
205
206        if (index == -1) {
207            sendErrorResponse(response, "Adn record don't exist for " + oldAdn);
208            return;
209        }
210
211        Message pendingResponse = userWriteResponse.get(efid);
212
213        if (pendingResponse != null) {
214            sendErrorResponse(response, "Have pending update for EF:" + efid);
215            return;
216        }
217
218        userWriteResponse.put(efid, response);
219
220        new AdnRecordLoader(phone).updateEF(newAdn, efid, extensionEF,
221                index, pin2,
222                obtainMessage(EVENT_UPDATE_ADN_DONE, efid, index, newAdn));
223    }
224
225
226    /**
227     * Responds with exception (in response) if efid is not a known ADN-like
228     * record
229     */
230    public void
231    requestLoadAllAdnLike (int efid, int extensionEf, Message response) {
232        ArrayList<Message> waiters;
233        ArrayList<AdnRecord> result;
234
235        if (efid == EF_PBR) {
236            result = mUsimPhoneBookManager.loadEfFilesFromUsim();
237        } else {
238            result = getRecordsIfLoaded(efid);
239        }
240
241        // Have we already loaded this efid?
242        if (result != null) {
243            if (response != null) {
244                AsyncResult.forMessage(response).result = result;
245                response.sendToTarget();
246            }
247
248            return;
249        }
250
251        // Have we already *started* loading this efid?
252
253        waiters = adnLikeWaiters.get(efid);
254
255        if (waiters != null) {
256            // There's a pending request for this EF already
257            // just add ourselves to it
258
259            waiters.add(response);
260            return;
261        }
262
263        // Start loading efid
264
265        waiters = new ArrayList<Message>();
266        waiters.add(response);
267
268        adnLikeWaiters.put(efid, waiters);
269
270
271        if (extensionEf < 0) {
272            // respond with error if not known ADN-like record
273
274            if (response != null) {
275                AsyncResult.forMessage(response).exception
276                    = new RuntimeException("EF is not known ADN-like EF:" + efid);
277                response.sendToTarget();
278            }
279
280            return;
281        }
282
283        new AdnRecordLoader(phone).loadAllFromEF(efid, extensionEf,
284            obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0));
285    }
286
287    //***** Private methods
288
289    private void
290    notifyWaiters(ArrayList<Message> waiters, AsyncResult ar) {
291
292        if (waiters == null) {
293            return;
294        }
295
296        for (int i = 0, s = waiters.size() ; i < s ; i++) {
297            Message waiter = waiters.get(i);
298
299            AsyncResult.forMessage(waiter, ar.result, ar.exception);
300            waiter.sendToTarget();
301        }
302    }
303
304    //***** Overridden from Handler
305
306    public void
307    handleMessage(Message msg) {
308        AsyncResult ar;
309        int efid;
310
311        switch(msg.what) {
312            case EVENT_LOAD_ALL_ADN_LIKE_DONE:
313                /* arg1 is efid, obj.result is ArrayList<AdnRecord>*/
314                ar = (AsyncResult) msg.obj;
315                efid = msg.arg1;
316                ArrayList<Message> waiters;
317
318                waiters = adnLikeWaiters.get(efid);
319                adnLikeWaiters.delete(efid);
320
321                if (ar.exception == null) {
322                    adnLikeFiles.put(efid, (ArrayList<AdnRecord>) ar.result);
323                }
324                notifyWaiters(waiters, ar);
325                break;
326            case EVENT_UPDATE_ADN_DONE:
327                ar = (AsyncResult)msg.obj;
328                efid = msg.arg1;
329                int index = msg.arg2;
330                AdnRecord adn = (AdnRecord) (ar.userObj);
331
332                if (ar.exception == null) {
333                    adnLikeFiles.get(efid).set(index - 1, adn);
334                }
335
336                Message response = userWriteResponse.get(efid);
337                userWriteResponse.delete(efid);
338
339                AsyncResult.forMessage(response, null, ar.exception);
340                response.sendToTarget();
341                break;
342        }
343
344    }
345
346
347}
348