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 324