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 java.util.List; 27 import java.util.concurrent.atomic.AtomicBoolean; 28 29 /** 30 * SimPhoneBookInterfaceManager to provide an inter-process communication to 31 * access ADN-like SIM records. 32 */ 33 public 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 } 107 108 public void dispose() { 109 } 110 111 protected void publish() { 112 //NOTE service "simphonebook" added by IccSmsInterfaceManagerProxy 113 ServiceManager.addService("simphonebook", this); 114 } 115 116 protected abstract void logd(String msg); 117 118 protected abstract void loge(String msg); 119 120 /** 121 * Replace oldAdn with newAdn in ADN-like record in EF 122 * 123 * getAdnRecordsInEf must be called at least once before this function, 124 * otherwise an error will be returned. Currently the email field 125 * if set in the ADN record is ignored. 126 * throws SecurityException if no WRITE_CONTACTS permission 127 * 128 * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN 129 * @param oldTag adn tag to be replaced 130 * @param oldPhoneNumber adn number to be replaced 131 * Set both oldTag and oldPhoneNubmer to "" means to replace an 132 * empty record, aka, insert new record 133 * @param newTag adn tag to be stored 134 * @param newPhoneNumber adn number ot be stored 135 * Set both newTag and newPhoneNubmer to "" means to replace the old 136 * record with empty one, aka, delete old record 137 * @param pin2 required to update EF_FDN, otherwise must be null 138 * @return true for success 139 */ 140 public boolean 141 updateAdnRecordsInEfBySearch (int efid, 142 String oldTag, String oldPhoneNumber, 143 String newTag, String newPhoneNumber, String pin2) { 144 145 146 if (phone.getContext().checkCallingOrSelfPermission( 147 android.Manifest.permission.WRITE_CONTACTS) 148 != PackageManager.PERMISSION_GRANTED) { 149 throw new SecurityException( 150 "Requires android.permission.WRITE_CONTACTS permission"); 151 } 152 153 154 if (DBG) logd("updateAdnRecordsInEfBySearch: efid=" + efid + 155 " ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" + 156 " ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); 157 158 efid = updateEfForIccType(efid); 159 160 synchronized(mLock) { 161 checkThread(); 162 success = false; 163 AtomicBoolean status = new AtomicBoolean(false); 164 Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, status); 165 AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber); 166 AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber); 167 adnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response); 168 waitForResult(status); 169 } 170 return success; 171 } 172 173 /** 174 * Update an ADN-like EF record by record index 175 * 176 * This is useful for iteration the whole ADN file, such as write the whole 177 * phone book or erase/format the whole phonebook. Currently the email field 178 * if set in the ADN record is ignored. 179 * throws SecurityException if no WRITE_CONTACTS permission 180 * 181 * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN 182 * @param newTag adn tag to be stored 183 * @param newPhoneNumber adn number to be stored 184 * Set both newTag and newPhoneNubmer to "" means to replace the old 185 * record with empty one, aka, delete old record 186 * @param index is 1-based adn record index to be updated 187 * @param pin2 required to update EF_FDN, otherwise must be null 188 * @return true for success 189 */ 190 public boolean 191 updateAdnRecordsInEfByIndex(int efid, String newTag, 192 String newPhoneNumber, int index, String pin2) { 193 194 if (phone.getContext().checkCallingOrSelfPermission( 195 android.Manifest.permission.WRITE_CONTACTS) 196 != PackageManager.PERMISSION_GRANTED) { 197 throw new SecurityException( 198 "Requires android.permission.WRITE_CONTACTS permission"); 199 } 200 201 if (DBG) logd("updateAdnRecordsInEfByIndex: efid=" + efid + 202 " Index=" + index + " ==> " + 203 "("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); 204 synchronized(mLock) { 205 checkThread(); 206 success = false; 207 AtomicBoolean status = new AtomicBoolean(false); 208 Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, status); 209 AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber); 210 adnCache.updateAdnByIndex(efid, newAdn, index, pin2, response); 211 waitForResult(status); 212 } 213 return success; 214 } 215 216 /** 217 * Get the capacity of records in efid 218 * 219 * @param efid the EF id of a ADN-like ICC 220 * @return int[3] array 221 * recordSizes[0] is the single record length 222 * recordSizes[1] is the total length of the EF file 223 * recordSizes[2] is the number of records in the EF file 224 */ 225 public abstract int[] getAdnRecordsSize(int efid); 226 227 /** 228 * Loads the AdnRecords in efid and returns them as a 229 * List of AdnRecords 230 * 231 * throws SecurityException if no READ_CONTACTS permission 232 * 233 * @param efid the EF id of a ADN-like ICC 234 * @return List of AdnRecord 235 */ 236 public List<AdnRecord> getAdnRecordsInEf(int efid) { 237 238 if (phone.getContext().checkCallingOrSelfPermission( 239 android.Manifest.permission.READ_CONTACTS) 240 != PackageManager.PERMISSION_GRANTED) { 241 throw new SecurityException( 242 "Requires android.permission.READ_CONTACTS permission"); 243 } 244 245 efid = updateEfForIccType(efid); 246 if (DBG) logd("getAdnRecordsInEF: efid=" + efid); 247 248 synchronized(mLock) { 249 checkThread(); 250 AtomicBoolean status = new AtomicBoolean(false); 251 Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE, status); 252 adnCache.requestLoadAllAdnLike(efid, adnCache.extensionEfForEf(efid), response); 253 waitForResult(status); 254 } 255 return records; 256 } 257 258 protected void checkThread() { 259 if (!ALLOW_SIM_OP_IN_UI_THREAD) { 260 // Make sure this isn't the UI thread, since it will block 261 if (mBaseHandler.getLooper().equals(Looper.myLooper())) { 262 loge("query() called on the main UI thread!"); 263 throw new IllegalStateException( 264 "You cannot call query on this provder from the main UI thread."); 265 } 266 } 267 } 268 269 protected void waitForResult(AtomicBoolean status) { 270 while (!status.get()) { 271 try { 272 mLock.wait(); 273 } catch (InterruptedException e) { 274 logd("interrupted while trying to update by search"); 275 } 276 } 277 } 278 279 private int updateEfForIccType(int efid) { 280 // Check if we are trying to read ADN records 281 if (efid == IccConstants.EF_ADN) { 282 if (phone.getIccCard().isApplicationOnIcc(IccCardApplication.AppType.APPTYPE_USIM)) { 283 return IccConstants.EF_PBR; 284 } 285 } 286 return efid; 287 } 288 } 289 290