blob: cda84fd449f5fa58243b91a75d0b25b89681cc13 [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 Furtado5bc7f962016-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;
Nikolab93ad242017-09-22 11:21:28 -070022import 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;
Qimeng Pan01813f62018-01-09 18:06:50 +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;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080035import android.os.ParcelFileDescriptor;
Amith Yamasani43f9fb22014-09-10 15:56:47 -070036import android.os.UserHandle;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080037import android.provider.BaseColumns;
Mark Wagner8e5ee782010-01-04 17:39:06 -080038import android.provider.Telephony;
Yusuf T. Mobile21c25bc2009-06-16 13:40:38 -070039import android.provider.Telephony.CanonicalAddressesColumns;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080040import android.provider.Telephony.Mms;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080041import android.provider.Telephony.Mms.Addr;
Nikolab93ad242017-09-22 11:21:28 -070042import android.provider.Telephony.Mms.Inbox;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080043import 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;
Yingren Wang4022d072019-03-25 21:00:25 +080047import android.support.v4.content.FileProvider;
Jayachandran C2408bc22019-10-14 17:27:44 -070048import android.system.ErrnoException;
49import android.system.Os;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080050import android.text.TextUtils;
Aishwarya Mallampati4eba1aa2022-08-17 23:21:18 +000051import android.util.EventLog;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080052import android.util.Log;
53
Qimeng Pan01813f62018-01-09 18:06:50 +080054import com.google.android.mms.MmsException;
55import com.google.android.mms.pdu.GenericPdu;
56import com.google.android.mms.pdu.PduComposer;
57import com.google.android.mms.pdu.RetrieveConf;
Tom Taylorb1bae652010-03-08 16:33:42 -080058import com.google.android.mms.pdu.PduHeaders;
Qimeng Pan01813f62018-01-09 18:06:50 +080059import com.google.android.mms.pdu.PduPersister;
60import com.google.android.mms.pdu.PduParser;
61import com.google.android.mms.pdu.SendReq;
Tom Taylorc2db47d2012-03-27 15:15:39 -070062import com.google.android.mms.util.DownloadDrmHelper;
Tom Taylorc71e7702010-01-28 09:23:12 -080063
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080064import java.io.File;
Qimeng Pan01813f62018-01-09 18:06:50 +080065import java.io.FileInputStream;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080066import java.io.FileNotFoundException;
67import java.io.IOException;
Qimeng Pan01813f62018-01-09 18:06:50 +080068import java.util.HashSet;
Amith Yamasani43f9fb22014-09-10 15:56:47 -070069
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080070/**
71 * The class to provide base facility to access MMS related content,
72 * which is stored in a SQLite database and in the file system.
73 */
74public class MmsProvider extends ContentProvider {
75 static final String TABLE_PDU = "pdu";
76 static final String TABLE_ADDR = "addr";
77 static final String TABLE_PART = "part";
78 static final String TABLE_RATE = "rate";
79 static final String TABLE_DRM = "drm";
Mark Wagner8e5ee782010-01-04 17:39:06 -080080 static final String TABLE_WORDS = "words";
Ye Wen72f13552015-03-10 14:17:13 -070081 static final String VIEW_PDU_RESTRICTED = "pdu_restricted";
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080082
Ye Weneaa93e62015-01-06 13:08:53 -080083 // The name of parts directory. The full dir is "app_parts".
Ji Yang0b59ce42016-02-18 18:53:18 -080084 static final String PARTS_DIR_NAME = "parts";
Qimeng Pan01813f62018-01-09 18:06:50 +080085 static final String COLUMN_PDU_PATH = "pdu_path";
86
87 private static final int MAX_FILE_NAME_LENGTH = 30;
88
89 private final static String[] PDU_COLUMNS = new String[] {
90 "_id",
91 "pdu_path",
92 "pdu_data"
93 };
Tom Taylorc2db47d2012-03-27 15:15:39 -070094
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080095 @Override
96 public boolean onCreate() {
Ye Wenb2ce2d32014-07-28 14:49:30 -070097 setAppOps(AppOpsManager.OP_READ_SMS, AppOpsManager.OP_WRITE_SMS);
Ji Yang5a3194d2016-01-27 16:34:40 -080098 mOpenHelper = MmsSmsDatabaseHelper.getInstanceForCe(getContext());
Roman Sorokin2d3a7792016-04-13 18:20:01 +020099 TelephonyBackupAgent.DeferredSmsMmsRestoreService.startIfFilesExist(getContext());
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800100 return true;
101 }
102
Ye Wen72f13552015-03-10 14:17:13 -0700103 /**
104 * Return the proper view of "pdu" table for the current access status.
105 *
106 * @param accessRestricted If the access is restricted
107 * @return the table/view name of the mms data
108 */
109 public static String getPduTable(boolean accessRestricted) {
110 return accessRestricted ? VIEW_PDU_RESTRICTED : TABLE_PDU;
111 }
112
Qimeng Pan01813f62018-01-09 18:06:50 +0800113 private byte[] getPduDataFromDB(int msgId, int msgType) {
114 Uri uri = ContentUris.withAppendedId(Mms.CONTENT_URI, msgId);
115 PduPersister persister = PduPersister.getPduPersister(getContext());
116 byte[] mmsData = null;
117 try {
118 if (Mms.MESSAGE_BOX_INBOX == msgType
119 || Mms.MESSAGE_BOX_SENT == msgType) {
120 GenericPdu pdu = persister.load(uri);
121 if (pdu != null) {
122 mmsData = new PduComposer(getContext(), pdu).make();
123 }
124 }
125 } catch (MmsException e) {
126 Log.e(TAG, "MmsException e=" + e);
127 }
128 return mmsData;
129 }
130
131 private Cursor getPdus(int itemCount, int dataCount, String[] data) {
132 MatrixCursor cursor = new MatrixCursor(PDU_COLUMNS, 1);
133 long token = Binder.clearCallingIdentity();
134 SQLiteDatabase db = mOpenHelper.getReadableDatabase();
135 db.beginTransaction();
136 try {
137 for (int i = 0; i < dataCount; i++) {
138 int msgId = Integer.parseInt(data[i * itemCount]);
139 int msgType = Integer.parseInt(data[i * itemCount + 1]);
140 String pduPath = data[i * itemCount + 2];
141 byte[] pduData = getPduDataFromDB(msgId, msgType);
142 if (pduData == null || pduData.length == 0) {
143 Log.e(TAG, "can't get msgId:" + msgId + " pdu data.");
144 continue;
145 }
146 Object[] row = new Object[3];
147 row[0] = msgId;
148 row[1] = pduPath;
149 row[2] = pduData;
150 cursor.addRow(row);
151 }
152 db.setTransactionSuccessful();
153 } catch (Exception e) {
154 Log.e(TAG, "Exception e =", e);
155 } finally {
156 Binder.restoreCallingIdentity(token);
157 db.endTransaction();
158 }
159 return cursor;
160 }
161
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800162 @Override
163 public Cursor query(Uri uri, String[] projection,
164 String selection, String[] selectionArgs, String sortOrder) {
Ye Wen72f13552015-03-10 14:17:13 -0700165 // First check if a restricted view of the "pdu" table should be used based on the
166 // caller's identity. Only system, phone or the default sms app can have full access
167 // of mms data. For other apps, we present a restricted view which only contains sent
168 // or received messages, without wap pushes.
169 final boolean accessRestricted = ProviderUtil.isAccessRestricted(
170 getContext(), getCallingPackage(), Binder.getCallingUid());
171 final String pduTable = getPduTable(accessRestricted);
172
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800173 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
174
175 // Generate the body of the query.
176 int match = sURLMatcher.match(uri);
177 if (LOCAL_LOGV) {
178 Log.v(TAG, "Query uri=" + uri + ", match=" + match);
179 }
180
181 switch (match) {
182 case MMS_ALL:
Ye Wen72f13552015-03-10 14:17:13 -0700183 constructQueryForBox(qb, Mms.MESSAGE_BOX_ALL, pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800184 break;
185 case MMS_INBOX:
Ye Wen72f13552015-03-10 14:17:13 -0700186 constructQueryForBox(qb, Mms.MESSAGE_BOX_INBOX, pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800187 break;
188 case MMS_SENT:
Ye Wen72f13552015-03-10 14:17:13 -0700189 constructQueryForBox(qb, Mms.MESSAGE_BOX_SENT, pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800190 break;
191 case MMS_DRAFTS:
Ye Wen72f13552015-03-10 14:17:13 -0700192 constructQueryForBox(qb, Mms.MESSAGE_BOX_DRAFTS, pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800193 break;
194 case MMS_OUTBOX:
Ye Wen72f13552015-03-10 14:17:13 -0700195 constructQueryForBox(qb, Mms.MESSAGE_BOX_OUTBOX, pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800196 break;
197 case MMS_ALL_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(0));
200 break;
201 case MMS_INBOX_ID:
202 case MMS_SENT_ID:
203 case MMS_DRAFTS_ID:
204 case MMS_OUTBOX_ID:
Ye Wen72f13552015-03-10 14:17:13 -0700205 qb.setTables(pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800206 qb.appendWhere(Mms._ID + "=" + uri.getPathSegments().get(1));
207 qb.appendWhere(" AND " + Mms.MESSAGE_BOX + "="
208 + getMessageBoxByMatch(match));
209 break;
210 case MMS_ALL_PART:
211 qb.setTables(TABLE_PART);
212 break;
213 case MMS_MSG_PART:
214 qb.setTables(TABLE_PART);
215 qb.appendWhere(Part.MSG_ID + "=" + uri.getPathSegments().get(0));
216 break;
217 case MMS_PART_ID:
218 qb.setTables(TABLE_PART);
219 qb.appendWhere(Part._ID + "=" + uri.getPathSegments().get(1));
220 break;
221 case MMS_MSG_ADDR:
222 qb.setTables(TABLE_ADDR);
223 qb.appendWhere(Addr.MSG_ID + "=" + uri.getPathSegments().get(0));
224 break;
225 case MMS_REPORT_STATUS:
226 /*
227 SELECT DISTINCT address,
228 T.delivery_status AS delivery_status,
229 T.read_status AS read_status
230 FROM addr
231 INNER JOIN (SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3,
232 ifnull(P2.st, 0) AS delivery_status,
233 ifnull(P3.read_status, 0) AS read_status
234 FROM pdu P1
235 INNER JOIN pdu P2
236 ON P1.m_id = P2.m_id AND P2.m_type = 134
237 LEFT JOIN pdu P3
238 ON P1.m_id = P3.m_id AND P3.m_type = 136
239 UNION
240 SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3,
241 ifnull(P2.st, 0) AS delivery_status,
242 ifnull(P3.read_status, 0) AS read_status
243 FROM pdu P1
244 INNER JOIN pdu P3
245 ON P1.m_id = P3.m_id AND P3.m_type = 136
246 LEFT JOIN pdu P2
247 ON P1.m_id = P2.m_id AND P2.m_type = 134) T
248 ON (msg_id = id2 AND type = 151)
249 OR (msg_id = id3 AND type = 137)
250 WHERE T.id1 = ?;
251 */
Ye Wen72f13552015-03-10 14:17:13 -0700252 qb.setTables(TABLE_ADDR + " INNER JOIN "
253 + "(SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3, "
254 + "ifnull(P2.st, 0) AS delivery_status, "
255 + "ifnull(P3.read_status, 0) AS read_status "
256 + "FROM " + pduTable + " P1 INNER JOIN " + pduTable + " P2 "
257 + "ON P1.m_id=P2.m_id AND P2.m_type=134 "
258 + "LEFT JOIN " + pduTable + " P3 "
259 + "ON P1.m_id=P3.m_id AND P3.m_type=136 "
260 + "UNION "
261 + "SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3, "
262 + "ifnull(P2.st, 0) AS delivery_status, "
263 + "ifnull(P3.read_status, 0) AS read_status "
264 + "FROM " + pduTable + " P1 INNER JOIN " + pduTable + " P3 "
265 + "ON P1.m_id=P3.m_id AND P3.m_type=136 "
266 + "LEFT JOIN " + pduTable + " P2 "
267 + "ON P1.m_id=P2.m_id AND P2.m_type=134) T "
268 + "ON (msg_id=id2 AND type=151) OR (msg_id=id3 AND type=137)");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800269 qb.appendWhere("T.id1 = " + uri.getLastPathSegment());
270 qb.setDistinct(true);
271 break;
272 case MMS_REPORT_REQUEST:
273 /*
274 SELECT address, d_rpt, rr
275 FROM addr join pdu on pdu._id = addr.msg_id
276 WHERE pdu._id = messageId AND addr.type = 151
277 */
278 qb.setTables(TABLE_ADDR + " join " +
Ye Wen72f13552015-03-10 14:17:13 -0700279 pduTable + " on " + pduTable + "._id = addr.msg_id");
280 qb.appendWhere(pduTable + "._id = " + uri.getLastPathSegment());
281 qb.appendWhere(" AND " + TABLE_ADDR + ".type = " + PduHeaders.TO);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800282 break;
283 case MMS_SENDING_RATE:
284 qb.setTables(TABLE_RATE);
285 break;
286 case MMS_DRM_STORAGE_ID:
287 qb.setTables(TABLE_DRM);
288 qb.appendWhere(BaseColumns._ID + "=" + uri.getLastPathSegment());
289 break;
Tom Taylor6c0ef242009-06-01 12:05:00 -0700290 case MMS_THREADS:
Ye Wen72f13552015-03-10 14:17:13 -0700291 qb.setTables(pduTable + " group by thread_id");
Tom Taylor6c0ef242009-06-01 12:05:00 -0700292 break;
Qimeng Pan01813f62018-01-09 18:06:50 +0800293 case MMS_GET_PDU:
294 int itemCount = Integer.parseInt(uri.getQueryParameter("item_count"));
295 int dataCount = Integer.parseInt(uri.getQueryParameter("data_count"));
296 String split = uri.getQueryParameter("data_split");
297 String[] data = null;
298 if (!TextUtils.isEmpty(uri.getQueryParameter("data"))) {
299 data = uri.getQueryParameter("data").split(split);
300 Log.d(TAG, "data.length :" + data.length);
301 return getPdus(itemCount, dataCount, data);
302 } else {
303 Log.e(TAG, "MMS get pdu date return null");
304 return null;
305 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800306 default:
Wei Huang2914a7a2009-09-23 00:45:36 -0700307 Log.e(TAG, "query: invalid request: " + uri);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800308 return null;
309 }
310
311 String finalSortOrder = null;
312 if (TextUtils.isEmpty(sortOrder)) {
Ye Wen72f13552015-03-10 14:17:13 -0700313 if (qb.getTables().equals(pduTable)) {
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800314 finalSortOrder = Mms.DATE + " DESC";
315 } else if (qb.getTables().equals(TABLE_PART)) {
316 finalSortOrder = Part.SEQ;
317 }
318 } else {
319 finalSortOrder = sortOrder;
320 }
321
Tom Taylor3ad9da42014-01-16 13:57:11 -0800322 Cursor ret;
323 try {
324 SQLiteDatabase db = mOpenHelper.getReadableDatabase();
325 ret = qb.query(db, projection, selection,
326 selectionArgs, null, null, finalSortOrder);
327 } catch (SQLiteException e) {
328 Log.e(TAG, "returning NULL cursor, query: " + uri, e);
329 return null;
330 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800331
332 // TODO: Does this need to be a URI for this provider.
333 ret.setNotificationUri(getContext().getContentResolver(), uri);
334 return ret;
335 }
336
Ye Wen72f13552015-03-10 14:17:13 -0700337 private void constructQueryForBox(SQLiteQueryBuilder qb, int msgBox, String pduTable) {
338 qb.setTables(pduTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800339
340 if (msgBox != Mms.MESSAGE_BOX_ALL) {
341 qb.appendWhere(Mms.MESSAGE_BOX + "=" + msgBox);
342 }
343 }
344
345 @Override
346 public String getType(Uri uri) {
347 int match = sURLMatcher.match(uri);
348 switch (match) {
349 case MMS_ALL:
350 case MMS_INBOX:
351 case MMS_SENT:
352 case MMS_DRAFTS:
353 case MMS_OUTBOX:
354 return VND_ANDROID_DIR_MMS;
355 case MMS_ALL_ID:
356 case MMS_INBOX_ID:
357 case MMS_SENT_ID:
358 case MMS_DRAFTS_ID:
359 case MMS_OUTBOX_ID:
360 return VND_ANDROID_MMS;
361 case MMS_PART_ID: {
362 Cursor cursor = mOpenHelper.getReadableDatabase().query(
363 TABLE_PART, new String[] { Part.CONTENT_TYPE },
364 Part._ID + " = ?", new String[] { uri.getLastPathSegment() },
365 null, null, null);
366 if (cursor != null) {
367 try {
368 if ((cursor.getCount() == 1) && cursor.moveToFirst()) {
369 return cursor.getString(0);
370 } else {
371 Log.e(TAG, "cursor.count() != 1: " + uri);
372 }
373 } finally {
374 cursor.close();
375 }
376 } else {
377 Log.e(TAG, "cursor == null: " + uri);
378 }
379 return "*/*";
380 }
381 case MMS_ALL_PART:
382 case MMS_MSG_PART:
383 case MMS_MSG_ADDR:
384 default:
385 return "*/*";
386 }
387 }
388
Yingren Wang4022d072019-03-25 21:00:25 +0800389 private byte[] getPduDataFromFile(String pduPath, String authority) {
Qimeng Pan01813f62018-01-09 18:06:50 +0800390 FileInputStream fileInputStream = null;
391 byte[] data = null;
392 try {
Yingren Wang4022d072019-03-25 21:00:25 +0800393 Uri fileUri = FileProvider.getUriForFile(getContext(), authority, new File(pduPath));
394 ParcelFileDescriptor parcelFileDescriptor =
395 getContext().getContentResolver().openFileDescriptor(fileUri, "r");
396 fileInputStream = new FileInputStream(parcelFileDescriptor.getFileDescriptor());
397 data = new byte[fileInputStream.available()];
Qimeng Pan01813f62018-01-09 18:06:50 +0800398 fileInputStream.read(data);
399 } catch (Exception e) {
400 Log.e(TAG, "read file exception :", e);
401 } finally {
402 try {
403 if (fileInputStream != null) {
404 fileInputStream.close();
405 }
406 } catch (Exception e) {
407 Log.e(TAG, "close file stream exception :", e);
408 }
409 }
410 return data;
411 }
412
Yingren Wang4022d072019-03-25 21:00:25 +0800413 private Uri restorePduFile(Uri uri, String pduPath, String authority) {
Qimeng Pan01813f62018-01-09 18:06:50 +0800414 Uri msgUri = null;
Yingren Wang4022d072019-03-25 21:00:25 +0800415 if (uri == null || TextUtils.isEmpty(pduPath) || TextUtils.isEmpty(authority)) {
Qimeng Pan01813f62018-01-09 18:06:50 +0800416 return null;
417 }
418
419 try {
Yingren Wang4022d072019-03-25 21:00:25 +0800420 byte[] pduData = getPduDataFromFile(pduPath, authority);
Qimeng Pan01813f62018-01-09 18:06:50 +0800421 PduPersister pduPersister = PduPersister.getPduPersister(getContext());
422 if (pduData != null && pduData.length > 0) {
423 if (Mms.Sent.CONTENT_URI.equals(uri)
424 || Mms.Inbox.CONTENT_URI.equals(uri)) {
425 GenericPdu pdu = new PduParser(pduData, true).parse();
426 msgUri = pduPersister.persist(
427 pdu, uri, true, false, null);
428 } else {
429 Log.e(TAG,"Unsupported uri :" + uri);
430 }
431 }
432 } catch (MmsException e) {
433 Log.e(TAG, "MmsException: ", e);
434 }
435 return msgUri;
436 }
437
438 private String getPduPath(String dir, ContentValues values) {
439 if (dir != null && values != null && values.containsKey(COLUMN_PDU_PATH)) {
440 String path = values.getAsString(COLUMN_PDU_PATH);
441 if (!TextUtils.isEmpty(path)) {
442 return dir + "/" + path;
443 }
444 }
445 return null;
446 }
447
Yingren Wang4022d072019-03-25 21:00:25 +0800448 private int restoreMms(Uri uri, ContentValues values, String dir, String authority) {
Qimeng Pan01813f62018-01-09 18:06:50 +0800449 int count = 0;
Yingren Wang4022d072019-03-25 21:00:25 +0800450 Uri msgUri = restorePduFile(uri, getPduPath(dir, values), authority);
Qimeng Pan01813f62018-01-09 18:06:50 +0800451 if (msgUri != null) {
452 String selection = Mms._ID + "=" + msgUri.getLastPathSegment();
453 values.remove(COLUMN_PDU_PATH);
454 ContentValues finalValues = new ContentValues(values);
455 // now only support bulkInsert pdu table.
456 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
457 count = db.update(TABLE_PDU, finalValues, selection, null);
458 }
459 return count;
460 }
461
462 @Override
463 public int bulkInsert(Uri uri, ContentValues[] values) {
464 String dir = uri.getQueryParameter("restore_dir");
Yingren Wang4022d072019-03-25 21:00:25 +0800465 String authority = uri.getQueryParameter("authorities");
Qimeng Pan01813f62018-01-09 18:06:50 +0800466 if (TextUtils.isEmpty(dir)) {
467 return super.bulkInsert(uri, values);
468 }
469
470 Uri insertUri = null;
471 int match = sURLMatcher.match(uri);
472 switch (match) {
473 case MMS_INBOX:
474 insertUri = Mms.Inbox.CONTENT_URI;
475 break;
476 case MMS_SENT:
477 insertUri = Mms.Sent.CONTENT_URI;
478 break;
479 default:
480 return 0;
481 }
482
483 long token = Binder.clearCallingIdentity();
484 int count = 0;
485 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
486 db.beginTransaction();
487 try {
488 for (ContentValues value : values) {
Yingren Wang4022d072019-03-25 21:00:25 +0800489 count += restoreMms(insertUri, value, dir, authority);
Qimeng Pan01813f62018-01-09 18:06:50 +0800490 }
491
492 Log.d(TAG, "bulkInsert request count: " + values.length
493 + " successfully count : " + count);
494 if (count == values.length) {
495 db.setTransactionSuccessful();
496 }
497 return count;
498 } finally {
499 db.endTransaction();
500 Binder.restoreCallingIdentity(token);
501 }
502 }
503
504
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800505 @Override
506 public Uri insert(Uri uri, ContentValues values) {
Ye Wene07acb92014-11-19 12:06:05 -0800507 final int callerUid = Binder.getCallingUid();
Ye Wen72f13552015-03-10 14:17:13 -0700508 final String callerPkg = getCallingPackage();
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800509 int msgBox = Mms.MESSAGE_BOX_ALL;
510 boolean notify = true;
511
512 int match = sURLMatcher.match(uri);
513 if (LOCAL_LOGV) {
514 Log.v(TAG, "Insert uri=" + uri + ", match=" + match);
515 }
516
517 String table = TABLE_PDU;
518 switch (match) {
519 case MMS_ALL:
520 Object msgBoxObj = values.getAsInteger(Mms.MESSAGE_BOX);
521 if (msgBoxObj != null) {
522 msgBox = (Integer) msgBoxObj;
523 }
524 else {
525 // default to inbox
526 msgBox = Mms.MESSAGE_BOX_INBOX;
527 }
528 break;
529 case MMS_INBOX:
530 msgBox = Mms.MESSAGE_BOX_INBOX;
531 break;
532 case MMS_SENT:
533 msgBox = Mms.MESSAGE_BOX_SENT;
534 break;
535 case MMS_DRAFTS:
536 msgBox = Mms.MESSAGE_BOX_DRAFTS;
537 break;
538 case MMS_OUTBOX:
539 msgBox = Mms.MESSAGE_BOX_OUTBOX;
540 break;
541 case MMS_MSG_PART:
542 notify = false;
543 table = TABLE_PART;
544 break;
545 case MMS_MSG_ADDR:
546 notify = false;
547 table = TABLE_ADDR;
548 break;
549 case MMS_SENDING_RATE:
550 notify = false;
551 table = TABLE_RATE;
552 break;
553 case MMS_DRM_STORAGE:
554 notify = false;
555 table = TABLE_DRM;
556 break;
557 default:
Wei Huang2914a7a2009-09-23 00:45:36 -0700558 Log.e(TAG, "insert: invalid request: " + uri);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800559 return null;
560 }
561
562 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
563 ContentValues finalValues;
564 Uri res = Mms.CONTENT_URI;
Nikolab93ad242017-09-22 11:21:28 -0700565 Uri caseSpecificUri = null;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800566 long rowId;
567
568 if (table.equals(TABLE_PDU)) {
569 boolean addDate = !values.containsKey(Mms.DATE);
570 boolean addMsgBox = !values.containsKey(Mms.MESSAGE_BOX);
571
572 // Filter keys we don't support yet.
573 filterUnsupportedKeys(values);
574
575 // TODO: Should initialValues be validated, e.g. if it
576 // missed some significant keys?
577 finalValues = new ContentValues(values);
578
579 long timeInMillis = System.currentTimeMillis();
580
581 if (addDate) {
582 finalValues.put(Mms.DATE, timeInMillis / 1000L);
583 }
584
585 if (addMsgBox && (msgBox != Mms.MESSAGE_BOX_ALL)) {
586 finalValues.put(Mms.MESSAGE_BOX, msgBox);
587 }
588
589 if (msgBox != Mms.MESSAGE_BOX_INBOX) {
590 // Mark all non-inbox messages read.
591 finalValues.put(Mms.READ, 1);
592 }
593
Yusuf T. Mobile21c25bc2009-06-16 13:40:38 -0700594 // thread_id
595 Long threadId = values.getAsLong(Mms.THREAD_ID);
596 String address = values.getAsString(CanonicalAddressesColumns.ADDRESS);
597
Tom Taylor59269962012-05-09 14:42:35 -0700598 if (((threadId == null) || (threadId == 0)) && (!TextUtils.isEmpty(address))) {
Yusuf T. Mobile21c25bc2009-06-16 13:40:38 -0700599 finalValues.put(Mms.THREAD_ID, Threads.getOrCreateThreadId(getContext(), address));
600 }
601
Ye Wene07acb92014-11-19 12:06:05 -0800602 if (ProviderUtil.shouldSetCreator(finalValues, callerUid)) {
603 // Only SYSTEM or PHONE can set CREATOR
604 // If caller is not SYSTEM or PHONE, or SYSTEM or PHONE does not set CREATOR
605 // set CREATOR using the truth on caller.
606 // Note: Inferring package name from UID may include unrelated package names
Ye Wen72f13552015-03-10 14:17:13 -0700607 finalValues.put(Telephony.Mms.CREATOR, callerPkg);
Ye Wene07acb92014-11-19 12:06:05 -0800608 }
609
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800610 if ((rowId = db.insert(table, null, finalValues)) <= 0) {
Ye Wene07acb92014-11-19 12:06:05 -0800611 Log.e(TAG, "MmsProvider.insert: failed!");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800612 return null;
613 }
614
Nikolab93ad242017-09-22 11:21:28 -0700615 // Notify change when an MMS is received.
616 if (msgBox == Mms.MESSAGE_BOX_INBOX) {
617 caseSpecificUri = ContentUris.withAppendedId(Mms.Inbox.CONTENT_URI, rowId);
618 }
619
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800620 res = Uri.parse(res + "/" + rowId);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800621 } else if (table.equals(TABLE_ADDR)) {
622 finalValues = new ContentValues(values);
623 finalValues.put(Addr.MSG_ID, uri.getPathSegments().get(0));
624
625 if ((rowId = db.insert(table, null, finalValues)) <= 0) {
Ye Wene07acb92014-11-19 12:06:05 -0800626 Log.e(TAG, "Failed to insert address");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800627 return null;
628 }
629
630 res = Uri.parse(res + "/addr/" + rowId);
631 } else if (table.equals(TABLE_PART)) {
Tom Taylor785f9212017-03-27 11:39:54 -0700632 boolean containsDataPath = values != null && values.containsKey(Part._DATA);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800633 finalValues = new ContentValues(values);
634
635 if (match == MMS_MSG_PART) {
636 finalValues.put(Part.MSG_ID, uri.getPathSegments().get(0));
637 }
638
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700639 String contentType = values.getAsString("ct");
Mark Wagner8e5ee782010-01-04 17:39:06 -0800640
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700641 // text/plain and app application/smil store their "data" inline in the
642 // table so there's no need to create the file
Tom Taylorc2db47d2012-03-27 15:15:39 -0700643 boolean plainText = false;
644 boolean smilText = false;
645 if ("text/plain".equals(contentType)) {
Tom Taylor785f9212017-03-27 11:39:54 -0700646 if (containsDataPath) {
647 Log.e(TAG, "insert: can't insert text/plain with _data");
648 return null;
649 }
Tom Taylorc2db47d2012-03-27 15:15:39 -0700650 plainText = true;
651 } else if ("application/smil".equals(contentType)) {
Tom Taylor785f9212017-03-27 11:39:54 -0700652 if (containsDataPath) {
653 Log.e(TAG, "insert: can't insert application/smil with _data");
654 return null;
655 }
Tom Taylorc2db47d2012-03-27 15:15:39 -0700656 smilText = true;
657 }
Mark Wagner8e5ee782010-01-04 17:39:06 -0800658 if (!plainText && !smilText) {
Tom Taylor785f9212017-03-27 11:39:54 -0700659 String path;
660 if (containsDataPath) {
661 // The _data column is filled internally in MmsProvider or from the
662 // TelephonyBackupAgent, so this check is just to avoid it from being
663 // inadvertently set. This is not supposed to be a protection against malicious
664 // attack, since sql injection could still be attempted to bypass the check.
665 // On the other hand, the MmsProvider does verify that the _data column has an
666 // allowed value before opening any uri/files.
667 if (!"com.android.providers.telephony".equals(callerPkg)) {
668 Log.e(TAG, "insert: can't insert _data");
669 return null;
670 }
671 try {
672 path = values.getAsString(Part._DATA);
673 final String partsDirPath = getContext()
674 .getDir(PARTS_DIR_NAME, 0).getCanonicalPath();
675 if (!new File(path).getCanonicalPath().startsWith(partsDirPath)) {
676 Log.e(TAG, "insert: path "
677 + path
678 + " does not start with "
679 + partsDirPath);
680 // Don't care return value
681 return null;
682 }
683 } catch (IOException e) {
684 Log.e(TAG, "insert part: create path failed " + e, e);
685 return null;
686 }
Tom Taylorc2db47d2012-03-27 15:15:39 -0700687 } else {
Tom Taylor785f9212017-03-27 11:39:54 -0700688 // Use the filename if possible, otherwise use the current time as the name.
689 String contentLocation = values.getAsString("cl");
690 if (!TextUtils.isEmpty(contentLocation)) {
691 File f = new File(contentLocation);
692 contentLocation = "_" + f.getName();
693 } else {
694 contentLocation = "";
695 }
Tom Taylorc2db47d2012-03-27 15:15:39 -0700696
Tom Taylor785f9212017-03-27 11:39:54 -0700697 // Generate the '_data' field of the part with default
698 // permission settings.
699 path = getContext().getDir(PARTS_DIR_NAME, 0).getPath()
700 + "/PART_" + System.currentTimeMillis() + contentLocation;
Tom Taylorc2db47d2012-03-27 15:15:39 -0700701
Tom Taylor785f9212017-03-27 11:39:54 -0700702 if (DownloadDrmHelper.isDrmConvertNeeded(contentType)) {
703 // Adds the .fl extension to the filename if contentType is
704 // "application/vnd.oma.drm.message"
705 path = DownloadDrmHelper.modifyDrmFwLockFileExtension(path);
706 }
Tom Taylorc2db47d2012-03-27 15:15:39 -0700707 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800708
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700709 finalValues.put(Part._DATA, path);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800710
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700711 File partFile = new File(path);
712 if (!partFile.exists()) {
713 try {
714 if (!partFile.createNewFile()) {
715 throw new IllegalStateException(
716 "Unable to create new partFile: " + path);
717 }
Tom Taylorc2db47d2012-03-27 15:15:39 -0700718 // Give everyone rw permission until we encrypt the file
719 // (in PduPersister.persistData). Once the file is encrypted, the
720 // permissions will be set to 0644.
Jayachandran C2408bc22019-10-14 17:27:44 -0700721 try {
722 Os.chmod(path, 0666);
723 if (LOCAL_LOGV) {
724 Log.d(TAG, "MmsProvider.insert chmod is successful");
725 }
726 } catch (ErrnoException e) {
727 Log.e(TAG, "Exception in chmod: " + e);
Tom Taylorc2db47d2012-03-27 15:15:39 -0700728 }
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700729 } catch (IOException e) {
730 Log.e(TAG, "createNewFile", e);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800731 throw new IllegalStateException(
732 "Unable to create new partFile: " + path);
733 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800734 }
735 }
736
737 if ((rowId = db.insert(table, null, finalValues)) <= 0) {
Ye Wene07acb92014-11-19 12:06:05 -0800738 Log.e(TAG, "MmsProvider.insert: failed!");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800739 return null;
740 }
741
742 res = Uri.parse(res + "/part/" + rowId);
Mark Wagner8e5ee782010-01-04 17:39:06 -0800743
744 // Don't use a trigger for updating the words table because of a bug
745 // in FTS3. The bug is such that the call to get the last inserted
746 // row is incorrect.
747 if (plainText) {
748 // Update the words table with a corresponding row. The words table
749 // allows us to search for words quickly, without scanning the whole
750 // table;
751 ContentValues cv = new ContentValues();
752
753 // we're using the row id of the part table row but we're also using ids
754 // from the sms table so this divides the space into two large chunks.
755 // The row ids from the part table start at 2 << 32.
Ian Rogersba1cc5d2016-03-15 23:11:13 -0700756 cv.put(Telephony.MmsSms.WordsTable.ID, (2L << 32) + rowId);
Mark Wagner8e5ee782010-01-04 17:39:06 -0800757 cv.put(Telephony.MmsSms.WordsTable.INDEXED_TEXT, values.getAsString("text"));
758 cv.put(Telephony.MmsSms.WordsTable.SOURCE_ROW_ID, rowId);
759 cv.put(Telephony.MmsSms.WordsTable.TABLE_ID, 2);
760 db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv);
761 }
762
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800763 } else if (table.equals(TABLE_RATE)) {
764 long now = values.getAsLong(Rate.SENT_TIME);
765 long oneHourAgo = now - 1000 * 60 * 60;
766 // Delete all unused rows (time earlier than one hour ago).
767 db.delete(table, Rate.SENT_TIME + "<=" + oneHourAgo, null);
768 db.insert(table, null, values);
769 } else if (table.equals(TABLE_DRM)) {
Ye Weneaa93e62015-01-06 13:08:53 -0800770 String path = getContext().getDir(PARTS_DIR_NAME, 0).getPath()
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800771 + "/PART_" + System.currentTimeMillis();
772 finalValues = new ContentValues(1);
773 finalValues.put("_data", path);
774
775 File partFile = new File(path);
776 if (!partFile.exists()) {
777 try {
778 if (!partFile.createNewFile()) {
779 throw new IllegalStateException(
780 "Unable to create new file: " + path);
781 }
782 } catch (IOException e) {
783 Log.e(TAG, "createNewFile", e);
784 throw new IllegalStateException(
785 "Unable to create new file: " + path);
786 }
787 }
788
789 if ((rowId = db.insert(table, null, finalValues)) <= 0) {
Ye Wene07acb92014-11-19 12:06:05 -0800790 Log.e(TAG, "MmsProvider.insert: failed!");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800791 return null;
792 }
793 res = Uri.parse(res + "/drm/" + rowId);
794 } else {
795 throw new AssertionError("Unknown table type: " + table);
796 }
797
798 if (notify) {
Nikolab93ad242017-09-22 11:21:28 -0700799 notifyChange(res, caseSpecificUri);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800800 }
801 return res;
802 }
803
Qimeng Pan01813f62018-01-09 18:06:50 +0800804 private String getFileName(String fileLocation) {
805 File f = new File(fileLocation);
806 String fileName = f.getName();
807 if (fileName.length() >= MAX_FILE_NAME_LENGTH) {
808 fileName = fileName.substring(0, MAX_FILE_NAME_LENGTH);
809 }
810 return fileName;
811 }
812
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800813 private int getMessageBoxByMatch(int match) {
814 switch (match) {
815 case MMS_INBOX_ID:
816 case MMS_INBOX:
817 return Mms.MESSAGE_BOX_INBOX;
818 case MMS_SENT_ID:
819 case MMS_SENT:
820 return Mms.MESSAGE_BOX_SENT;
821 case MMS_DRAFTS_ID:
822 case MMS_DRAFTS:
823 return Mms.MESSAGE_BOX_DRAFTS;
824 case MMS_OUTBOX_ID:
825 case MMS_OUTBOX:
826 return Mms.MESSAGE_BOX_OUTBOX;
827 default:
828 throw new IllegalArgumentException("bad Arg: " + match);
829 }
830 }
831
832 @Override
833 public int delete(Uri uri, String selection,
834 String[] selectionArgs) {
835 int match = sURLMatcher.match(uri);
836 if (LOCAL_LOGV) {
837 Log.v(TAG, "Delete uri=" + uri + ", match=" + match);
838 }
839
840 String table, extraSelection = null;
841 boolean notify = false;
842
843 switch (match) {
844 case MMS_ALL_ID:
845 case MMS_INBOX_ID:
846 case MMS_SENT_ID:
847 case MMS_DRAFTS_ID:
848 case MMS_OUTBOX_ID:
849 notify = true;
850 table = TABLE_PDU;
851 extraSelection = Mms._ID + "=" + uri.getLastPathSegment();
852 break;
853 case MMS_ALL:
854 case MMS_INBOX:
855 case MMS_SENT:
856 case MMS_DRAFTS:
857 case MMS_OUTBOX:
858 notify = true;
859 table = TABLE_PDU;
860 if (match != MMS_ALL) {
861 int msgBox = getMessageBoxByMatch(match);
862 extraSelection = Mms.MESSAGE_BOX + "=" + msgBox;
863 }
864 break;
865 case MMS_ALL_PART:
866 table = TABLE_PART;
867 break;
868 case MMS_MSG_PART:
869 table = TABLE_PART;
870 extraSelection = Part.MSG_ID + "=" + uri.getPathSegments().get(0);
871 break;
872 case MMS_PART_ID:
873 table = TABLE_PART;
874 extraSelection = Part._ID + "=" + uri.getPathSegments().get(1);
875 break;
876 case MMS_MSG_ADDR:
877 table = TABLE_ADDR;
878 extraSelection = Addr.MSG_ID + "=" + uri.getPathSegments().get(0);
879 break;
880 case MMS_DRM_STORAGE:
881 table = TABLE_DRM;
882 break;
883 default:
884 Log.w(TAG, "No match for URI '" + uri + "'");
885 return 0;
886 }
887
888 String finalSelection = concatSelections(selection, extraSelection);
889 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
890 int deletedRows = 0;
891
892 if (TABLE_PDU.equals(table)) {
893 deletedRows = deleteMessages(getContext(), db, finalSelection,
894 selectionArgs, uri);
895 } else if (TABLE_PART.equals(table)) {
896 deletedRows = deleteParts(db, finalSelection, selectionArgs);
Qimeng Pan01813f62018-01-09 18:06:50 +0800897 cleanUpWords(db);
898 updateHasAttachment(db);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800899 } else if (TABLE_DRM.equals(table)) {
900 deletedRows = deleteTempDrmData(db, finalSelection, selectionArgs);
901 } else {
902 deletedRows = db.delete(table, finalSelection, selectionArgs);
903 }
904
905 if ((deletedRows > 0) && notify) {
Nikolab93ad242017-09-22 11:21:28 -0700906 notifyChange(uri, null);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800907 }
908 return deletedRows;
909 }
910
911 static int deleteMessages(Context context, SQLiteDatabase db,
912 String selection, String[] selectionArgs, Uri uri) {
Qimeng Pan01813f62018-01-09 18:06:50 +0800913 Cursor cursor = db.query(TABLE_PDU, new String[] { Mms._ID, Mms.THREAD_ID },
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800914 selection, selectionArgs, null, null, null);
915 if (cursor == null) {
916 return 0;
917 }
918
Qimeng Pan01813f62018-01-09 18:06:50 +0800919 HashSet<Long> threadIds = new HashSet<Long>();
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800920 try {
921 if (cursor.getCount() == 0) {
922 return 0;
923 }
924
925 while (cursor.moveToNext()) {
926 deleteParts(db, Part.MSG_ID + " = ?",
927 new String[] { String.valueOf(cursor.getLong(0)) });
Qimeng Pan01813f62018-01-09 18:06:50 +0800928 threadIds.add(cursor.getLong(1));
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800929 }
Qimeng Pan01813f62018-01-09 18:06:50 +0800930 cleanUpWords(db);
931 updateHasAttachment(db);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800932 } finally {
933 cursor.close();
934 }
935
936 int count = db.delete(TABLE_PDU, selection, selectionArgs);
Qimeng Pan01813f62018-01-09 18:06:50 +0800937 for (long thread : threadIds) {
938 MmsSmsDatabaseHelper.updateThread(db, thread);
939 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800940 if (count > 0) {
941 Intent intent = new Intent(Mms.Intents.CONTENT_CHANGED_ACTION);
942 intent.putExtra(Mms.Intents.DELETED_CONTENTS, uri);
943 if (LOCAL_LOGV) {
944 Log.v(TAG, "Broadcasting intent: " + intent);
945 }
946 context.sendBroadcast(intent);
947 }
948 return count;
949 }
950
Qimeng Pan01813f62018-01-09 18:06:50 +0800951 private static void cleanUpWords(SQLiteDatabase db) {
952 db.execSQL("DELETE FROM words WHERE source_id not in (select _id from part) AND "
953 + "table_to_use = 2");
954 }
955
956 private static void updateHasAttachment(SQLiteDatabase db) {
957 db.execSQL("UPDATE threads SET has_attachment = CASE "
958 + "(SELECT COUNT(*) FROM part JOIN pdu WHERE part.mid = pdu._id AND "
959 + "pdu.thread_id = threads._id AND part.ct != 'text/plain' "
960 + "AND part.ct != 'application/smil') WHEN 0 THEN 0 ELSE 1 END");
961 }
962
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800963 private static int deleteParts(SQLiteDatabase db, String selection,
964 String[] selectionArgs) {
965 return deleteDataRows(db, TABLE_PART, selection, selectionArgs);
966 }
967
968 private static int deleteTempDrmData(SQLiteDatabase db, String selection,
969 String[] selectionArgs) {
970 return deleteDataRows(db, TABLE_DRM, selection, selectionArgs);
971 }
972
973 private static int deleteDataRows(SQLiteDatabase db, String table,
974 String selection, String[] selectionArgs) {
975 Cursor cursor = db.query(table, new String[] { "_data" },
976 selection, selectionArgs, null, null, null);
977 if (cursor == null) {
978 // FIXME: This might be an error, ignore it may cause
979 // unpredictable result.
980 return 0;
981 }
982
983 try {
984 if (cursor.getCount() == 0) {
985 return 0;
986 }
987
988 while (cursor.moveToNext()) {
989 try {
990 // Delete the associated files saved on file-system.
Mark Wagnerf0a9e902009-06-19 16:00:13 -0700991 String path = cursor.getString(0);
992 if (path != null) {
993 new File(path).delete();
994 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800995 } catch (Throwable ex) {
996 Log.e(TAG, ex.getMessage(), ex);
997 }
998 }
999 } finally {
1000 cursor.close();
1001 }
1002
1003 return db.delete(table, selection, selectionArgs);
1004 }
1005
1006 @Override
Andre Furtado5bc7f962016-08-18 10:28:37 -07001007 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
1008 // The _data column is filled internally in MmsProvider, so this check is just to avoid
1009 // it from being inadvertently set. This is not supposed to be a protection against
1010 // malicious attack, since sql injection could still be attempted to bypass the check. On
1011 // the other hand, the MmsProvider does verify that the _data column has an allowed value
1012 // before opening any uri/files.
Tom Taylor438403e2013-02-21 16:56:01 -08001013 if (values != null && values.containsKey(Part._DATA)) {
1014 return 0;
1015 }
Ye Wene07acb92014-11-19 12:06:05 -08001016 final int callerUid = Binder.getCallingUid();
Ye Wen72f13552015-03-10 14:17:13 -07001017 final String callerPkg = getCallingPackage();
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001018 int match = sURLMatcher.match(uri);
1019 if (LOCAL_LOGV) {
1020 Log.v(TAG, "Update uri=" + uri + ", match=" + match);
1021 }
1022
1023 boolean notify = false;
1024 String msgId = null;
1025 String table;
1026
1027 switch (match) {
1028 case MMS_ALL_ID:
1029 case MMS_INBOX_ID:
1030 case MMS_SENT_ID:
1031 case MMS_DRAFTS_ID:
1032 case MMS_OUTBOX_ID:
1033 msgId = uri.getLastPathSegment();
1034 // fall-through
1035 case MMS_ALL:
1036 case MMS_INBOX:
1037 case MMS_SENT:
1038 case MMS_DRAFTS:
1039 case MMS_OUTBOX:
1040 notify = true;
1041 table = TABLE_PDU;
1042 break;
Tom Taylorc2db47d2012-03-27 15:15:39 -07001043
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001044 case MMS_MSG_PART:
1045 case MMS_PART_ID:
1046 table = TABLE_PART;
1047 break;
Tom Taylorc2db47d2012-03-27 15:15:39 -07001048
1049 case MMS_PART_RESET_FILE_PERMISSION:
Ye Weneaa93e62015-01-06 13:08:53 -08001050 String path = getContext().getDir(PARTS_DIR_NAME, 0).getPath() + '/' +
Tom Taylorc2db47d2012-03-27 15:15:39 -07001051 uri.getPathSegments().get(1);
Aishwarya Mallampati4eba1aa2022-08-17 23:21:18 +00001052
Jayachandran C2408bc22019-10-14 17:27:44 -07001053 try {
Aishwarya Mallampatif7943542023-05-10 21:54:43 +00001054 File canonicalFile = new File(path).getCanonicalFile();
Aishwarya Mallampati4eba1aa2022-08-17 23:21:18 +00001055 String partsDirPath = getContext().getDir(PARTS_DIR_NAME, 0).getCanonicalPath();
Aishwarya Mallampatif7943542023-05-10 21:54:43 +00001056 if (!canonicalFile.getPath().startsWith(partsDirPath + '/')) {
Aishwarya Mallampati4eba1aa2022-08-17 23:21:18 +00001057 EventLog.writeEvent(0x534e4554, "240685104",
1058 Binder.getCallingUid(), (TAG + " update: path " + path +
1059 " does not start with " + partsDirPath));
1060 return 0;
1061 }
1062 // Reset the file permission back to read for everyone but me.
Aishwarya Mallampatif7943542023-05-10 21:54:43 +00001063 Os.chmod(canonicalFile.getPath(), 0644);
Jayachandran C2408bc22019-10-14 17:27:44 -07001064 if (LOCAL_LOGV) {
1065 Log.d(TAG, "MmsProvider.update chmod is successful for path: " + path);
1066 }
Aishwarya Mallampati4eba1aa2022-08-17 23:21:18 +00001067 } catch (ErrnoException | IOException e) {
Jayachandran C2408bc22019-10-14 17:27:44 -07001068 Log.e(TAG, "Exception in chmod: " + e);
Tom Taylorc2db47d2012-03-27 15:15:39 -07001069 }
1070 return 0;
1071
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001072 default:
1073 Log.w(TAG, "Update operation for '" + uri + "' not implemented.");
1074 return 0;
1075 }
1076
1077 String extraSelection = null;
1078 ContentValues finalValues;
1079 if (table.equals(TABLE_PDU)) {
1080 // Filter keys that we don't support yet.
1081 filterUnsupportedKeys(values);
Ye Wene07acb92014-11-19 12:06:05 -08001082 if (ProviderUtil.shouldRemoveCreator(values, callerUid)) {
1083 // CREATOR should not be changed by non-SYSTEM/PHONE apps
Ye Wen72f13552015-03-10 14:17:13 -07001084 Log.w(TAG, callerPkg + " tries to update CREATOR");
Ye Wene07acb92014-11-19 12:06:05 -08001085 values.remove(Mms.CREATOR);
1086 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001087 finalValues = new ContentValues(values);
1088
1089 if (msgId != null) {
1090 extraSelection = Mms._ID + "=" + msgId;
1091 }
1092 } else if (table.equals(TABLE_PART)) {
1093 finalValues = new ContentValues(values);
1094
1095 switch (match) {
1096 case MMS_MSG_PART:
1097 extraSelection = Part.MSG_ID + "=" + uri.getPathSegments().get(0);
1098 break;
1099 case MMS_PART_ID:
1100 extraSelection = Part._ID + "=" + uri.getPathSegments().get(1);
1101 break;
1102 default:
1103 break;
1104 }
1105 } else {
1106 return 0;
1107 }
1108
1109 String finalSelection = concatSelections(selection, extraSelection);
1110 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1111 int count = db.update(table, finalValues, finalSelection, selectionArgs);
1112 if (notify && (count > 0)) {
Nikolab93ad242017-09-22 11:21:28 -07001113 notifyChange(uri, null);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001114 }
1115 return count;
1116 }
1117
1118 @Override
Wei Huang2914a7a2009-09-23 00:45:36 -07001119 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
Wei Huang2914a7a2009-09-23 00:45:36 -07001120 int match = sURLMatcher.match(uri);
1121
1122 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Tom Taylor8168ff82013-02-19 14:30:23 -08001123 Log.d(TAG, "openFile: uri=" + uri + ", mode=" + mode + ", match=" + match);
Wei Huang2914a7a2009-09-23 00:45:36 -07001124 }
1125
Tom Taylor8168ff82013-02-19 14:30:23 -08001126 if (match != MMS_PART_ID) {
1127 return null;
Wei Huang2914a7a2009-09-23 00:45:36 -07001128 }
1129
Andre Furtado5bc7f962016-08-18 10:28:37 -07001130 return safeOpenFileHelper(uri, mode);
1131 }
1132
1133 @NonNull
1134 private ParcelFileDescriptor safeOpenFileHelper(
1135 @NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
Tom Taylor8168ff82013-02-19 14:30:23 -08001136 Cursor c = query(uri, new String[]{"_data"}, null, null, null);
1137 int count = (c != null) ? c.getCount() : 0;
1138 if (count != 1) {
1139 // If there is not exactly one result, throw an appropriate
1140 // exception.
1141 if (c != null) {
1142 c.close();
1143 }
1144 if (count == 0) {
1145 throw new FileNotFoundException("No entry for " + uri);
1146 }
1147 throw new FileNotFoundException("Multiple items at " + uri);
1148 }
1149
1150 c.moveToFirst();
1151 int i = c.getColumnIndex("_data");
1152 String path = (i >= 0 ? c.getString(i) : null);
1153 c.close();
1154
1155 if (path == null) {
Andre Furtado5bc7f962016-08-18 10:28:37 -07001156 throw new FileNotFoundException("Column _data not found.");
Tom Taylor8168ff82013-02-19 14:30:23 -08001157 }
Andre Furtado5bc7f962016-08-18 10:28:37 -07001158
1159 File filePath = new File(path);
Tom Taylor8168ff82013-02-19 14:30:23 -08001160 try {
Andre Furtado5bc7f962016-08-18 10:28:37 -07001161 // The MmsProvider shouldn't open a file that isn't MMS data, so we verify that the
1162 // _data path actually points to MMS data. That safeguards ourselves from callers who
1163 // inserted or updated a URI (more specifically the _data column) with disallowed paths.
1164 // TODO(afurtado): provide a more robust mechanism to avoid disallowed _data paths to
1165 // be inserted/updated in the first place, including via SQL injection.
Tom Taylor8168ff82013-02-19 14:30:23 -08001166 if (!filePath.getCanonicalPath()
Amith Yamasanida40d6a2015-04-14 13:37:12 -07001167 .startsWith(getContext().getDir(PARTS_DIR_NAME, 0).getCanonicalPath())) {
Ye Weneaa93e62015-01-06 13:08:53 -08001168 Log.e(TAG, "openFile: path "
1169 + filePath.getCanonicalPath()
1170 + " does not start with "
Amith Yamasanida40d6a2015-04-14 13:37:12 -07001171 + getContext().getDir(PARTS_DIR_NAME, 0).getCanonicalPath());
Ye Weneaa93e62015-01-06 13:08:53 -08001172 // Don't care return value
Tom Taylor8168ff82013-02-19 14:30:23 -08001173 return null;
1174 }
1175 } catch (IOException e) {
Ye Weneaa93e62015-01-06 13:08:53 -08001176 Log.e(TAG, "openFile: create path failed " + e, e);
Tom Taylor8168ff82013-02-19 14:30:23 -08001177 return null;
1178 }
1179
Andre Furtado5bc7f962016-08-18 10:28:37 -07001180 int modeBits = ParcelFileDescriptor.parseMode(mode);
1181 return ParcelFileDescriptor.open(filePath, modeBits);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001182 }
1183
1184 private void filterUnsupportedKeys(ContentValues values) {
1185 // Some columns are unsupported. They should therefore
1186 // neither be inserted nor updated. Filter them out.
1187 values.remove(Mms.DELIVERY_TIME_TOKEN);
1188 values.remove(Mms.SENDER_VISIBILITY);
1189 values.remove(Mms.REPLY_CHARGING);
1190 values.remove(Mms.REPLY_CHARGING_DEADLINE_TOKEN);
1191 values.remove(Mms.REPLY_CHARGING_DEADLINE);
1192 values.remove(Mms.REPLY_CHARGING_ID);
1193 values.remove(Mms.REPLY_CHARGING_SIZE);
1194 values.remove(Mms.PREVIOUSLY_SENT_BY);
1195 values.remove(Mms.PREVIOUSLY_SENT_DATE);
1196 values.remove(Mms.STORE);
1197 values.remove(Mms.MM_STATE);
1198 values.remove(Mms.MM_FLAGS_TOKEN);
1199 values.remove(Mms.MM_FLAGS);
1200 values.remove(Mms.STORE_STATUS);
1201 values.remove(Mms.STORE_STATUS_TEXT);
1202 values.remove(Mms.STORED);
1203 values.remove(Mms.TOTALS);
1204 values.remove(Mms.MBOX_TOTALS);
1205 values.remove(Mms.MBOX_TOTALS_TOKEN);
1206 values.remove(Mms.QUOTAS);
1207 values.remove(Mms.MBOX_QUOTAS);
1208 values.remove(Mms.MBOX_QUOTAS_TOKEN);
1209 values.remove(Mms.MESSAGE_COUNT);
1210 values.remove(Mms.START);
1211 values.remove(Mms.DISTRIBUTION_INDICATOR);
1212 values.remove(Mms.ELEMENT_DESCRIPTOR);
1213 values.remove(Mms.LIMIT);
1214 values.remove(Mms.RECOMMENDED_RETRIEVAL_MODE);
1215 values.remove(Mms.RECOMMENDED_RETRIEVAL_MODE_TEXT);
1216 values.remove(Mms.STATUS_TEXT);
1217 values.remove(Mms.APPLIC_ID);
1218 values.remove(Mms.REPLY_APPLIC_ID);
1219 values.remove(Mms.AUX_APPLIC_ID);
1220 values.remove(Mms.DRM_CONTENT);
1221 values.remove(Mms.ADAPTATION_ALLOWED);
1222 values.remove(Mms.REPLACE_ID);
1223 values.remove(Mms.CANCEL_ID);
1224 values.remove(Mms.CANCEL_STATUS);
1225
1226 // Keys shouldn't be inserted or updated.
1227 values.remove(Mms._ID);
1228 }
1229
Nikolab93ad242017-09-22 11:21:28 -07001230 private void notifyChange(final Uri uri, final Uri caseSpecificUri) {
Tom Taylore583f852015-11-20 12:16:16 -08001231 final Context context = getContext();
Nikolab93ad242017-09-22 11:21:28 -07001232 if (caseSpecificUri != null) {
1233 context.getContentResolver().notifyChange(
1234 caseSpecificUri, null, true, UserHandle.USER_ALL);
1235 }
Tom Taylore583f852015-11-20 12:16:16 -08001236 context.getContentResolver().notifyChange(
Amith Yamasani43f9fb22014-09-10 15:56:47 -07001237 MmsSms.CONTENT_URI, null, true, UserHandle.USER_ALL);
Nikolab93ad242017-09-22 11:21:28 -07001238 ProviderUtil.notifyIfNotDefaultSmsApp(caseSpecificUri == null ? uri : caseSpecificUri,
1239 getCallingPackage(), context);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001240 }
1241
1242 private final static String TAG = "MmsProvider";
1243 private final static String VND_ANDROID_MMS = "vnd.android/mms";
1244 private final static String VND_ANDROID_DIR_MMS = "vnd.android-dir/mms";
1245 private final static boolean DEBUG = false;
Joe Onoratodc946792011-04-07 18:41:15 -07001246 private final static boolean LOCAL_LOGV = false;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001247
1248 private static final int MMS_ALL = 0;
1249 private static final int MMS_ALL_ID = 1;
1250 private static final int MMS_INBOX = 2;
1251 private static final int MMS_INBOX_ID = 3;
1252 private static final int MMS_SENT = 4;
1253 private static final int MMS_SENT_ID = 5;
1254 private static final int MMS_DRAFTS = 6;
1255 private static final int MMS_DRAFTS_ID = 7;
1256 private static final int MMS_OUTBOX = 8;
1257 private static final int MMS_OUTBOX_ID = 9;
1258 private static final int MMS_ALL_PART = 10;
1259 private static final int MMS_MSG_PART = 11;
1260 private static final int MMS_PART_ID = 12;
1261 private static final int MMS_MSG_ADDR = 13;
1262 private static final int MMS_SENDING_RATE = 14;
Tom Taylor6c0ef242009-06-01 12:05:00 -07001263 private static final int MMS_REPORT_STATUS = 15;
1264 private static final int MMS_REPORT_REQUEST = 16;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001265 private static final int MMS_DRM_STORAGE = 17;
1266 private static final int MMS_DRM_STORAGE_ID = 18;
Tom Taylor6c0ef242009-06-01 12:05:00 -07001267 private static final int MMS_THREADS = 19;
Tom Taylorc2db47d2012-03-27 15:15:39 -07001268 private static final int MMS_PART_RESET_FILE_PERMISSION = 20;
Qimeng Pan01813f62018-01-09 18:06:50 +08001269 private static final int MMS_GET_PDU = 21;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001270
1271 private static final UriMatcher
1272 sURLMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1273
1274 static {
1275 sURLMatcher.addURI("mms", null, MMS_ALL);
1276 sURLMatcher.addURI("mms", "#", MMS_ALL_ID);
1277 sURLMatcher.addURI("mms", "inbox", MMS_INBOX);
1278 sURLMatcher.addURI("mms", "inbox/#", MMS_INBOX_ID);
1279 sURLMatcher.addURI("mms", "sent", MMS_SENT);
1280 sURLMatcher.addURI("mms", "sent/#", MMS_SENT_ID);
1281 sURLMatcher.addURI("mms", "drafts", MMS_DRAFTS);
1282 sURLMatcher.addURI("mms", "drafts/#", MMS_DRAFTS_ID);
1283 sURLMatcher.addURI("mms", "outbox", MMS_OUTBOX);
1284 sURLMatcher.addURI("mms", "outbox/#", MMS_OUTBOX_ID);
1285 sURLMatcher.addURI("mms", "part", MMS_ALL_PART);
1286 sURLMatcher.addURI("mms", "#/part", MMS_MSG_PART);
1287 sURLMatcher.addURI("mms", "part/#", MMS_PART_ID);
1288 sURLMatcher.addURI("mms", "#/addr", MMS_MSG_ADDR);
1289 sURLMatcher.addURI("mms", "rate", MMS_SENDING_RATE);
1290 sURLMatcher.addURI("mms", "report-status/#", MMS_REPORT_STATUS);
1291 sURLMatcher.addURI("mms", "report-request/#", MMS_REPORT_REQUEST);
1292 sURLMatcher.addURI("mms", "drm", MMS_DRM_STORAGE);
1293 sURLMatcher.addURI("mms", "drm/#", MMS_DRM_STORAGE_ID);
Tom Taylor6c0ef242009-06-01 12:05:00 -07001294 sURLMatcher.addURI("mms", "threads", MMS_THREADS);
Tom Taylorc2db47d2012-03-27 15:15:39 -07001295 sURLMatcher.addURI("mms", "resetFilePerm/*", MMS_PART_RESET_FILE_PERMISSION);
Qimeng Pan01813f62018-01-09 18:06:50 +08001296 sURLMatcher.addURI("mms", "get-pdu", MMS_GET_PDU);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001297 }
1298
1299 private SQLiteOpenHelper mOpenHelper;
1300
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001301 private static String concatSelections(String selection1, String selection2) {
1302 if (TextUtils.isEmpty(selection1)) {
1303 return selection2;
1304 } else if (TextUtils.isEmpty(selection2)) {
1305 return selection1;
1306 } else {
1307 return selection1 + " AND " + selection2;
1308 }
1309 }
1310}