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