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