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