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.AbstractCursor; 23import android.database.Cursor; 24import android.database.CursorWindow; 25import android.net.Uri; 26import android.os.SystemProperties; 27import android.os.RemoteException; 28import android.os.ServiceManager; 29import android.text.TextUtils; 30import android.util.Log; 31 32import java.util.ArrayList; 33import java.util.List; 34 35import com.android.internal.telephony.IccConstants; 36import com.android.internal.telephony.AdnRecord; 37import com.android.internal.telephony.IIccPhoneBook; 38 39/** 40 * XXX old code -- should be replaced with MatrixCursor. 41 * @deprecated This is has been replaced by MatrixCursor. 42*/ 43class ArrayListCursor extends AbstractCursor { 44 private String[] mColumnNames; 45 private ArrayList<Object>[] mRows; 46 47 @SuppressWarnings({"unchecked"}) 48 public ArrayListCursor(String[] columnNames, ArrayList<ArrayList> rows) { 49 int colCount = columnNames.length; 50 boolean foundID = false; 51 // Add an _id column if not in columnNames 52 for (int i = 0; i < colCount; ++i) { 53 if (columnNames[i].compareToIgnoreCase("_id") == 0) { 54 mColumnNames = columnNames; 55 foundID = true; 56 break; 57 } 58 } 59 60 if (!foundID) { 61 mColumnNames = new String[colCount + 1]; 62 System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length); 63 mColumnNames[colCount] = "_id"; 64 } 65 66 int rowCount = rows.size(); 67 mRows = new ArrayList[rowCount]; 68 69 for (int i = 0; i < rowCount; ++i) { 70 mRows[i] = rows.get(i); 71 if (!foundID) { 72 mRows[i].add(i); 73 } 74 } 75 } 76 77 @Override 78 public void fillWindow(int position, CursorWindow window) { 79 if (position < 0 || position > getCount()) { 80 return; 81 } 82 83 window.acquireReference(); 84 try { 85 int oldpos = mPos; 86 mPos = position - 1; 87 window.clear(); 88 window.setStartPosition(position); 89 int columnNum = getColumnCount(); 90 window.setNumColumns(columnNum); 91 while (moveToNext() && window.allocRow()) { 92 for (int i = 0; i < columnNum; i++) { 93 final Object data = mRows[mPos].get(i); 94 if (data != null) { 95 if (data instanceof byte[]) { 96 byte[] field = (byte[]) data; 97 if (!window.putBlob(field, mPos, i)) { 98 window.freeLastRow(); 99 break; 100 } 101 } else { 102 String field = data.toString(); 103 if (!window.putString(field, mPos, i)) { 104 window.freeLastRow(); 105 break; 106 } 107 } 108 } else { 109 if (!window.putNull(mPos, i)) { 110 window.freeLastRow(); 111 break; 112 } 113 } 114 } 115 } 116 117 mPos = oldpos; 118 } catch (IllegalStateException e){ 119 // simply ignore it 120 } finally { 121 window.releaseReference(); 122 } 123 } 124 125 @Override 126 public int getCount() { 127 return mRows.length; 128 } 129 130 @Override 131 public String[] getColumnNames() { 132 return mColumnNames; 133 } 134 135 @Override 136 public byte[] getBlob(int columnIndex) { 137 return (byte[]) mRows[mPos].get(columnIndex); 138 } 139 140 @Override 141 public String getString(int columnIndex) { 142 Object cell = mRows[mPos].get(columnIndex); 143 return (cell == null) ? null : cell.toString(); 144 } 145 146 @Override 147 public short getShort(int columnIndex) { 148 Number num = (Number) mRows[mPos].get(columnIndex); 149 return num.shortValue(); 150 } 151 152 @Override 153 public int getInt(int columnIndex) { 154 Number num = (Number) mRows[mPos].get(columnIndex); 155 return num.intValue(); 156 } 157 158 @Override 159 public long getLong(int columnIndex) { 160 Number num = (Number) mRows[mPos].get(columnIndex); 161 return num.longValue(); 162 } 163 164 @Override 165 public float getFloat(int columnIndex) { 166 Number num = (Number) mRows[mPos].get(columnIndex); 167 return num.floatValue(); 168 } 169 170 @Override 171 public double getDouble(int columnIndex) { 172 Number num = (Number) mRows[mPos].get(columnIndex); 173 return num.doubleValue(); 174 } 175 176 @Override 177 public boolean isNull(int columnIndex) { 178 return mRows[mPos].get(columnIndex) == null; 179 } 180} 181 182 183/** 184 * {@hide} 185 */ 186public class IccProvider extends ContentProvider { 187 private static final String TAG = "IccProvider"; 188 private static final boolean DBG = false; 189 190 191 private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] { 192 "name", 193 "number", 194 "emails" 195 }; 196 197 private static final int ADN = 1; 198 private static final int FDN = 2; 199 private static final int SDN = 3; 200 201 private static final String STR_TAG = "tag"; 202 private static final String STR_NUMBER = "number"; 203 private static final String STR_EMAILS = "emails"; 204 private static final String STR_PIN2 = "pin2"; 205 206 private static final UriMatcher URL_MATCHER = 207 new UriMatcher(UriMatcher.NO_MATCH); 208 209 static { 210 URL_MATCHER.addURI("icc", "adn", ADN); 211 URL_MATCHER.addURI("icc", "fdn", FDN); 212 URL_MATCHER.addURI("icc", "sdn", SDN); 213 } 214 215 216 private boolean mSimulator; 217 218 @Override 219 public boolean onCreate() { 220 String device = SystemProperties.get("ro.product.device"); 221 if (!TextUtils.isEmpty(device)) { 222 mSimulator = false; 223 } else { 224 // simulator 225 mSimulator = true; 226 } 227 228 return true; 229 } 230 231 @Override 232 public Cursor query(Uri url, String[] projection, String selection, 233 String[] selectionArgs, String sort) { 234 ArrayList<ArrayList> results; 235 236 if (!mSimulator) { 237 switch (URL_MATCHER.match(url)) { 238 case ADN: 239 results = loadFromEf(IccConstants.EF_ADN); 240 break; 241 242 case FDN: 243 results = loadFromEf(IccConstants.EF_FDN); 244 break; 245 246 case SDN: 247 results = loadFromEf(IccConstants.EF_SDN); 248 break; 249 250 default: 251 throw new IllegalArgumentException("Unknown URL " + url); 252 } 253 } else { 254 // Fake up some data for the simulator 255 results = new ArrayList<ArrayList>(4); 256 ArrayList<String> contact; 257 258 contact = new ArrayList<String>(); 259 contact.add("Ron Stevens/H"); 260 contact.add("512-555-5038"); 261 results.add(contact); 262 263 contact = new ArrayList<String>(); 264 contact.add("Ron Stevens/M"); 265 contact.add("512-555-8305"); 266 results.add(contact); 267 268 contact = new ArrayList<String>(); 269 contact.add("Melissa Owens"); 270 contact.add("512-555-8305"); 271 results.add(contact); 272 273 contact = new ArrayList<String>(); 274 contact.add("Directory Assistence"); 275 contact.add("411"); 276 results.add(contact); 277 } 278 279 return new ArrayListCursor(ADDRESS_BOOK_COLUMN_NAMES, results); 280 } 281 282 @Override 283 public String getType(Uri url) { 284 switch (URL_MATCHER.match(url)) { 285 case ADN: 286 case FDN: 287 case SDN: 288 return "vnd.android.cursor.dir/sim-contact"; 289 290 default: 291 throw new IllegalArgumentException("Unknown URL " + url); 292 } 293 } 294 295 @Override 296 public Uri insert(Uri url, ContentValues initialValues) { 297 Uri resultUri; 298 int efType; 299 String pin2 = null; 300 301 if (DBG) log("insert"); 302 303 int match = URL_MATCHER.match(url); 304 switch (match) { 305 case ADN: 306 efType = IccConstants.EF_ADN; 307 break; 308 309 case FDN: 310 efType = IccConstants.EF_FDN; 311 pin2 = initialValues.getAsString("pin2"); 312 break; 313 314 default: 315 throw new UnsupportedOperationException( 316 "Cannot insert into URL: " + url); 317 } 318 319 String tag = initialValues.getAsString("tag"); 320 String number = initialValues.getAsString("number"); 321 // TODO(): Read email instead of sending null. 322 boolean success = addIccRecordToEf(efType, tag, number, null, pin2); 323 324 if (!success) { 325 return null; 326 } 327 328 StringBuilder buf = new StringBuilder("content://icc/"); 329 switch (match) { 330 case ADN: 331 buf.append("adn/"); 332 break; 333 334 case FDN: 335 buf.append("fdn/"); 336 break; 337 } 338 339 // TODO: we need to find out the rowId for the newly added record 340 buf.append(0); 341 342 resultUri = Uri.parse(buf.toString()); 343 344 /* 345 // notify interested parties that an insertion happened 346 getContext().getContentResolver().notifyInsert( 347 resultUri, rowID, null); 348 */ 349 350 return resultUri; 351 } 352 353 private String normalizeValue(String inVal) { 354 int len = inVal.length(); 355 String retVal = inVal; 356 357 if (inVal.charAt(0) == '\'' && inVal.charAt(len-1) == '\'') { 358 retVal = inVal.substring(1, len-1); 359 } 360 361 return retVal; 362 } 363 364 @Override 365 public int delete(Uri url, String where, String[] whereArgs) { 366 int efType; 367 368 if (DBG) log("delete"); 369 370 int match = URL_MATCHER.match(url); 371 switch (match) { 372 case ADN: 373 efType = IccConstants.EF_ADN; 374 break; 375 376 case FDN: 377 efType = IccConstants.EF_FDN; 378 break; 379 380 default: 381 throw new UnsupportedOperationException( 382 "Cannot insert into URL: " + url); 383 } 384 385 // parse where clause 386 String tag = null; 387 String number = null; 388 String[] emails = null; 389 String pin2 = null; 390 391 String[] tokens = where.split("AND"); 392 int n = tokens.length; 393 394 while (--n >= 0) { 395 String param = tokens[n]; 396 if (DBG) log("parsing '" + param + "'"); 397 398 String[] pair = param.split("="); 399 400 if (pair.length != 2) { 401 Log.e(TAG, "resolve: bad whereClause parameter: " + param); 402 continue; 403 } 404 405 String key = pair[0].trim(); 406 String val = pair[1].trim(); 407 408 if (STR_TAG.equals(key)) { 409 tag = normalizeValue(val); 410 } else if (STR_NUMBER.equals(key)) { 411 number = normalizeValue(val); 412 } else if (STR_EMAILS.equals(key)) { 413 //TODO(): Email is null. 414 emails = null; 415 } else if (STR_PIN2.equals(key)) { 416 pin2 = normalizeValue(val); 417 } 418 } 419 420 if (TextUtils.isEmpty(tag)) { 421 return 0; 422 } 423 424 if (efType == FDN && TextUtils.isEmpty(pin2)) { 425 return 0; 426 } 427 428 boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2); 429 if (!success) { 430 return 0; 431 } 432 433 return 1; 434 } 435 436 @Override 437 public int update(Uri url, ContentValues values, String where, String[] whereArgs) { 438 int efType; 439 String pin2 = null; 440 441 if (DBG) log("update"); 442 443 int match = URL_MATCHER.match(url); 444 switch (match) { 445 case ADN: 446 efType = IccConstants.EF_ADN; 447 break; 448 449 case FDN: 450 efType = IccConstants.EF_FDN; 451 pin2 = values.getAsString("pin2"); 452 break; 453 454 default: 455 throw new UnsupportedOperationException( 456 "Cannot insert into URL: " + url); 457 } 458 459 String tag = values.getAsString("tag"); 460 String number = values.getAsString("number"); 461 String[] emails = null; 462 String newTag = values.getAsString("newTag"); 463 String newNumber = values.getAsString("newNumber"); 464 String[] newEmails = null; 465 // TODO(): Update for email. 466 boolean success = updateIccRecordInEf(efType, tag, number, 467 newTag, newNumber, pin2); 468 469 if (!success) { 470 return 0; 471 } 472 473 return 1; 474 } 475 476 private ArrayList<ArrayList> loadFromEf(int efType) { 477 ArrayList<ArrayList> results = new ArrayList<ArrayList>(); 478 List<AdnRecord> adnRecords = null; 479 480 if (DBG) log("loadFromEf: efType=" + efType); 481 482 try { 483 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 484 ServiceManager.getService("simphonebook")); 485 if (iccIpb != null) { 486 adnRecords = iccIpb.getAdnRecordsInEf(efType); 487 } 488 } catch (RemoteException ex) { 489 // ignore it 490 } catch (SecurityException ex) { 491 if (DBG) log(ex.toString()); 492 } 493 if (adnRecords != null) { 494 // Load the results 495 496 int N = adnRecords.size(); 497 if (DBG) log("adnRecords.size=" + N); 498 for (int i = 0; i < N ; i++) { 499 loadRecord(adnRecords.get(i), results); 500 } 501 } else { 502 // No results to load 503 Log.w(TAG, "Cannot load ADN records"); 504 results.clear(); 505 } 506 if (DBG) log("loadFromEf: return results"); 507 return results; 508 } 509 510 private boolean 511 addIccRecordToEf(int efType, String name, String number, String[] emails, String pin2) { 512 if (DBG) log("addIccRecordToEf: efType=" + efType + ", name=" + name + 513 ", number=" + number + ", emails=" + emails); 514 515 boolean success = false; 516 517 // TODO: do we need to call getAdnRecordsInEf() before calling 518 // updateAdnRecordsInEfBySearch()? In any case, we will leave 519 // the UI level logic to fill that prereq if necessary. But 520 // hopefully, we can remove this requirement. 521 522 try { 523 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 524 ServiceManager.getService("simphonebook")); 525 if (iccIpb != null) { 526 success = iccIpb.updateAdnRecordsInEfBySearch(efType, "", "", 527 name, number, pin2); 528 } 529 } catch (RemoteException ex) { 530 // ignore it 531 } catch (SecurityException ex) { 532 if (DBG) log(ex.toString()); 533 } 534 if (DBG) log("addIccRecordToEf: " + success); 535 return success; 536 } 537 538 private boolean 539 updateIccRecordInEf(int efType, String oldName, String oldNumber, 540 String newName, String newNumber, String pin2) { 541 if (DBG) log("updateIccRecordInEf: efType=" + efType + 542 ", oldname=" + oldName + ", oldnumber=" + oldNumber + 543 ", newname=" + newName + ", newnumber=" + newNumber); 544 boolean success = false; 545 546 try { 547 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 548 ServiceManager.getService("simphonebook")); 549 if (iccIpb != null) { 550 success = iccIpb.updateAdnRecordsInEfBySearch(efType, 551 oldName, oldNumber, newName, newNumber, pin2); 552 } 553 } catch (RemoteException ex) { 554 // ignore it 555 } catch (SecurityException ex) { 556 if (DBG) log(ex.toString()); 557 } 558 if (DBG) log("updateIccRecordInEf: " + success); 559 return success; 560 } 561 562 563 private boolean deleteIccRecordFromEf(int efType, String name, String number, String[] emails, 564 String pin2) { 565 if (DBG) log("deleteIccRecordFromEf: efType=" + efType + 566 ", name=" + name + ", number=" + number + ", emails=" + emails + ", pin2=" + pin2); 567 568 boolean success = false; 569 570 try { 571 IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( 572 ServiceManager.getService("simphonebook")); 573 if (iccIpb != null) { 574 success = iccIpb.updateAdnRecordsInEfBySearch(efType, 575 name, number, "", "", pin2); 576 } 577 } catch (RemoteException ex) { 578 // ignore it 579 } catch (SecurityException ex) { 580 if (DBG) log(ex.toString()); 581 } 582 if (DBG) log("deleteIccRecordFromEf: " + success); 583 return success; 584 } 585 586 /** 587 * Loads an AdnRecord into an ArrayList. Must be called with mLock held. 588 * 589 * @param record the ADN record to load from 590 * @param results the array list to put the results in 591 */ 592 private void loadRecord(AdnRecord record, 593 ArrayList<ArrayList> results) { 594 if (!record.isEmpty()) { 595 ArrayList<String> contact = new ArrayList<String>(); 596 String alphaTag = record.getAlphaTag(); 597 String number = record.getNumber(); 598 String[] emails = record.getEmails(); 599 600 if (DBG) log("loadRecord: " + alphaTag + ", " + number + ","); 601 contact.add(alphaTag); 602 contact.add(number); 603 StringBuilder emailString = new StringBuilder(); 604 605 if (emails != null) { 606 for (String email: emails) { 607 if (DBG) log("Adding email:" + email); 608 emailString.append(email); 609 emailString.append(","); 610 } 611 contact.add(emailString.toString()); 612 } else { 613 contact.add(null); 614 } 615 results.add(contact); 616 } 617 } 618 619 private void log(String msg) { 620 Log.d(TAG, "[IccProvider] " + msg); 621 } 622 623} 624