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