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