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