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