IccPhoneBookInterfaceManager.java revision 22d85a8e3a575a6d01d2c788587971657dfe20c6
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            mAdnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response);
187            waitForResult(status);
188        }
189        return mSuccess;
190    }
191
192    /**
193     * Update an ADN-like EF record by record index
194     *
195     * This is useful for iteration the whole ADN file, such as write the whole
196     * phone book or erase/format the whole phonebook. Currently the email field
197     * if set in the ADN record is ignored.
198     * throws SecurityException if no WRITE_CONTACTS permission
199     *
200     * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
201     * @param newTag adn tag to be stored
202     * @param newPhoneNumber adn number to be stored
203     *        Set both newTag and newPhoneNubmer to "" means to replace the old
204     *        record with empty one, aka, delete old record
205     * @param index is 1-based adn record index to be updated
206     * @param pin2 required to update EF_FDN, otherwise must be null
207     * @return true for success
208     */
209    @Override
210    public boolean
211    updateAdnRecordsInEfByIndex(int efid, String newTag,
212            String newPhoneNumber, int index, String pin2) {
213
214        if (mPhone.getContext().checkCallingOrSelfPermission(
215                android.Manifest.permission.WRITE_CONTACTS)
216                != PackageManager.PERMISSION_GRANTED) {
217            throw new SecurityException(
218                    "Requires android.permission.WRITE_CONTACTS permission");
219        }
220
221        if (DBG) logd("updateAdnRecordsInEfByIndex: efid=" + efid +
222                " Index=" + index + " ==> " +
223                "("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2);
224        synchronized(mLock) {
225            checkThread();
226            mSuccess = false;
227            AtomicBoolean status = new AtomicBoolean(false);
228            Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, status);
229            AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
230            mAdnCache.updateAdnByIndex(efid, newAdn, index, pin2, response);
231            waitForResult(status);
232        }
233        return mSuccess;
234    }
235
236    /**
237     * Get the capacity of records in efid
238     *
239     * @param efid the EF id of a ADN-like ICC
240     * @return  int[3] array
241     *            recordSizes[0]  is the single record length
242     *            recordSizes[1]  is the total length of the EF file
243     *            recordSizes[2]  is the number of records in the EF file
244     */
245    @Override
246    public abstract int[] getAdnRecordsSize(int efid);
247
248    /**
249     * Loads the AdnRecords in efid and returns them as a
250     * List of AdnRecords
251     *
252     * throws SecurityException if no READ_CONTACTS permission
253     *
254     * @param efid the EF id of a ADN-like ICC
255     * @return List of AdnRecord
256     */
257    @Override
258    public List<AdnRecord> getAdnRecordsInEf(int efid) {
259
260        if (mPhone.getContext().checkCallingOrSelfPermission(
261                android.Manifest.permission.READ_CONTACTS)
262                != PackageManager.PERMISSION_GRANTED) {
263            throw new SecurityException(
264                    "Requires android.permission.READ_CONTACTS permission");
265        }
266
267        efid = updateEfForIccType(efid);
268        if (DBG) logd("getAdnRecordsInEF: efid=" + efid);
269
270        synchronized(mLock) {
271            checkThread();
272            AtomicBoolean status = new AtomicBoolean(false);
273            Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE, status);
274            mAdnCache.requestLoadAllAdnLike(efid, mAdnCache.extensionEfForEf(efid), response);
275            waitForResult(status);
276        }
277        return mRecords;
278    }
279
280    protected void checkThread() {
281        if (!ALLOW_SIM_OP_IN_UI_THREAD) {
282            // Make sure this isn't the UI thread, since it will block
283            if (mBaseHandler.getLooper().equals(Looper.myLooper())) {
284                loge("query() called on the main UI thread!");
285                throw new IllegalStateException(
286                        "You cannot call query on this provder from the main UI thread.");
287            }
288        }
289    }
290
291    protected void waitForResult(AtomicBoolean status) {
292        while (!status.get()) {
293            try {
294                mLock.wait();
295            } catch (InterruptedException e) {
296                logd("interrupted while trying to update by search");
297            }
298        }
299    }
300
301    private int updateEfForIccType(int efid) {
302        // Check if we are trying to read ADN records
303        if (efid == IccConstants.EF_ADN) {
304            if (mPhone.getCurrentUiccAppType() == AppType.APPTYPE_USIM) {
305                return IccConstants.EF_PBR;
306            }
307        }
308        return efid;
309    }
310}
311
312