blob: eae9c9c74f7e0ef51d725ce189b4d09badba3f18 [file] [log] [blame]
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001/*
2 * Copyright (C) 2006 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
yanglv9123f152015-01-26 16:29:24 +080019import static android.telephony.SmsMessage.ENCODING_16BIT;
20import static android.telephony.SmsMessage.ENCODING_7BIT;
21import static android.telephony.SmsMessage.ENCODING_UNKNOWN;
22import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
23import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
24
Dianne Hackbornf27792f2013-02-04 18:26:53 -080025import android.app.AppOpsManager;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080026import android.content.ContentProvider;
27import android.content.ContentResolver;
28import android.content.ContentValues;
29import android.content.UriMatcher;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080030import android.database.Cursor;
31import android.database.DatabaseUtils;
Jeff Brownb6bf84c2011-10-12 18:51:05 -070032import android.database.MatrixCursor;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080033import android.database.sqlite.SQLiteDatabase;
34import android.database.sqlite.SQLiteOpenHelper;
35import android.database.sqlite.SQLiteQueryBuilder;
36import android.net.Uri;
Jake Hamby1d714632013-09-18 12:19:33 -070037import android.os.Binder;
Amith Yamasani43f9fb22014-09-10 15:56:47 -070038import android.os.UserHandle;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080039import android.provider.Contacts;
Mark Wagner8e5ee782010-01-04 17:39:06 -080040import android.provider.Telephony;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080041import android.provider.Telephony.MmsSms;
42import android.provider.Telephony.Sms;
43import android.provider.Telephony.TextBasedSmsColumns;
44import android.provider.Telephony.Threads;
yanglv9123f152015-01-26 16:29:24 +080045import android.telephony.PhoneNumberUtils;
Wink Saville05117fe2009-04-02 11:00:57 -070046import android.telephony.SmsManager;
47import android.telephony.SmsMessage;
Sridhar Dubbaka33b17332014-10-04 09:05:04 +053048import android.telephony.SubscriptionManager;
yanglv9123f152015-01-26 16:29:24 +080049import android.telephony.TelephonyManager;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080050import android.text.TextUtils;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080051import android.util.Log;
52
yanglv9123f152015-01-26 16:29:24 +080053import java.io.ByteArrayOutputStream;
54import java.io.DataOutputStream;
55import java.io.IOException;
56import java.io.UnsupportedEncodingException;
57import java.text.SimpleDateFormat;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080058import java.util.ArrayList;
59import java.util.HashMap;
yanglv9123f152015-01-26 16:29:24 +080060import java.util.Locale;
61
62import com.android.internal.telephony.EncodeException;
63import com.android.internal.telephony.GsmAlphabet;
64import com.android.internal.telephony.PhoneConstants;
65import com.android.internal.telephony.SmsHeader;
66import com.android.internal.telephony.cdma.sms.BearerData;
67import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
68import com.android.internal.telephony.cdma.sms.UserData;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080069
70public class SmsProvider extends ContentProvider {
71 private static final Uri NOTIFICATION_URI = Uri.parse("content://sms");
Wink Saville05117fe2009-04-02 11:00:57 -070072 private static final Uri ICC_URI = Uri.parse("content://sms/icc");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080073 static final String TABLE_SMS = "sms";
Ye Wencfb8bbd2014-06-17 09:00:38 -070074 static final String TABLE_RAW = "raw";
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080075 private static final String TABLE_SR_PENDING = "sr_pending";
Mark Wagner8e5ee782010-01-04 17:39:06 -080076 private static final String TABLE_WORDS = "words";
Ye Wen72f13552015-03-10 14:17:13 -070077 static final String VIEW_SMS_RESTRICTED = "sms_restricted";
Tom Taylor20c4dbd2009-10-22 11:46:29 -070078
yanglv4ab54ef2014-07-31 20:20:02 +080079 private static final int DELETE_SUCCESS = 1;
80 private static final int DELETE_FAIL = 0;
81 private static final int MESSAGE_ID = 1;
82 private static final int SLOT1 = 0;
83 private static final int SLOT2 = 1;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080084 private static final Integer ONE = Integer.valueOf(1);
yanglv9123f152015-01-26 16:29:24 +080085 private static final int OFFSET_ADDRESS_LENGTH = 0;
86 private static final int OFFSET_TOA = 1;
87 private static final int OFFSET_ADDRESS_VALUE = 2;
88 private static final int TIMESTAMP_LENGTH = 7; // See TS 23.040 9.2.3.11
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080089
Wei Huang52dfa832009-09-08 15:36:11 -070090 private static final String[] CONTACT_QUERY_PROJECTION =
91 new String[] { Contacts.Phones.PERSON_ID };
92 private static final int PERSON_ID_COLUMN = 0;
93
yanglv9123f152015-01-26 16:29:24 +080094 private static final String SMS_BOX_ID = "box_id";
95 private static final Uri INSERT_SMS_INTO_ICC_SUCCESS = Uri.parse("content://iccsms/success");
96 private static final Uri INSERT_SMS_INTO_ICC_FAIL = Uri.parse("content://iccsms/fail");
97
The Android Open Source Project7236c3a2009-03-03 19:32:44 -080098 /**
99 * These are the columns that are available when reading SMS
Wink Saville05117fe2009-04-02 11:00:57 -0700100 * messages from the ICC. Columns whose names begin with "is_"
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800101 * have either "true" or "false" as their values.
102 */
Wink Saville05117fe2009-04-02 11:00:57 -0700103 private final static String[] ICC_COLUMNS = new String[] {
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800104 // N.B.: These columns must appear in the same order as the
Wink Saville05117fe2009-04-02 11:00:57 -0700105 // calls to add appear in convertIccToSms.
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800106 "service_center_address", // getServiceCenterAddress
107 "address", // getDisplayOriginatingAddress
108 "message_class", // getMessageClass
109 "body", // getDisplayMessageBody
110 "date", // getTimestampMillis
Wink Saville05117fe2009-04-02 11:00:57 -0700111 "status", // getStatusOnIcc
112 "index_on_icc", // getIndexOnIcc
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800113 "is_status_report", // isStatusReportMessage
114 "transport_type", // Always "sms".
Tom Taylor20c4dbd2009-10-22 11:46:29 -0700115 "type", // Always MESSAGE_TYPE_ALL.
Tom Taylorddf267c2009-10-28 18:14:55 -0700116 "locked", // Always 0 (false).
Jeff Brownb6bf84c2011-10-12 18:51:05 -0700117 "error_code", // Always 0
yanglv4ab54ef2014-07-31 20:20:02 +0800118 "_id",
119 "sub_id"
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800120 };
121
122 @Override
123 public boolean onCreate() {
Ye Wenb2ce2d32014-07-28 14:49:30 -0700124 setAppOps(AppOpsManager.OP_READ_SMS, AppOpsManager.OP_WRITE_SMS);
125 mOpenHelper = MmsSmsDatabaseHelper.getInstance(getContext());
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800126 return true;
127 }
128
Ye Wen72f13552015-03-10 14:17:13 -0700129 /**
130 * Return the proper view of "sms" table for the current access status.
131 *
132 * @param accessRestricted If the access is restricted
133 * @return the table/view name of the "sms" data
134 */
135 public static String getSmsTable(boolean accessRestricted) {
136 return accessRestricted ? VIEW_SMS_RESTRICTED : TABLE_SMS;
137 }
138
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800139 @Override
140 public Cursor query(Uri url, String[] projectionIn, String selection,
141 String[] selectionArgs, String sort) {
Ye Wen72f13552015-03-10 14:17:13 -0700142 // First check if a restricted view of the "sms" table should be used based on the
143 // caller's identity. Only system, phone or the default sms app can have full access
144 // of sms data. For other apps, we present a restricted view which only contains sent
145 // or received messages.
146 final boolean accessRestricted = ProviderUtil.isAccessRestricted(
147 getContext(), getCallingPackage(), Binder.getCallingUid());
148 final String smsTable = getSmsTable(accessRestricted);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800149 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
150
151 // Generate the body of the query.
152 int match = sURLMatcher.match(url);
153 switch (match) {
154 case SMS_ALL:
Ye Wen72f13552015-03-10 14:17:13 -0700155 constructQueryForBox(qb, Sms.MESSAGE_TYPE_ALL, smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800156 break;
157
158 case SMS_UNDELIVERED:
Ye Wen72f13552015-03-10 14:17:13 -0700159 constructQueryForUndelivered(qb, smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800160 break;
Tom Taylor20c4dbd2009-10-22 11:46:29 -0700161
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800162 case SMS_FAILED:
Ye Wen72f13552015-03-10 14:17:13 -0700163 constructQueryForBox(qb, Sms.MESSAGE_TYPE_FAILED, smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800164 break;
165
166 case SMS_QUEUED:
Ye Wen72f13552015-03-10 14:17:13 -0700167 constructQueryForBox(qb, Sms.MESSAGE_TYPE_QUEUED, smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800168 break;
Tom Taylor20c4dbd2009-10-22 11:46:29 -0700169
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800170 case SMS_INBOX:
Ye Wen72f13552015-03-10 14:17:13 -0700171 constructQueryForBox(qb, Sms.MESSAGE_TYPE_INBOX, smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800172 break;
173
174 case SMS_SENT:
Ye Wen72f13552015-03-10 14:17:13 -0700175 constructQueryForBox(qb, Sms.MESSAGE_TYPE_SENT, smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800176 break;
177
178 case SMS_DRAFT:
Ye Wen72f13552015-03-10 14:17:13 -0700179 constructQueryForBox(qb, Sms.MESSAGE_TYPE_DRAFT, smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800180 break;
181
182 case SMS_OUTBOX:
Ye Wen72f13552015-03-10 14:17:13 -0700183 constructQueryForBox(qb, Sms.MESSAGE_TYPE_OUTBOX, smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800184 break;
185
186 case SMS_ALL_ID:
Ye Wen72f13552015-03-10 14:17:13 -0700187 qb.setTables(smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800188 qb.appendWhere("(_id = " + url.getPathSegments().get(0) + ")");
189 break;
190
191 case SMS_INBOX_ID:
192 case SMS_FAILED_ID:
193 case SMS_SENT_ID:
194 case SMS_DRAFT_ID:
195 case SMS_OUTBOX_ID:
Ye Wen72f13552015-03-10 14:17:13 -0700196 qb.setTables(smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800197 qb.appendWhere("(_id = " + url.getPathSegments().get(1) + ")");
198 break;
199
200 case SMS_CONVERSATIONS_ID:
201 int threadID;
202
203 try {
204 threadID = Integer.parseInt(url.getPathSegments().get(1));
Wei Huang70ec48c2009-09-02 23:47:40 -0700205 if (Log.isLoggable(TAG, Log.VERBOSE)) {
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800206 Log.d(TAG, "query conversations: threadID=" + threadID);
207 }
208 }
209 catch (Exception ex) {
210 Log.e(TAG,
211 "Bad conversation thread id: "
212 + url.getPathSegments().get(1));
213 return null;
214 }
215
Ye Wen72f13552015-03-10 14:17:13 -0700216 qb.setTables(smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800217 qb.appendWhere("thread_id = " + threadID);
218 break;
219
220 case SMS_CONVERSATIONS:
Ye Wen72f13552015-03-10 14:17:13 -0700221 qb.setTables(smsTable + ", "
222 + "(SELECT thread_id AS group_thread_id, "
223 + "MAX(date) AS group_date, "
224 + "COUNT(*) AS msg_count "
225 + "FROM " + smsTable + " "
226 + "GROUP BY thread_id) AS groups");
227 qb.appendWhere(smsTable + ".thread_id=groups.group_thread_id"
228 + " AND " + smsTable + ".date=groups.group_date");
229 final HashMap<String, String> projectionMap = new HashMap<>();
230 projectionMap.put(Sms.Conversations.SNIPPET,
231 smsTable + ".body AS snippet");
232 projectionMap.put(Sms.Conversations.THREAD_ID,
233 smsTable + ".thread_id AS thread_id");
234 projectionMap.put(Sms.Conversations.MESSAGE_COUNT,
235 "groups.msg_count AS msg_count");
236 projectionMap.put("delta", null);
237 qb.setProjectionMap(projectionMap);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800238 break;
239
240 case SMS_RAW_MESSAGE:
241 qb.setTables("raw");
242 break;
243
244 case SMS_STATUS_PENDING:
245 qb.setTables("sr_pending");
246 break;
247
248 case SMS_ATTACHMENT:
249 qb.setTables("attachments");
250 break;
251
252 case SMS_ATTACHMENT_ID:
253 qb.setTables("attachments");
254 qb.appendWhere(
255 "(sms_id = " + url.getPathSegments().get(1) + ")");
256 break;
257
258 case SMS_QUERY_THREAD_ID:
259 qb.setTables("canonical_addresses");
260 if (projectionIn == null) {
261 projectionIn = sIDProjection;
262 }
263 break;
264
265 case SMS_STATUS_ID:
Ye Wen72f13552015-03-10 14:17:13 -0700266 qb.setTables(smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800267 qb.appendWhere("(_id = " + url.getPathSegments().get(1) + ")");
268 break;
269
Wink Saville05117fe2009-04-02 11:00:57 -0700270 case SMS_ALL_ICC:
yanglv4ab54ef2014-07-31 20:20:02 +0800271 return getAllMessagesFromIcc(SubscriptionManager.getDefaultSmsSubId());
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800272
Wink Saville05117fe2009-04-02 11:00:57 -0700273 case SMS_ICC:
yanglv4ab54ef2014-07-31 20:20:02 +0800274 String messageIndexIcc = url.getPathSegments().get(MESSAGE_ID);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800275
yanglv4ab54ef2014-07-31 20:20:02 +0800276 return getSingleMessageFromIcc(messageIndexIcc,
277 SubscriptionManager.getDefaultSmsSubId());
278
279 case SMS_ALL_ICC1:
280 return getAllMessagesFromIcc(SubscriptionManager.getSubId(SLOT1)[0]);
281
282 case SMS_ICC1:
283 String messageIndexIcc1 = url.getPathSegments().get(MESSAGE_ID);
284 return getSingleMessageFromIcc(messageIndexIcc1,
285 SubscriptionManager.getSubId(SLOT1)[0]);
286
287 case SMS_ALL_ICC2:
288 return getAllMessagesFromIcc(SubscriptionManager.getSubId(SLOT2)[0]);
289
290 case SMS_ICC2:
291 String messageIndexIcc2 = url.getPathSegments().get(MESSAGE_ID);
292 return getSingleMessageFromIcc(messageIndexIcc2,
293 SubscriptionManager.getSubId(SLOT2)[0]);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800294
295 default:
296 Log.e(TAG, "Invalid request: " + url);
297 return null;
298 }
299
300 String orderBy = null;
301
302 if (!TextUtils.isEmpty(sort)) {
303 orderBy = sort;
Ye Wen72f13552015-03-10 14:17:13 -0700304 } else if (qb.getTables().equals(smsTable)) {
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800305 orderBy = Sms.DEFAULT_SORT_ORDER;
306 }
307
308 SQLiteDatabase db = mOpenHelper.getReadableDatabase();
309 Cursor ret = qb.query(db, projectionIn, selection, selectionArgs,
310 null, null, orderBy);
311
312 // TODO: Since the URLs are a mess, always use content://sms
313 ret.setNotificationUri(getContext().getContentResolver(),
314 NOTIFICATION_URI);
315 return ret;
316 }
317
yanglv4ab54ef2014-07-31 20:20:02 +0800318 private Object[] convertIccToSms(SmsMessage message, int id, int subId) {
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800319 // N.B.: These calls must appear in the same order as the
Wink Saville05117fe2009-04-02 11:00:57 -0700320 // columns appear in ICC_COLUMNS.
yanglv4ab54ef2014-07-31 20:20:02 +0800321 int statusOnIcc = message.getStatusOnIcc();
322 int type = Sms.MESSAGE_TYPE_ALL;
323 switch (statusOnIcc) {
324 case SmsManager.STATUS_ON_ICC_READ:
325 case SmsManager.STATUS_ON_ICC_UNREAD:
326 type = Sms.MESSAGE_TYPE_INBOX;
327 break;
328 case SmsManager.STATUS_ON_ICC_SENT:
329 type = Sms.MESSAGE_TYPE_SENT;
330 break;
331 case SmsManager.STATUS_ON_ICC_UNSENT:
332 type = Sms.MESSAGE_TYPE_OUTBOX;
333 break;
334 }
335 Object[] row = new Object[14];
Jeff Brownb6bf84c2011-10-12 18:51:05 -0700336 row[0] = message.getServiceCenterAddress();
yanglv4ab54ef2014-07-31 20:20:02 +0800337 row[1] = (type == Sms.MESSAGE_TYPE_INBOX) ? message.getDisplayOriginatingAddress()
338 : message.getRecipientAddress();
Jeff Brownb6bf84c2011-10-12 18:51:05 -0700339 row[2] = String.valueOf(message.getMessageClass());
340 row[3] = message.getDisplayMessageBody();
341 row[4] = message.getTimestampMillis();
yanglv4ab54ef2014-07-31 20:20:02 +0800342 row[5] = statusOnIcc;
Jeff Brownb6bf84c2011-10-12 18:51:05 -0700343 row[6] = message.getIndexOnIcc();
344 row[7] = message.isStatusReportMessage();
345 row[8] = "sms";
yanglv4ab54ef2014-07-31 20:20:02 +0800346 row[9] = type;
Jeff Brownb6bf84c2011-10-12 18:51:05 -0700347 row[10] = 0; // locked
348 row[11] = 0; // error_code
349 row[12] = id;
yanglv4ab54ef2014-07-31 20:20:02 +0800350 row[13] = subId;
Jeff Brownb6bf84c2011-10-12 18:51:05 -0700351 return row;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800352 }
353
354 /**
Wink Saville05117fe2009-04-02 11:00:57 -0700355 * Return a Cursor containing just one message from the ICC.
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800356 */
yanglv4ab54ef2014-07-31 20:20:02 +0800357 private Cursor getSingleMessageFromIcc(String messageIndexString, int subId) {
358 ArrayList<SmsMessage> messages;
Ye Weneb47d5d2014-09-17 12:15:13 -0700359 int messageIndex = -1;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800360 try {
Ye Weneb47d5d2014-09-17 12:15:13 -0700361 Integer.parseInt(messageIndexString);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800362 } catch (NumberFormatException exception) {
Ye Weneb47d5d2014-09-17 12:15:13 -0700363 throw new IllegalArgumentException("Bad SMS ICC ID: " + messageIndexString);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800364 }
Ye Weneb47d5d2014-09-17 12:15:13 -0700365 long token = Binder.clearCallingIdentity();
366 try {
yanglv4ab54ef2014-07-31 20:20:02 +0800367 messages = SmsManager.getSmsManagerForSubscriptionId(subId).getAllMessagesFromIcc();
Ye Weneb47d5d2014-09-17 12:15:13 -0700368 } finally {
369 Binder.restoreCallingIdentity(token);
370 }
371 if (messages == null) {
372 throw new IllegalArgumentException("ICC message not retrieved");
373 }
374 final SmsMessage message = messages.get(messageIndex);
375 if (message == null) {
376 throw new IllegalArgumentException(
377 "Message not retrieved. ID: " + messageIndexString);
378 }
379 MatrixCursor cursor = new MatrixCursor(ICC_COLUMNS, 1);
yanglv4ab54ef2014-07-31 20:20:02 +0800380 cursor.addRow(convertIccToSms(message, 0, subId));
Ye Weneb47d5d2014-09-17 12:15:13 -0700381 return withIccNotificationUri(cursor);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800382 }
383
384 /**
Wink Saville05117fe2009-04-02 11:00:57 -0700385 * Return a Cursor listing all the messages stored on the ICC.
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800386 */
yanglv4ab54ef2014-07-31 20:20:02 +0800387 private Cursor getAllMessagesFromIcc(int subId) {
Jake Hamby1d714632013-09-18 12:19:33 -0700388 ArrayList<SmsMessage> messages;
389
390 // use phone app permissions to avoid UID mismatch in AppOpsManager.noteOp() call
391 long token = Binder.clearCallingIdentity();
392 try {
yanglv4ab54ef2014-07-31 20:20:02 +0800393 messages = SmsManager.getSmsManagerForSubscriptionId(subId)
394 .getAllMessagesFromIcc();
Jake Hamby1d714632013-09-18 12:19:33 -0700395 } finally {
396 Binder.restoreCallingIdentity(token);
397 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800398
Jeff Brownb6bf84c2011-10-12 18:51:05 -0700399 final int count = messages.size();
400 MatrixCursor cursor = new MatrixCursor(ICC_COLUMNS, count);
401 for (int i = 0; i < count; i++) {
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800402 SmsMessage message = messages.get(i);
403 if (message != null) {
yanglv4ab54ef2014-07-31 20:20:02 +0800404 cursor.addRow(convertIccToSms(message, i, subId));
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800405 }
406 }
Jeff Brownb6bf84c2011-10-12 18:51:05 -0700407 return withIccNotificationUri(cursor);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800408 }
409
Wink Saville05117fe2009-04-02 11:00:57 -0700410 private Cursor withIccNotificationUri(Cursor cursor) {
411 cursor.setNotificationUri(getContext().getContentResolver(), ICC_URI);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800412 return cursor;
413 }
414
Ye Wen72f13552015-03-10 14:17:13 -0700415 private void constructQueryForBox(SQLiteQueryBuilder qb, int type, String smsTable) {
416 qb.setTables(smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800417
418 if (type != Sms.MESSAGE_TYPE_ALL) {
419 qb.appendWhere("type=" + type);
420 }
421 }
422
Ye Wen72f13552015-03-10 14:17:13 -0700423 private void constructQueryForUndelivered(SQLiteQueryBuilder qb, String smsTable) {
424 qb.setTables(smsTable);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800425
426 qb.appendWhere("(type=" + Sms.MESSAGE_TYPE_OUTBOX +
427 " OR type=" + Sms.MESSAGE_TYPE_FAILED +
428 " OR type=" + Sms.MESSAGE_TYPE_QUEUED + ")");
429 }
Tom Taylor20c4dbd2009-10-22 11:46:29 -0700430
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800431 @Override
432 public String getType(Uri url) {
433 switch (url.getPathSegments().size()) {
434 case 0:
435 return VND_ANDROID_DIR_SMS;
436 case 1:
437 try {
438 Integer.parseInt(url.getPathSegments().get(0));
439 return VND_ANDROID_SMS;
440 } catch (NumberFormatException ex) {
441 return VND_ANDROID_DIR_SMS;
442 }
443 case 2:
444 // TODO: What about "threadID"?
445 if (url.getPathSegments().get(0).equals("conversations")) {
446 return VND_ANDROID_SMSCHAT;
447 } else {
448 return VND_ANDROID_SMS;
449 }
450 }
451 return null;
452 }
453
454 @Override
455 public Uri insert(Uri url, ContentValues initialValues) {
Ye Wene07acb92014-11-19 12:06:05 -0800456 final int callerUid = Binder.getCallingUid();
Ye Wen72f13552015-03-10 14:17:13 -0700457 final String callerPkg = getCallingPackage();
Jake Hambydeb745d2013-10-09 17:39:11 -0700458 long token = Binder.clearCallingIdentity();
459 try {
kaiyiza4238572014-08-09 15:07:18 +0800460 return insertInner(url, initialValues, callerUid, callerPkg, true);
Jake Hambydeb745d2013-10-09 17:39:11 -0700461 } finally {
462 Binder.restoreCallingIdentity(token);
463 }
464 }
465
kaiyiza4238572014-08-09 15:07:18 +0800466 private Uri insertInner(Uri url, ContentValues initialValues, int callerUid, String callerPkg,
467 boolean notify) {
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800468 ContentValues values;
469 long rowID;
470 int type = Sms.MESSAGE_TYPE_ALL;
471
472 int match = sURLMatcher.match(url);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800473 String table = TABLE_SMS;
474
475 switch (match) {
476 case SMS_ALL:
477 Integer typeObj = initialValues.getAsInteger(Sms.TYPE);
478 if (typeObj != null) {
479 type = typeObj.intValue();
480 } else {
481 // default to inbox
482 type = Sms.MESSAGE_TYPE_INBOX;
483 }
484 break;
485
486 case SMS_INBOX:
487 type = Sms.MESSAGE_TYPE_INBOX;
488 break;
489
490 case SMS_FAILED:
491 type = Sms.MESSAGE_TYPE_FAILED;
492 break;
493
494 case SMS_QUEUED:
495 type = Sms.MESSAGE_TYPE_QUEUED;
496 break;
Tom Taylor20c4dbd2009-10-22 11:46:29 -0700497
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800498 case SMS_SENT:
499 type = Sms.MESSAGE_TYPE_SENT;
500 break;
501
502 case SMS_DRAFT:
503 type = Sms.MESSAGE_TYPE_DRAFT;
504 break;
505
506 case SMS_OUTBOX:
507 type = Sms.MESSAGE_TYPE_OUTBOX;
508 break;
509
510 case SMS_RAW_MESSAGE:
511 table = "raw";
512 break;
513
514 case SMS_STATUS_PENDING:
515 table = "sr_pending";
516 break;
517
518 case SMS_ATTACHMENT:
519 table = "attachments";
520 break;
521
522 case SMS_NEW_THREAD_ID:
523 table = "canonical_addresses";
524 break;
525
yanglv9123f152015-01-26 16:29:24 +0800526 case SMS_ALL_ICC:
527 return insertMessageIntoIcc(initialValues);
528
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800529 default:
530 Log.e(TAG, "Invalid request: " + url);
531 return null;
532 }
533
Ficus Kirkpatrick5ff7b6a2009-04-13 23:52:37 -0700534 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
535
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800536 if (table.equals(TABLE_SMS)) {
537 boolean addDate = false;
538 boolean addType = false;
539
540 // Make sure that the date and type are set
541 if (initialValues == null) {
542 values = new ContentValues(1);
543 addDate = true;
544 addType = true;
545 } else {
546 values = new ContentValues(initialValues);
547
548 if (!initialValues.containsKey(Sms.DATE)) {
549 addDate = true;
550 }
551
552 if (!initialValues.containsKey(Sms.TYPE)) {
553 addType = true;
554 }
555 }
556
557 if (addDate) {
558 values.put(Sms.DATE, new Long(System.currentTimeMillis()));
559 }
560
561 if (addType && (type != Sms.MESSAGE_TYPE_ALL)) {
562 values.put(Sms.TYPE, Integer.valueOf(type));
563 }
564
565 // thread_id
566 Long threadId = values.getAsLong(Sms.THREAD_ID);
567 String address = values.getAsString(Sms.ADDRESS);
568
Tom Taylor59269962012-05-09 14:42:35 -0700569 if (((threadId == null) || (threadId == 0)) && (!TextUtils.isEmpty(address))) {
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800570 values.put(Sms.THREAD_ID, Threads.getOrCreateThreadId(
571 getContext(), address));
572 }
573
Ficus Kirkpatrick5ff7b6a2009-04-13 23:52:37 -0700574 // If this message is going in as a draft, it should replace any
575 // other draft messages in the thread. Just delete all draft
576 // messages with this thread ID. We could add an OR REPLACE to
577 // the insert below, but we'd have to query to find the old _id
578 // to produce a conflict anyway.
579 if (values.getAsInteger(Sms.TYPE) == Sms.MESSAGE_TYPE_DRAFT) {
580 db.delete(TABLE_SMS, "thread_id=? AND type=?",
581 new String[] { values.getAsString(Sms.THREAD_ID),
582 Integer.toString(Sms.MESSAGE_TYPE_DRAFT) });
583 }
584
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800585 if (type == Sms.MESSAGE_TYPE_INBOX) {
586 // Look up the person if not already filled in.
Wei Huang52dfa832009-09-08 15:36:11 -0700587 if ((values.getAsLong(Sms.PERSON) == null) && (!TextUtils.isEmpty(address))) {
588 Cursor cursor = null;
589 Uri uri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL,
590 Uri.encode(address));
591 try {
592 cursor = getContext().getContentResolver().query(
593 uri,
594 CONTACT_QUERY_PROJECTION,
595 null, null, null);
596
597 if (cursor.moveToFirst()) {
598 Long id = Long.valueOf(cursor.getLong(PERSON_ID_COLUMN));
599 values.put(Sms.PERSON, id);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800600 }
Wei Huang52dfa832009-09-08 15:36:11 -0700601 } catch (Exception ex) {
602 Log.e(TAG, "insert: query contact uri " + uri + " caught ", ex);
603 } finally {
604 if (cursor != null) {
605 cursor.close();
606 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800607 }
608 }
609 } else {
610 // Mark all non-inbox messages read.
611 values.put(Sms.READ, ONE);
612 }
Ye Wene07acb92014-11-19 12:06:05 -0800613 if (ProviderUtil.shouldSetCreator(values, callerUid)) {
614 // Only SYSTEM or PHONE can set CREATOR
615 // If caller is not SYSTEM or PHONE, or SYSTEM or PHONE does not set CREATOR
616 // set CREATOR using the truth on caller.
617 // Note: Inferring package name from UID may include unrelated package names
Ye Wen72f13552015-03-10 14:17:13 -0700618 values.put(Sms.CREATOR, callerPkg);
Ye Wene07acb92014-11-19 12:06:05 -0800619 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800620 } else {
621 if (initialValues == null) {
622 values = new ContentValues(1);
623 } else {
624 values = initialValues;
625 }
626 }
627
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800628 rowID = db.insert(table, "body", values);
Mark Wagner8e5ee782010-01-04 17:39:06 -0800629
630 // Don't use a trigger for updating the words table because of a bug
631 // in FTS3. The bug is such that the call to get the last inserted
632 // row is incorrect.
633 if (table == TABLE_SMS) {
634 // Update the words table with a corresponding row. The words table
635 // allows us to search for words quickly, without scanning the whole
636 // table;
637 ContentValues cv = new ContentValues();
638 cv.put(Telephony.MmsSms.WordsTable.ID, rowID);
639 cv.put(Telephony.MmsSms.WordsTable.INDEXED_TEXT, values.getAsString("body"));
640 cv.put(Telephony.MmsSms.WordsTable.SOURCE_ROW_ID, rowID);
641 cv.put(Telephony.MmsSms.WordsTable.TABLE_ID, 1);
642 db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv);
643 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800644 if (rowID > 0) {
645 Uri uri = Uri.parse("content://" + table + "/" + rowID);
Wei Huang70ec48c2009-09-02 23:47:40 -0700646
647 if (Log.isLoggable(TAG, Log.VERBOSE)) {
648 Log.d(TAG, "insert " + uri + " succeeded");
649 }
kaiyiza4238572014-08-09 15:07:18 +0800650 if (notify) {
651 notifyChange(uri);
652 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800653 return uri;
654 } else {
Ye Wene07acb92014-11-19 12:06:05 -0800655 Log.e(TAG,"insert: failed!");
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800656 }
657
658 return null;
659 }
660
yanglv9123f152015-01-26 16:29:24 +0800661 private Uri insertMessageIntoIcc(ContentValues values) {
662 if (values == null) {
663 return INSERT_SMS_INTO_ICC_FAIL;
664 }
665 int subId = values.getAsInteger(PhoneConstants.SUBSCRIPTION_KEY);
666 String address = values.getAsString(Sms.ADDRESS);
667 String message = values.getAsString(Sms.BODY);
668 int boxId = values.getAsInteger(SMS_BOX_ID);
669 long timestamp = values.getAsLong(Sms.DATE);
670 byte pdu[] = null;
671 int status;
672 if (Sms.isOutgoingFolder(boxId)) {
673 pdu = SmsMessage.getSubmitPdu(null, address, message, false, subId).encodedMessage;
674 status = SmsManager.STATUS_ON_ICC_SENT;
675 } else {
676 pdu = getDeliveryPdu(null, address, message, timestamp, subId);
677 status = SmsManager.STATUS_ON_ICC_READ;
678 }
679 boolean result = SmsManager.getSmsManagerForSubscriptionId(subId).copyMessageToIcc(null,
680 pdu, status);
681 return result ? INSERT_SMS_INTO_ICC_SUCCESS : INSERT_SMS_INTO_ICC_FAIL;
682 }
683
684 /**
685 * Generate a Delivery PDU byte array. see getSubmitPdu for reference.
686 */
687 public static byte[] getDeliveryPdu(String scAddress, String destinationAddress, String message,
688 long date, int subscription) {
689 if (isCdmaPhone(subscription)) {
690 return getCdmaDeliveryPdu(scAddress, destinationAddress, message, date);
691 } else {
692 return getGsmDeliveryPdu(scAddress, destinationAddress, message, date, null,
693 ENCODING_UNKNOWN);
694 }
695 }
696
697 private static boolean isCdmaPhone(int subscription) {
698 boolean isCdma = false;
699 int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subscription);
700 if (TelephonyManager.PHONE_TYPE_CDMA == activePhone) {
701 isCdma = true;
702 }
703 return isCdma;
704 }
705
706 public static byte[] getCdmaDeliveryPdu(String scAddress, String destinationAddress,
707 String message, long date) {
708 // Perform null parameter checks.
709 if (message == null || destinationAddress == null) {
710 Log.d(TAG, "getCDMADeliveryPdu,message =null");
711 return null;
712 }
713
714 // according to submit pdu encoding as written in privateGetSubmitPdu
715
716 // MTI = SMS-DELIVERY, UDHI = header != null
717 byte[] header = null;
718 byte mtiByte = (byte) (0x00 | (header != null ? 0x40 : 0x00));
719 ByteArrayOutputStream headerStream = getDeliveryPduHeader(destinationAddress, mtiByte);
720
721 ByteArrayOutputStream byteStream = new ByteArrayOutputStream(MAX_USER_DATA_BYTES + 40);
722
723 DataOutputStream dos = new DataOutputStream(byteStream);
724 // int status,Status of message. See TS 27.005 3.1, "<stat>"
725
726 /* 0 = "REC UNREAD" */
727 /* 1 = "REC READ" */
728 /* 2 = "STO UNSENT" */
729 /* 3 = "STO SENT" */
730
731 try {
732 // int uTeleserviceID;
733 int uTeleserviceID = 0; //.TELESERVICE_CT_WAP;// int
734 dos.writeInt(uTeleserviceID);
735
736 // unsigned char bIsServicePresent
737 byte bIsServicePresent = 0;// byte
738 dos.writeInt(bIsServicePresent);
739
740 // uServicecategory
741 int uServicecategory = 0;// int
742 dos.writeInt(uServicecategory);
743
744 // RIL_CDMA_SMS_Address
745 // digit_mode
746 // number_mode
747 // number_type
748 // number_plan
749 // number_of_digits
750 // digits[]
751 CdmaSmsAddress destAddr = CdmaSmsAddress.parse(PhoneNumberUtils
752 .cdmaCheckAndProcessPlusCode(destinationAddress));
753 if (destAddr == null)
754 return null;
755 dos.writeByte(destAddr.digitMode);// int
756 dos.writeByte(destAddr.numberMode);// int
757 dos.writeByte(destAddr.ton);// int
758 Log.d(TAG, "message type=" + destAddr.ton + "destination add=" + destinationAddress
759 + "message=" + message);
760 dos.writeByte(destAddr.numberPlan);// int
761 dos.writeByte(destAddr.numberOfDigits);// byte
762 dos.write(destAddr.origBytes, 0, destAddr.origBytes.length); // digits
763
764 // RIL_CDMA_SMS_Subaddress
765 // Subaddress is not supported.
766 dos.writeByte(0); // subaddressType int
767 dos.writeByte(0); // subaddr_odd byte
768 dos.writeByte(0); // subaddr_nbr_of_digits byte
769
770 SmsHeader smsHeader = new SmsHeader().fromByteArray(headerStream.toByteArray());
771 UserData uData = new UserData();
772 uData.payloadStr = message;
773 // uData.userDataHeader = smsHeader;
774 uData.msgEncodingSet = true;
775 uData.msgEncoding = UserData.ENCODING_UNICODE_16;
776
777 BearerData bearerData = new BearerData();
778 bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
779
780 bearerData.deliveryAckReq = false;
781 bearerData.userAckReq = false;
782 bearerData.readAckReq = false;
783 bearerData.reportReq = false;
784
785 bearerData.userData = uData;
786
787 byte[] encodedBearerData = BearerData.encode(bearerData);
788 if (null != encodedBearerData) {
789 // bearer data len
790 dos.writeByte(encodedBearerData.length);// int
791 Log.d(TAG, "encodedBearerData length=" + encodedBearerData.length);
792
793 // aBearerData
794 dos.write(encodedBearerData, 0, encodedBearerData.length);
795 } else {
796 dos.writeByte(0);
797 }
798
799 } catch (IOException e) {
800 Log.e(TAG, "Error writing dos", e);
801 } finally {
802 try {
803 if (null != byteStream) {
804 byteStream.close();
805 }
806
807 if (null != dos) {
808 dos.close();
809 }
810
811 if (null != headerStream) {
812 headerStream.close();
813 }
814 } catch (IOException e) {
815 Log.e(TAG, "Error close dos", e);
816 }
817 }
818
819 return byteStream.toByteArray();
820 }
821
822 /**
823 * Generate a Delivery PDU byte array. see getSubmitPdu for reference.
824 */
825 public static byte[] getGsmDeliveryPdu(String scAddress, String destinationAddress,
826 String message, long date, byte[] header, int encoding) {
827 // Perform null parameter checks.
828 if (message == null || destinationAddress == null) {
829 return null;
830 }
831
832 // MTI = SMS-DELIVERY, UDHI = header != null
833 byte mtiByte = (byte)(0x00 | (header != null ? 0x40 : 0x00));
834 ByteArrayOutputStream bo = getDeliveryPduHeader(destinationAddress, mtiByte);
835 // User Data (and length)
836 byte[] userData;
837 if (encoding == ENCODING_UNKNOWN) {
838 // First, try encoding it with the GSM alphabet
839 encoding = ENCODING_7BIT;
840 }
841 try {
842 if (encoding == ENCODING_7BIT) {
843 userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header, 0, 0);
844 } else { //assume UCS-2
845 try {
846 userData = encodeUCS2(message, header);
847 } catch (UnsupportedEncodingException uex) {
848 Log.e("GSM", "Implausible UnsupportedEncodingException ",
849 uex);
850 return null;
851 }
852 }
853 } catch (EncodeException ex) {
854 // Encoding to the 7-bit alphabet failed. Let's see if we can
855 // encode it as a UCS-2 encoded message
856 try {
857 userData = encodeUCS2(message, header);
858 encoding = ENCODING_16BIT;
859 } catch (UnsupportedEncodingException uex) {
860 Log.e("GSM", "Implausible UnsupportedEncodingException ",
861 uex);
862 return null;
863 }
864 }
865
866 if (encoding == ENCODING_7BIT) {
867 if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
868 // Message too long
869 return null;
870 }
871 bo.write(0x00);
872 } else { //assume UCS-2
873 if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
874 // Message too long
875 return null;
876 }
877 // TP-Data-Coding-Scheme
878 // Class 3, UCS-2 encoding, uncompressed
879 bo.write(0x0b);
880 }
881 byte[] timestamp = getTimestamp(date);
882 bo.write(timestamp, 0, timestamp.length);
883
884 bo.write(userData, 0, userData.length);
885 return bo.toByteArray();
886 }
887
888 private static ByteArrayOutputStream getDeliveryPduHeader(
889 String destinationAddress, byte mtiByte) {
890 ByteArrayOutputStream bo = new ByteArrayOutputStream(
891 MAX_USER_DATA_BYTES + 40);
892 bo.write(mtiByte);
893
894 byte[] daBytes;
895 daBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(destinationAddress);
896
897 if (daBytes == null) {
898 Log.d(TAG, "The number can not convert to BCD, it's an An alphanumeric address, " +
899 "destinationAddress = " + destinationAddress);
900 // Convert address to GSM 7 bit packed bytes.
901 try {
902 byte[] numberdata = GsmAlphabet
903 .stringToGsm7BitPacked(destinationAddress);
904 // Get the real address data
905 byte[] addressData = new byte[numberdata.length - 1];
906 System.arraycopy(numberdata, 1, addressData, 0, addressData.length);
907
908 daBytes = new byte[addressData.length + OFFSET_ADDRESS_VALUE];
909 // Get the address length
910 int addressLen = numberdata[0];
911 daBytes[OFFSET_ADDRESS_LENGTH] = (byte) ((addressLen * 7 % 4 != 0 ?
912 addressLen * 7 / 4 + 1 : addressLen * 7 / 4));
913 // Set address type to Alphanumeric according to 3GPP TS 23.040 [9.1.2.5]
914 daBytes[OFFSET_TOA] = (byte) 0xd0;
915 System.arraycopy(addressData, 0, daBytes, OFFSET_ADDRESS_VALUE, addressData.length);
916 } catch (Exception e) {
917 Log.e(TAG, "Exception when encoding to 7 bit data.");
918 }
919 } else {
920 // destination address length in BCD digits, ignoring TON byte and pad
921 // TODO Should be better.
922 bo.write((daBytes.length - 1) * 2
923 - ((daBytes[daBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
924 }
925
926 // destination address
927 bo.write(daBytes, 0, daBytes.length);
928
929 // TP-Protocol-Identifier
930 bo.write(0);
931 return bo;
932 }
933
934 private static byte[] encodeUCS2(String message, byte[] header)
935 throws UnsupportedEncodingException {
936 byte[] userData, textPart;
937 textPart = message.getBytes("utf-16be");
938
939 if (header != null) {
940 // Need 1 byte for UDHL
941 userData = new byte[header.length + textPart.length + 1];
942
943 userData[0] = (byte)header.length;
944 System.arraycopy(header, 0, userData, 1, header.length);
945 System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
946 }
947 else {
948 userData = textPart;
949 }
950 byte[] ret = new byte[userData.length+1];
951 ret[0] = (byte) (userData.length & 0xff );
952 System.arraycopy(userData, 0, ret, 1, userData.length);
953 return ret;
954 }
955
956 private static byte[] getTimestamp(long time) {
957 // See TS 23.040 9.2.3.11
958 byte[] timestamp = new byte[TIMESTAMP_LENGTH];
959 SimpleDateFormat sdf = new SimpleDateFormat("yyMMddkkmmss:Z", Locale.US);
960 String[] date = sdf.format(time).split(":");
961 // generate timezone value
962 String timezone = date[date.length - 1];
963 String signMark = timezone.substring(0, 1);
964 int hour = Integer.parseInt(timezone.substring(1, 3));
965 int min = Integer.parseInt(timezone.substring(3));
966 int timezoneValue = hour * 4 + min / 15;
967 // append timezone value to date[0] (time string)
968 String timestampStr = date[0] + timezoneValue;
969
970 int digitCount = 0;
971 for (int i = 0; i < timestampStr.length(); i++) {
972 char c = timestampStr.charAt(i);
973 int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
974 timestamp[(digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
975 digitCount++;
976 }
977
978 if (signMark.equals("-")) {
979 timestamp[timestamp.length - 1] = (byte) (timestamp[timestamp.length - 1] | 0x08);
980 }
981
982 return timestamp;
983 }
984
985 private static int charToBCD(char c) {
986 if (c >= '0' && c <= '9') {
987 return c - '0';
988 } else {
989 throw new RuntimeException ("invalid char for BCD " + c);
990 }
991 }
992
The Android Open Source Project7236c3a2009-03-03 19:32:44 -0800993 @Override
994 public int delete(Uri url, String where, String[] whereArgs) {
995 int count;
996 int match = sURLMatcher.match(url);
997 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
998 switch (match) {
999 case SMS_ALL:
1000 count = db.delete(TABLE_SMS, where, whereArgs);
1001 if (count != 0) {
1002 // Don't update threads unless something changed.
1003 MmsSmsDatabaseHelper.updateAllThreads(db, where, whereArgs);
1004 }
1005 break;
Tom Taylor20c4dbd2009-10-22 11:46:29 -07001006
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001007 case SMS_ALL_ID:
1008 try {
1009 int message_id = Integer.parseInt(url.getPathSegments().get(0));
1010 count = MmsSmsDatabaseHelper.deleteOneSms(db, message_id);
1011 } catch (Exception e) {
1012 throw new IllegalArgumentException(
1013 "Bad message id: " + url.getPathSegments().get(0));
1014 }
1015 break;
1016
1017 case SMS_CONVERSATIONS_ID:
1018 int threadID;
1019
1020 try {
1021 threadID = Integer.parseInt(url.getPathSegments().get(1));
1022 } catch (Exception ex) {
1023 throw new IllegalArgumentException(
1024 "Bad conversation thread id: "
1025 + url.getPathSegments().get(1));
1026 }
1027
1028 // delete the messages from the sms table
1029 where = DatabaseUtils.concatenateWhere("thread_id=" + threadID, where);
1030 count = db.delete(TABLE_SMS, where, whereArgs);
1031 MmsSmsDatabaseHelper.updateThread(db, threadID);
1032 break;
1033
1034 case SMS_RAW_MESSAGE:
1035 count = db.delete("raw", where, whereArgs);
1036 break;
1037
1038 case SMS_STATUS_PENDING:
1039 count = db.delete("sr_pending", where, whereArgs);
1040 break;
1041
Wink Saville05117fe2009-04-02 11:00:57 -07001042 case SMS_ICC:
yanglv4ab54ef2014-07-31 20:20:02 +08001043 String messageIndexIcc = url.getPathSegments().get(MESSAGE_ID);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001044
yanglv4ab54ef2014-07-31 20:20:02 +08001045 return deleteMessageFromIcc(messageIndexIcc,
1046 SubscriptionManager.getDefaultSmsSubId());
1047
1048 case SMS_ICC1:
1049 String messageIndexIcc1 = url.getPathSegments().get(MESSAGE_ID);
1050 return deleteMessageFromIcc(messageIndexIcc1,
1051 SubscriptionManager.getSubId(SLOT1)[0]);
1052
1053 case SMS_ICC2:
1054 String messageIndexIcc2 = url.getPathSegments().get(MESSAGE_ID);
1055 return deleteMessageFromIcc(messageIndexIcc2,
1056 SubscriptionManager.getSubId(SLOT2)[0]);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001057
1058 default:
1059 throw new IllegalArgumentException("Unknown URL");
1060 }
1061
1062 if (count > 0) {
1063 notifyChange(url);
1064 }
1065 return count;
1066 }
1067
1068 /**
Wink Saville05117fe2009-04-02 11:00:57 -07001069 * Delete the message at index from ICC. Return true iff
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001070 * successful.
1071 */
yanglv4ab54ef2014-07-31 20:20:02 +08001072 private int deleteMessageFromIcc(String messageIndexString, int subId) {
Ye Weneb47d5d2014-09-17 12:15:13 -07001073 long token = Binder.clearCallingIdentity();
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001074 try {
yanglv4ab54ef2014-07-31 20:20:02 +08001075 return SmsManager.getSmsManagerForSubscriptionId(subId).deleteMessageFromIcc(
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001076 Integer.parseInt(messageIndexString))
yanglv4ab54ef2014-07-31 20:20:02 +08001077 ? DELETE_SUCCESS : DELETE_FAIL;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001078 } catch (NumberFormatException exception) {
1079 throw new IllegalArgumentException(
Wink Saville05117fe2009-04-02 11:00:57 -07001080 "Bad SMS ICC ID: " + messageIndexString);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001081 } finally {
1082 ContentResolver cr = getContext().getContentResolver();
Amith Yamasani43f9fb22014-09-10 15:56:47 -07001083 cr.notifyChange(ICC_URI, null, true, UserHandle.USER_ALL);
Ye Weneb47d5d2014-09-17 12:15:13 -07001084
1085 Binder.restoreCallingIdentity(token);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001086 }
1087 }
1088
1089 @Override
kaiyiza4238572014-08-09 15:07:18 +08001090 public int bulkInsert(Uri uri, ContentValues[] values) {
wangjingff3d2c72015-04-03 16:37:38 +08001091 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
kaiyiza4238572014-08-09 15:07:18 +08001092 db.beginTransaction();
1093 long token = Binder.clearCallingIdentity();
1094 int numValues = values.length;
1095 final int callerUid = Binder.getCallingUid();
1096 final String callerPkg = getCallingPackage();
1097 Log.d(TAG, "start bulkInsert uri: " + uri + " count: " + numValues);
1098 try {
1099 for (int i = 0; i < numValues; i++) {
1100 insertInner(uri, values[i], callerUid, callerPkg, false);
1101 }
1102 notifyChange(uri);
1103 db.setTransactionSuccessful();
1104 Log.d(TAG, "bulkInsert successfully: ");
1105 } finally {
1106 db.endTransaction();
1107 Binder.restoreCallingIdentity(token);
1108 Log.d(TAG, "bulkInsert finish: ");
1109 }
1110 return numValues;
1111 }
1112
1113 @Override
Wei Huang70ec48c2009-09-02 23:47:40 -07001114 public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
Ye Wene07acb92014-11-19 12:06:05 -08001115 final int callerUid = Binder.getCallingUid();
Ye Wen72f13552015-03-10 14:17:13 -07001116 final String callerPkg = getCallingPackage();
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001117 int count = 0;
1118 String table = TABLE_SMS;
1119 String extraWhere = null;
1120 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1121
1122 switch (sURLMatcher.match(url)) {
1123 case SMS_RAW_MESSAGE:
1124 table = TABLE_RAW;
1125 break;
1126
1127 case SMS_STATUS_PENDING:
1128 table = TABLE_SR_PENDING;
1129 break;
1130
1131 case SMS_ALL:
1132 case SMS_FAILED:
1133 case SMS_QUEUED:
1134 case SMS_INBOX:
1135 case SMS_SENT:
1136 case SMS_DRAFT:
1137 case SMS_OUTBOX:
1138 case SMS_CONVERSATIONS:
1139 break;
1140
1141 case SMS_ALL_ID:
1142 extraWhere = "_id=" + url.getPathSegments().get(0);
1143 break;
1144
1145 case SMS_INBOX_ID:
1146 case SMS_FAILED_ID:
1147 case SMS_SENT_ID:
1148 case SMS_DRAFT_ID:
1149 case SMS_OUTBOX_ID:
1150 extraWhere = "_id=" + url.getPathSegments().get(1);
1151 break;
1152
1153 case SMS_CONVERSATIONS_ID: {
1154 String threadId = url.getPathSegments().get(1);
1155
1156 try {
1157 Integer.parseInt(threadId);
1158 } catch (Exception ex) {
1159 Log.e(TAG, "Bad conversation thread id: " + threadId);
1160 break;
1161 }
1162
1163 extraWhere = "thread_id=" + threadId;
1164 break;
1165 }
1166
1167 case SMS_STATUS_ID:
1168 extraWhere = "_id=" + url.getPathSegments().get(1);
Tom Taylor20c4dbd2009-10-22 11:46:29 -07001169 break;
1170
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001171 default:
1172 throw new UnsupportedOperationException(
1173 "URI " + url + " not supported");
1174 }
1175
Ye Wene07acb92014-11-19 12:06:05 -08001176 if (table.equals(TABLE_SMS) && ProviderUtil.shouldRemoveCreator(values, callerUid)) {
1177 // CREATOR should not be changed by non-SYSTEM/PHONE apps
Ye Wen72f13552015-03-10 14:17:13 -07001178 Log.w(TAG, callerPkg + " tries to update CREATOR");
Ye Wene07acb92014-11-19 12:06:05 -08001179 values.remove(Sms.CREATOR);
1180 }
1181
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001182 where = DatabaseUtils.concatenateWhere(where, extraWhere);
1183 count = db.update(table, values, where, whereArgs);
Tom Taylor20c4dbd2009-10-22 11:46:29 -07001184
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001185 if (count > 0) {
Wei Huang70ec48c2009-09-02 23:47:40 -07001186 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1187 Log.d(TAG, "update " + url + " succeeded");
1188 }
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001189 notifyChange(url);
1190 }
1191 return count;
1192 }
1193
1194 private void notifyChange(Uri uri) {
1195 ContentResolver cr = getContext().getContentResolver();
Amith Yamasani43f9fb22014-09-10 15:56:47 -07001196 cr.notifyChange(uri, null, true, UserHandle.USER_ALL);
1197 cr.notifyChange(MmsSms.CONTENT_URI, null, true, UserHandle.USER_ALL);
1198 cr.notifyChange(Uri.parse("content://mms-sms/conversations/"), null, true,
1199 UserHandle.USER_ALL);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001200 }
1201
1202 private SQLiteOpenHelper mOpenHelper;
1203
1204 private final static String TAG = "SmsProvider";
1205 private final static String VND_ANDROID_SMS = "vnd.android.cursor.item/sms";
1206 private final static String VND_ANDROID_SMSCHAT =
1207 "vnd.android.cursor.item/sms-chat";
1208 private final static String VND_ANDROID_DIR_SMS =
1209 "vnd.android.cursor.dir/sms";
1210
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001211 private static final String[] sIDProjection = new String[] { "_id" };
1212
1213 private static final int SMS_ALL = 0;
1214 private static final int SMS_ALL_ID = 1;
1215 private static final int SMS_INBOX = 2;
1216 private static final int SMS_INBOX_ID = 3;
1217 private static final int SMS_SENT = 4;
1218 private static final int SMS_SENT_ID = 5;
1219 private static final int SMS_DRAFT = 6;
1220 private static final int SMS_DRAFT_ID = 7;
1221 private static final int SMS_OUTBOX = 8;
1222 private static final int SMS_OUTBOX_ID = 9;
1223 private static final int SMS_CONVERSATIONS = 10;
1224 private static final int SMS_CONVERSATIONS_ID = 11;
1225 private static final int SMS_RAW_MESSAGE = 15;
1226 private static final int SMS_ATTACHMENT = 16;
1227 private static final int SMS_ATTACHMENT_ID = 17;
1228 private static final int SMS_NEW_THREAD_ID = 18;
1229 private static final int SMS_QUERY_THREAD_ID = 19;
1230 private static final int SMS_STATUS_ID = 20;
1231 private static final int SMS_STATUS_PENDING = 21;
Wink Saville05117fe2009-04-02 11:00:57 -07001232 private static final int SMS_ALL_ICC = 22;
1233 private static final int SMS_ICC = 23;
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001234 private static final int SMS_FAILED = 24;
1235 private static final int SMS_FAILED_ID = 25;
1236 private static final int SMS_QUEUED = 26;
1237 private static final int SMS_UNDELIVERED = 27;
yanglv4ab54ef2014-07-31 20:20:02 +08001238 private static final int SMS_ALL_ICC1 = 28;
1239 private static final int SMS_ICC1 = 29;
1240 private static final int SMS_ALL_ICC2 = 30;
1241 private static final int SMS_ICC2 = 31;
Tom Taylor20c4dbd2009-10-22 11:46:29 -07001242
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001243 private static final UriMatcher sURLMatcher =
1244 new UriMatcher(UriMatcher.NO_MATCH);
1245
1246 static {
1247 sURLMatcher.addURI("sms", null, SMS_ALL);
1248 sURLMatcher.addURI("sms", "#", SMS_ALL_ID);
1249 sURLMatcher.addURI("sms", "inbox", SMS_INBOX);
1250 sURLMatcher.addURI("sms", "inbox/#", SMS_INBOX_ID);
1251 sURLMatcher.addURI("sms", "sent", SMS_SENT);
1252 sURLMatcher.addURI("sms", "sent/#", SMS_SENT_ID);
1253 sURLMatcher.addURI("sms", "draft", SMS_DRAFT);
1254 sURLMatcher.addURI("sms", "draft/#", SMS_DRAFT_ID);
1255 sURLMatcher.addURI("sms", "outbox", SMS_OUTBOX);
1256 sURLMatcher.addURI("sms", "outbox/#", SMS_OUTBOX_ID);
Tom Taylor20c4dbd2009-10-22 11:46:29 -07001257 sURLMatcher.addURI("sms", "undelivered", SMS_UNDELIVERED);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001258 sURLMatcher.addURI("sms", "failed", SMS_FAILED);
1259 sURLMatcher.addURI("sms", "failed/#", SMS_FAILED_ID);
1260 sURLMatcher.addURI("sms", "queued", SMS_QUEUED);
1261 sURLMatcher.addURI("sms", "conversations", SMS_CONVERSATIONS);
1262 sURLMatcher.addURI("sms", "conversations/*", SMS_CONVERSATIONS_ID);
1263 sURLMatcher.addURI("sms", "raw", SMS_RAW_MESSAGE);
1264 sURLMatcher.addURI("sms", "attachments", SMS_ATTACHMENT);
1265 sURLMatcher.addURI("sms", "attachments/#", SMS_ATTACHMENT_ID);
1266 sURLMatcher.addURI("sms", "threadID", SMS_NEW_THREAD_ID);
1267 sURLMatcher.addURI("sms", "threadID/*", SMS_QUERY_THREAD_ID);
1268 sURLMatcher.addURI("sms", "status/#", SMS_STATUS_ID);
1269 sURLMatcher.addURI("sms", "sr_pending", SMS_STATUS_PENDING);
Wink Saville05117fe2009-04-02 11:00:57 -07001270 sURLMatcher.addURI("sms", "icc", SMS_ALL_ICC);
1271 sURLMatcher.addURI("sms", "icc/#", SMS_ICC);
yanglv4ab54ef2014-07-31 20:20:02 +08001272 sURLMatcher.addURI("sms", "icc1", SMS_ALL_ICC1);
1273 sURLMatcher.addURI("sms", "icc1/#", SMS_ICC1);
1274 sURLMatcher.addURI("sms", "icc2", SMS_ALL_ICC2);
1275 sURLMatcher.addURI("sms", "icc2/#", SMS_ICC2);
Wink Saville05117fe2009-04-02 11:00:57 -07001276 //we keep these for not breaking old applications
1277 sURLMatcher.addURI("sms", "sim", SMS_ALL_ICC);
1278 sURLMatcher.addURI("sms", "sim/#", SMS_ICC);
The Android Open Source Project7236c3a2009-03-03 19:32:44 -08001279 }
1280}