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.ContentProvider; 20 import android.content.UriMatcher; 21 import android.content.ContentValues; 22 import android.database.Cursor; 23 import android.database.MatrixCursor; 24 import android.net.Uri; 25 import android.os.RemoteException; 26 import android.os.ServiceManager; 27 import android.text.TextUtils; 28 import android.telephony.Rlog; 29 30 import java.util.List; 31 32 import com.android.internal.telephony.IIccPhoneBook; 33 import com.android.internal.telephony.uicc.AdnRecord; 34 import com.android.internal.telephony.uicc.IccConstants; 35 36 37 /** 38 * {@hide} 39 */ 40 public class IccProvider extends ContentProvider { 41 private static final String TAG = "IccProvider"; 42 private static final boolean DBG = false; 43 44 45 private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] { 46 "name", 47 "number", 48 "emails", 49 "_id" 50 }; 51 52 private static final int ADN = 1; 53 private static final int FDN = 2; 54 private static final int SDN = 3; 55 56 private static final String STR_TAG = "tag"; 57 private static final String STR_NUMBER = "number"; 58 private static final String STR_EMAILS = "emails"; 59 private static final String STR_PIN2 = "pin2"; 60 61 private static final UriMatcher URL_MATCHER = 62 new UriMatcher(UriMatcher.NO_MATCH); 63 64 static { 65 URL_MATCHER.addURI("icc", "adn", ADN); 66 URL_MATCHER.addURI("icc", "fdn", FDN); 67 URL_MATCHER.addURI("icc", "sdn", SDN); 68 } 69 70 71 @Override onCreate()72 public boolean onCreate() { 73 return true; 74 } 75 76 @Override query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort)77 public Cursor query(Uri url, String[] projection, String selection, 78 String[] selectionArgs, String sort) { 79 switch (URL_MATCHER.match(url)) { 80 case ADN: 81 return loadFromEf(IccConstants.EF_ADN); 82 83 case FDN: 84 return loadFromEf(IccConstants.EF_FDN); 85 86 case SDN: 87 return loadFromEf(IccConstants.EF_SDN); 88 89 default: 90 throw new IllegalArgumentException("Unknown URL " + url); 91 } 92 } 93 94 @Override getType(Uri url)95 public String getType(Uri url) { 96 switch (URL_MATCHER.match(url)) { 97 case ADN: 98 case FDN: 99 case SDN: 100 return "vnd.android.cursor.dir/sim-contact"; 101 102 default: 103 throw new IllegalArgumentException("Unknown URL " + url); 104 } 105 } 106 107 @Override insert(Uri url, ContentValues initialValues)108 public Uri insert(Uri url, ContentValues initialValues) { 109 Uri resultUri; 110 int efType; 111 String pin2 = null; 112 113 if (DBG) log("insert"); 114 115 int match = URL_MATCHER.match(url); 116 switch (match) { 117 case ADN: 118 efType = IccConstants.EF_ADN; 119 break; 120 121 case FDN: 122 efType = IccConstants.EF_FDN; 123 pin2 = initialValues.getAsString("pin2"); 124 break; 125 126 default: 127 throw new UnsupportedOperationException( 128 "Cannot insert into URL: " + url); 129 } 130 131 String tag = initialValues.getAsString("tag"); 132 String number = initialValues.getAsString("number"); 133 // TODO(): Read email instead of sending null. 134 boolean success = addIccRecordToEf(efType, tag, number, null, pin2); 135 136 if (!success) { 137 return null; 138 } 139 140 StringBuilder buf = new StringBuilder("content://icc/"); 141 switch (match) { 142 case ADN: 143 buf.append("adn/"); 144 break; 145 146 case FDN: 147 buf.append("fdn/"); 148 break; 149 } 150 151 // TODO: we need to find out the rowId for the newly added record 152 buf.append(0); 153 154 resultUri = Uri.parse(buf.toString()); 155 156 /* 157 // notify interested parties that an insertion happened 158 getContext().getContentResolver().notifyInsert( 159 resultUri, rowID, null); 160 */ 161 162 return resultUri; 163 } 164 normalizeValue(String inVal)165 private String normalizeValue(String inVal) { 166 int len = inVal.length(); 167 String retVal = inVal; 168 169 if (inVal.charAt(0) == '\'' && inVal.charAt(len-1) == '\'') { 170 retVal = inVal.substring(1, len-1); 171 } 172 173 return retVal; 174 } 175 176 @Override delete(Uri url, String where, String[] whereArgs)177 public int delete(Uri url, String where, String[] whereArgs) { 178 int efType; 179 180 if (DBG) log("delete"); 181 182 int match = URL_MATCHER.match(url); 183 switch (match) { 184 case ADN: 185 efType = IccConstants.EF_ADN; 186 break; 187 188 case FDN: 189 efType = IccConstants.EF_FDN; 190 break; 191 192 default: 193 throw new UnsupportedOperationException( 194 "Cannot insert into URL: " + url); 195 } 196 197 // parse where clause 198 String tag = null; 199 String number = null; 200 String[] emails = null; 201 String pin2 = null; 202 203 String[] tokens = where.split("AND"); 204 int n = tokens.length; 205 206 while (--n >= 0) { 207 String param = tokens[n]; 208 if (DBG) log("parsing '" + param + "'"); 209 210 String[] pair = param.split("=", 2); 211 212 String key = pair[0].trim(); 213 String val = pair[1].trim(); 214 215 if (STR_TAG.equals(key)) { 216 tag = normalizeValue(val); 217 } else if (STR_NUMBER.equals(key)) { 218 number = normalizeValue(val); 219 } else if (STR_EMAILS.equals(key)) { 220 //TODO(): Email is null. 221 emails = null; 222 } else if (STR_PIN2.equals(key)) { 223 pin2 = normalizeValue(val); 224 } 225 } 226 227 if (TextUtils.isEmpty(number)) { 228 return 0; 229 } 230 231 if (efType == IccConstants.EF_FDN && TextUtils.isEmpty(pin2)) { 232 return 0; 233 } 234 235 boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2); 236 if (!success) { 237 return 0; 238 } 239 240 return 1; 241 } 242 243 @Override update(Uri url, ContentValues values, String where, String[] whereArgs)244 public int update(Uri url, ContentValues values, String where, String[] whereArgs) { 245 int efType; 246 String pin2 = null; 247 248 if (DBG) log("update"); 249 250 int match = URL_MATCHER.match(url); 251 switch (match) { 252 case ADN: 253 efType = IccConstants.EF_ADN; 254 break; 255 256 case FDN: 257 efType = IccConstants.EF_FDN; 258 pin2 = values.getAsString("pin2"); 259 break; 260 261 default: 262 throw new UnsupportedOperationException( 263 "Cannot insert into URL: " + url); 264 } 265 266 String tag = values.getAsString("tag"); 267 String number = values.getAsString("number"); 268 String[] emails = null; 269 String newTag = values.getAsString("newTag"); 270 String newNumber = values.getAsString("newNumber"); 271 String[] newEmails = null; 272 // TODO(): Update for email. 273 boolean success = updateIccRecordInEf(efType, tag, number, 274 newTag, newNumber, pin2); 275 276 if (!success) { 277 return 0; 278 } 279 280 return 1; 281 } 282 loadFromEf(int efType)283 private MatrixCursor loadFromEf(int efType) { 284 if (DBG) log("loadFromEf: efType=" + efType); 285 286 List<AdnRecord> adnRecords = null; 287 try { 288 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 289 ServiceManager.getService("simphonebook")); 290 if (iccIpb != null) { 291 adnRecords = iccIpb.getAdnRecordsInEf(efType); 292 } 293 } catch (RemoteException ex) { 294 // ignore it 295 } catch (SecurityException ex) { 296 if (DBG) log(ex.toString()); 297 } 298 299 if (adnRecords != null) { 300 // Load the results 301 final int N = adnRecords.size(); 302 final MatrixCursor cursor = new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES, N); 303 if (DBG) log("adnRecords.size=" + N); 304 for (int i = 0; i < N ; i++) { 305 loadRecord(adnRecords.get(i), cursor, i); 306 } 307 return cursor; 308 } else { 309 // No results to load 310 Rlog.w(TAG, "Cannot load ADN records"); 311 return new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES); 312 } 313 } 314 315 private boolean addIccRecordToEf(int efType, String name, String number, String[] emails, String pin2)316 addIccRecordToEf(int efType, String name, String number, String[] emails, String pin2) { 317 if (DBG) log("addIccRecordToEf: efType=" + efType + ", name=" + name + 318 ", number=" + number + ", emails=" + emails); 319 320 boolean success = false; 321 322 // TODO: do we need to call getAdnRecordsInEf() before calling 323 // updateAdnRecordsInEfBySearch()? In any case, we will leave 324 // the UI level logic to fill that prereq if necessary. But 325 // hopefully, we can remove this requirement. 326 327 try { 328 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 329 ServiceManager.getService("simphonebook")); 330 if (iccIpb != null) { 331 success = iccIpb.updateAdnRecordsInEfBySearch(efType, "", "", 332 name, number, pin2); 333 } 334 } catch (RemoteException ex) { 335 // ignore it 336 } catch (SecurityException ex) { 337 if (DBG) log(ex.toString()); 338 } 339 if (DBG) log("addIccRecordToEf: " + success); 340 return success; 341 } 342 343 private boolean updateIccRecordInEf(int efType, String oldName, String oldNumber, String newName, String newNumber, String pin2)344 updateIccRecordInEf(int efType, String oldName, String oldNumber, 345 String newName, String newNumber, String pin2) { 346 if (DBG) log("updateIccRecordInEf: efType=" + efType + 347 ", oldname=" + oldName + ", oldnumber=" + oldNumber + 348 ", newname=" + newName + ", newnumber=" + newNumber); 349 boolean success = false; 350 351 try { 352 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 353 ServiceManager.getService("simphonebook")); 354 if (iccIpb != null) { 355 success = iccIpb.updateAdnRecordsInEfBySearch(efType, 356 oldName, oldNumber, newName, newNumber, pin2); 357 } 358 } catch (RemoteException ex) { 359 // ignore it 360 } catch (SecurityException ex) { 361 if (DBG) log(ex.toString()); 362 } 363 if (DBG) log("updateIccRecordInEf: " + success); 364 return success; 365 } 366 367 deleteIccRecordFromEf(int efType, String name, String number, String[] emails, String pin2)368 private boolean deleteIccRecordFromEf(int efType, String name, String number, String[] emails, 369 String pin2) { 370 if (DBG) log("deleteIccRecordFromEf: efType=" + efType + 371 ", name=" + name + ", number=" + number + ", emails=" + emails + ", pin2=" + pin2); 372 373 boolean success = false; 374 375 try { 376 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 377 ServiceManager.getService("simphonebook")); 378 if (iccIpb != null) { 379 success = iccIpb.updateAdnRecordsInEfBySearch(efType, 380 name, number, "", "", pin2); 381 } 382 } catch (RemoteException ex) { 383 // ignore it 384 } catch (SecurityException ex) { 385 if (DBG) log(ex.toString()); 386 } 387 if (DBG) log("deleteIccRecordFromEf: " + success); 388 return success; 389 } 390 391 /** 392 * Loads an AdnRecord into a MatrixCursor. Must be called with mLock held. 393 * 394 * @param record the ADN record to load from 395 * @param cursor the cursor to receive the results 396 */ loadRecord(AdnRecord record, MatrixCursor cursor, int id)397 private void loadRecord(AdnRecord record, MatrixCursor cursor, int id) { 398 if (!record.isEmpty()) { 399 Object[] contact = new Object[4]; 400 String alphaTag = record.getAlphaTag(); 401 String number = record.getNumber(); 402 403 if (DBG) log("loadRecord: " + alphaTag + ", " + number + ","); 404 contact[0] = alphaTag; 405 contact[1] = number; 406 407 String[] emails = record.getEmails(); 408 if (emails != null) { 409 StringBuilder emailString = new StringBuilder(); 410 for (String email: emails) { 411 if (DBG) log("Adding email:" + email); 412 emailString.append(email); 413 emailString.append(","); 414 } 415 contact[2] = emailString.toString(); 416 } 417 contact[3] = id; 418 cursor.addRow(contact); 419 } 420 } 421 log(String msg)422 private void log(String msg) { 423 Rlog.d(TAG, "[IccProvider] " + msg); 424 } 425 426 } 427