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