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.providers.telephony; 18 19 import android.content.ContentProvider; 20 import android.content.ContentResolver; 21 import android.content.ContentValues; 22 import android.content.UriMatcher; 23 24 import android.database.Cursor; 25 import android.database.DatabaseUtils; 26 import android.database.MatrixCursor; 27 import android.database.sqlite.SQLiteDatabase; 28 import android.database.sqlite.SQLiteOpenHelper; 29 import android.database.sqlite.SQLiteQueryBuilder; 30 import android.net.Uri; 31 import android.provider.Contacts; 32 import android.provider.Telephony; 33 import android.provider.Telephony.Mms; 34 import android.provider.Telephony.MmsSms; 35 import android.provider.Telephony.Sms; 36 import android.provider.Telephony.TextBasedSmsColumns; 37 import android.provider.Telephony.Threads; 38 import android.telephony.SmsManager; 39 import android.telephony.SmsMessage; 40 import android.text.TextUtils; 41 import android.util.Log; 42 43 import java.util.ArrayList; 44 import java.util.HashMap; 45 46 public class SmsProvider extends ContentProvider { 47 private static final Uri NOTIFICATION_URI = Uri.parse("content://sms"); 48 private static final Uri ICC_URI = Uri.parse("content://sms/icc"); 49 static final String TABLE_SMS = "sms"; 50 private static final String TABLE_RAW = "raw"; 51 private static final String TABLE_SR_PENDING = "sr_pending"; 52 private static final String TABLE_WORDS = "words"; 53 54 private static final Integer ONE = Integer.valueOf(1); 55 56 private static final String[] CONTACT_QUERY_PROJECTION = 57 new String[] { Contacts.Phones.PERSON_ID }; 58 private static final int PERSON_ID_COLUMN = 0; 59 60 /** 61 * These are the columns that are available when reading SMS 62 * messages from the ICC. Columns whose names begin with "is_" 63 * have either "true" or "false" as their values. 64 */ 65 private final static String[] ICC_COLUMNS = new String[] { 66 // N.B.: These columns must appear in the same order as the 67 // calls to add appear in convertIccToSms. 68 "service_center_address", // getServiceCenterAddress 69 "address", // getDisplayOriginatingAddress 70 "message_class", // getMessageClass 71 "body", // getDisplayMessageBody 72 "date", // getTimestampMillis 73 "status", // getStatusOnIcc 74 "index_on_icc", // getIndexOnIcc 75 "is_status_report", // isStatusReportMessage 76 "transport_type", // Always "sms". 77 "type", // Always MESSAGE_TYPE_ALL. 78 "locked", // Always 0 (false). 79 "error_code", // Always 0 80 "_id" 81 }; 82 83 @Override onCreate()84 public boolean onCreate() { 85 mOpenHelper = MmsSmsDatabaseHelper.getInstance(getContext()); 86 return true; 87 } 88 89 @Override query(Uri url, String[] projectionIn, String selection, String[] selectionArgs, String sort)90 public Cursor query(Uri url, String[] projectionIn, String selection, 91 String[] selectionArgs, String sort) { 92 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 93 94 // Generate the body of the query. 95 int match = sURLMatcher.match(url); 96 switch (match) { 97 case SMS_ALL: 98 constructQueryForBox(qb, Sms.MESSAGE_TYPE_ALL); 99 break; 100 101 case SMS_UNDELIVERED: 102 constructQueryForUndelivered(qb); 103 break; 104 105 case SMS_FAILED: 106 constructQueryForBox(qb, Sms.MESSAGE_TYPE_FAILED); 107 break; 108 109 case SMS_QUEUED: 110 constructQueryForBox(qb, Sms.MESSAGE_TYPE_QUEUED); 111 break; 112 113 case SMS_INBOX: 114 constructQueryForBox(qb, Sms.MESSAGE_TYPE_INBOX); 115 break; 116 117 case SMS_SENT: 118 constructQueryForBox(qb, Sms.MESSAGE_TYPE_SENT); 119 break; 120 121 case SMS_DRAFT: 122 constructQueryForBox(qb, Sms.MESSAGE_TYPE_DRAFT); 123 break; 124 125 case SMS_OUTBOX: 126 constructQueryForBox(qb, Sms.MESSAGE_TYPE_OUTBOX); 127 break; 128 129 case SMS_ALL_ID: 130 qb.setTables(TABLE_SMS); 131 qb.appendWhere("(_id = " + url.getPathSegments().get(0) + ")"); 132 break; 133 134 case SMS_INBOX_ID: 135 case SMS_FAILED_ID: 136 case SMS_SENT_ID: 137 case SMS_DRAFT_ID: 138 case SMS_OUTBOX_ID: 139 qb.setTables(TABLE_SMS); 140 qb.appendWhere("(_id = " + url.getPathSegments().get(1) + ")"); 141 break; 142 143 case SMS_CONVERSATIONS_ID: 144 int threadID; 145 146 try { 147 threadID = Integer.parseInt(url.getPathSegments().get(1)); 148 if (Log.isLoggable(TAG, Log.VERBOSE)) { 149 Log.d(TAG, "query conversations: threadID=" + threadID); 150 } 151 } 152 catch (Exception ex) { 153 Log.e(TAG, 154 "Bad conversation thread id: " 155 + url.getPathSegments().get(1)); 156 return null; 157 } 158 159 qb.setTables(TABLE_SMS); 160 qb.appendWhere("thread_id = " + threadID); 161 break; 162 163 case SMS_CONVERSATIONS: 164 qb.setTables("sms, (SELECT thread_id AS group_thread_id, MAX(date)AS group_date," 165 + "COUNT(*) AS msg_count FROM sms GROUP BY thread_id) AS groups"); 166 qb.appendWhere("sms.thread_id = groups.group_thread_id AND sms.date =" 167 + "groups.group_date"); 168 qb.setProjectionMap(sConversationProjectionMap); 169 break; 170 171 case SMS_RAW_MESSAGE: 172 qb.setTables("raw"); 173 break; 174 175 case SMS_STATUS_PENDING: 176 qb.setTables("sr_pending"); 177 break; 178 179 case SMS_ATTACHMENT: 180 qb.setTables("attachments"); 181 break; 182 183 case SMS_ATTACHMENT_ID: 184 qb.setTables("attachments"); 185 qb.appendWhere( 186 "(sms_id = " + url.getPathSegments().get(1) + ")"); 187 break; 188 189 case SMS_QUERY_THREAD_ID: 190 qb.setTables("canonical_addresses"); 191 if (projectionIn == null) { 192 projectionIn = sIDProjection; 193 } 194 break; 195 196 case SMS_STATUS_ID: 197 qb.setTables(TABLE_SMS); 198 qb.appendWhere("(_id = " + url.getPathSegments().get(1) + ")"); 199 break; 200 201 case SMS_ALL_ICC: 202 return getAllMessagesFromIcc(); 203 204 case SMS_ICC: 205 String messageIndexString = url.getPathSegments().get(1); 206 207 return getSingleMessageFromIcc(messageIndexString); 208 209 default: 210 Log.e(TAG, "Invalid request: " + url); 211 return null; 212 } 213 214 String orderBy = null; 215 216 if (!TextUtils.isEmpty(sort)) { 217 orderBy = sort; 218 } else if (qb.getTables().equals(TABLE_SMS)) { 219 orderBy = Sms.DEFAULT_SORT_ORDER; 220 } 221 222 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 223 Cursor ret = qb.query(db, projectionIn, selection, selectionArgs, 224 null, null, orderBy); 225 226 // TODO: Since the URLs are a mess, always use content://sms 227 ret.setNotificationUri(getContext().getContentResolver(), 228 NOTIFICATION_URI); 229 return ret; 230 } 231 convertIccToSms(SmsMessage message, int id)232 private Object[] convertIccToSms(SmsMessage message, int id) { 233 // N.B.: These calls must appear in the same order as the 234 // columns appear in ICC_COLUMNS. 235 Object[] row = new Object[13]; 236 row[0] = message.getServiceCenterAddress(); 237 row[1] = message.getDisplayOriginatingAddress(); 238 row[2] = String.valueOf(message.getMessageClass()); 239 row[3] = message.getDisplayMessageBody(); 240 row[4] = message.getTimestampMillis(); 241 row[5] = Sms.STATUS_NONE; 242 row[6] = message.getIndexOnIcc(); 243 row[7] = message.isStatusReportMessage(); 244 row[8] = "sms"; 245 row[9] = TextBasedSmsColumns.MESSAGE_TYPE_ALL; 246 row[10] = 0; // locked 247 row[11] = 0; // error_code 248 row[12] = id; 249 return row; 250 } 251 252 /** 253 * Return a Cursor containing just one message from the ICC. 254 */ getSingleMessageFromIcc(String messageIndexString)255 private Cursor getSingleMessageFromIcc(String messageIndexString) { 256 try { 257 int messageIndex = Integer.parseInt(messageIndexString); 258 SmsManager smsManager = SmsManager.getDefault(); 259 ArrayList<SmsMessage> messages = smsManager.getAllMessagesFromIcc(); 260 261 SmsMessage message = messages.get(messageIndex); 262 if (message == null) { 263 throw new IllegalArgumentException( 264 "Message not retrieved. ID: " + messageIndexString); 265 } 266 MatrixCursor cursor = new MatrixCursor(ICC_COLUMNS, 1); 267 cursor.addRow(convertIccToSms(message, 0)); 268 return withIccNotificationUri(cursor); 269 } catch (NumberFormatException exception) { 270 throw new IllegalArgumentException( 271 "Bad SMS ICC ID: " + messageIndexString); 272 } 273 } 274 275 /** 276 * Return a Cursor listing all the messages stored on the ICC. 277 */ getAllMessagesFromIcc()278 private Cursor getAllMessagesFromIcc() { 279 SmsManager smsManager = SmsManager.getDefault(); 280 ArrayList<SmsMessage> messages = smsManager.getAllMessagesFromIcc(); 281 282 final int count = messages.size(); 283 MatrixCursor cursor = new MatrixCursor(ICC_COLUMNS, count); 284 for (int i = 0; i < count; i++) { 285 SmsMessage message = messages.get(i); 286 if (message != null) { 287 cursor.addRow(convertIccToSms(message, i)); 288 } 289 } 290 return withIccNotificationUri(cursor); 291 } 292 withIccNotificationUri(Cursor cursor)293 private Cursor withIccNotificationUri(Cursor cursor) { 294 cursor.setNotificationUri(getContext().getContentResolver(), ICC_URI); 295 return cursor; 296 } 297 constructQueryForBox(SQLiteQueryBuilder qb, int type)298 private void constructQueryForBox(SQLiteQueryBuilder qb, int type) { 299 qb.setTables(TABLE_SMS); 300 301 if (type != Sms.MESSAGE_TYPE_ALL) { 302 qb.appendWhere("type=" + type); 303 } 304 } 305 constructQueryForUndelivered(SQLiteQueryBuilder qb)306 private void constructQueryForUndelivered(SQLiteQueryBuilder qb) { 307 qb.setTables(TABLE_SMS); 308 309 qb.appendWhere("(type=" + Sms.MESSAGE_TYPE_OUTBOX + 310 " OR type=" + Sms.MESSAGE_TYPE_FAILED + 311 " OR type=" + Sms.MESSAGE_TYPE_QUEUED + ")"); 312 } 313 314 @Override getType(Uri url)315 public String getType(Uri url) { 316 switch (url.getPathSegments().size()) { 317 case 0: 318 return VND_ANDROID_DIR_SMS; 319 case 1: 320 try { 321 Integer.parseInt(url.getPathSegments().get(0)); 322 return VND_ANDROID_SMS; 323 } catch (NumberFormatException ex) { 324 return VND_ANDROID_DIR_SMS; 325 } 326 case 2: 327 // TODO: What about "threadID"? 328 if (url.getPathSegments().get(0).equals("conversations")) { 329 return VND_ANDROID_SMSCHAT; 330 } else { 331 return VND_ANDROID_SMS; 332 } 333 } 334 return null; 335 } 336 337 @Override insert(Uri url, ContentValues initialValues)338 public Uri insert(Uri url, ContentValues initialValues) { 339 ContentValues values; 340 long rowID; 341 int type = Sms.MESSAGE_TYPE_ALL; 342 343 int match = sURLMatcher.match(url); 344 String table = TABLE_SMS; 345 346 switch (match) { 347 case SMS_ALL: 348 Integer typeObj = initialValues.getAsInteger(Sms.TYPE); 349 if (typeObj != null) { 350 type = typeObj.intValue(); 351 } else { 352 // default to inbox 353 type = Sms.MESSAGE_TYPE_INBOX; 354 } 355 break; 356 357 case SMS_INBOX: 358 type = Sms.MESSAGE_TYPE_INBOX; 359 break; 360 361 case SMS_FAILED: 362 type = Sms.MESSAGE_TYPE_FAILED; 363 break; 364 365 case SMS_QUEUED: 366 type = Sms.MESSAGE_TYPE_QUEUED; 367 break; 368 369 case SMS_SENT: 370 type = Sms.MESSAGE_TYPE_SENT; 371 break; 372 373 case SMS_DRAFT: 374 type = Sms.MESSAGE_TYPE_DRAFT; 375 break; 376 377 case SMS_OUTBOX: 378 type = Sms.MESSAGE_TYPE_OUTBOX; 379 break; 380 381 case SMS_RAW_MESSAGE: 382 table = "raw"; 383 break; 384 385 case SMS_STATUS_PENDING: 386 table = "sr_pending"; 387 break; 388 389 case SMS_ATTACHMENT: 390 table = "attachments"; 391 break; 392 393 case SMS_NEW_THREAD_ID: 394 table = "canonical_addresses"; 395 break; 396 397 default: 398 Log.e(TAG, "Invalid request: " + url); 399 return null; 400 } 401 402 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 403 404 if (table.equals(TABLE_SMS)) { 405 boolean addDate = false; 406 boolean addType = false; 407 408 // Make sure that the date and type are set 409 if (initialValues == null) { 410 values = new ContentValues(1); 411 addDate = true; 412 addType = true; 413 } else { 414 values = new ContentValues(initialValues); 415 416 if (!initialValues.containsKey(Sms.DATE)) { 417 addDate = true; 418 } 419 420 if (!initialValues.containsKey(Sms.TYPE)) { 421 addType = true; 422 } 423 } 424 425 if (addDate) { 426 values.put(Sms.DATE, new Long(System.currentTimeMillis())); 427 } 428 429 if (addType && (type != Sms.MESSAGE_TYPE_ALL)) { 430 values.put(Sms.TYPE, Integer.valueOf(type)); 431 } 432 433 // thread_id 434 Long threadId = values.getAsLong(Sms.THREAD_ID); 435 String address = values.getAsString(Sms.ADDRESS); 436 437 if (((threadId == null) || (threadId == 0)) && (!TextUtils.isEmpty(address))) { 438 values.put(Sms.THREAD_ID, Threads.getOrCreateThreadId( 439 getContext(), address)); 440 } 441 442 // If this message is going in as a draft, it should replace any 443 // other draft messages in the thread. Just delete all draft 444 // messages with this thread ID. We could add an OR REPLACE to 445 // the insert below, but we'd have to query to find the old _id 446 // to produce a conflict anyway. 447 if (values.getAsInteger(Sms.TYPE) == Sms.MESSAGE_TYPE_DRAFT) { 448 db.delete(TABLE_SMS, "thread_id=? AND type=?", 449 new String[] { values.getAsString(Sms.THREAD_ID), 450 Integer.toString(Sms.MESSAGE_TYPE_DRAFT) }); 451 } 452 453 if (type == Sms.MESSAGE_TYPE_INBOX) { 454 // Look up the person if not already filled in. 455 if ((values.getAsLong(Sms.PERSON) == null) && (!TextUtils.isEmpty(address))) { 456 Cursor cursor = null; 457 Uri uri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, 458 Uri.encode(address)); 459 try { 460 cursor = getContext().getContentResolver().query( 461 uri, 462 CONTACT_QUERY_PROJECTION, 463 null, null, null); 464 465 if (cursor.moveToFirst()) { 466 Long id = Long.valueOf(cursor.getLong(PERSON_ID_COLUMN)); 467 values.put(Sms.PERSON, id); 468 } 469 } catch (Exception ex) { 470 Log.e(TAG, "insert: query contact uri " + uri + " caught ", ex); 471 } finally { 472 if (cursor != null) { 473 cursor.close(); 474 } 475 } 476 } 477 } else { 478 // Mark all non-inbox messages read. 479 values.put(Sms.READ, ONE); 480 } 481 } else { 482 if (initialValues == null) { 483 values = new ContentValues(1); 484 } else { 485 values = initialValues; 486 } 487 } 488 489 rowID = db.insert(table, "body", values); 490 491 // Don't use a trigger for updating the words table because of a bug 492 // in FTS3. The bug is such that the call to get the last inserted 493 // row is incorrect. 494 if (table == TABLE_SMS) { 495 // Update the words table with a corresponding row. The words table 496 // allows us to search for words quickly, without scanning the whole 497 // table; 498 ContentValues cv = new ContentValues(); 499 cv.put(Telephony.MmsSms.WordsTable.ID, rowID); 500 cv.put(Telephony.MmsSms.WordsTable.INDEXED_TEXT, values.getAsString("body")); 501 cv.put(Telephony.MmsSms.WordsTable.SOURCE_ROW_ID, rowID); 502 cv.put(Telephony.MmsSms.WordsTable.TABLE_ID, 1); 503 db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv); 504 } 505 if (rowID > 0) { 506 Uri uri = Uri.parse("content://" + table + "/" + rowID); 507 508 if (Log.isLoggable(TAG, Log.VERBOSE)) { 509 Log.d(TAG, "insert " + uri + " succeeded"); 510 } 511 notifyChange(uri); 512 return uri; 513 } else { 514 Log.e(TAG,"insert: failed! " + values.toString()); 515 } 516 517 return null; 518 } 519 520 @Override delete(Uri url, String where, String[] whereArgs)521 public int delete(Uri url, String where, String[] whereArgs) { 522 int count; 523 int match = sURLMatcher.match(url); 524 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 525 switch (match) { 526 case SMS_ALL: 527 count = db.delete(TABLE_SMS, where, whereArgs); 528 if (count != 0) { 529 // Don't update threads unless something changed. 530 MmsSmsDatabaseHelper.updateAllThreads(db, where, whereArgs); 531 } 532 break; 533 534 case SMS_ALL_ID: 535 try { 536 int message_id = Integer.parseInt(url.getPathSegments().get(0)); 537 count = MmsSmsDatabaseHelper.deleteOneSms(db, message_id); 538 } catch (Exception e) { 539 throw new IllegalArgumentException( 540 "Bad message id: " + url.getPathSegments().get(0)); 541 } 542 break; 543 544 case SMS_CONVERSATIONS_ID: 545 int threadID; 546 547 try { 548 threadID = Integer.parseInt(url.getPathSegments().get(1)); 549 } catch (Exception ex) { 550 throw new IllegalArgumentException( 551 "Bad conversation thread id: " 552 + url.getPathSegments().get(1)); 553 } 554 555 // delete the messages from the sms table 556 where = DatabaseUtils.concatenateWhere("thread_id=" + threadID, where); 557 count = db.delete(TABLE_SMS, where, whereArgs); 558 MmsSmsDatabaseHelper.updateThread(db, threadID); 559 break; 560 561 case SMS_RAW_MESSAGE: 562 count = db.delete("raw", where, whereArgs); 563 break; 564 565 case SMS_STATUS_PENDING: 566 count = db.delete("sr_pending", where, whereArgs); 567 break; 568 569 case SMS_ICC: 570 String messageIndexString = url.getPathSegments().get(1); 571 572 return deleteMessageFromIcc(messageIndexString); 573 574 default: 575 throw new IllegalArgumentException("Unknown URL"); 576 } 577 578 if (count > 0) { 579 notifyChange(url); 580 } 581 return count; 582 } 583 584 /** 585 * Delete the message at index from ICC. Return true iff 586 * successful. 587 */ deleteMessageFromIcc(String messageIndexString)588 private int deleteMessageFromIcc(String messageIndexString) { 589 SmsManager smsManager = SmsManager.getDefault(); 590 591 try { 592 return smsManager.deleteMessageFromIcc( 593 Integer.parseInt(messageIndexString)) 594 ? 1 : 0; 595 } catch (NumberFormatException exception) { 596 throw new IllegalArgumentException( 597 "Bad SMS ICC ID: " + messageIndexString); 598 } finally { 599 ContentResolver cr = getContext().getContentResolver(); 600 601 cr.notifyChange(ICC_URI, null); 602 } 603 } 604 605 @Override update(Uri url, ContentValues values, String where, String[] whereArgs)606 public int update(Uri url, ContentValues values, String where, String[] whereArgs) { 607 int count = 0; 608 String table = TABLE_SMS; 609 String extraWhere = null; 610 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 611 612 switch (sURLMatcher.match(url)) { 613 case SMS_RAW_MESSAGE: 614 table = TABLE_RAW; 615 break; 616 617 case SMS_STATUS_PENDING: 618 table = TABLE_SR_PENDING; 619 break; 620 621 case SMS_ALL: 622 case SMS_FAILED: 623 case SMS_QUEUED: 624 case SMS_INBOX: 625 case SMS_SENT: 626 case SMS_DRAFT: 627 case SMS_OUTBOX: 628 case SMS_CONVERSATIONS: 629 break; 630 631 case SMS_ALL_ID: 632 extraWhere = "_id=" + url.getPathSegments().get(0); 633 break; 634 635 case SMS_INBOX_ID: 636 case SMS_FAILED_ID: 637 case SMS_SENT_ID: 638 case SMS_DRAFT_ID: 639 case SMS_OUTBOX_ID: 640 extraWhere = "_id=" + url.getPathSegments().get(1); 641 break; 642 643 case SMS_CONVERSATIONS_ID: { 644 String threadId = url.getPathSegments().get(1); 645 646 try { 647 Integer.parseInt(threadId); 648 } catch (Exception ex) { 649 Log.e(TAG, "Bad conversation thread id: " + threadId); 650 break; 651 } 652 653 extraWhere = "thread_id=" + threadId; 654 break; 655 } 656 657 case SMS_STATUS_ID: 658 extraWhere = "_id=" + url.getPathSegments().get(1); 659 break; 660 661 default: 662 throw new UnsupportedOperationException( 663 "URI " + url + " not supported"); 664 } 665 666 where = DatabaseUtils.concatenateWhere(where, extraWhere); 667 count = db.update(table, values, where, whereArgs); 668 669 if (count > 0) { 670 if (Log.isLoggable(TAG, Log.VERBOSE)) { 671 Log.d(TAG, "update " + url + " succeeded"); 672 } 673 notifyChange(url); 674 } 675 return count; 676 } 677 notifyChange(Uri uri)678 private void notifyChange(Uri uri) { 679 ContentResolver cr = getContext().getContentResolver(); 680 cr.notifyChange(uri, null); 681 cr.notifyChange(MmsSms.CONTENT_URI, null); 682 cr.notifyChange(Uri.parse("content://mms-sms/conversations/"), null); 683 } 684 685 private SQLiteOpenHelper mOpenHelper; 686 687 private final static String TAG = "SmsProvider"; 688 private final static String VND_ANDROID_SMS = "vnd.android.cursor.item/sms"; 689 private final static String VND_ANDROID_SMSCHAT = 690 "vnd.android.cursor.item/sms-chat"; 691 private final static String VND_ANDROID_DIR_SMS = 692 "vnd.android.cursor.dir/sms"; 693 694 private static final HashMap<String, String> sConversationProjectionMap = 695 new HashMap<String, String>(); 696 private static final String[] sIDProjection = new String[] { "_id" }; 697 698 private static final int SMS_ALL = 0; 699 private static final int SMS_ALL_ID = 1; 700 private static final int SMS_INBOX = 2; 701 private static final int SMS_INBOX_ID = 3; 702 private static final int SMS_SENT = 4; 703 private static final int SMS_SENT_ID = 5; 704 private static final int SMS_DRAFT = 6; 705 private static final int SMS_DRAFT_ID = 7; 706 private static final int SMS_OUTBOX = 8; 707 private static final int SMS_OUTBOX_ID = 9; 708 private static final int SMS_CONVERSATIONS = 10; 709 private static final int SMS_CONVERSATIONS_ID = 11; 710 private static final int SMS_RAW_MESSAGE = 15; 711 private static final int SMS_ATTACHMENT = 16; 712 private static final int SMS_ATTACHMENT_ID = 17; 713 private static final int SMS_NEW_THREAD_ID = 18; 714 private static final int SMS_QUERY_THREAD_ID = 19; 715 private static final int SMS_STATUS_ID = 20; 716 private static final int SMS_STATUS_PENDING = 21; 717 private static final int SMS_ALL_ICC = 22; 718 private static final int SMS_ICC = 23; 719 private static final int SMS_FAILED = 24; 720 private static final int SMS_FAILED_ID = 25; 721 private static final int SMS_QUEUED = 26; 722 private static final int SMS_UNDELIVERED = 27; 723 724 private static final UriMatcher sURLMatcher = 725 new UriMatcher(UriMatcher.NO_MATCH); 726 727 static { 728 sURLMatcher.addURI("sms", null, SMS_ALL); 729 sURLMatcher.addURI("sms", "#", SMS_ALL_ID); 730 sURLMatcher.addURI("sms", "inbox", SMS_INBOX); 731 sURLMatcher.addURI("sms", "inbox/#", SMS_INBOX_ID); 732 sURLMatcher.addURI("sms", "sent", SMS_SENT); 733 sURLMatcher.addURI("sms", "sent/#", SMS_SENT_ID); 734 sURLMatcher.addURI("sms", "draft", SMS_DRAFT); 735 sURLMatcher.addURI("sms", "draft/#", SMS_DRAFT_ID); 736 sURLMatcher.addURI("sms", "outbox", SMS_OUTBOX); 737 sURLMatcher.addURI("sms", "outbox/#", SMS_OUTBOX_ID); 738 sURLMatcher.addURI("sms", "undelivered", SMS_UNDELIVERED); 739 sURLMatcher.addURI("sms", "failed", SMS_FAILED); 740 sURLMatcher.addURI("sms", "failed/#", SMS_FAILED_ID); 741 sURLMatcher.addURI("sms", "queued", SMS_QUEUED); 742 sURLMatcher.addURI("sms", "conversations", SMS_CONVERSATIONS); 743 sURLMatcher.addURI("sms", "conversations/*", SMS_CONVERSATIONS_ID); 744 sURLMatcher.addURI("sms", "raw", SMS_RAW_MESSAGE); 745 sURLMatcher.addURI("sms", "attachments", SMS_ATTACHMENT); 746 sURLMatcher.addURI("sms", "attachments/#", SMS_ATTACHMENT_ID); 747 sURLMatcher.addURI("sms", "threadID", SMS_NEW_THREAD_ID); 748 sURLMatcher.addURI("sms", "threadID/*", SMS_QUERY_THREAD_ID); 749 sURLMatcher.addURI("sms", "status/#", SMS_STATUS_ID); 750 sURLMatcher.addURI("sms", "sr_pending", SMS_STATUS_PENDING); 751 sURLMatcher.addURI("sms", "icc", SMS_ALL_ICC); 752 sURLMatcher.addURI("sms", "icc/#", SMS_ICC); 753 //we keep these for not breaking old applications 754 sURLMatcher.addURI("sms", "sim", SMS_ALL_ICC); 755 sURLMatcher.addURI("sms", "sim/#", SMS_ICC); 756 sConversationProjectionMap.put(Sms.Conversations.SNIPPET, "sms.body AS snippet")757 sConversationProjectionMap.put(Sms.Conversations.SNIPPET, 758 "sms.body AS snippet"); sConversationProjectionMap.put(Sms.Conversations.THREAD_ID, "sms.thread_id AS thread_id")759 sConversationProjectionMap.put(Sms.Conversations.THREAD_ID, 760 "sms.thread_id AS thread_id"); sConversationProjectionMap.put(Sms.Conversations.MESSAGE_COUNT, "groups.msg_count AS msg_count")761 sConversationProjectionMap.put(Sms.Conversations.MESSAGE_COUNT, 762 "groups.msg_count AS msg_count"); 763 sConversationProjectionMap.put("delta", null); 764 } 765 } 766