Merge commit '903be2fc' into fix-merge-conflict
[android/platform/frameworks/opt/telephony.git] / src / java / com / android / internal / telephony / IccPhoneBookInterfaceManager.java
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
17 package com.android.internal.telephony;
18
19 import android.content.pm.PackageManager;
20 import android.os.AsyncResult;
21 import android.os.Handler;
22 import android.os.Looper;
23 import android.os.Message;
24 import android.os.ServiceManager;
25
26 import com.android.internal.telephony.uicc.AdnRecord;
27 import com.android.internal.telephony.uicc.AdnRecordCache;
28 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
29 import com.android.internal.telephony.uicc.IccConstants;
30 import com.android.internal.telephony.uicc.IccRecords;
31
32 import java.util.List;
33 import java.util.concurrent.atomic.AtomicBoolean;
34
35 /**
36  * SimPhoneBookInterfaceManager to provide an inter-process communication to
37  * access ADN-like SIM records.
38  */
39 public 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