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