blob: ccd9ab191b758e59de869ab55625a111142d21de [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
Andre Furtado867477a2016-08-18 10:28:37 -070019import android.annotation.NonNull;
Dianne Hackbornf27792f2013-02-04 18:26:53 -080020import android.app.AppOpsManager;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080021import android.content.ContentProvider;
kaiyiz81ee3bc2015-02-06 13:43:48 +080022import android.content.ContentUris;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080023import android.content.ContentValues;
24import android.content.Context;
25import android.content.Intent;
26import android.content.UriMatcher;
27import android.database.Cursor;
kaiyiz81ee3bc2015-02-06 13:43:48 +080028import android.database.MatrixCursor;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080029import android.database.sqlite.SQLiteDatabase;
Tom Taylor3ad9da42014-01-16 13:57:11 -080030import android.database.sqlite.SQLiteException;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080031import android.database.sqlite.SQLiteOpenHelper;
32import android.database.sqlite.SQLiteQueryBuilder;
33import android.net.Uri;
Ye Wene07acb92014-11-19 12:06:05 -080034import android.os.Binder;
Tom Taylorc2db47d2012-03-27 15:15:39 -070035import android.os.FileUtils;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080036import android.os.ParcelFileDescriptor;
Amith Yamasani43f9fb22014-09-10 15:56:47 -070037import android.os.UserHandle;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080038import android.provider.BaseColumns;
Mark Wagner8e5ee782010-01-04 17:39:06 -080039import android.provider.Telephony;
Yusuf T. Mobile21c25bc2009-06-16 13:40:38 -070040import android.provider.Telephony.CanonicalAddressesColumns;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080041import android.provider.Telephony.Mms;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080042import android.provider.Telephony.Mms.Addr;
43import android.provider.Telephony.Mms.Part;
44import android.provider.Telephony.Mms.Rate;
Ye Wene07acb92014-11-19 12:06:05 -080045import android.provider.Telephony.MmsSms;
46import android.provider.Telephony.Threads;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080047import android.text.TextUtils;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080048import android.util.Log;
49
kaiyiz81ee3bc2015-02-06 13:43:48 +080050import com.google.android.mms.MmsException;
51import com.google.android.mms.pdu.GenericPdu;
52import com.google.android.mms.pdu.PduComposer;
53import com.google.android.mms.pdu.RetrieveConf;
Tom Taylorb1bae652010-03-08 16:33:42 -080054import com.google.android.mms.pdu.PduHeaders;
kaiyiz81ee3bc2015-02-06 13:43:48 +080055import com.google.android.mms.pdu.PduPersister;
56import com.google.android.mms.pdu.PduParser;
57import com.google.android.mms.pdu.SendReq;
Tom Taylorc2db47d2012-03-27 15:15:39 -070058import com.google.android.mms.util.DownloadDrmHelper;
Tom Taylorc71e7702010-01-28 09:23:12 -080059
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080060import java.io.File;
kaiyiz81ee3bc2015-02-06 13:43:48 +080061import java.io.FileInputStream;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080062import java.io.FileNotFoundException;
63import java.io.IOException;
kaiyizcd21c4f2015-01-30 11:22:36 +080064import java.util.HashSet;
Amith Yamasani43f9fb22014-09-10 15:56:47 -070065
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080066/**
67 * The class to provide base facility to access MMS related content,
68 * which is stored in a SQLite database and in the file system.
69 */
70public class MmsProvider extends ContentProvider {
71 static final String TABLE_PDU = "pdu";
72 static final String TABLE_ADDR = "addr";
73 static final String TABLE_PART = "part";
74 static final String TABLE_RATE = "rate";
75 static final String TABLE_DRM = "drm";
Mark Wagner8e5ee782010-01-04 17:39:06 -080076 static final String TABLE_WORDS = "words";
Ye Wen72f13552015-03-10 14:17:13 -070077 static final String VIEW_PDU_RESTRICTED = "pdu_restricted";
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080078
Ye Weneaa93e62015-01-06 13:08:53 -080079 // The name of parts directory. The full dir is "app_parts".
80 private static final String PARTS_DIR_NAME = "parts";
kaiyiz81ee3bc2015-02-06 13:43:48 +080081 static final String COLUMN_PDU_PATH = "pdu_path";
82
83 private final static String[] PDU_COLUMNS = new String[] {
84 "_id",
85 "pdu_path",
86 "pdu_data"
87 };
Tom Taylorc2db47d2012-03-27 15:15:39 -070088
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080089 @Override
90 public boolean onCreate() {
Ye Wenb2ce2d32014-07-28 14:49:30 -070091 setAppOps(AppOpsManager.OP_READ_SMS, AppOpsManager.OP_WRITE_SMS);
92 mOpenHelper = MmsSmsDatabaseHelper.getInstance(getContext());
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080093 return true;
94 }
95
Ye Wen72f13552015-03-10 14:17:13 -070096 /**
97 * Return the proper view of "pdu" table for the current access status.
98 *
99 * @param accessRestricted If the access is restricted
100 * @return the table/view name of the mms data
101 */
102 public static String getPduTable(boolean accessRestricted) {
103 return accessRestricted ? VIEW_PDU_RESTRICTED : TABLE_PDU;
104 }
105
kaiyiz81ee3bc2015-02-06 13:43:48 +0800106 private byte[] getPduDataFromDB(int msgId, int msgType) {
107 Uri uri = ContentUris.withAppendedId(Mms.CONTENT_URI, msgId);
108 PduPersister persister = PduPersister.getPduPersister(getContext());
109 byte[] mmsData = null;
110 try {
111 if (Mms.MESSAGE_BOX_INBOX == msgType
112 || Mms.MESSAGE_BOX_SENT == msgType) {
113 GenericPdu pdu = persister.load(uri);
114 if (pdu != null) {
115 mmsData = new PduComposer(getContext(), pdu).make();
116 }
117 }
118 } catch (MmsException e) {
119 Log.e(TAG, "MmsException e=" + e);
120 }
121 return mmsData;
122 }
123
124 private Cursor getPdus(int itemCount, int dataCount, String[] data) {
125 MatrixCursor cursor = new MatrixCursor(PDU_COLUMNS, 1);
126 long token = Binder.clearCallingIdentity();
127 SQLiteDatabase db = mOpenHelper.getReadableDatabase();
128 db.beginTransaction();
129 try {
130 for (int i = 0; i < dataCount; i++) {
131 int msgId = Integer.parseInt(data[i * itemCount]);
132 int msgType = Integer.parseInt(data[i * itemCount + 1]);
133 String pduPath = data[i * itemCount + 2];
134 byte[] pduData = getPduDataFromDB(msgId, msgType);
135 if (pduData == null || pduData.length == 0) {
136 Log.e(TAG, "can't get msgId:" + msgId + " pdu data.");
137 continue;
138 }
139 Object[] row = new Object[3];
140 row[0] = msgId;
141 row[1] = pduPath;
142 row[2] = pduData;
143 cursor.addRow(row);
144 }
145 db.setTransactionSuccessful();
146 } catch (Exception e) {
147 Log.e(TAG, "Exception e =", e);
148 } finally {
149 Binder.restoreCallingIdentity(token);
150 db.endTransaction();
151 }
152 return cursor;
153 }
154
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800155 @Override
156 public Cursor query(Uri uri, String[] projection,
157 String selection, String[] selectionArgs, String sortOrder) {
Ye Wen72f13552015-03-10 14:17:13 -0700158 // First check if a restricted view of the "pdu" table should be used based on the
159 // caller's identity. Only system, phone or the default sms app can have full access
160 // of mms data. For other apps, we present a restricted view which only contains sent
161 // or received messages, without wap pushes.
162 final boolean accessRestricted = ProviderUtil.isAccessRestricted(
163 getContext(), getCallingPackage(), Binder.getCallingUid());
164 final String pduTable = getPduTable(accessRestricted);
165
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800166 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
167
168 // Generate the body of the query.
169 int match = sURLMatcher.match(uri);
170 if (LOCAL_LOGV) {
171 Log.v(TAG, "Query uri=" + uri + ", match=" + match);
172 }
173
174 switch (match) {
175 case MMS_ALL:
Ye Wen72f13552015-03-10 14:17:13 -0700176 constructQueryForBox(qb, Mms.MESSAGE_BOX_ALL, pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800177 break;
178 case MMS_INBOX:
Ye Wen72f13552015-03-10 14:17:13 -0700179 constructQueryForBox(qb, Mms.MESSAGE_BOX_INBOX, pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800180 break;
181 case MMS_SENT:
Ye Wen72f13552015-03-10 14:17:13 -0700182 constructQueryForBox(qb, Mms.MESSAGE_BOX_SENT, pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800183 break;
184 case MMS_DRAFTS:
Ye Wen72f13552015-03-10 14:17:13 -0700185 constructQueryForBox(qb, Mms.MESSAGE_BOX_DRAFTS, pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800186 break;
187 case MMS_OUTBOX:
Ye Wen72f13552015-03-10 14:17:13 -0700188 constructQueryForBox(qb, Mms.MESSAGE_BOX_OUTBOX, pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800189 break;
190 case MMS_ALL_ID:
Ye Wen72f13552015-03-10 14:17:13 -0700191 qb.setTables(pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800192 qb.appendWhere(Mms._ID + "=" + uri.getPathSegments().get(0));
193 break;
194 case MMS_INBOX_ID:
195 case MMS_SENT_ID:
196 case MMS_DRAFTS_ID:
197 case MMS_OUTBOX_ID:
Ye Wen72f13552015-03-10 14:17:13 -0700198 qb.setTables(pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800199 qb.appendWhere(Mms._ID + "=" + uri.getPathSegments().get(1));
200 qb.appendWhere(" AND " + Mms.MESSAGE_BOX + "="
201 + getMessageBoxByMatch(match));
202 break;
203 case MMS_ALL_PART:
204 qb.setTables(TABLE_PART);
205 break;
206 case MMS_MSG_PART:
207 qb.setTables(TABLE_PART);
208 qb.appendWhere(Part.MSG_ID + "=" + uri.getPathSegments().get(0));
209 break;
210 case MMS_PART_ID:
211 qb.setTables(TABLE_PART);
212 qb.appendWhere(Part._ID + "=" + uri.getPathSegments().get(1));
213 break;
214 case MMS_MSG_ADDR:
215 qb.setTables(TABLE_ADDR);
216 qb.appendWhere(Addr.MSG_ID + "=" + uri.getPathSegments().get(0));
217 break;
218 case MMS_REPORT_STATUS:
219 /*
220 SELECT DISTINCT address,
221 T.delivery_status AS delivery_status,
222 T.read_status AS read_status
223 FROM addr
224 INNER JOIN (SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3,
225 ifnull(P2.st, 0) AS delivery_status,
226 ifnull(P3.read_status, 0) AS read_status
227 FROM pdu P1
228 INNER JOIN pdu P2
229 ON P1.m_id = P2.m_id AND P2.m_type = 134
230 LEFT JOIN pdu P3
231 ON P1.m_id = P3.m_id AND P3.m_type = 136
232 UNION
233 SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3,
234 ifnull(P2.st, 0) AS delivery_status,
235 ifnull(P3.read_status, 0) AS read_status
236 FROM pdu P1
237 INNER JOIN pdu P3
238 ON P1.m_id = P3.m_id AND P3.m_type = 136
239 LEFT JOIN pdu P2
240 ON P1.m_id = P2.m_id AND P2.m_type = 134) T
241 ON (msg_id = id2 AND type = 151)
242 OR (msg_id = id3 AND type = 137)
243 WHERE T.id1 = ?;
244 */
Ye Wen72f13552015-03-10 14:17:13 -0700245 qb.setTables(TABLE_ADDR + " INNER JOIN "
246 + "(SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3, "
247 + "ifnull(P2.st, 0) AS delivery_status, "
248 + "ifnull(P3.read_status, 0) AS read_status "
249 + "FROM " + pduTable + " P1 INNER JOIN " + pduTable + " P2 "
250 + "ON P1.m_id=P2.m_id AND P2.m_type=134 "
251 + "LEFT JOIN " + pduTable + " P3 "
252 + "ON P1.m_id=P3.m_id AND P3.m_type=136 "
253 + "UNION "
254 + "SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3, "
255 + "ifnull(P2.st, 0) AS delivery_status, "
256 + "ifnull(P3.read_status, 0) AS read_status "
257 + "FROM " + pduTable + " P1 INNER JOIN " + pduTable + " P3 "
258 + "ON P1.m_id=P3.m_id AND P3.m_type=136 "
259 + "LEFT JOIN " + pduTable + " P2 "
260 + "ON P1.m_id=P2.m_id AND P2.m_type=134) T "
261 + "ON (msg_id=id2 AND type=151) OR (msg_id=id3 AND type=137)");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800262 qb.appendWhere("T.id1 = " + uri.getLastPathSegment());
263 qb.setDistinct(true);
264 break;
265 case MMS_REPORT_REQUEST:
266 /*
267 SELECT address, d_rpt, rr
268 FROM addr join pdu on pdu._id = addr.msg_id
269 WHERE pdu._id = messageId AND addr.type = 151
270 */
271 qb.setTables(TABLE_ADDR + " join " +
Ye Wen72f13552015-03-10 14:17:13 -0700272 pduTable + " on " + pduTable + "._id = addr.msg_id");
273 qb.appendWhere(pduTable + "._id = " + uri.getLastPathSegment());
274 qb.appendWhere(" AND " + TABLE_ADDR + ".type = " + PduHeaders.TO);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800275 break;
276 case MMS_SENDING_RATE:
277 qb.setTables(TABLE_RATE);
278 break;
279 case MMS_DRM_STORAGE_ID:
280 qb.setTables(TABLE_DRM);
281 qb.appendWhere(BaseColumns._ID + "=" + uri.getLastPathSegment());
282 break;
Tom Taylor6c0ef242009-06-01 12:05:00 -0700283 case MMS_THREADS:
Ye Wen72f13552015-03-10 14:17:13 -0700284 qb.setTables(pduTable + " group by thread_id");
Tom Taylor6c0ef242009-06-01 12:05:00 -0700285 break;
kaiyiz81ee3bc2015-02-06 13:43:48 +0800286 case MMS_GET_PDU:
287 int itemCount = Integer.parseInt(uri.getQueryParameter("item_count"));
288 int dataCount = Integer.parseInt(uri.getQueryParameter("data_count"));
289 String split = uri.getQueryParameter("data_split");
290 String[] data = null;
291 if (!TextUtils.isEmpty(uri.getQueryParameter("data"))) {
292 data = uri.getQueryParameter("data").split(split);
293 Log.d(TAG, "data.length :" + data.length);
294 return getPdus(itemCount, dataCount, data);
295 } else {
296 Log.e(TAG, "MMS get pdu date return null");
297 return null;
298 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800299 default:
Wei Huang2914a7a2009-09-23 00:45:36 -0700300 Log.e(TAG, "query: invalid request: " + uri);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800301 return null;
302 }
303
304 String finalSortOrder = null;
305 if (TextUtils.isEmpty(sortOrder)) {
Ye Wen72f13552015-03-10 14:17:13 -0700306 if (qb.getTables().equals(pduTable)) {
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800307 finalSortOrder = Mms.DATE + " DESC";
308 } else if (qb.getTables().equals(TABLE_PART)) {
309 finalSortOrder = Part.SEQ;
310 }
311 } else {
312 finalSortOrder = sortOrder;
313 }
314
Tom Taylor3ad9da42014-01-16 13:57:11 -0800315 Cursor ret;
316 try {
317 SQLiteDatabase db = mOpenHelper.getReadableDatabase();
318 ret = qb.query(db, projection, selection,
319 selectionArgs, null, null, finalSortOrder);
320 } catch (SQLiteException e) {
321 Log.e(TAG, "returning NULL cursor, query: " + uri, e);
322 return null;
323 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800324
325 // TODO: Does this need to be a URI for this provider.
326 ret.setNotificationUri(getContext().getContentResolver(), uri);
327 return ret;
328 }
329
Ye Wen72f13552015-03-10 14:17:13 -0700330 private void constructQueryForBox(SQLiteQueryBuilder qb, int msgBox, String pduTable) {
331 qb.setTables(pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800332
333 if (msgBox != Mms.MESSAGE_BOX_ALL) {
334 qb.appendWhere(Mms.MESSAGE_BOX + "=" + msgBox);
335 }
336 }
337
338 @Override
339 public String getType(Uri uri) {
340 int match = sURLMatcher.match(uri);
341 switch (match) {
342 case MMS_ALL:
343 case MMS_INBOX:
344 case MMS_SENT:
345 case MMS_DRAFTS:
346 case MMS_OUTBOX:
347 return VND_ANDROID_DIR_MMS;
348 case MMS_ALL_ID:
349 case MMS_INBOX_ID:
350 case MMS_SENT_ID:
351 case MMS_DRAFTS_ID:
352 case MMS_OUTBOX_ID:
353 return VND_ANDROID_MMS;
354 case MMS_PART_ID: {
355 Cursor cursor = mOpenHelper.getReadableDatabase().query(
356 TABLE_PART, new String[] { Part.CONTENT_TYPE },
357 Part._ID + " = ?", new String[] { uri.getLastPathSegment() },
358 null, null, null);
359 if (cursor != null) {
360 try {
361 if ((cursor.getCount() == 1) && cursor.moveToFirst()) {
362 return cursor.getString(0);
363 } else {
364 Log.e(TAG, "cursor.count() != 1: " + uri);
365 }
366 } finally {
367 cursor.close();
368 }
369 } else {
370 Log.e(TAG, "cursor == null: " + uri);
371 }
372 return "*/*";
373 }
374 case MMS_ALL_PART:
375 case MMS_MSG_PART:
376 case MMS_MSG_ADDR:
377 default:
378 return "*/*";
379 }
380 }
381
kaiyiz81ee3bc2015-02-06 13:43:48 +0800382 private byte[] getPduDataFromFile(String pduPath) {
383 FileInputStream fileInputStream = null;
384 byte[] data = null;
385 try {
386 File pduFile = new File(pduPath);
387 data = new byte[(int)pduFile.length()];
388 fileInputStream = new FileInputStream(pduFile);
389 fileInputStream.read(data);
390 } catch (Exception e) {
391 Log.e(TAG, "read file exception :", e);
392 } finally {
393 try {
394 if (fileInputStream != null) {
395 fileInputStream.close();
396 }
397 } catch (Exception e) {
398 Log.e(TAG, "close file stream exception :", e);
399 }
400 }
401 return data;
402 }
403
404 private Uri restorePduFile(Uri uri, String pduPath) {
405 Uri msgUri = null;
406 if (uri == null || TextUtils.isEmpty(pduPath)) {
407 return null;
408 }
409
410 try {
411 byte[] pduData = getPduDataFromFile(pduPath);
412 PduPersister pduPersister = PduPersister.getPduPersister(getContext());
413 if (pduData != null && pduData.length > 0) {
414 if (Mms.Sent.CONTENT_URI.equals(uri)
415 || Mms.Inbox.CONTENT_URI.equals(uri)) {
416 GenericPdu pdu = new PduParser(pduData, true).parse();
417 msgUri = pduPersister.persist(
418 pdu, uri, true, false, null);
419 } else {
420 Log.e(TAG,"Unsupported uri :" + uri);
421 }
422 }
423 } catch (MmsException e) {
424 Log.e(TAG, "MmsException: ", e);
425 }
426 return msgUri;
427 }
428
429 private String getPduPath(String dir, ContentValues values) {
430 if (dir != null && values != null && values.containsKey(COLUMN_PDU_PATH)) {
431 String path = values.getAsString(COLUMN_PDU_PATH);
432 if (!TextUtils.isEmpty(path)) {
433 return dir + "/" + path;
434 }
435 }
436 return null;
437 }
438
439 private int restoreMms(Uri uri, ContentValues values, String dir) {
440 int count = 0;
441 Uri msgUri = restorePduFile(uri, getPduPath(dir, values));
442 if (msgUri != null) {
443 String selection = Mms._ID + "=" + msgUri.getLastPathSegment();
444 values.remove(COLUMN_PDU_PATH);
445 ContentValues finalValues = new ContentValues(values);
446 // now only support bulkInsert pdu table.
wangjingff3d2c72015-04-03 16:37:38 +0800447 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
kaiyiz81ee3bc2015-02-06 13:43:48 +0800448 count = db.update(TABLE_PDU, finalValues, selection, null);
449 }
450 return count;
451 }
452
453 @Override
454 public int bulkInsert(Uri uri, ContentValues[] values) {
455 String dir = uri.getQueryParameter("restore_dir");
456 if (TextUtils.isEmpty(dir)) {
457 return super.bulkInsert(uri, values);
458 }
459
460 Uri insertUri = null;
461 int match = sURLMatcher.match(uri);
462 switch (match) {
463 case MMS_INBOX:
464 insertUri = Mms.Inbox.CONTENT_URI;
465 break;
466 case MMS_SENT:
467 insertUri = Mms.Sent.CONTENT_URI;
468 break;
469 default:
470 return 0;
471 }
472
473 long token = Binder.clearCallingIdentity();
474 int count = 0;
wangjingff3d2c72015-04-03 16:37:38 +0800475 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
kaiyiz81ee3bc2015-02-06 13:43:48 +0800476 db.beginTransaction();
477 try {
478 for (ContentValues value : values) {
479 count += restoreMms(insertUri, value, dir);
480 }
481
482 Log.d(TAG, "bulkInsert request count: " + values.length
483 + " successfully count : " + count);
484 if (count == values.length) {
485 db.setTransactionSuccessful();
486 }
487 return count;
488 } finally {
489 db.endTransaction();
490 Binder.restoreCallingIdentity(token);
491 }
492 }
493
494
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800495 @Override
496 public Uri insert(Uri uri, ContentValues values) {
Andre Furtado867477a2016-08-18 10:28:37 -0700497 // The _data column is filled internally in MmsProvider, so this check is just to avoid
498 // it from being inadvertently set. This is not supposed to be a protection against
499 // malicious attack, since sql injection could still be attempted to bypass the check. On
500 // the other hand, the MmsProvider does verify that the _data column has an allowed value
501 // before opening any uri/files.
Tom Taylor438403e2013-02-21 16:56:01 -0800502 if (values != null && values.containsKey(Part._DATA)) {
503 return null;
504 }
Ye Wene07acb92014-11-19 12:06:05 -0800505 final int callerUid = Binder.getCallingUid();
Ye Wen72f13552015-03-10 14:17:13 -0700506 final String callerPkg = getCallingPackage();
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800507 int msgBox = Mms.MESSAGE_BOX_ALL;
508 boolean notify = true;
509
510 int match = sURLMatcher.match(uri);
511 if (LOCAL_LOGV) {
512 Log.v(TAG, "Insert uri=" + uri + ", match=" + match);
513 }
514
515 String table = TABLE_PDU;
516 switch (match) {
517 case MMS_ALL:
518 Object msgBoxObj = values.getAsInteger(Mms.MESSAGE_BOX);
519 if (msgBoxObj != null) {
520 msgBox = (Integer) msgBoxObj;
521 }
522 else {
523 // default to inbox
524 msgBox = Mms.MESSAGE_BOX_INBOX;
525 }
526 break;
527 case MMS_INBOX:
528 msgBox = Mms.MESSAGE_BOX_INBOX;
529 break;
530 case MMS_SENT:
531 msgBox = Mms.MESSAGE_BOX_SENT;
532 break;
533 case MMS_DRAFTS:
534 msgBox = Mms.MESSAGE_BOX_DRAFTS;
535 break;
536 case MMS_OUTBOX:
537 msgBox = Mms.MESSAGE_BOX_OUTBOX;
538 break;
539 case MMS_MSG_PART:
540 notify = false;
541 table = TABLE_PART;
542 break;
543 case MMS_MSG_ADDR:
544 notify = false;
545 table = TABLE_ADDR;
546 break;
547 case MMS_SENDING_RATE:
548 notify = false;
549 table = TABLE_RATE;
550 break;
551 case MMS_DRM_STORAGE:
552 notify = false;
553 table = TABLE_DRM;
554 break;
555 default:
Wei Huang2914a7a2009-09-23 00:45:36 -0700556 Log.e(TAG, "insert: invalid request: " + uri);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800557 return null;
558 }
559
560 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
561 ContentValues finalValues;
562 Uri res = Mms.CONTENT_URI;
563 long rowId;
564
565 if (table.equals(TABLE_PDU)) {
566 boolean addDate = !values.containsKey(Mms.DATE);
567 boolean addMsgBox = !values.containsKey(Mms.MESSAGE_BOX);
568
569 // Filter keys we don't support yet.
570 filterUnsupportedKeys(values);
571
572 // TODO: Should initialValues be validated, e.g. if it
573 // missed some significant keys?
574 finalValues = new ContentValues(values);
575
576 long timeInMillis = System.currentTimeMillis();
577
578 if (addDate) {
579 finalValues.put(Mms.DATE, timeInMillis / 1000L);
580 }
581
582 if (addMsgBox && (msgBox != Mms.MESSAGE_BOX_ALL)) {
583 finalValues.put(Mms.MESSAGE_BOX, msgBox);
584 }
585
586 if (msgBox != Mms.MESSAGE_BOX_INBOX) {
587 // Mark all non-inbox messages read.
588 finalValues.put(Mms.READ, 1);
589 }
590
Yusuf T. Mobile21c25bc2009-06-16 13:40:38 -0700591 // thread_id
592 Long threadId = values.getAsLong(Mms.THREAD_ID);
593 String address = values.getAsString(CanonicalAddressesColumns.ADDRESS);
594
Tom Taylor59269962012-05-09 14:42:35 -0700595 if (((threadId == null) || (threadId == 0)) && (!TextUtils.isEmpty(address))) {
Yusuf T. Mobile21c25bc2009-06-16 13:40:38 -0700596 finalValues.put(Mms.THREAD_ID, Threads.getOrCreateThreadId(getContext(), address));
597 }
598
Ye Wene07acb92014-11-19 12:06:05 -0800599 if (ProviderUtil.shouldSetCreator(finalValues, callerUid)) {
600 // Only SYSTEM or PHONE can set CREATOR
601 // If caller is not SYSTEM or PHONE, or SYSTEM or PHONE does not set CREATOR
602 // set CREATOR using the truth on caller.
603 // Note: Inferring package name from UID may include unrelated package names
Ye Wen72f13552015-03-10 14:17:13 -0700604 finalValues.put(Telephony.Mms.CREATOR, callerPkg);
Ye Wene07acb92014-11-19 12:06:05 -0800605 }
606
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800607 if ((rowId = db.insert(table, null, finalValues)) <= 0) {
Ye Wene07acb92014-11-19 12:06:05 -0800608 Log.e(TAG, "MmsProvider.insert: failed!");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800609 return null;
610 }
611
612 res = Uri.parse(res + "/" + rowId);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800613 } else if (table.equals(TABLE_ADDR)) {
614 finalValues = new ContentValues(values);
615 finalValues.put(Addr.MSG_ID, uri.getPathSegments().get(0));
616
617 if ((rowId = db.insert(table, null, finalValues)) <= 0) {
Ye Wene07acb92014-11-19 12:06:05 -0800618 Log.e(TAG, "Failed to insert address");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800619 return null;
620 }
621
622 res = Uri.parse(res + "/addr/" + rowId);
623 } else if (table.equals(TABLE_PART)) {
624 finalValues = new ContentValues(values);
625
626 if (match == MMS_MSG_PART) {
627 finalValues.put(Part.MSG_ID, uri.getPathSegments().get(0));
628 }
629
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700630 String contentType = values.getAsString("ct");
Mark Wagner8e5ee782010-01-04 17:39:06 -0800631
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700632 // text/plain and app application/smil store their "data" inline in the
633 // table so there's no need to create the file
Tom Taylorc2db47d2012-03-27 15:15:39 -0700634 boolean plainText = false;
635 boolean smilText = false;
636 if ("text/plain".equals(contentType)) {
637 plainText = true;
638 } else if ("application/smil".equals(contentType)) {
639 smilText = true;
640 }
Mark Wagner8e5ee782010-01-04 17:39:06 -0800641 if (!plainText && !smilText) {
Tom Taylorc2db47d2012-03-27 15:15:39 -0700642 // Use the filename if possible, otherwise use the current time as the name.
643 String contentLocation = values.getAsString("cl");
644 if (!TextUtils.isEmpty(contentLocation)) {
645 File f = new File(contentLocation);
646 contentLocation = "_" + f.getName();
647 } else {
648 contentLocation = "";
649 }
650
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700651 // Generate the '_data' field of the part with default
652 // permission settings.
Ye Weneaa93e62015-01-06 13:08:53 -0800653 String path = getContext().getDir(PARTS_DIR_NAME, 0).getPath()
Tom Taylorc2db47d2012-03-27 15:15:39 -0700654 + "/PART_" + System.currentTimeMillis() + contentLocation;
655
656 if (DownloadDrmHelper.isDrmConvertNeeded(contentType)) {
657 // Adds the .fl extension to the filename if contentType is
658 // "application/vnd.oma.drm.message"
659 path = DownloadDrmHelper.modifyDrmFwLockFileExtension(path);
660 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800661
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700662 finalValues.put(Part._DATA, path);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800663
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700664 File partFile = new File(path);
665 if (!partFile.exists()) {
666 try {
667 if (!partFile.createNewFile()) {
668 throw new IllegalStateException(
669 "Unable to create new partFile: " + path);
670 }
Tom Taylorc2db47d2012-03-27 15:15:39 -0700671 // Give everyone rw permission until we encrypt the file
672 // (in PduPersister.persistData). Once the file is encrypted, the
673 // permissions will be set to 0644.
674 int result = FileUtils.setPermissions(path, 0666, -1, -1);
675 if (LOCAL_LOGV) {
676 Log.d(TAG, "MmsProvider.insert setPermissions result: " + result);
677 }
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700678 } catch (IOException e) {
679 Log.e(TAG, "createNewFile", e);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800680 throw new IllegalStateException(
681 "Unable to create new partFile: " + path);
682 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800683 }
684 }
685
686 if ((rowId = db.insert(table, null, finalValues)) <= 0) {
Ye Wene07acb92014-11-19 12:06:05 -0800687 Log.e(TAG, "MmsProvider.insert: failed!");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800688 return null;
689 }
690
691 res = Uri.parse(res + "/part/" + rowId);
Mark Wagner8e5ee782010-01-04 17:39:06 -0800692
693 // Don't use a trigger for updating the words table because of a bug
694 // in FTS3. The bug is such that the call to get the last inserted
695 // row is incorrect.
696 if (plainText) {
697 // Update the words table with a corresponding row. The words table
698 // allows us to search for words quickly, without scanning the whole
699 // table;
700 ContentValues cv = new ContentValues();
701
702 // we're using the row id of the part table row but we're also using ids
703 // from the sms table so this divides the space into two large chunks.
704 // The row ids from the part table start at 2 << 32.
705 cv.put(Telephony.MmsSms.WordsTable.ID, (2 << 32) + rowId);
706 cv.put(Telephony.MmsSms.WordsTable.INDEXED_TEXT, values.getAsString("text"));
707 cv.put(Telephony.MmsSms.WordsTable.SOURCE_ROW_ID, rowId);
708 cv.put(Telephony.MmsSms.WordsTable.TABLE_ID, 2);
709 db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv);
710 }
711
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800712 } else if (table.equals(TABLE_RATE)) {
713 long now = values.getAsLong(Rate.SENT_TIME);
714 long oneHourAgo = now - 1000 * 60 * 60;
715 // Delete all unused rows (time earlier than one hour ago).
716 db.delete(table, Rate.SENT_TIME + "<=" + oneHourAgo, null);
717 db.insert(table, null, values);
718 } else if (table.equals(TABLE_DRM)) {
Ye Weneaa93e62015-01-06 13:08:53 -0800719 String path = getContext().getDir(PARTS_DIR_NAME, 0).getPath()
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800720 + "/PART_" + System.currentTimeMillis();
721 finalValues = new ContentValues(1);
722 finalValues.put("_data", path);
723
724 File partFile = new File(path);
725 if (!partFile.exists()) {
726 try {
727 if (!partFile.createNewFile()) {
728 throw new IllegalStateException(
729 "Unable to create new file: " + path);
730 }
731 } catch (IOException e) {
732 Log.e(TAG, "createNewFile", e);
733 throw new IllegalStateException(
734 "Unable to create new file: " + path);
735 }
736 }
737
738 if ((rowId = db.insert(table, null, finalValues)) <= 0) {
Ye Wene07acb92014-11-19 12:06:05 -0800739 Log.e(TAG, "MmsProvider.insert: failed!");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800740 return null;
741 }
742 res = Uri.parse(res + "/drm/" + rowId);
743 } else {
744 throw new AssertionError("Unknown table type: " + table);
745 }
746
747 if (notify) {
748 notifyChange();
749 }
750 return res;
751 }
752
753 private int getMessageBoxByMatch(int match) {
754 switch (match) {
755 case MMS_INBOX_ID:
756 case MMS_INBOX:
757 return Mms.MESSAGE_BOX_INBOX;
758 case MMS_SENT_ID:
759 case MMS_SENT:
760 return Mms.MESSAGE_BOX_SENT;
761 case MMS_DRAFTS_ID:
762 case MMS_DRAFTS:
763 return Mms.MESSAGE_BOX_DRAFTS;
764 case MMS_OUTBOX_ID:
765 case MMS_OUTBOX:
766 return Mms.MESSAGE_BOX_OUTBOX;
767 default:
768 throw new IllegalArgumentException("bad Arg: " + match);
769 }
770 }
771
772 @Override
773 public int delete(Uri uri, String selection,
774 String[] selectionArgs) {
775 int match = sURLMatcher.match(uri);
776 if (LOCAL_LOGV) {
777 Log.v(TAG, "Delete uri=" + uri + ", match=" + match);
778 }
779
780 String table, extraSelection = null;
781 boolean notify = false;
782
783 switch (match) {
784 case MMS_ALL_ID:
785 case MMS_INBOX_ID:
786 case MMS_SENT_ID:
787 case MMS_DRAFTS_ID:
788 case MMS_OUTBOX_ID:
789 notify = true;
790 table = TABLE_PDU;
791 extraSelection = Mms._ID + "=" + uri.getLastPathSegment();
792 break;
793 case MMS_ALL:
794 case MMS_INBOX:
795 case MMS_SENT:
796 case MMS_DRAFTS:
797 case MMS_OUTBOX:
798 notify = true;
799 table = TABLE_PDU;
800 if (match != MMS_ALL) {
801 int msgBox = getMessageBoxByMatch(match);
802 extraSelection = Mms.MESSAGE_BOX + "=" + msgBox;
803 }
804 break;
805 case MMS_ALL_PART:
806 table = TABLE_PART;
807 break;
808 case MMS_MSG_PART:
809 table = TABLE_PART;
810 extraSelection = Part.MSG_ID + "=" + uri.getPathSegments().get(0);
811 break;
812 case MMS_PART_ID:
813 table = TABLE_PART;
814 extraSelection = Part._ID + "=" + uri.getPathSegments().get(1);
815 break;
816 case MMS_MSG_ADDR:
817 table = TABLE_ADDR;
818 extraSelection = Addr.MSG_ID + "=" + uri.getPathSegments().get(0);
819 break;
820 case MMS_DRM_STORAGE:
821 table = TABLE_DRM;
822 break;
823 default:
824 Log.w(TAG, "No match for URI '" + uri + "'");
825 return 0;
826 }
827
828 String finalSelection = concatSelections(selection, extraSelection);
829 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
830 int deletedRows = 0;
831
832 if (TABLE_PDU.equals(table)) {
833 deletedRows = deleteMessages(getContext(), db, finalSelection,
834 selectionArgs, uri);
835 } else if (TABLE_PART.equals(table)) {
836 deletedRows = deleteParts(db, finalSelection, selectionArgs);
kaiyizcd21c4f2015-01-30 11:22:36 +0800837 cleanUpWords(db);
838 updateHasAttachment(db);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800839 } else if (TABLE_DRM.equals(table)) {
840 deletedRows = deleteTempDrmData(db, finalSelection, selectionArgs);
841 } else {
842 deletedRows = db.delete(table, finalSelection, selectionArgs);
843 }
844
845 if ((deletedRows > 0) && notify) {
846 notifyChange();
847 }
848 return deletedRows;
849 }
850
851 static int deleteMessages(Context context, SQLiteDatabase db,
852 String selection, String[] selectionArgs, Uri uri) {
kaiyizcd21c4f2015-01-30 11:22:36 +0800853 Cursor cursor = db.query(TABLE_PDU, new String[] { Mms._ID, Mms.THREAD_ID },
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800854 selection, selectionArgs, null, null, null);
855 if (cursor == null) {
856 return 0;
857 }
858
kaiyizcd21c4f2015-01-30 11:22:36 +0800859 HashSet<Long> threadIds = new HashSet<Long>();
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800860 try {
861 if (cursor.getCount() == 0) {
862 return 0;
863 }
864
865 while (cursor.moveToNext()) {
866 deleteParts(db, Part.MSG_ID + " = ?",
867 new String[] { String.valueOf(cursor.getLong(0)) });
kaiyizcd21c4f2015-01-30 11:22:36 +0800868 threadIds.add(cursor.getLong(1));
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800869 }
kaiyizcd21c4f2015-01-30 11:22:36 +0800870 cleanUpWords(db);
871 updateHasAttachment(db);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800872 } finally {
873 cursor.close();
874 }
875
876 int count = db.delete(TABLE_PDU, selection, selectionArgs);
kaiyizcd21c4f2015-01-30 11:22:36 +0800877 for (long thread : threadIds) {
878 MmsSmsDatabaseHelper.updateThread(db, thread);
879 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800880 if (count > 0) {
881 Intent intent = new Intent(Mms.Intents.CONTENT_CHANGED_ACTION);
882 intent.putExtra(Mms.Intents.DELETED_CONTENTS, uri);
883 if (LOCAL_LOGV) {
884 Log.v(TAG, "Broadcasting intent: " + intent);
885 }
886 context.sendBroadcast(intent);
887 }
888 return count;
889 }
890
kaiyizcd21c4f2015-01-30 11:22:36 +0800891 private static void cleanUpWords(SQLiteDatabase db) {
892 db.execSQL("DELETE FROM words WHERE source_id not in (select _id from part) AND "
893 + "table_to_use = 2");
894 }
895
896 private static void updateHasAttachment(SQLiteDatabase db) {
897 db.execSQL("UPDATE threads SET has_attachment = CASE "
898 + "(SELECT COUNT(*) FROM part JOIN pdu WHERE part.mid = pdu._id AND "
899 + "pdu.thread_id = threads._id AND part.ct != 'text/plain' "
900 + "AND part.ct != 'application/smil') WHEN 0 THEN 0 ELSE 1 END");
901 }
902
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800903 private static int deleteParts(SQLiteDatabase db, String selection,
904 String[] selectionArgs) {
905 return deleteDataRows(db, TABLE_PART, selection, selectionArgs);
906 }
907
908 private static int deleteTempDrmData(SQLiteDatabase db, String selection,
909 String[] selectionArgs) {
910 return deleteDataRows(db, TABLE_DRM, selection, selectionArgs);
911 }
912
913 private static int deleteDataRows(SQLiteDatabase db, String table,
914 String selection, String[] selectionArgs) {
915 Cursor cursor = db.query(table, new String[] { "_data" },
916 selection, selectionArgs, null, null, null);
917 if (cursor == null) {
918 // FIXME: This might be an error, ignore it may cause
919 // unpredictable result.
920 return 0;
921 }
922
923 try {
924 if (cursor.getCount() == 0) {
925 return 0;
926 }
927
928 while (cursor.moveToNext()) {
929 try {
930 // Delete the associated files saved on file-system.
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700931 String path = cursor.getString(0);
932 if (path != null) {
933 new File(path).delete();
934 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800935 } catch (Throwable ex) {
936 Log.e(TAG, ex.getMessage(), ex);
937 }
938 }
939 } finally {
940 cursor.close();
941 }
942
943 return db.delete(table, selection, selectionArgs);
944 }
945
946 @Override
Andre Furtado867477a2016-08-18 10:28:37 -0700947 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
948 // The _data column is filled internally in MmsProvider, so this check is just to avoid
949 // it from being inadvertently set. This is not supposed to be a protection against
950 // malicious attack, since sql injection could still be attempted to bypass the check. On
951 // the other hand, the MmsProvider does verify that the _data column has an allowed value
952 // before opening any uri/files.
Tom Taylor438403e2013-02-21 16:56:01 -0800953 if (values != null && values.containsKey(Part._DATA)) {
954 return 0;
955 }
Ye Wene07acb92014-11-19 12:06:05 -0800956 final int callerUid = Binder.getCallingUid();
Ye Wen72f13552015-03-10 14:17:13 -0700957 final String callerPkg = getCallingPackage();
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800958 int match = sURLMatcher.match(uri);
959 if (LOCAL_LOGV) {
960 Log.v(TAG, "Update uri=" + uri + ", match=" + match);
961 }
962
963 boolean notify = false;
964 String msgId = null;
965 String table;
966
967 switch (match) {
968 case MMS_ALL_ID:
969 case MMS_INBOX_ID:
970 case MMS_SENT_ID:
971 case MMS_DRAFTS_ID:
972 case MMS_OUTBOX_ID:
973 msgId = uri.getLastPathSegment();
974 // fall-through
975 case MMS_ALL:
976 case MMS_INBOX:
977 case MMS_SENT:
978 case MMS_DRAFTS:
979 case MMS_OUTBOX:
980 notify = true;
981 table = TABLE_PDU;
982 break;
Tom Taylorc2db47d2012-03-27 15:15:39 -0700983
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800984 case MMS_MSG_PART:
985 case MMS_PART_ID:
986 table = TABLE_PART;
987 break;
Tom Taylorc2db47d2012-03-27 15:15:39 -0700988
989 case MMS_PART_RESET_FILE_PERMISSION:
Ye Weneaa93e62015-01-06 13:08:53 -0800990 String path = getContext().getDir(PARTS_DIR_NAME, 0).getPath() + '/' +
Tom Taylorc2db47d2012-03-27 15:15:39 -0700991 uri.getPathSegments().get(1);
992 // Reset the file permission back to read for everyone but me.
993 int result = FileUtils.setPermissions(path, 0644, -1, -1);
994 if (LOCAL_LOGV) {
995 Log.d(TAG, "MmsProvider.update setPermissions result: " + result +
996 " for path: " + path);
997 }
998 return 0;
999
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001000 default:
1001 Log.w(TAG, "Update operation for '" + uri + "' not implemented.");
1002 return 0;
1003 }
1004
1005 String extraSelection = null;
1006 ContentValues finalValues;
1007 if (table.equals(TABLE_PDU)) {
1008 // Filter keys that we don't support yet.
1009 filterUnsupportedKeys(values);
Ye Wene07acb92014-11-19 12:06:05 -08001010 if (ProviderUtil.shouldRemoveCreator(values, callerUid)) {
1011 // CREATOR should not be changed by non-SYSTEM/PHONE apps
Ye Wen72f13552015-03-10 14:17:13 -07001012 Log.w(TAG, callerPkg + " tries to update CREATOR");
Ye Wene07acb92014-11-19 12:06:05 -08001013 values.remove(Mms.CREATOR);
1014 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001015 finalValues = new ContentValues(values);
1016
1017 if (msgId != null) {
1018 extraSelection = Mms._ID + "=" + msgId;
1019 }
1020 } else if (table.equals(TABLE_PART)) {
1021 finalValues = new ContentValues(values);
1022
1023 switch (match) {
1024 case MMS_MSG_PART:
1025 extraSelection = Part.MSG_ID + "=" + uri.getPathSegments().get(0);
1026 break;
1027 case MMS_PART_ID:
1028 extraSelection = Part._ID + "=" + uri.getPathSegments().get(1);
1029 break;
1030 default:
1031 break;
1032 }
1033 } else {
1034 return 0;
1035 }
1036
1037 String finalSelection = concatSelections(selection, extraSelection);
1038 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1039 int count = db.update(table, finalValues, finalSelection, selectionArgs);
1040 if (notify && (count > 0)) {
1041 notifyChange();
1042 }
1043 return count;
1044 }
1045
1046 @Override
Wei Huang2914a7a2009-09-23 00:45:36 -07001047 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
Wei Huang2914a7a2009-09-23 00:45:36 -07001048 int match = sURLMatcher.match(uri);
1049
1050 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Tom Taylor8168ff82013-02-19 14:30:23 -08001051 Log.d(TAG, "openFile: uri=" + uri + ", mode=" + mode + ", match=" + match);
Wei Huang2914a7a2009-09-23 00:45:36 -07001052 }
1053
Tom Taylor8168ff82013-02-19 14:30:23 -08001054 if (match != MMS_PART_ID) {
1055 return null;
Wei Huang2914a7a2009-09-23 00:45:36 -07001056 }
1057
Andre Furtado867477a2016-08-18 10:28:37 -07001058 return safeOpenFileHelper(uri, mode);
1059 }
1060
1061 @NonNull
1062 private ParcelFileDescriptor safeOpenFileHelper(
1063 @NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
Tom Taylor8168ff82013-02-19 14:30:23 -08001064 Cursor c = query(uri, new String[]{"_data"}, null, null, null);
1065 int count = (c != null) ? c.getCount() : 0;
1066 if (count != 1) {
1067 // If there is not exactly one result, throw an appropriate
1068 // exception.
1069 if (c != null) {
1070 c.close();
1071 }
1072 if (count == 0) {
1073 throw new FileNotFoundException("No entry for " + uri);
1074 }
1075 throw new FileNotFoundException("Multiple items at " + uri);
1076 }
1077
1078 c.moveToFirst();
1079 int i = c.getColumnIndex("_data");
1080 String path = (i >= 0 ? c.getString(i) : null);
1081 c.close();
1082
1083 if (path == null) {
Andre Furtado867477a2016-08-18 10:28:37 -07001084 throw new FileNotFoundException("Column _data not found.");
Tom Taylor8168ff82013-02-19 14:30:23 -08001085 }
Andre Furtado867477a2016-08-18 10:28:37 -07001086
1087 File filePath = new File(path);
Tom Taylor8168ff82013-02-19 14:30:23 -08001088 try {
Andre Furtado867477a2016-08-18 10:28:37 -07001089 // The MmsProvider shouldn't open a file that isn't MMS data, so we verify that the
1090 // _data path actually points to MMS data. That safeguards ourselves from callers who
1091 // inserted or updated a URI (more specifically the _data column) with disallowed paths.
1092 // TODO(afurtado): provide a more robust mechanism to avoid disallowed _data paths to
1093 // be inserted/updated in the first place, including via SQL injection.
Tom Taylor8168ff82013-02-19 14:30:23 -08001094 if (!filePath.getCanonicalPath()
Amith Yamasanida40d6a2015-04-14 13:37:12 -07001095 .startsWith(getContext().getDir(PARTS_DIR_NAME, 0).getCanonicalPath())) {
Ye Weneaa93e62015-01-06 13:08:53 -08001096 Log.e(TAG, "openFile: path "
1097 + filePath.getCanonicalPath()
1098 + " does not start with "
Amith Yamasanida40d6a2015-04-14 13:37:12 -07001099 + getContext().getDir(PARTS_DIR_NAME, 0).getCanonicalPath());
Ye Weneaa93e62015-01-06 13:08:53 -08001100 // Don't care return value
Tom Taylor8168ff82013-02-19 14:30:23 -08001101 return null;
1102 }
1103 } catch (IOException e) {
Ye Weneaa93e62015-01-06 13:08:53 -08001104 Log.e(TAG, "openFile: create path failed " + e, e);
Tom Taylor8168ff82013-02-19 14:30:23 -08001105 return null;
1106 }
1107
Andre Furtado867477a2016-08-18 10:28:37 -07001108 int modeBits = ParcelFileDescriptor.parseMode(mode);
1109 return ParcelFileDescriptor.open(filePath, modeBits);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001110 }
1111
1112 private void filterUnsupportedKeys(ContentValues values) {
1113 // Some columns are unsupported. They should therefore
1114 // neither be inserted nor updated. Filter them out.
1115 values.remove(Mms.DELIVERY_TIME_TOKEN);
1116 values.remove(Mms.SENDER_VISIBILITY);
1117 values.remove(Mms.REPLY_CHARGING);
1118 values.remove(Mms.REPLY_CHARGING_DEADLINE_TOKEN);
1119 values.remove(Mms.REPLY_CHARGING_DEADLINE);
1120 values.remove(Mms.REPLY_CHARGING_ID);
1121 values.remove(Mms.REPLY_CHARGING_SIZE);
1122 values.remove(Mms.PREVIOUSLY_SENT_BY);
1123 values.remove(Mms.PREVIOUSLY_SENT_DATE);
1124 values.remove(Mms.STORE);
1125 values.remove(Mms.MM_STATE);
1126 values.remove(Mms.MM_FLAGS_TOKEN);
1127 values.remove(Mms.MM_FLAGS);
1128 values.remove(Mms.STORE_STATUS);
1129 values.remove(Mms.STORE_STATUS_TEXT);
1130 values.remove(Mms.STORED);
1131 values.remove(Mms.TOTALS);
1132 values.remove(Mms.MBOX_TOTALS);
1133 values.remove(Mms.MBOX_TOTALS_TOKEN);
1134 values.remove(Mms.QUOTAS);
1135 values.remove(Mms.MBOX_QUOTAS);
1136 values.remove(Mms.MBOX_QUOTAS_TOKEN);
1137 values.remove(Mms.MESSAGE_COUNT);
1138 values.remove(Mms.START);
1139 values.remove(Mms.DISTRIBUTION_INDICATOR);
1140 values.remove(Mms.ELEMENT_DESCRIPTOR);
1141 values.remove(Mms.LIMIT);
1142 values.remove(Mms.RECOMMENDED_RETRIEVAL_MODE);
1143 values.remove(Mms.RECOMMENDED_RETRIEVAL_MODE_TEXT);
1144 values.remove(Mms.STATUS_TEXT);
1145 values.remove(Mms.APPLIC_ID);
1146 values.remove(Mms.REPLY_APPLIC_ID);
1147 values.remove(Mms.AUX_APPLIC_ID);
1148 values.remove(Mms.DRM_CONTENT);
1149 values.remove(Mms.ADAPTATION_ALLOWED);
1150 values.remove(Mms.REPLACE_ID);
1151 values.remove(Mms.CANCEL_ID);
1152 values.remove(Mms.CANCEL_STATUS);
1153
1154 // Keys shouldn't be inserted or updated.
1155 values.remove(Mms._ID);
1156 }
1157
1158 private void notifyChange() {
1159 getContext().getContentResolver().notifyChange(
Amith Yamasani43f9fb22014-09-10 15:56:47 -07001160 MmsSms.CONTENT_URI, null, true, UserHandle.USER_ALL);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001161 }
1162
1163 private final static String TAG = "MmsProvider";
1164 private final static String VND_ANDROID_MMS = "vnd.android/mms";
1165 private final static String VND_ANDROID_DIR_MMS = "vnd.android-dir/mms";
1166 private final static boolean DEBUG = false;
Joe Onoratodc946792011-04-07 18:41:15 -07001167 private final static boolean LOCAL_LOGV = false;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001168
1169 private static final int MMS_ALL = 0;
1170 private static final int MMS_ALL_ID = 1;
1171 private static final int MMS_INBOX = 2;
1172 private static final int MMS_INBOX_ID = 3;
1173 private static final int MMS_SENT = 4;
1174 private static final int MMS_SENT_ID = 5;
1175 private static final int MMS_DRAFTS = 6;
1176 private static final int MMS_DRAFTS_ID = 7;
1177 private static final int MMS_OUTBOX = 8;
1178 private static final int MMS_OUTBOX_ID = 9;
1179 private static final int MMS_ALL_PART = 10;
1180 private static final int MMS_MSG_PART = 11;
1181 private static final int MMS_PART_ID = 12;
1182 private static final int MMS_MSG_ADDR = 13;
1183 private static final int MMS_SENDING_RATE = 14;
Tom Taylor6c0ef242009-06-01 12:05:00 -07001184 private static final int MMS_REPORT_STATUS = 15;
1185 private static final int MMS_REPORT_REQUEST = 16;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001186 private static final int MMS_DRM_STORAGE = 17;
1187 private static final int MMS_DRM_STORAGE_ID = 18;
Tom Taylor6c0ef242009-06-01 12:05:00 -07001188 private static final int MMS_THREADS = 19;
Tom Taylorc2db47d2012-03-27 15:15:39 -07001189 private static final int MMS_PART_RESET_FILE_PERMISSION = 20;
kaiyiz81ee3bc2015-02-06 13:43:48 +08001190 private static final int MMS_GET_PDU = 21;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001191
1192 private static final UriMatcher
1193 sURLMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1194
1195 static {
1196 sURLMatcher.addURI("mms", null, MMS_ALL);
1197 sURLMatcher.addURI("mms", "#", MMS_ALL_ID);
1198 sURLMatcher.addURI("mms", "inbox", MMS_INBOX);
1199 sURLMatcher.addURI("mms", "inbox/#", MMS_INBOX_ID);
1200 sURLMatcher.addURI("mms", "sent", MMS_SENT);
1201 sURLMatcher.addURI("mms", "sent/#", MMS_SENT_ID);
1202 sURLMatcher.addURI("mms", "drafts", MMS_DRAFTS);
1203 sURLMatcher.addURI("mms", "drafts/#", MMS_DRAFTS_ID);
1204 sURLMatcher.addURI("mms", "outbox", MMS_OUTBOX);
1205 sURLMatcher.addURI("mms", "outbox/#", MMS_OUTBOX_ID);
1206 sURLMatcher.addURI("mms", "part", MMS_ALL_PART);
1207 sURLMatcher.addURI("mms", "#/part", MMS_MSG_PART);
1208 sURLMatcher.addURI("mms", "part/#", MMS_PART_ID);
1209 sURLMatcher.addURI("mms", "#/addr", MMS_MSG_ADDR);
1210 sURLMatcher.addURI("mms", "rate", MMS_SENDING_RATE);
1211 sURLMatcher.addURI("mms", "report-status/#", MMS_REPORT_STATUS);
1212 sURLMatcher.addURI("mms", "report-request/#", MMS_REPORT_REQUEST);
1213 sURLMatcher.addURI("mms", "drm", MMS_DRM_STORAGE);
1214 sURLMatcher.addURI("mms", "drm/#", MMS_DRM_STORAGE_ID);
Tom Taylor6c0ef242009-06-01 12:05:00 -07001215 sURLMatcher.addURI("mms", "threads", MMS_THREADS);
Tom Taylorc2db47d2012-03-27 15:15:39 -07001216 sURLMatcher.addURI("mms", "resetFilePerm/*", MMS_PART_RESET_FILE_PERMISSION);
kaiyiz81ee3bc2015-02-06 13:43:48 +08001217 sURLMatcher.addURI("mms", "get-pdu", MMS_GET_PDU);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001218 }
1219
1220 private SQLiteOpenHelper mOpenHelper;
1221
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001222 private static String concatSelections(String selection1, String selection2) {
1223 if (TextUtils.isEmpty(selection1)) {
1224 return selection2;
1225 } else if (TextUtils.isEmpty(selection2)) {
1226 return selection1;
1227 } else {
1228 return selection1 + " AND " + selection2;
1229 }
1230 }
1231}
1232