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