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