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