1/* 2 * Copyright (C) 2007 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.ContentValues; 21import android.content.Context; 22import android.content.Intent; 23import android.content.UriMatcher; 24import android.database.Cursor; 25import android.database.sqlite.SQLiteDatabase; 26import android.database.sqlite.SQLiteOpenHelper; 27import android.database.sqlite.SQLiteQueryBuilder; 28import android.net.Uri; 29import android.os.ParcelFileDescriptor; 30import android.provider.BaseColumns; 31import android.provider.Telephony; 32import android.provider.Telephony.CanonicalAddressesColumns; 33import android.provider.Telephony.Mms; 34import android.provider.Telephony.MmsSms; 35import android.provider.Telephony.Mms.Addr; 36import android.provider.Telephony.Mms.Part; 37import android.provider.Telephony.Mms.Rate; 38import android.text.TextUtils; 39import android.util.Log; 40 41import com.google.android.mms.pdu.PduHeaders; 42 43import java.io.File; 44import java.io.FileNotFoundException; 45import java.io.IOException; 46import android.provider.Telephony.Threads; 47 48/** 49 * The class to provide base facility to access MMS related content, 50 * which is stored in a SQLite database and in the file system. 51 */ 52public class MmsProvider extends ContentProvider { 53 static final String TABLE_PDU = "pdu"; 54 static final String TABLE_ADDR = "addr"; 55 static final String TABLE_PART = "part"; 56 static final String TABLE_RATE = "rate"; 57 static final String TABLE_DRM = "drm"; 58 static final String TABLE_WORDS = "words"; 59 60 @Override 61 public boolean onCreate() { 62 mOpenHelper = MmsSmsDatabaseHelper.getInstance(getContext()); 63 return true; 64 } 65 66 @Override 67 public Cursor query(Uri uri, String[] projection, 68 String selection, String[] selectionArgs, String sortOrder) { 69 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 70 71 // Generate the body of the query. 72 int match = sURLMatcher.match(uri); 73 if (LOCAL_LOGV) { 74 Log.v(TAG, "Query uri=" + uri + ", match=" + match); 75 } 76 77 switch (match) { 78 case MMS_ALL: 79 constructQueryForBox(qb, Mms.MESSAGE_BOX_ALL); 80 break; 81 case MMS_INBOX: 82 constructQueryForBox(qb, Mms.MESSAGE_BOX_INBOX); 83 break; 84 case MMS_SENT: 85 constructQueryForBox(qb, Mms.MESSAGE_BOX_SENT); 86 break; 87 case MMS_DRAFTS: 88 constructQueryForBox(qb, Mms.MESSAGE_BOX_DRAFTS); 89 break; 90 case MMS_OUTBOX: 91 constructQueryForBox(qb, Mms.MESSAGE_BOX_OUTBOX); 92 break; 93 case MMS_ALL_ID: 94 qb.setTables(TABLE_PDU); 95 qb.appendWhere(Mms._ID + "=" + uri.getPathSegments().get(0)); 96 break; 97 case MMS_INBOX_ID: 98 case MMS_SENT_ID: 99 case MMS_DRAFTS_ID: 100 case MMS_OUTBOX_ID: 101 qb.setTables(TABLE_PDU); 102 qb.appendWhere(Mms._ID + "=" + uri.getPathSegments().get(1)); 103 qb.appendWhere(" AND " + Mms.MESSAGE_BOX + "=" 104 + getMessageBoxByMatch(match)); 105 break; 106 case MMS_ALL_PART: 107 qb.setTables(TABLE_PART); 108 break; 109 case MMS_MSG_PART: 110 qb.setTables(TABLE_PART); 111 qb.appendWhere(Part.MSG_ID + "=" + uri.getPathSegments().get(0)); 112 break; 113 case MMS_PART_ID: 114 qb.setTables(TABLE_PART); 115 qb.appendWhere(Part._ID + "=" + uri.getPathSegments().get(1)); 116 break; 117 case MMS_MSG_ADDR: 118 qb.setTables(TABLE_ADDR); 119 qb.appendWhere(Addr.MSG_ID + "=" + uri.getPathSegments().get(0)); 120 break; 121 case MMS_REPORT_STATUS: 122 /* 123 SELECT DISTINCT address, 124 T.delivery_status AS delivery_status, 125 T.read_status AS read_status 126 FROM addr 127 INNER JOIN (SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3, 128 ifnull(P2.st, 0) AS delivery_status, 129 ifnull(P3.read_status, 0) AS read_status 130 FROM pdu P1 131 INNER JOIN pdu P2 132 ON P1.m_id = P2.m_id AND P2.m_type = 134 133 LEFT JOIN pdu P3 134 ON P1.m_id = P3.m_id AND P3.m_type = 136 135 UNION 136 SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3, 137 ifnull(P2.st, 0) AS delivery_status, 138 ifnull(P3.read_status, 0) AS read_status 139 FROM pdu P1 140 INNER JOIN pdu P3 141 ON P1.m_id = P3.m_id AND P3.m_type = 136 142 LEFT JOIN pdu P2 143 ON P1.m_id = P2.m_id AND P2.m_type = 134) T 144 ON (msg_id = id2 AND type = 151) 145 OR (msg_id = id3 AND type = 137) 146 WHERE T.id1 = ?; 147 */ 148 qb.setTables("addr INNER JOIN (SELECT P1._id AS id1, P2._id" + 149 " AS id2, P3._id AS id3, ifnull(P2.st, 0) AS" + 150 " delivery_status, ifnull(P3.read_status, 0) AS" + 151 " read_status FROM pdu P1 INNER JOIN pdu P2 ON" + 152 " P1.m_id=P2.m_id AND P2.m_type=134 LEFT JOIN" + 153 " pdu P3 ON P1.m_id=P3.m_id AND P3.m_type=136" + 154 " UNION SELECT P1._id AS id1, P2._id AS id2, P3._id" + 155 " AS id3, ifnull(P2.st, 0) AS delivery_status," + 156 " ifnull(P3.read_status, 0) AS read_status FROM" + 157 " pdu P1 INNER JOIN pdu P3 ON P1.m_id=P3.m_id AND" + 158 " P3.m_type=136 LEFT JOIN pdu P2 ON P1.m_id=P2.m_id" + 159 " AND P2.m_type=134) T ON (msg_id=id2 AND type=151)" + 160 " OR (msg_id=id3 AND type=137)"); 161 qb.appendWhere("T.id1 = " + uri.getLastPathSegment()); 162 qb.setDistinct(true); 163 break; 164 case MMS_REPORT_REQUEST: 165 /* 166 SELECT address, d_rpt, rr 167 FROM addr join pdu on pdu._id = addr.msg_id 168 WHERE pdu._id = messageId AND addr.type = 151 169 */ 170 qb.setTables(TABLE_ADDR + " join " + 171 TABLE_PDU + " on pdu._id = addr.msg_id"); 172 qb.appendWhere("pdu._id = " + uri.getLastPathSegment()); 173 qb.appendWhere(" AND " + "addr.type = " + PduHeaders.TO); 174 break; 175 case MMS_SENDING_RATE: 176 qb.setTables(TABLE_RATE); 177 break; 178 case MMS_DRM_STORAGE_ID: 179 qb.setTables(TABLE_DRM); 180 qb.appendWhere(BaseColumns._ID + "=" + uri.getLastPathSegment()); 181 break; 182 case MMS_THREADS: 183 qb.setTables("pdu group by thread_id"); 184 break; 185 default: 186 Log.e(TAG, "query: invalid request: " + uri); 187 return null; 188 } 189 190 String finalSortOrder = null; 191 if (TextUtils.isEmpty(sortOrder)) { 192 if (qb.getTables().equals(TABLE_PDU)) { 193 finalSortOrder = Mms.DATE + " DESC"; 194 } else if (qb.getTables().equals(TABLE_PART)) { 195 finalSortOrder = Part.SEQ; 196 } 197 } else { 198 finalSortOrder = sortOrder; 199 } 200 201 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 202 Cursor ret = qb.query(db, projection, selection, 203 selectionArgs, null, null, finalSortOrder); 204 205 // TODO: Does this need to be a URI for this provider. 206 ret.setNotificationUri(getContext().getContentResolver(), uri); 207 return ret; 208 } 209 210 private void constructQueryForBox(SQLiteQueryBuilder qb, int msgBox) { 211 qb.setTables(TABLE_PDU); 212 213 if (msgBox != Mms.MESSAGE_BOX_ALL) { 214 qb.appendWhere(Mms.MESSAGE_BOX + "=" + msgBox); 215 } 216 } 217 218 @Override 219 public String getType(Uri uri) { 220 int match = sURLMatcher.match(uri); 221 switch (match) { 222 case MMS_ALL: 223 case MMS_INBOX: 224 case MMS_SENT: 225 case MMS_DRAFTS: 226 case MMS_OUTBOX: 227 return VND_ANDROID_DIR_MMS; 228 case MMS_ALL_ID: 229 case MMS_INBOX_ID: 230 case MMS_SENT_ID: 231 case MMS_DRAFTS_ID: 232 case MMS_OUTBOX_ID: 233 return VND_ANDROID_MMS; 234 case MMS_PART_ID: { 235 Cursor cursor = mOpenHelper.getReadableDatabase().query( 236 TABLE_PART, new String[] { Part.CONTENT_TYPE }, 237 Part._ID + " = ?", new String[] { uri.getLastPathSegment() }, 238 null, null, null); 239 if (cursor != null) { 240 try { 241 if ((cursor.getCount() == 1) && cursor.moveToFirst()) { 242 return cursor.getString(0); 243 } else { 244 Log.e(TAG, "cursor.count() != 1: " + uri); 245 } 246 } finally { 247 cursor.close(); 248 } 249 } else { 250 Log.e(TAG, "cursor == null: " + uri); 251 } 252 return "*/*"; 253 } 254 case MMS_ALL_PART: 255 case MMS_MSG_PART: 256 case MMS_MSG_ADDR: 257 default: 258 return "*/*"; 259 } 260 } 261 262 @Override 263 public Uri insert(Uri uri, ContentValues values) { 264 int msgBox = Mms.MESSAGE_BOX_ALL; 265 boolean notify = true; 266 267 int match = sURLMatcher.match(uri); 268 if (LOCAL_LOGV) { 269 Log.v(TAG, "Insert uri=" + uri + ", match=" + match); 270 } 271 272 String table = TABLE_PDU; 273 switch (match) { 274 case MMS_ALL: 275 Object msgBoxObj = values.getAsInteger(Mms.MESSAGE_BOX); 276 if (msgBoxObj != null) { 277 msgBox = (Integer) msgBoxObj; 278 } 279 else { 280 // default to inbox 281 msgBox = Mms.MESSAGE_BOX_INBOX; 282 } 283 break; 284 case MMS_INBOX: 285 msgBox = Mms.MESSAGE_BOX_INBOX; 286 break; 287 case MMS_SENT: 288 msgBox = Mms.MESSAGE_BOX_SENT; 289 break; 290 case MMS_DRAFTS: 291 msgBox = Mms.MESSAGE_BOX_DRAFTS; 292 break; 293 case MMS_OUTBOX: 294 msgBox = Mms.MESSAGE_BOX_OUTBOX; 295 break; 296 case MMS_MSG_PART: 297 notify = false; 298 table = TABLE_PART; 299 break; 300 case MMS_MSG_ADDR: 301 notify = false; 302 table = TABLE_ADDR; 303 break; 304 case MMS_SENDING_RATE: 305 notify = false; 306 table = TABLE_RATE; 307 break; 308 case MMS_DRM_STORAGE: 309 notify = false; 310 table = TABLE_DRM; 311 break; 312 default: 313 Log.e(TAG, "insert: invalid request: " + uri); 314 return null; 315 } 316 317 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 318 ContentValues finalValues; 319 Uri res = Mms.CONTENT_URI; 320 long rowId; 321 322 if (table.equals(TABLE_PDU)) { 323 boolean addDate = !values.containsKey(Mms.DATE); 324 boolean addMsgBox = !values.containsKey(Mms.MESSAGE_BOX); 325 326 // Filter keys we don't support yet. 327 filterUnsupportedKeys(values); 328 329 // TODO: Should initialValues be validated, e.g. if it 330 // missed some significant keys? 331 finalValues = new ContentValues(values); 332 333 long timeInMillis = System.currentTimeMillis(); 334 335 if (addDate) { 336 finalValues.put(Mms.DATE, timeInMillis / 1000L); 337 } 338 339 if (addMsgBox && (msgBox != Mms.MESSAGE_BOX_ALL)) { 340 finalValues.put(Mms.MESSAGE_BOX, msgBox); 341 } 342 343 if (msgBox != Mms.MESSAGE_BOX_INBOX) { 344 // Mark all non-inbox messages read. 345 finalValues.put(Mms.READ, 1); 346 } 347 348 // thread_id 349 Long threadId = values.getAsLong(Mms.THREAD_ID); 350 String address = values.getAsString(CanonicalAddressesColumns.ADDRESS); 351 352 if (((threadId == null) || (threadId == 0)) && (address != null)) { 353 finalValues.put(Mms.THREAD_ID, Threads.getOrCreateThreadId(getContext(), address)); 354 } 355 356 if ((rowId = db.insert(table, null, finalValues)) <= 0) { 357 Log.e(TAG, "MmsProvider.insert: failed! " + finalValues); 358 return null; 359 } 360 361 res = Uri.parse(res + "/" + rowId); 362 363 } else if (table.equals(TABLE_ADDR)) { 364 finalValues = new ContentValues(values); 365 finalValues.put(Addr.MSG_ID, uri.getPathSegments().get(0)); 366 367 if ((rowId = db.insert(table, null, finalValues)) <= 0) { 368 Log.e(TAG, "Failed to insert address: " + finalValues); 369 return null; 370 } 371 372 res = Uri.parse(res + "/addr/" + rowId); 373 } else if (table.equals(TABLE_PART)) { 374 finalValues = new ContentValues(values); 375 376 if (match == MMS_MSG_PART) { 377 finalValues.put(Part.MSG_ID, uri.getPathSegments().get(0)); 378 } 379 380 String contentType = values.getAsString("ct"); 381 382 // text/plain and app application/smil store their "data" inline in the 383 // table so there's no need to create the file 384 boolean plainText = "text/plain".equals(contentType); 385 boolean smilText = "application/smil".equals(contentType); 386 if (!plainText && !smilText) { 387 // Generate the '_data' field of the part with default 388 // permission settings. 389 String path = getContext().getDir("parts", 0).getPath() 390 + "/PART_" + System.currentTimeMillis(); 391 392 finalValues.put(Part._DATA, path); 393 394 File partFile = new File(path); 395 if (!partFile.exists()) { 396 try { 397 if (!partFile.createNewFile()) { 398 throw new IllegalStateException( 399 "Unable to create new partFile: " + path); 400 } 401 } catch (IOException e) { 402 Log.e(TAG, "createNewFile", e); 403 throw new IllegalStateException( 404 "Unable to create new partFile: " + path); 405 } 406 } 407 } 408 409 if ((rowId = db.insert(table, null, finalValues)) <= 0) { 410 Log.e(TAG, "MmsProvider.insert: failed! " + finalValues); 411 return null; 412 } 413 414 res = Uri.parse(res + "/part/" + rowId); 415 416 // Don't use a trigger for updating the words table because of a bug 417 // in FTS3. The bug is such that the call to get the last inserted 418 // row is incorrect. 419 if (plainText) { 420 // Update the words table with a corresponding row. The words table 421 // allows us to search for words quickly, without scanning the whole 422 // table; 423 ContentValues cv = new ContentValues(); 424 425 // we're using the row id of the part table row but we're also using ids 426 // from the sms table so this divides the space into two large chunks. 427 // The row ids from the part table start at 2 << 32. 428 cv.put(Telephony.MmsSms.WordsTable.ID, (2 << 32) + rowId); 429 cv.put(Telephony.MmsSms.WordsTable.INDEXED_TEXT, values.getAsString("text")); 430 cv.put(Telephony.MmsSms.WordsTable.SOURCE_ROW_ID, rowId); 431 cv.put(Telephony.MmsSms.WordsTable.TABLE_ID, 2); 432 db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv); 433 } 434 435 } else if (table.equals(TABLE_RATE)) { 436 long now = values.getAsLong(Rate.SENT_TIME); 437 long oneHourAgo = now - 1000 * 60 * 60; 438 // Delete all unused rows (time earlier than one hour ago). 439 db.delete(table, Rate.SENT_TIME + "<=" + oneHourAgo, null); 440 db.insert(table, null, values); 441 } else if (table.equals(TABLE_DRM)) { 442 String path = getContext().getDir("parts", 0).getPath() 443 + "/PART_" + System.currentTimeMillis(); 444 finalValues = new ContentValues(1); 445 finalValues.put("_data", path); 446 447 File partFile = new File(path); 448 if (!partFile.exists()) { 449 try { 450 if (!partFile.createNewFile()) { 451 throw new IllegalStateException( 452 "Unable to create new file: " + path); 453 } 454 } catch (IOException e) { 455 Log.e(TAG, "createNewFile", e); 456 throw new IllegalStateException( 457 "Unable to create new file: " + path); 458 } 459 } 460 461 if ((rowId = db.insert(table, null, finalValues)) <= 0) { 462 Log.e(TAG, "MmsProvider.insert: failed! " + finalValues); 463 return null; 464 } 465 res = Uri.parse(res + "/drm/" + rowId); 466 } else { 467 throw new AssertionError("Unknown table type: " + table); 468 } 469 470 if (notify) { 471 notifyChange(); 472 } 473 return res; 474 } 475 476 private int getMessageBoxByMatch(int match) { 477 switch (match) { 478 case MMS_INBOX_ID: 479 case MMS_INBOX: 480 return Mms.MESSAGE_BOX_INBOX; 481 case MMS_SENT_ID: 482 case MMS_SENT: 483 return Mms.MESSAGE_BOX_SENT; 484 case MMS_DRAFTS_ID: 485 case MMS_DRAFTS: 486 return Mms.MESSAGE_BOX_DRAFTS; 487 case MMS_OUTBOX_ID: 488 case MMS_OUTBOX: 489 return Mms.MESSAGE_BOX_OUTBOX; 490 default: 491 throw new IllegalArgumentException("bad Arg: " + match); 492 } 493 } 494 495 @Override 496 public int delete(Uri uri, String selection, 497 String[] selectionArgs) { 498 int match = sURLMatcher.match(uri); 499 if (LOCAL_LOGV) { 500 Log.v(TAG, "Delete uri=" + uri + ", match=" + match); 501 } 502 503 String table, extraSelection = null; 504 boolean notify = false; 505 506 switch (match) { 507 case MMS_ALL_ID: 508 case MMS_INBOX_ID: 509 case MMS_SENT_ID: 510 case MMS_DRAFTS_ID: 511 case MMS_OUTBOX_ID: 512 notify = true; 513 table = TABLE_PDU; 514 extraSelection = Mms._ID + "=" + uri.getLastPathSegment(); 515 break; 516 case MMS_ALL: 517 case MMS_INBOX: 518 case MMS_SENT: 519 case MMS_DRAFTS: 520 case MMS_OUTBOX: 521 notify = true; 522 table = TABLE_PDU; 523 if (match != MMS_ALL) { 524 int msgBox = getMessageBoxByMatch(match); 525 extraSelection = Mms.MESSAGE_BOX + "=" + msgBox; 526 } 527 break; 528 case MMS_ALL_PART: 529 table = TABLE_PART; 530 break; 531 case MMS_MSG_PART: 532 table = TABLE_PART; 533 extraSelection = Part.MSG_ID + "=" + uri.getPathSegments().get(0); 534 break; 535 case MMS_PART_ID: 536 table = TABLE_PART; 537 extraSelection = Part._ID + "=" + uri.getPathSegments().get(1); 538 break; 539 case MMS_MSG_ADDR: 540 table = TABLE_ADDR; 541 extraSelection = Addr.MSG_ID + "=" + uri.getPathSegments().get(0); 542 break; 543 case MMS_DRM_STORAGE: 544 table = TABLE_DRM; 545 break; 546 default: 547 Log.w(TAG, "No match for URI '" + uri + "'"); 548 return 0; 549 } 550 551 String finalSelection = concatSelections(selection, extraSelection); 552 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 553 int deletedRows = 0; 554 555 if (TABLE_PDU.equals(table)) { 556 deletedRows = deleteMessages(getContext(), db, finalSelection, 557 selectionArgs, uri); 558 } else if (TABLE_PART.equals(table)) { 559 deletedRows = deleteParts(db, finalSelection, selectionArgs); 560 } else if (TABLE_DRM.equals(table)) { 561 deletedRows = deleteTempDrmData(db, finalSelection, selectionArgs); 562 } else { 563 deletedRows = db.delete(table, finalSelection, selectionArgs); 564 } 565 566 if ((deletedRows > 0) && notify) { 567 notifyChange(); 568 } 569 return deletedRows; 570 } 571 572 static int deleteMessages(Context context, SQLiteDatabase db, 573 String selection, String[] selectionArgs, Uri uri) { 574 Cursor cursor = db.query(TABLE_PDU, new String[] { Mms._ID }, 575 selection, selectionArgs, null, null, null); 576 if (cursor == null) { 577 return 0; 578 } 579 580 try { 581 if (cursor.getCount() == 0) { 582 return 0; 583 } 584 585 while (cursor.moveToNext()) { 586 deleteParts(db, Part.MSG_ID + " = ?", 587 new String[] { String.valueOf(cursor.getLong(0)) }); 588 } 589 } finally { 590 cursor.close(); 591 } 592 593 int count = db.delete(TABLE_PDU, selection, selectionArgs); 594 if (count > 0) { 595 Intent intent = new Intent(Mms.Intents.CONTENT_CHANGED_ACTION); 596 intent.putExtra(Mms.Intents.DELETED_CONTENTS, uri); 597 if (LOCAL_LOGV) { 598 Log.v(TAG, "Broadcasting intent: " + intent); 599 } 600 context.sendBroadcast(intent); 601 } 602 return count; 603 } 604 605 private static int deleteParts(SQLiteDatabase db, String selection, 606 String[] selectionArgs) { 607 return deleteDataRows(db, TABLE_PART, selection, selectionArgs); 608 } 609 610 private static int deleteTempDrmData(SQLiteDatabase db, String selection, 611 String[] selectionArgs) { 612 return deleteDataRows(db, TABLE_DRM, selection, selectionArgs); 613 } 614 615 private static int deleteDataRows(SQLiteDatabase db, String table, 616 String selection, String[] selectionArgs) { 617 Cursor cursor = db.query(table, new String[] { "_data" }, 618 selection, selectionArgs, null, null, null); 619 if (cursor == null) { 620 // FIXME: This might be an error, ignore it may cause 621 // unpredictable result. 622 return 0; 623 } 624 625 try { 626 if (cursor.getCount() == 0) { 627 return 0; 628 } 629 630 while (cursor.moveToNext()) { 631 try { 632 // Delete the associated files saved on file-system. 633 String path = cursor.getString(0); 634 if (path != null) { 635 new File(path).delete(); 636 } 637 } catch (Throwable ex) { 638 Log.e(TAG, ex.getMessage(), ex); 639 } 640 } 641 } finally { 642 cursor.close(); 643 } 644 645 return db.delete(table, selection, selectionArgs); 646 } 647 648 @Override 649 public int update(Uri uri, ContentValues values, 650 String selection, String[] selectionArgs) { 651 int match = sURLMatcher.match(uri); 652 if (LOCAL_LOGV) { 653 Log.v(TAG, "Update uri=" + uri + ", match=" + match); 654 } 655 656 boolean notify = false; 657 String msgId = null; 658 String table; 659 660 switch (match) { 661 case MMS_ALL_ID: 662 case MMS_INBOX_ID: 663 case MMS_SENT_ID: 664 case MMS_DRAFTS_ID: 665 case MMS_OUTBOX_ID: 666 msgId = uri.getLastPathSegment(); 667 // fall-through 668 case MMS_ALL: 669 case MMS_INBOX: 670 case MMS_SENT: 671 case MMS_DRAFTS: 672 case MMS_OUTBOX: 673 notify = true; 674 table = TABLE_PDU; 675 break; 676 case MMS_MSG_PART: 677 case MMS_PART_ID: 678 table = TABLE_PART; 679 break; 680 default: 681 Log.w(TAG, "Update operation for '" + uri + "' not implemented."); 682 return 0; 683 } 684 685 String extraSelection = null; 686 ContentValues finalValues; 687 if (table.equals(TABLE_PDU)) { 688 // Filter keys that we don't support yet. 689 filterUnsupportedKeys(values); 690 finalValues = new ContentValues(values); 691 692 if (msgId != null) { 693 extraSelection = Mms._ID + "=" + msgId; 694 } 695 } else if (table.equals(TABLE_PART)) { 696 finalValues = new ContentValues(values); 697 698 switch (match) { 699 case MMS_MSG_PART: 700 extraSelection = Part.MSG_ID + "=" + uri.getPathSegments().get(0); 701 break; 702 case MMS_PART_ID: 703 extraSelection = Part._ID + "=" + uri.getPathSegments().get(1); 704 break; 705 default: 706 break; 707 } 708 } else { 709 return 0; 710 } 711 712 String finalSelection = concatSelections(selection, extraSelection); 713 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 714 int count = db.update(table, finalValues, finalSelection, selectionArgs); 715 if (notify && (count > 0)) { 716 notifyChange(); 717 } 718 return count; 719 } 720 721 @Override 722 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 723 // TODO do we even need this anymore? 724 ParcelFileDescriptor fd; 725 int match = sURLMatcher.match(uri); 726 727 if (Log.isLoggable(TAG, Log.VERBOSE)) { 728 Log.d(TAG, "openFile: uri=" + uri + ", mode=" + mode); 729 } 730 731 switch (match) { 732 default: 733 fd = openFileHelper(uri, mode); 734 } 735 736 return fd; 737 } 738 739 private void filterUnsupportedKeys(ContentValues values) { 740 // Some columns are unsupported. They should therefore 741 // neither be inserted nor updated. Filter them out. 742 values.remove(Mms.DELIVERY_TIME_TOKEN); 743 values.remove(Mms.SENDER_VISIBILITY); 744 values.remove(Mms.REPLY_CHARGING); 745 values.remove(Mms.REPLY_CHARGING_DEADLINE_TOKEN); 746 values.remove(Mms.REPLY_CHARGING_DEADLINE); 747 values.remove(Mms.REPLY_CHARGING_ID); 748 values.remove(Mms.REPLY_CHARGING_SIZE); 749 values.remove(Mms.PREVIOUSLY_SENT_BY); 750 values.remove(Mms.PREVIOUSLY_SENT_DATE); 751 values.remove(Mms.STORE); 752 values.remove(Mms.MM_STATE); 753 values.remove(Mms.MM_FLAGS_TOKEN); 754 values.remove(Mms.MM_FLAGS); 755 values.remove(Mms.STORE_STATUS); 756 values.remove(Mms.STORE_STATUS_TEXT); 757 values.remove(Mms.STORED); 758 values.remove(Mms.TOTALS); 759 values.remove(Mms.MBOX_TOTALS); 760 values.remove(Mms.MBOX_TOTALS_TOKEN); 761 values.remove(Mms.QUOTAS); 762 values.remove(Mms.MBOX_QUOTAS); 763 values.remove(Mms.MBOX_QUOTAS_TOKEN); 764 values.remove(Mms.MESSAGE_COUNT); 765 values.remove(Mms.START); 766 values.remove(Mms.DISTRIBUTION_INDICATOR); 767 values.remove(Mms.ELEMENT_DESCRIPTOR); 768 values.remove(Mms.LIMIT); 769 values.remove(Mms.RECOMMENDED_RETRIEVAL_MODE); 770 values.remove(Mms.RECOMMENDED_RETRIEVAL_MODE_TEXT); 771 values.remove(Mms.STATUS_TEXT); 772 values.remove(Mms.APPLIC_ID); 773 values.remove(Mms.REPLY_APPLIC_ID); 774 values.remove(Mms.AUX_APPLIC_ID); 775 values.remove(Mms.DRM_CONTENT); 776 values.remove(Mms.ADAPTATION_ALLOWED); 777 values.remove(Mms.REPLACE_ID); 778 values.remove(Mms.CANCEL_ID); 779 values.remove(Mms.CANCEL_STATUS); 780 781 // Keys shouldn't be inserted or updated. 782 values.remove(Mms._ID); 783 } 784 785 private void notifyChange() { 786 getContext().getContentResolver().notifyChange( 787 MmsSms.CONTENT_URI, null); 788 } 789 790 private final static String TAG = "MmsProvider"; 791 private final static String VND_ANDROID_MMS = "vnd.android/mms"; 792 private final static String VND_ANDROID_DIR_MMS = "vnd.android-dir/mms"; 793 private final static boolean DEBUG = false; 794 private final static boolean LOCAL_LOGV = false; 795 796 private static final int MMS_ALL = 0; 797 private static final int MMS_ALL_ID = 1; 798 private static final int MMS_INBOX = 2; 799 private static final int MMS_INBOX_ID = 3; 800 private static final int MMS_SENT = 4; 801 private static final int MMS_SENT_ID = 5; 802 private static final int MMS_DRAFTS = 6; 803 private static final int MMS_DRAFTS_ID = 7; 804 private static final int MMS_OUTBOX = 8; 805 private static final int MMS_OUTBOX_ID = 9; 806 private static final int MMS_ALL_PART = 10; 807 private static final int MMS_MSG_PART = 11; 808 private static final int MMS_PART_ID = 12; 809 private static final int MMS_MSG_ADDR = 13; 810 private static final int MMS_SENDING_RATE = 14; 811 private static final int MMS_REPORT_STATUS = 15; 812 private static final int MMS_REPORT_REQUEST = 16; 813 private static final int MMS_DRM_STORAGE = 17; 814 private static final int MMS_DRM_STORAGE_ID = 18; 815 private static final int MMS_THREADS = 19; 816 817 private static final UriMatcher 818 sURLMatcher = new UriMatcher(UriMatcher.NO_MATCH); 819 820 static { 821 sURLMatcher.addURI("mms", null, MMS_ALL); 822 sURLMatcher.addURI("mms", "#", MMS_ALL_ID); 823 sURLMatcher.addURI("mms", "inbox", MMS_INBOX); 824 sURLMatcher.addURI("mms", "inbox/#", MMS_INBOX_ID); 825 sURLMatcher.addURI("mms", "sent", MMS_SENT); 826 sURLMatcher.addURI("mms", "sent/#", MMS_SENT_ID); 827 sURLMatcher.addURI("mms", "drafts", MMS_DRAFTS); 828 sURLMatcher.addURI("mms", "drafts/#", MMS_DRAFTS_ID); 829 sURLMatcher.addURI("mms", "outbox", MMS_OUTBOX); 830 sURLMatcher.addURI("mms", "outbox/#", MMS_OUTBOX_ID); 831 sURLMatcher.addURI("mms", "part", MMS_ALL_PART); 832 sURLMatcher.addURI("mms", "#/part", MMS_MSG_PART); 833 sURLMatcher.addURI("mms", "part/#", MMS_PART_ID); 834 sURLMatcher.addURI("mms", "#/addr", MMS_MSG_ADDR); 835 sURLMatcher.addURI("mms", "rate", MMS_SENDING_RATE); 836 sURLMatcher.addURI("mms", "report-status/#", MMS_REPORT_STATUS); 837 sURLMatcher.addURI("mms", "report-request/#", MMS_REPORT_REQUEST); 838 sURLMatcher.addURI("mms", "drm", MMS_DRM_STORAGE); 839 sURLMatcher.addURI("mms", "drm/#", MMS_DRM_STORAGE_ID); 840 sURLMatcher.addURI("mms", "threads", MMS_THREADS); 841 } 842 843 private SQLiteOpenHelper mOpenHelper; 844 845 private static String concatSelections(String selection1, String selection2) { 846 if (TextUtils.isEmpty(selection1)) { 847 return selection2; 848 } else if (TextUtils.isEmpty(selection2)) { 849 return selection1; 850 } else { 851 return selection1 + " AND " + selection2; 852 } 853 } 854} 855 856