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.MergeCursor; 24 import android.database.MatrixCursor; 25 import android.net.Uri; 26 import android.os.RemoteException; 27 import android.os.ServiceManager; 28 import android.telephony.SubscriptionInfo; 29 import android.telephony.SubscriptionManager; 30 import android.text.TextUtils; 31 import android.telephony.Rlog; 32 33 import java.util.List; 34 35 import com.android.internal.telephony.uicc.AdnRecord; 36 import com.android.internal.telephony.uicc.IccConstants; 37 38 39 /** 40 * {@hide} 41 */ 42 public 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 onCreate()82 public boolean onCreate() { 83 mSubscriptionManager = SubscriptionManager.from(getContext()); 84 return true; 85 } 86 87 @Override query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort)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 loadAllSimContacts(int efType)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 getType(Uri url)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 insert(Uri url, ContentValues initialValues)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 normalizeValue(String inVal)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 delete(Uri url, String where, String[] whereArgs)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 update(Uri url, ContentValues values, String where, String[] whereArgs)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 loadFromEf(int efType, int subId)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 addIccRecordToEf(int efType, String name, String number, String[] emails, String pin2, int subId)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 updateIccRecordInEf(int efType, String oldName, String oldNumber, String newName, String newNumber, String pin2, int subId)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 deleteIccRecordFromEf(int efType, String name, String number, String[] emails, String pin2, int subId)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 */ loadRecord(AdnRecord record, MatrixCursor cursor, int id)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 log(String msg)539 private void log(String msg) { 540 Rlog.d(TAG, "[IccProvider] " + msg); 541 } 542 getRequestSubId(Uri url)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