2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.telephony;
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;
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 import com.android.internal.telephony.uicc.UiccCard;
32 import com.android.internal.telephony.uicc.UiccCardApplication;
34 import java.util.List;
35 import java.util.concurrent.atomic.AtomicBoolean;
38 * SimPhoneBookInterfaceManager to provide an inter-process communication to
39 * access ADN-like SIM records.
41 public abstract class IccPhoneBookInterfaceManager {
42 protected static final boolean DBG = true;
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;
54 protected static final boolean ALLOW_SIM_OP_IN_UI_THREAD = false;
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;
60 protected Handler mBaseHandler = new Handler() {
62 public void handleMessage(Message msg) {
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]);
81 case EVENT_UPDATE_DONE:
82 ar = (AsyncResult) msg.obj;
83 synchronized (mLock) {
84 mSuccess = (ar.exception == null);
89 ar = (AsyncResult)msg.obj;
90 synchronized (mLock) {
91 if (ar.exception == null) {
92 mRecords = (List<AdnRecord>) ar.result;
94 if(DBG) logd("Cannot load ADN records");
95 if (mRecords != null) {
105 private void notifyPending(AsyncResult ar) {
106 if (ar.userObj != null) {
107 AtomicBoolean status = (AtomicBoolean) ar.userObj;
114 public IccPhoneBookInterfaceManager(PhoneBase phone) {
116 IccRecords r = phone.mIccRecords.get();
118 mAdnCache = r.getAdnCache();
122 public void dispose() {
125 public void updateIccRecords(IccRecords iccRecords) {
126 if (iccRecords != null) {
127 mAdnCache = iccRecords.getAdnCache();
133 protected abstract void logd(String msg);
135 protected abstract void loge(String msg);
138 * Replace oldAdn with newAdn in ADN-like record in EF
140 * getAdnRecordsInEf must be called at least once before this function,
141 * otherwise an error will be returned. Currently the email field
142 * if set in the ADN record is ignored.
143 * throws SecurityException if no WRITE_CONTACTS permission
145 * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
146 * @param oldTag adn tag to be replaced
147 * @param oldPhoneNumber adn number to be replaced
148 * Set both oldTag and oldPhoneNubmer to "" means to replace an
149 * empty record, aka, insert new record
150 * @param newTag adn tag to be stored
151 * @param newPhoneNumber adn number ot be stored
152 * Set both newTag and newPhoneNubmer to "" means to replace the old
153 * record with empty one, aka, delete old record
154 * @param pin2 required to update EF_FDN, otherwise must be null
155 * @return true for success
158 updateAdnRecordsInEfBySearch (int efid,
159 String oldTag, String oldPhoneNumber,
160 String newTag, String newPhoneNumber, String pin2) {
163 if (mPhone.getContext().checkCallingOrSelfPermission(
164 android.Manifest.permission.WRITE_CONTACTS)
165 != PackageManager.PERMISSION_GRANTED) {
166 throw new SecurityException(
167 "Requires android.permission.WRITE_CONTACTS permission");
171 if (DBG) logd("updateAdnRecordsInEfBySearch: efid=" + efid +
172 " ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" +
173 " ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2);
175 efid = updateEfForIccType(efid);
177 synchronized(mLock) {
180 AtomicBoolean status = new AtomicBoolean(false);
181 Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, status);
182 AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber);
183 AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
184 if (mAdnCache != null) {
185 mAdnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response);
186 waitForResult(status);
188 loge("Failure while trying to update by search due to uninitialised adncache");
195 * Update an ADN-like EF record by record index
197 * This is useful for iteration the whole ADN file, such as write the whole
198 * phone book or erase/format the whole phonebook. Currently the email field
199 * if set in the ADN record is ignored.
200 * throws SecurityException if no WRITE_CONTACTS permission
202 * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
203 * @param newTag adn tag to be stored
204 * @param newPhoneNumber adn number to be stored
205 * Set both newTag and newPhoneNubmer to "" means to replace the old
206 * record with empty one, aka, delete old record
207 * @param index is 1-based adn record index to be updated
208 * @param pin2 required to update EF_FDN, otherwise must be null
209 * @return true for success
212 updateAdnRecordsInEfByIndex(int efid, String newTag,
213 String newPhoneNumber, int index, String pin2) {
215 if (mPhone.getContext().checkCallingOrSelfPermission(
216 android.Manifest.permission.WRITE_CONTACTS)
217 != PackageManager.PERMISSION_GRANTED) {
218 throw new SecurityException(
219 "Requires android.permission.WRITE_CONTACTS permission");
222 if (DBG) logd("updateAdnRecordsInEfByIndex: efid=" + efid +
223 " Index=" + index + " ==> " +
224 "("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2);
225 synchronized(mLock) {
228 AtomicBoolean status = new AtomicBoolean(false);
229 Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, status);
230 AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
231 if (mAdnCache != null) {
232 mAdnCache.updateAdnByIndex(efid, newAdn, index, pin2, response);
233 waitForResult(status);
235 loge("Failure while trying to update by index due to uninitialised adncache");
242 * Get the capacity of records in efid
244 * @param efid the EF id of a ADN-like ICC
245 * @return int[3] array
246 * recordSizes[0] is the single record length
247 * recordSizes[1] is the total length of the EF file
248 * recordSizes[2] is the number of records in the EF file
250 public abstract int[] getAdnRecordsSize(int efid);
253 * Loads the AdnRecords in efid and returns them as a
256 * throws SecurityException if no READ_CONTACTS permission
258 * @param efid the EF id of a ADN-like ICC
259 * @return List of AdnRecord
261 public List<AdnRecord> getAdnRecordsInEf(int efid) {
263 if (mPhone.getContext().checkCallingOrSelfPermission(
264 android.Manifest.permission.READ_CONTACTS)
265 != PackageManager.PERMISSION_GRANTED) {
266 throw new SecurityException(
267 "Requires android.permission.READ_CONTACTS permission");
270 efid = updateEfForIccType(efid);
271 if (DBG) logd("getAdnRecordsInEF: efid=" + efid);
273 synchronized(mLock) {
275 AtomicBoolean status = new AtomicBoolean(false);
276 Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE, status);
277 if (mAdnCache != null) {
278 mAdnCache.requestLoadAllAdnLike(efid, mAdnCache.extensionEfForEf(efid), response);
279 waitForResult(status);
281 loge("Failure while trying to load from SIM due to uninitialised adncache");
287 protected void checkThread() {
288 if (!ALLOW_SIM_OP_IN_UI_THREAD) {
289 // Make sure this isn't the UI thread, since it will block
290 if (mBaseHandler.getLooper().equals(Looper.myLooper())) {
291 loge("query() called on the main UI thread!");
292 throw new IllegalStateException(
293 "You cannot call query on this provder from the main UI thread.");
298 protected void waitForResult(AtomicBoolean status) {
299 while (!status.get()) {
302 } catch (InterruptedException e) {
303 logd("interrupted while trying to update by search");
308 private int updateEfForIccType(int efid) {
309 // Check if we are trying to read ADN records
310 if (efid == IccConstants.EF_ADN) {
311 if (mPhone.getCurrentUiccAppType() == AppType.APPTYPE_USIM) {
312 return IccConstants.EF_PBR;