IccPhoneBookInterfaceManager.java revision a8467dd0c524787104b1ccdddc5e8af10ba729ed
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.content.pm.PackageManager;
20import android.os.AsyncResult;
21import android.os.Handler;
22import android.os.Looper;
23import android.os.Message;
24import android.os.ServiceManager;
25
26import com.android.internal.telephony.uicc.AdnRecord;
27import com.android.internal.telephony.uicc.AdnRecordCache;
28import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
29import com.android.internal.telephony.uicc.IccConstants;
30import com.android.internal.telephony.uicc.IccRecords;
31import com.android.internal.telephony.uicc.UiccCard;
32import com.android.internal.telephony.uicc.UiccCardApplication;
33
34import java.util.List;
35import java.util.concurrent.atomic.AtomicBoolean;
36
37/**
38 * SimPhoneBookInterfaceManager to provide an inter-process communication to
39 * access ADN-like SIM records.
40 */
41public abstract class IccPhoneBookInterfaceManager {
42    protected static final boolean DBG = true;
43
44    protected PhoneBase mPhone;
45    private   UiccCardApplication mCurrentApp = null;
46    protected AdnRecordCache mAdnCache;
47    protected final Object mLock = new Object();
48    protected int mRecordSize[];
49    protected boolean mSuccess;
50    private   boolean mIs3gCard = false;  // flag to determine if card is 3G or 2G
51    protected List<AdnRecord> mRecords;
52
53
54    protected static final boolean ALLOW_SIM_OP_IN_UI_THREAD = false;
55
56    protected static final int EVENT_GET_SIZE_DONE = 1;
57    protected static final int EVENT_LOAD_DONE = 2;
58    protected static final int EVENT_UPDATE_DONE = 3;
59
60    protected Handler mBaseHandler = new Handler() {
61        @Override
62        public void handleMessage(Message msg) {
63            AsyncResult ar;
64
65            switch (msg.what) {
66                case EVENT_GET_SIZE_DONE:
67                    ar = (AsyncResult) msg.obj;
68                    synchronized (mLock) {
69                        if (ar.exception == null) {
70                            mRecordSize = (int[])ar.result;
71                            // recordSize[0]  is the record length
72                            // recordSize[1]  is the total length of the EF file
73                            // recordSize[2]  is the number of records in the EF file
74                            logd("GET_RECORD_SIZE Size " + mRecordSize[0] +
75                                    " total " + mRecordSize[1] +
76                                    " #record " + mRecordSize[2]);
77                        }
78                        notifyPending(ar);
79                    }
80                    break;
81                case EVENT_UPDATE_DONE:
82                    ar = (AsyncResult) msg.obj;
83                    synchronized (mLock) {
84                        mSuccess = (ar.exception == null);
85                        notifyPending(ar);
86                    }
87                    break;
88                case EVENT_LOAD_DONE:
89                    ar = (AsyncResult)msg.obj;
90                    synchronized (mLock) {
91                        if (ar.exception == null) {
92                            mRecords = (List<AdnRecord>) ar.result;
93                        } else {
94                            if(DBG) logd("Cannot load ADN records");
95                            if (mRecords != null) {
96                                mRecords.clear();
97                            }
98                        }
99                        notifyPending(ar);
100                    }
101                    break;
102            }
103        }
104
105        private void notifyPending(AsyncResult ar) {
106            if (ar.userObj == null) {
107                return;
108            }
109            AtomicBoolean status = (AtomicBoolean) ar.userObj;
110            status.set(true);
111            mLock.notifyAll();
112        }
113    };
114
115    public IccPhoneBookInterfaceManager(PhoneBase phone) {
116        this.mPhone = phone;
117        IccRecords r = phone.mIccRecords.get();
118        if (r != null) {
119            mAdnCache = r.getAdnCache();
120        }
121    }
122
123    public void dispose() {
124    }
125
126    public void updateIccRecords(IccRecords iccRecords) {
127        if (iccRecords != null) {
128            mAdnCache = iccRecords.getAdnCache();
129        } else {
130            mAdnCache = null;
131        }
132    }
133
134    protected abstract void logd(String msg);
135
136    protected abstract void loge(String msg);
137
138    /**
139     * Replace oldAdn with newAdn in ADN-like record in EF
140     *
141     * getAdnRecordsInEf must be called at least once before this function,
142     * otherwise an error will be returned. Currently the email field
143     * if set in the ADN record is ignored.
144     * throws SecurityException if no WRITE_CONTACTS permission
145     *
146     * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
147     * @param oldTag adn tag to be replaced
148     * @param oldPhoneNumber adn number to be replaced
149     *        Set both oldTag and oldPhoneNubmer to "" means to replace an
150     *        empty record, aka, insert new record
151     * @param newTag adn tag to be stored
152     * @param newPhoneNumber adn number ot be stored
153     *        Set both newTag and newPhoneNubmer to "" means to replace the old
154     *        record with empty one, aka, delete old record
155     * @param pin2 required to update EF_FDN, otherwise must be null
156     * @return true for success
157     */
158    public boolean
159    updateAdnRecordsInEfBySearch (int efid,
160            String oldTag, String oldPhoneNumber,
161            String newTag, String newPhoneNumber, String pin2) {
162
163
164        if (mPhone.getContext().checkCallingOrSelfPermission(
165                android.Manifest.permission.WRITE_CONTACTS)
166            != PackageManager.PERMISSION_GRANTED) {
167            throw new SecurityException(
168                    "Requires android.permission.WRITE_CONTACTS permission");
169        }
170
171
172        if (DBG) logd("updateAdnRecordsInEfBySearch: efid=" + efid +
173                " ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" +
174                " ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2);
175
176        efid = updateEfForIccType(efid);
177
178        synchronized(mLock) {
179            checkThread();
180            mSuccess = false;
181            AtomicBoolean status = new AtomicBoolean(false);
182            Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, status);
183            AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber);
184            AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
185            if (mAdnCache != null) {
186                mAdnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response);
187                waitForResult(status);
188            } else {
189                loge("Failure while trying to update by search due to uninitialised adncache");
190            }
191        }
192        return mSuccess;
193    }
194
195    /**
196     * Update an ADN-like EF record by record index
197     *
198     * This is useful for iteration the whole ADN file, such as write the whole
199     * phone book or erase/format the whole phonebook. Currently the email field
200     * if set in the ADN record is ignored.
201     * throws SecurityException if no WRITE_CONTACTS permission
202     *
203     * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
204     * @param newTag adn tag to be stored
205     * @param newPhoneNumber adn number to be stored
206     *        Set both newTag and newPhoneNubmer to "" means to replace the old
207     *        record with empty one, aka, delete old record
208     * @param index is 1-based adn record index to be updated
209     * @param pin2 required to update EF_FDN, otherwise must be null
210     * @return true for success
211     */
212    public boolean
213    updateAdnRecordsInEfByIndex(int efid, String newTag,
214            String newPhoneNumber, int index, String pin2) {
215
216        if (mPhone.getContext().checkCallingOrSelfPermission(
217                android.Manifest.permission.WRITE_CONTACTS)
218                != PackageManager.PERMISSION_GRANTED) {
219            throw new SecurityException(
220                    "Requires android.permission.WRITE_CONTACTS permission");
221        }
222
223        if (DBG) logd("updateAdnRecordsInEfByIndex: efid=" + efid +
224                " Index=" + index + " ==> " +
225                "("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2);
226        synchronized(mLock) {
227            checkThread();
228            mSuccess = false;
229            AtomicBoolean status = new AtomicBoolean(false);
230            Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, status);
231            AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
232            if (mAdnCache != null) {
233                mAdnCache.updateAdnByIndex(efid, newAdn, index, pin2, response);
234                waitForResult(status);
235            } else {
236                loge("Failure while trying to update by index due to uninitialised adncache");
237            }
238        }
239        return mSuccess;
240    }
241
242    /**
243     * Get the capacity of records in efid
244     *
245     * @param efid the EF id of a ADN-like ICC
246     * @return  int[3] array
247     *            recordSizes[0]  is the single record length
248     *            recordSizes[1]  is the total length of the EF file
249     *            recordSizes[2]  is the number of records in the EF file
250     */
251    public abstract int[] getAdnRecordsSize(int efid);
252
253    /**
254     * Loads the AdnRecords in efid and returns them as a
255     * List of AdnRecords
256     *
257     * throws SecurityException if no READ_CONTACTS permission
258     *
259     * @param efid the EF id of a ADN-like ICC
260     * @return List of AdnRecord
261     */
262    public List<AdnRecord> getAdnRecordsInEf(int efid) {
263
264        if (mPhone.getContext().checkCallingOrSelfPermission(
265                android.Manifest.permission.READ_CONTACTS)
266                != PackageManager.PERMISSION_GRANTED) {
267            throw new SecurityException(
268                    "Requires android.permission.READ_CONTACTS permission");
269        }
270
271        efid = updateEfForIccType(efid);
272        if (DBG) logd("getAdnRecordsInEF: efid=" + efid);
273
274        synchronized(mLock) {
275            checkThread();
276            AtomicBoolean status = new AtomicBoolean(false);
277            Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE, status);
278            if (mAdnCache != null) {
279                mAdnCache.requestLoadAllAdnLike(efid, mAdnCache.extensionEfForEf(efid), response);
280                waitForResult(status);
281            } else {
282                loge("Failure while trying to load from SIM due to uninitialised adncache");
283            }
284        }
285        return mRecords;
286    }
287
288    protected void checkThread() {
289        if (!ALLOW_SIM_OP_IN_UI_THREAD) {
290            // Make sure this isn't the UI thread, since it will block
291            if (mBaseHandler.getLooper().equals(Looper.myLooper())) {
292                loge("query() called on the main UI thread!");
293                throw new IllegalStateException(
294                        "You cannot call query on this provder from the main UI thread.");
295            }
296        }
297    }
298
299    protected void waitForResult(AtomicBoolean status) {
300        while (!status.get()) {
301            try {
302                mLock.wait();
303            } catch (InterruptedException e) {
304                logd("interrupted while trying to update by search");
305            }
306        }
307    }
308
309    private int updateEfForIccType(int efid) {
310        // Check if we are trying to read ADN records
311        if (efid == IccConstants.EF_ADN) {
312            if (mPhone.getCurrentUiccAppType() == AppType.APPTYPE_USIM) {
313                return IccConstants.EF_PBR;
314            }
315        }
316        return efid;
317    }
318}
319
320