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 import com.android.internal.telephony.uicc.UiccCard; 32 import com.android.internal.telephony.uicc.UiccCardApplication; 33 34 import java.util.List; 35 import java.util.concurrent.atomic.AtomicBoolean; 36 37 /** 38 * SimPhoneBookInterfaceManager to provide an inter-process communication to 39 * access ADN-like SIM records. 40 */ 41 public 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 AtomicBoolean status = (AtomicBoolean) ar.userObj; 108 status.set(true); 109 } 110 mLock.notifyAll(); 111 } 112 }; 113 114 public IccPhoneBookInterfaceManager(PhoneBase phone) { 115 this.mPhone = phone; 116 IccRecords r = phone.mIccRecords.get(); 117 if (r != null) { 118 mAdnCache = r.getAdnCache(); 119 } 120 } 121 122 public void dispose() { 123 } 124 125 public void updateIccRecords(IccRecords iccRecords) { 126 if (iccRecords != null) { 127 mAdnCache = iccRecords.getAdnCache(); 128 } else { 129 mAdnCache = null; 130 } 131 } 132 133 protected abstract void logd(String msg); 134 135 protected abstract void loge(String msg); 136 137 /** 138 * Replace oldAdn with newAdn in ADN-like record in EF 139 * 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 144 * 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 156 */ 157 public boolean 158 updateAdnRecordsInEfBySearch (int efid, 159 String oldTag, String oldPhoneNumber, 160 String newTag, String newPhoneNumber, String pin2) { 161 162 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"); 168 } 169 170 171 if (DBG) logd("updateAdnRecordsInEfBySearch: efid=" + efid + 172 " ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" + 173 " ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); 174 175 efid = updateEfForIccType(efid); 176 177 synchronized(mLock) { 178 checkThread(); 179 mSuccess = false; 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); 187 } else { 188 loge("Failure while trying to update by search due to uninitialised adncache"); 189 } 190 } 191 return mSuccess; 192 } 193 194 /** 195 * Update an ADN-like EF record by record index 196 * 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 201 * 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 210 */ 211 public boolean 212 updateAdnRecordsInEfByIndex(int efid, String newTag, 213 String newPhoneNumber, int index, String pin2) { 214 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"); 220 } 221 222 if (DBG) logd("updateAdnRecordsInEfByIndex: efid=" + efid + 223 " Index=" + index + " ==> " + 224 "("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); 225 synchronized(mLock) { 226 checkThread(); 227 mSuccess = false; 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); 234 } else { 235 loge("Failure while trying to update by index due to uninitialised adncache"); 236 } 237 } 238 return mSuccess; 239 } 240 241 /** 242 * Get the capacity of records in efid 243 * 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 249 */ 250 public abstract int[] getAdnRecordsSize(int efid); 251 252 /** 253 * Loads the AdnRecords in efid and returns them as a 254 * List of AdnRecords 255 * 256 * throws SecurityException if no READ_CONTACTS permission 257 * 258 * @param efid the EF id of a ADN-like ICC 259 * @return List of AdnRecord 260 */ 261 public List<AdnRecord> getAdnRecordsInEf(int efid) { 262 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"); 268 } 269 270 efid = updateEfForIccType(efid); 271 if (DBG) logd("getAdnRecordsInEF: efid=" + efid); 272 273 synchronized(mLock) { 274 checkThread(); 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); 280 } else { 281 loge("Failure while trying to load from SIM due to uninitialised adncache"); 282 } 283 } 284 return mRecords; 285 } 286 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."); 294 } 295 } 296 } 297 298 protected void waitForResult(AtomicBoolean status) { 299 while (!status.get()) { 300 try { 301 mLock.wait(); 302 } catch (InterruptedException e) { 303 logd("interrupted while trying to update by search"); 304 } 305 } 306 } 307 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; 313 } 314 } 315 return efid; 316 } 317 } 318 319