blob: 5df3fdffd4bb0ca71aa357b02df00ebdb0a111d4 [file] [log] [blame]
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001/*
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
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080019import 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;
Mark Wagner8e5ee782010-01-04 17:39:06 -080031import android.provider.Telephony;
Yusuf T. Mobile21c25bc2009-06-16 13:40:38 -070032import android.provider.Telephony.CanonicalAddressesColumns;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080033import 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;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080039import android.util.Log;
40
Tom Taylorb1bae652010-03-08 16:33:42 -080041import com.google.android.mms.pdu.PduHeaders;
Tom Taylorc71e7702010-01-28 09:23:12 -080042
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080043import java.io.File;
44import java.io.FileNotFoundException;
45import java.io.IOException;
Yusuf T. Mobile21c25bc2009-06-16 13:40:38 -070046import android.provider.Telephony.Threads;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080047
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";
Mark Wagner8e5ee782010-01-04 17:39:06 -080058 static final String TABLE_WORDS = "words";
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080059
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;
Tom Taylor6c0ef242009-06-01 12:05:00 -0700182 case MMS_THREADS:
183 qb.setTables("pdu group by thread_id");
184 break;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800185 default:
Wei Huang2914a7a2009-09-23 00:45:36 -0700186 Log.e(TAG, "query: invalid request: " + uri);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800187 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:
Wei Huang2914a7a2009-09-23 00:45:36 -0700313 Log.e(TAG, "insert: invalid request: " + uri);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800314 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
Yusuf T. Mobile21c25bc2009-06-16 13:40:38 -0700348 // 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
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800356 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
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700380 String contentType = values.getAsString("ct");
Mark Wagner8e5ee782010-01-04 17:39:06 -0800381
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700382 // text/plain and app application/smil store their "data" inline in the
383 // table so there's no need to create the file
Mark Wagner8e5ee782010-01-04 17:39:06 -0800384 boolean plainText = "text/plain".equals(contentType);
385 boolean smilText = "application/smil".equals(contentType);
386 if (!plainText && !smilText) {
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700387 // Generate the '_data' field of the part with default
388 // permission settings.
389 String path = getContext().getDir("parts", 0).getPath()
390 + "/PART_" + System.currentTimeMillis();
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800391
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700392 finalValues.put(Part._DATA, path);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800393
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700394 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);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800403 throw new IllegalStateException(
404 "Unable to create new partFile: " + path);
405 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800406 }
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);
Mark Wagner8e5ee782010-01-04 17:39:06 -0800415
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
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800435 } 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.
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700633 String path = cursor.getString(0);
634 if (path != null) {
635 new File(path).delete();
636 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800637 } 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
Wei Huang2914a7a2009-09-23 00:45:36 -0700721 private ParcelFileDescriptor getTempStoreFd() {
722 String fileName = Mms.ScrapSpace.SCRAP_FILE_PATH;
723 ParcelFileDescriptor pfd = null;
724
725 try {
726 File file = new File(fileName);
727
728 // make sure the path is valid and directories created for this file.
729 File parentFile = file.getParentFile();
730 if (!parentFile.exists() && !parentFile.mkdirs()) {
731 Log.e(TAG, "[MmsProvider] getTempStoreFd: " + parentFile.getPath() +
732 "does not exist!");
733 return null;
734 }
735
736 pfd = ParcelFileDescriptor.open(file,
737 ParcelFileDescriptor.MODE_READ_WRITE
738 | android.os.ParcelFileDescriptor.MODE_CREATE);
739 } catch (Exception ex) {
740 Log.e(TAG, "getTempStoreFd: error creating pfd for " + fileName, ex);
741 }
742
743 return pfd;
744 }
745
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800746 @Override
Wei Huang2914a7a2009-09-23 00:45:36 -0700747 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
748 // if the url is "content://mms/takePictureTempStore", then it means the requester
749 // wants a file descriptor to write image data to.
750
751 ParcelFileDescriptor fd;
752 int match = sURLMatcher.match(uri);
753
754 if (Log.isLoggable(TAG, Log.VERBOSE)) {
755 Log.d(TAG, "openFile: uri=" + uri + ", mode=" + mode);
756 }
757
758 switch (match) {
759 case MMS_SCRAP_SPACE:
760 fd = getTempStoreFd();
761 break;
762
763 default:
764 fd = openFileHelper(uri, mode);
765 }
766
767 return fd;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800768 }
769
770 private void filterUnsupportedKeys(ContentValues values) {
771 // Some columns are unsupported. They should therefore
772 // neither be inserted nor updated. Filter them out.
773 values.remove(Mms.DELIVERY_TIME_TOKEN);
774 values.remove(Mms.SENDER_VISIBILITY);
775 values.remove(Mms.REPLY_CHARGING);
776 values.remove(Mms.REPLY_CHARGING_DEADLINE_TOKEN);
777 values.remove(Mms.REPLY_CHARGING_DEADLINE);
778 values.remove(Mms.REPLY_CHARGING_ID);
779 values.remove(Mms.REPLY_CHARGING_SIZE);
780 values.remove(Mms.PREVIOUSLY_SENT_BY);
781 values.remove(Mms.PREVIOUSLY_SENT_DATE);
782 values.remove(Mms.STORE);
783 values.remove(Mms.MM_STATE);
784 values.remove(Mms.MM_FLAGS_TOKEN);
785 values.remove(Mms.MM_FLAGS);
786 values.remove(Mms.STORE_STATUS);
787 values.remove(Mms.STORE_STATUS_TEXT);
788 values.remove(Mms.STORED);
789 values.remove(Mms.TOTALS);
790 values.remove(Mms.MBOX_TOTALS);
791 values.remove(Mms.MBOX_TOTALS_TOKEN);
792 values.remove(Mms.QUOTAS);
793 values.remove(Mms.MBOX_QUOTAS);
794 values.remove(Mms.MBOX_QUOTAS_TOKEN);
795 values.remove(Mms.MESSAGE_COUNT);
796 values.remove(Mms.START);
797 values.remove(Mms.DISTRIBUTION_INDICATOR);
798 values.remove(Mms.ELEMENT_DESCRIPTOR);
799 values.remove(Mms.LIMIT);
800 values.remove(Mms.RECOMMENDED_RETRIEVAL_MODE);
801 values.remove(Mms.RECOMMENDED_RETRIEVAL_MODE_TEXT);
802 values.remove(Mms.STATUS_TEXT);
803 values.remove(Mms.APPLIC_ID);
804 values.remove(Mms.REPLY_APPLIC_ID);
805 values.remove(Mms.AUX_APPLIC_ID);
806 values.remove(Mms.DRM_CONTENT);
807 values.remove(Mms.ADAPTATION_ALLOWED);
808 values.remove(Mms.REPLACE_ID);
809 values.remove(Mms.CANCEL_ID);
810 values.remove(Mms.CANCEL_STATUS);
811
812 // Keys shouldn't be inserted or updated.
813 values.remove(Mms._ID);
814 }
815
816 private void notifyChange() {
817 getContext().getContentResolver().notifyChange(
818 MmsSms.CONTENT_URI, null);
819 }
820
821 private final static String TAG = "MmsProvider";
822 private final static String VND_ANDROID_MMS = "vnd.android/mms";
823 private final static String VND_ANDROID_DIR_MMS = "vnd.android-dir/mms";
824 private final static boolean DEBUG = false;
Joe Onoratodc946792011-04-07 18:41:15 -0700825 private final static boolean LOCAL_LOGV = false;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800826
827 private static final int MMS_ALL = 0;
828 private static final int MMS_ALL_ID = 1;
829 private static final int MMS_INBOX = 2;
830 private static final int MMS_INBOX_ID = 3;
831 private static final int MMS_SENT = 4;
832 private static final int MMS_SENT_ID = 5;
833 private static final int MMS_DRAFTS = 6;
834 private static final int MMS_DRAFTS_ID = 7;
835 private static final int MMS_OUTBOX = 8;
836 private static final int MMS_OUTBOX_ID = 9;
837 private static final int MMS_ALL_PART = 10;
838 private static final int MMS_MSG_PART = 11;
839 private static final int MMS_PART_ID = 12;
840 private static final int MMS_MSG_ADDR = 13;
841 private static final int MMS_SENDING_RATE = 14;
Tom Taylor6c0ef242009-06-01 12:05:00 -0700842 private static final int MMS_REPORT_STATUS = 15;
843 private static final int MMS_REPORT_REQUEST = 16;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800844 private static final int MMS_DRM_STORAGE = 17;
845 private static final int MMS_DRM_STORAGE_ID = 18;
Tom Taylor6c0ef242009-06-01 12:05:00 -0700846 private static final int MMS_THREADS = 19;
Wei Huang2914a7a2009-09-23 00:45:36 -0700847 private static final int MMS_SCRAP_SPACE = 20;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800848
849 private static final UriMatcher
850 sURLMatcher = new UriMatcher(UriMatcher.NO_MATCH);
851
852 static {
853 sURLMatcher.addURI("mms", null, MMS_ALL);
854 sURLMatcher.addURI("mms", "#", MMS_ALL_ID);
855 sURLMatcher.addURI("mms", "inbox", MMS_INBOX);
856 sURLMatcher.addURI("mms", "inbox/#", MMS_INBOX_ID);
857 sURLMatcher.addURI("mms", "sent", MMS_SENT);
858 sURLMatcher.addURI("mms", "sent/#", MMS_SENT_ID);
859 sURLMatcher.addURI("mms", "drafts", MMS_DRAFTS);
860 sURLMatcher.addURI("mms", "drafts/#", MMS_DRAFTS_ID);
861 sURLMatcher.addURI("mms", "outbox", MMS_OUTBOX);
862 sURLMatcher.addURI("mms", "outbox/#", MMS_OUTBOX_ID);
863 sURLMatcher.addURI("mms", "part", MMS_ALL_PART);
864 sURLMatcher.addURI("mms", "#/part", MMS_MSG_PART);
865 sURLMatcher.addURI("mms", "part/#", MMS_PART_ID);
866 sURLMatcher.addURI("mms", "#/addr", MMS_MSG_ADDR);
867 sURLMatcher.addURI("mms", "rate", MMS_SENDING_RATE);
868 sURLMatcher.addURI("mms", "report-status/#", MMS_REPORT_STATUS);
869 sURLMatcher.addURI("mms", "report-request/#", MMS_REPORT_REQUEST);
870 sURLMatcher.addURI("mms", "drm", MMS_DRM_STORAGE);
871 sURLMatcher.addURI("mms", "drm/#", MMS_DRM_STORAGE_ID);
Tom Taylor6c0ef242009-06-01 12:05:00 -0700872 sURLMatcher.addURI("mms", "threads", MMS_THREADS);
Wei Huang2914a7a2009-09-23 00:45:36 -0700873 sURLMatcher.addURI("mms", "scrapSpace", MMS_SCRAP_SPACE);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800874 }
875
876 private SQLiteOpenHelper mOpenHelper;
877
878 private static String concatSelections(String selection1, String selection2) {
879 if (TextUtils.isEmpty(selection1)) {
880 return selection2;
881 } else if (TextUtils.isEmpty(selection2)) {
882 return selection1;
883 } else {
884 return selection1 + " AND " + selection2;
885 }
886 }
887}
888