blob: 5ddffe854b2cfe9c9dc2d7c3d16f4e2cade835c1 [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;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080032import 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
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;
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";
Mark Wagner8e5ee782010-01-04 17:39:06 -080057 static final String TABLE_WORDS = "words";
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080058
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;
Tom Taylor6c0ef242009-06-01 12:05:00 -0700181 case MMS_THREADS:
182 qb.setTables("pdu group by thread_id");
183 break;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800184 default:
Wei Huang2914a7a2009-09-23 00:45:36 -0700185 Log.e(TAG, "query: invalid request: " + uri);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800186 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:
Wei Huang2914a7a2009-09-23 00:45:36 -0700312 Log.e(TAG, "insert: invalid request: " + uri);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800313 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
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700371 String contentType = values.getAsString("ct");
Mark Wagner8e5ee782010-01-04 17:39:06 -0800372
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700373 // text/plain and app application/smil store their "data" inline in the
374 // table so there's no need to create the file
Mark Wagner8e5ee782010-01-04 17:39:06 -0800375 boolean plainText = "text/plain".equals(contentType);
376 boolean smilText = "application/smil".equals(contentType);
377 if (!plainText && !smilText) {
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700378 // Generate the '_data' field of the part with default
379 // permission settings.
380 String path = getContext().getDir("parts", 0).getPath()
381 + "/PART_" + System.currentTimeMillis();
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800382
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700383 finalValues.put(Part._DATA, path);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800384
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700385 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);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800394 throw new IllegalStateException(
395 "Unable to create new partFile: " + path);
396 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800397 }
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);
Mark Wagner8e5ee782010-01-04 17:39:06 -0800406
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
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800426 } 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.
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700624 String path = cursor.getString(0);
625 if (path != null) {
626 new File(path).delete();
627 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800628 } 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
Wei Huang2914a7a2009-09-23 00:45:36 -0700712 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
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800737 @Override
Wei Huang2914a7a2009-09-23 00:45:36 -0700738 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;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800759 }
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;
Tom Taylor6c0ef242009-06-01 12:05:00 -0700833 private static final int MMS_REPORT_STATUS = 15;
834 private static final int MMS_REPORT_REQUEST = 16;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800835 private static final int MMS_DRM_STORAGE = 17;
836 private static final int MMS_DRM_STORAGE_ID = 18;
Tom Taylor6c0ef242009-06-01 12:05:00 -0700837 private static final int MMS_THREADS = 19;
Wei Huang2914a7a2009-09-23 00:45:36 -0700838 private static final int MMS_SCRAP_SPACE = 20;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800839
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);
Tom Taylor6c0ef242009-06-01 12:05:00 -0700863 sURLMatcher.addURI("mms", "threads", MMS_THREADS);
Wei Huang2914a7a2009-09-23 00:45:36 -0700864 sURLMatcher.addURI("mms", "scrapSpace", MMS_SCRAP_SPACE);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800865 }
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