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