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