Restricted view of SMS/MMS providers (2/2)
Only expose sent/received SMS/MMS and non-wap push MMS messages for
non-default SMS apps, preventing non-default SMS apps to interfere
internal states of default SMS app, like stealing wap push to download
or sending outbox message.
b/19348537
Change-Id: Icec639a01ab8f5cd2d1346b76418ec487979295a
diff --git a/src/com/android/providers/telephony/MmsProvider.java b/src/com/android/providers/telephony/MmsProvider.java
index 6b5b29e..710fa43 100644
--- a/src/com/android/providers/telephony/MmsProvider.java
+++ b/src/com/android/providers/telephony/MmsProvider.java
@@ -62,6 +62,7 @@
static final String TABLE_RATE = "rate";
static final String TABLE_DRM = "drm";
static final String TABLE_WORDS = "words";
+ static final String VIEW_PDU_RESTRICTED = "pdu_restricted";
// The name of parts directory. The full dir is "app_parts".
private static final String PARTS_DIR_NAME = "parts";
@@ -73,9 +74,27 @@
return true;
}
+ /**
+ * Return the proper view of "pdu" table for the current access status.
+ *
+ * @param accessRestricted If the access is restricted
+ * @return the table/view name of the mms data
+ */
+ public static String getPduTable(boolean accessRestricted) {
+ return accessRestricted ? VIEW_PDU_RESTRICTED : TABLE_PDU;
+ }
+
@Override
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
+ // First check if a restricted view of the "pdu" table should be used based on the
+ // caller's identity. Only system, phone or the default sms app can have full access
+ // of mms data. For other apps, we present a restricted view which only contains sent
+ // or received messages, without wap pushes.
+ final boolean accessRestricted = ProviderUtil.isAccessRestricted(
+ getContext(), getCallingPackage(), Binder.getCallingUid());
+ final String pduTable = getPduTable(accessRestricted);
+
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
// Generate the body of the query.
@@ -86,29 +105,29 @@
switch (match) {
case MMS_ALL:
- constructQueryForBox(qb, Mms.MESSAGE_BOX_ALL);
+ constructQueryForBox(qb, Mms.MESSAGE_BOX_ALL, pduTable);
break;
case MMS_INBOX:
- constructQueryForBox(qb, Mms.MESSAGE_BOX_INBOX);
+ constructQueryForBox(qb, Mms.MESSAGE_BOX_INBOX, pduTable);
break;
case MMS_SENT:
- constructQueryForBox(qb, Mms.MESSAGE_BOX_SENT);
+ constructQueryForBox(qb, Mms.MESSAGE_BOX_SENT, pduTable);
break;
case MMS_DRAFTS:
- constructQueryForBox(qb, Mms.MESSAGE_BOX_DRAFTS);
+ constructQueryForBox(qb, Mms.MESSAGE_BOX_DRAFTS, pduTable);
break;
case MMS_OUTBOX:
- constructQueryForBox(qb, Mms.MESSAGE_BOX_OUTBOX);
+ constructQueryForBox(qb, Mms.MESSAGE_BOX_OUTBOX, pduTable);
break;
case MMS_ALL_ID:
- qb.setTables(TABLE_PDU);
+ qb.setTables(pduTable);
qb.appendWhere(Mms._ID + "=" + uri.getPathSegments().get(0));
break;
case MMS_INBOX_ID:
case MMS_SENT_ID:
case MMS_DRAFTS_ID:
case MMS_OUTBOX_ID:
- qb.setTables(TABLE_PDU);
+ qb.setTables(pduTable);
qb.appendWhere(Mms._ID + "=" + uri.getPathSegments().get(1));
qb.appendWhere(" AND " + Mms.MESSAGE_BOX + "="
+ getMessageBoxByMatch(match));
@@ -155,19 +174,23 @@
OR (msg_id = id3 AND type = 137)
WHERE T.id1 = ?;
*/
- qb.setTables("addr INNER JOIN (SELECT P1._id AS id1, P2._id" +
- " AS id2, P3._id AS id3, ifnull(P2.st, 0) AS" +
- " delivery_status, ifnull(P3.read_status, 0) AS" +
- " read_status FROM pdu P1 INNER JOIN pdu P2 ON" +
- " P1.m_id=P2.m_id AND P2.m_type=134 LEFT JOIN" +
- " pdu P3 ON P1.m_id=P3.m_id AND P3.m_type=136" +
- " UNION SELECT P1._id AS id1, P2._id AS id2, P3._id" +
- " AS id3, ifnull(P2.st, 0) AS delivery_status," +
- " ifnull(P3.read_status, 0) AS read_status FROM" +
- " pdu P1 INNER JOIN pdu P3 ON P1.m_id=P3.m_id AND" +
- " P3.m_type=136 LEFT JOIN pdu P2 ON P1.m_id=P2.m_id" +
- " AND P2.m_type=134) T ON (msg_id=id2 AND type=151)" +
- " OR (msg_id=id3 AND type=137)");
+ qb.setTables(TABLE_ADDR + " INNER JOIN "
+ + "(SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3, "
+ + "ifnull(P2.st, 0) AS delivery_status, "
+ + "ifnull(P3.read_status, 0) AS read_status "
+ + "FROM " + pduTable + " P1 INNER JOIN " + pduTable + " P2 "
+ + "ON P1.m_id=P2.m_id AND P2.m_type=134 "
+ + "LEFT JOIN " + pduTable + " P3 "
+ + "ON P1.m_id=P3.m_id AND P3.m_type=136 "
+ + "UNION "
+ + "SELECT P1._id AS id1, P2._id AS id2, P3._id AS id3, "
+ + "ifnull(P2.st, 0) AS delivery_status, "
+ + "ifnull(P3.read_status, 0) AS read_status "
+ + "FROM " + pduTable + " P1 INNER JOIN " + pduTable + " P3 "
+ + "ON P1.m_id=P3.m_id AND P3.m_type=136 "
+ + "LEFT JOIN " + pduTable + " P2 "
+ + "ON P1.m_id=P2.m_id AND P2.m_type=134) T "
+ + "ON (msg_id=id2 AND type=151) OR (msg_id=id3 AND type=137)");
qb.appendWhere("T.id1 = " + uri.getLastPathSegment());
qb.setDistinct(true);
break;
@@ -178,9 +201,9 @@
WHERE pdu._id = messageId AND addr.type = 151
*/
qb.setTables(TABLE_ADDR + " join " +
- TABLE_PDU + " on pdu._id = addr.msg_id");
- qb.appendWhere("pdu._id = " + uri.getLastPathSegment());
- qb.appendWhere(" AND " + "addr.type = " + PduHeaders.TO);
+ pduTable + " on " + pduTable + "._id = addr.msg_id");
+ qb.appendWhere(pduTable + "._id = " + uri.getLastPathSegment());
+ qb.appendWhere(" AND " + TABLE_ADDR + ".type = " + PduHeaders.TO);
break;
case MMS_SENDING_RATE:
qb.setTables(TABLE_RATE);
@@ -190,7 +213,7 @@
qb.appendWhere(BaseColumns._ID + "=" + uri.getLastPathSegment());
break;
case MMS_THREADS:
- qb.setTables("pdu group by thread_id");
+ qb.setTables(pduTable + " group by thread_id");
break;
default:
Log.e(TAG, "query: invalid request: " + uri);
@@ -199,7 +222,7 @@
String finalSortOrder = null;
if (TextUtils.isEmpty(sortOrder)) {
- if (qb.getTables().equals(TABLE_PDU)) {
+ if (qb.getTables().equals(pduTable)) {
finalSortOrder = Mms.DATE + " DESC";
} else if (qb.getTables().equals(TABLE_PART)) {
finalSortOrder = Part.SEQ;
@@ -223,8 +246,8 @@
return ret;
}
- private void constructQueryForBox(SQLiteQueryBuilder qb, int msgBox) {
- qb.setTables(TABLE_PDU);
+ private void constructQueryForBox(SQLiteQueryBuilder qb, int msgBox, String pduTable) {
+ qb.setTables(pduTable);
if (msgBox != Mms.MESSAGE_BOX_ALL) {
qb.appendWhere(Mms.MESSAGE_BOX + "=" + msgBox);
@@ -282,6 +305,7 @@
return null;
}
final int callerUid = Binder.getCallingUid();
+ final String callerPkg = getCallingPackage();
int msgBox = Mms.MESSAGE_BOX_ALL;
boolean notify = true;
@@ -379,8 +403,7 @@
// If caller is not SYSTEM or PHONE, or SYSTEM or PHONE does not set CREATOR
// set CREATOR using the truth on caller.
// Note: Inferring package name from UID may include unrelated package names
- finalValues.put(Telephony.Mms.CREATOR,
- ProviderUtil.getPackageNamesByUid(getContext(), callerUid));
+ finalValues.put(Telephony.Mms.CREATOR, callerPkg);
}
if ((rowId = db.insert(table, null, finalValues)) <= 0) {
@@ -709,6 +732,7 @@
return 0;
}
final int callerUid = Binder.getCallingUid();
+ final String callerPkg = getCallingPackage();
int match = sURLMatcher.match(uri);
if (LOCAL_LOGV) {
Log.v(TAG, "Update uri=" + uri + ", match=" + match);
@@ -763,8 +787,7 @@
filterUnsupportedKeys(values);
if (ProviderUtil.shouldRemoveCreator(values, callerUid)) {
// CREATOR should not be changed by non-SYSTEM/PHONE apps
- Log.w(TAG, ProviderUtil.getPackageNamesByUid(getContext(), callerUid) +
- " tries to update CREATOR");
+ Log.w(TAG, callerPkg + " tries to update CREATOR");
values.remove(Mms.CREATOR);
}
finalValues = new ContentValues(values);
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index 6325905..e763f35 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -16,14 +16,6 @@
package com.android.providers.telephony;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.FileInputStream;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
@@ -35,19 +27,27 @@
import android.provider.BaseColumns;
import android.provider.Telephony;
import android.provider.Telephony.Mms;
-import android.provider.Telephony.MmsSms;
-import android.provider.Telephony.Sms;
-import android.provider.Telephony.Threads;
import android.provider.Telephony.Mms.Addr;
import android.provider.Telephony.Mms.Part;
import android.provider.Telephony.Mms.Rate;
+import android.provider.Telephony.MmsSms;
import android.provider.Telephony.MmsSms.PendingMessages;
+import android.provider.Telephony.Sms;
+import android.provider.Telephony.Threads;
import android.telephony.SubscriptionManager;
import android.util.Log;
import com.google.android.mms.pdu.EncodedStringValue;
import com.google.android.mms.pdu.PduHeaders;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+
public class MmsSmsDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "MmsSmsDatabaseHelper";
@@ -216,7 +216,7 @@
private static boolean sFakeLowStorageTest = false; // for testing only
static final String DATABASE_NAME = "mmssms.db";
- static final int DATABASE_VERSION = 60;
+ static final int DATABASE_VERSION = 61;
private final Context mContext;
private LowStorageMonitor mLowStorageMonitor;
@@ -630,6 +630,15 @@
db.execSQL("CREATE TABLE " + MmsProvider.TABLE_DRM + " (" +
BaseColumns._ID + " INTEGER PRIMARY KEY," +
"_data TEXT);");
+
+ // Restricted view of pdu table, only sent/received messages without wap pushes
+ db.execSQL("CREATE VIEW " + MmsProvider.VIEW_PDU_RESTRICTED + " AS " +
+ "SELECT * FROM " + MmsProvider.TABLE_PDU + " WHERE " +
+ "(" + Mms.MESSAGE_BOX + "=" + Mms.MESSAGE_BOX_INBOX +
+ " OR " +
+ Mms.MESSAGE_BOX + "=" + Mms.MESSAGE_BOX_SENT + ")" +
+ " AND " +
+ "(" + Mms.MESSAGE_TYPE + "!=" + PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND + ");");
}
// Unlike the other trigger-creating functions, this function can be called multiple times
@@ -873,6 +882,13 @@
"reference_number INTEGER," +
"action TEXT," +
"data TEXT);");
+
+ // Restricted view of sms table, only sent/received messages
+ db.execSQL("CREATE VIEW " + SmsProvider.VIEW_SMS_RESTRICTED + " AS " +
+ "SELECT * FROM " + SmsProvider.TABLE_SMS + " WHERE " +
+ Sms.TYPE + "=" + Sms.MESSAGE_TYPE_INBOX +
+ " OR " +
+ Sms.TYPE + "=" + Sms.MESSAGE_TYPE_SENT + ";");
}
private void createCommonTables(SQLiteDatabase db) {
@@ -1331,6 +1347,22 @@
} finally {
db.endTransaction();
}
+ // fall through
+ case 60:
+ if (currentVersion <= 60) {
+ return;
+ }
+
+ db.beginTransaction();
+ try {
+ upgradeDatabaseToVersion61(db);
+ db.setTransactionSuccessful();
+ } catch (Throwable ex) {
+ Log.e(TAG, ex.getMessage(), ex);
+ break;
+ } finally {
+ db.endTransaction();
+ }
return;
}
@@ -1559,6 +1591,22 @@
+ Threads.ARCHIVED + " INTEGER DEFAULT 0");
}
+ private void upgradeDatabaseToVersion61(SQLiteDatabase db) {
+ db.execSQL("CREATE VIEW " + SmsProvider.VIEW_SMS_RESTRICTED + " AS " +
+ "SELECT * FROM " + SmsProvider.TABLE_SMS + " WHERE " +
+ Sms.TYPE + "=" + Sms.MESSAGE_TYPE_INBOX +
+ " OR " +
+ Sms.TYPE + "=" + Sms.MESSAGE_TYPE_SENT + ";");
+ db.execSQL("CREATE VIEW " + MmsProvider.VIEW_PDU_RESTRICTED + " AS " +
+ "SELECT * FROM " + MmsProvider.TABLE_PDU + " WHERE " +
+ "(" + Mms.MESSAGE_BOX + "=" + Mms.MESSAGE_BOX_INBOX +
+ " OR " +
+ Mms.MESSAGE_BOX + "=" + Mms.MESSAGE_BOX_SENT + ")" +
+ " AND " +
+ "(" + Mms.MESSAGE_TYPE + "!=" + PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND + ");");
+
+ }
+
@Override
public synchronized SQLiteDatabase getWritableDatabase() {
SQLiteDatabase db = super.getWritableDatabase();
diff --git a/src/com/android/providers/telephony/MmsSmsProvider.java b/src/com/android/providers/telephony/MmsSmsProvider.java
index 457e472..70d0dde 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -187,32 +187,50 @@
Mms.MESSAGE_TYPE + " = " + PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF + " OR " +
Mms.MESSAGE_TYPE + " = " + PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND + "))";
- // Search on the words table but return the rows from the corresponding sms table
- private static final String SMS_QUERY =
- "SELECT sms._id AS _id,thread_id,address,body,date,date_sent,index_text,words._id " +
- "FROM sms,words WHERE (index_text MATCH ? " +
- "AND sms._id=words.source_id AND words.table_to_use=1)";
+ private static String getTextSearchQuery(String smsTable, String pduTable) {
+ // Search on the words table but return the rows from the corresponding sms table
+ final String smsQuery = "SELECT "
+ + smsTable + "._id AS _id,"
+ + "thread_id,"
+ + "address,"
+ + "body,"
+ + "date,"
+ + "date_sent,"
+ + "index_text,"
+ + "words._id "
+ + "FROM " + smsTable + ",words "
+ + "WHERE (index_text MATCH ? "
+ + "AND " + smsTable + "._id=words.source_id "
+ + "AND words.table_to_use=1)";
- // Search on the words table but return the rows from the corresponding parts table
- private static final String MMS_QUERY =
- "SELECT pdu._id,thread_id,addr.address,part.text " +
- "AS body,pdu.date,pdu.date_sent,index_text,words._id " +
- "FROM pdu,part,addr,words WHERE ((part.mid=pdu._id) AND " +
- "(addr.msg_id=pdu._id) AND " +
- "(addr.type=" + PduHeaders.TO + ") AND " +
- "(part.ct='text/plain') AND " +
- "(index_text MATCH ?) AND " +
- "(part._id = words.source_id) AND " +
- "(words.table_to_use=2))";
+ // Search on the words table but return the rows from the corresponding parts table
+ final String mmsQuery = "SELECT "
+ + pduTable + "._id,"
+ + "thread_id,"
+ + "addr.address,"
+ + "part.text AS body,"
+ + pduTable + ".date,"
+ + pduTable + ".date_sent,"
+ + "index_text,"
+ + "words._id "
+ + "FROM " + pduTable + ",part,addr,words "
+ + "WHERE ((part.mid=" + pduTable + "._id) "
+ + "AND (addr.msg_id=" + pduTable + "._id) "
+ + "AND (addr.type=" + PduHeaders.TO + ") "
+ + "AND (part.ct='text/plain') "
+ + "AND (index_text MATCH ?) "
+ + "AND (part._id = words.source_id) "
+ + "AND (words.table_to_use=2))";
- // This code queries the sms and mms tables and returns a unified result set
- // of text matches. We query the sms table which is pretty simple. We also
- // query the pdu, part and addr table to get the mms result. Notet we're
- // using a UNION so we have to have the same number of result columns from
- // both queries.
- private static final String SMS_MMS_QUERY =
- SMS_QUERY + " UNION " + MMS_QUERY +
- " GROUP BY thread_id ORDER BY thread_id ASC, date DESC";
+ // This code queries the sms and mms tables and returns a unified result set
+ // of text matches. We query the sms table which is pretty simple. We also
+ // query the pdu, part and addr table to get the mms result. Note we're
+ // using a UNION so we have to have the same number of result columns from
+ // both queries.
+ return smsQuery + " UNION " + mmsQuery + " "
+ + "GROUP BY thread_id "
+ + "ORDER BY thread_id ASC, date DESC";
+ }
private static final String AUTHORITY = "mms-sms";
@@ -297,11 +315,22 @@
@Override
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
+ // First check if restricted views of the "sms" and "pdu" tables should be used based on the
+ // caller's identity. Only system, phone or the default sms app can have full access
+ // of sms/mms data. For other apps, we present a restricted view which only contains sent
+ // or received messages, without wap pushes.
+ final boolean accessRestricted = ProviderUtil.isAccessRestricted(
+ getContext(), getCallingPackage(), Binder.getCallingUid());
+ final String pduTable = MmsProvider.getPduTable(accessRestricted);
+ final String smsTable = SmsProvider.getSmsTable(accessRestricted);
+
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor cursor = null;
- switch(URI_MATCHER.match(uri)) {
+ final int match = URI_MATCHER.match(uri);
+ switch (match) {
case URI_COMPLETE_CONVERSATIONS:
- cursor = getCompleteConversations(projection, selection, sortOrder);
+ cursor = getCompleteConversations(projection, selection, sortOrder, smsTable,
+ pduTable);
break;
case URI_CONVERSATIONS:
String simple = uri.getQueryParameter("simple");
@@ -315,12 +344,12 @@
projection, selection, selectionArgs, sortOrder);
} else {
cursor = getConversations(
- projection, selection, sortOrder);
+ projection, selection, sortOrder, smsTable, pduTable);
}
break;
case URI_CONVERSATIONS_MESSAGES:
cursor = getConversationMessages(uri.getPathSegments().get(1), projection,
- selection, sortOrder);
+ selection, sortOrder, smsTable, pduTable);
break;
case URI_CONVERSATIONS_RECIPIENTS:
cursor = getConversationById(
@@ -334,7 +363,8 @@
break;
case URI_MESSAGES_BY_PHONE:
cursor = getMessagesByPhoneNumber(
- uri.getPathSegments().get(2), projection, selection, sortOrder);
+ uri.getPathSegments().get(2), projection, selection, sortOrder, smsTable,
+ pduTable);
break;
case URI_THREAD_ID:
List<String> recipients = uri.getQueryParameters("recipient");
@@ -387,7 +417,7 @@
switch (Integer.parseInt(uri.getQueryParameter("table_to_use"))) {
case 1: // sms
cursor = db.query(
- "sms",
+ smsTable,
new String[] { "thread_id" },
"_id=?",
new String[] { String.valueOf(id) },
@@ -396,9 +426,10 @@
null);
break;
case 2: // mms
- String mmsQuery =
- "SELECT thread_id FROM pdu,part WHERE ((part.mid=pdu._id) AND " +
- "(part._id=?))";
+ String mmsQuery = "SELECT thread_id "
+ + "FROM " + pduTable + ",part "
+ + "WHERE ((part.mid=" + pduTable + "._id) "
+ + "AND " + "(part._id=?))";
cursor = db.rawQuery(mmsQuery, new String[] { String.valueOf(id) });
break;
}
@@ -420,7 +451,8 @@
String searchString = uri.getQueryParameter("pattern") + "*";
try {
- cursor = db.rawQuery(SMS_MMS_QUERY, new String[] { searchString, searchString });
+ cursor = db.rawQuery(getTextSearchQuery(smsTable, pduTable),
+ new String[] { searchString, searchString });
} catch (Exception ex) {
Log.e(LOG_TAG, "got exception: " + ex.toString());
}
@@ -448,11 +480,11 @@
}
case URI_UNDELIVERED_MSG: {
cursor = getUndeliveredMessages(projection, selection,
- selectionArgs, sortOrder);
+ selectionArgs, sortOrder, smsTable, pduTable);
break;
}
case URI_DRAFT: {
- cursor = getDraftThread(projection, selection, sortOrder);
+ cursor = getDraftThread(projection, selection, sortOrder, smsTable, pduTable);
break;
}
case URI_FIRST_LOCKED_MESSAGE_BY_THREAD_ID: {
@@ -464,11 +496,12 @@
break;
}
cursor = getFirstLockedMessage(projection, "thread_id=" + Long.toString(threadId),
- sortOrder);
+ sortOrder, smsTable, pduTable);
break;
}
case URI_FIRST_LOCKED_MESSAGE_ALL: {
- cursor = getFirstLockedMessage(projection, selection, sortOrder);
+ cursor = getFirstLockedMessage(
+ projection, selection, sortOrder, smsTable, pduTable);
break;
}
default:
@@ -743,13 +776,13 @@
* ;
*/
private Cursor getDraftThread(String[] projection, String selection,
- String sortOrder) {
+ String sortOrder, String smsTable, String pduTable) {
String[] innerProjection = new String[] {BaseColumns._ID, Conversations.THREAD_ID};
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
- mmsQueryBuilder.setTables(MmsProvider.TABLE_PDU);
- smsQueryBuilder.setTables(SmsProvider.TABLE_SMS);
+ mmsQueryBuilder.setTables(pduTable);
+ smsQueryBuilder.setTables(smsTable);
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(
MmsSms.TYPE_DISCRIMINATOR_COLUMN, innerProjection,
@@ -803,12 +836,12 @@
* messages.
*/
private Cursor getConversations(String[] projection, String selection,
- String sortOrder) {
+ String sortOrder, String smsTable, String pduTable) {
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
- mmsQueryBuilder.setTables(MmsProvider.TABLE_PDU);
- smsQueryBuilder.setTables(SmsProvider.TABLE_SMS);
+ mmsQueryBuilder.setTables(pduTable);
+ smsQueryBuilder.setTables(smsTable);
String[] columns = handleNullMessageProjection(projection);
String[] innerMmsProjection = makeProjectionWithDateAndThreadId(
@@ -856,12 +889,12 @@
* there is *any* locked message, not the actual messages themselves.
*/
private Cursor getFirstLockedMessage(String[] projection, String selection,
- String sortOrder) {
+ String sortOrder, String smsTable, String pduTable) {
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
- mmsQueryBuilder.setTables(MmsProvider.TABLE_PDU);
- smsQueryBuilder.setTables(SmsProvider.TABLE_SMS);
+ mmsQueryBuilder.setTables(pduTable);
+ smsQueryBuilder.setTables(smsTable);
String[] idColumn = new String[] { BaseColumns._ID };
@@ -899,8 +932,9 @@
* and SMS.
*/
private Cursor getCompleteConversations(String[] projection,
- String selection, String sortOrder) {
- String unionQuery = buildConversationQuery(projection, selection, sortOrder);
+ String selection, String sortOrder, String smsTable, String pduTable) {
+ String unionQuery = buildConversationQuery(projection, selection, sortOrder, smsTable,
+ pduTable);
return mOpenHelper.getReadableDatabase().rawQuery(unionQuery, EMPTY_STRING_ARRAY);
}
@@ -929,7 +963,7 @@
*/
private Cursor getConversationMessages(
String threadIdString, String[] projection, String selection,
- String sortOrder) {
+ String sortOrder, String smsTable, String pduTable) {
try {
Long.parseLong(threadIdString);
} catch (NumberFormatException exception) {
@@ -939,7 +973,8 @@
String finalSelection = concatSelections(
selection, "thread_id = " + threadIdString);
- String unionQuery = buildConversationQuery(projection, finalSelection, sortOrder);
+ String unionQuery = buildConversationQuery(projection, finalSelection, sortOrder, smsTable,
+ pduTable);
return mOpenHelper.getReadableDatabase().rawQuery(unionQuery, EMPTY_STRING_ARRAY);
}
@@ -964,12 +999,12 @@
*/
private Cursor getMessagesByPhoneNumber(
String phoneNumber, String[] projection, String selection,
- String sortOrder) {
+ String sortOrder, String smsTable, String pduTable) {
String escapedPhoneNumber = DatabaseUtils.sqlEscapeString(phoneNumber);
String finalMmsSelection =
concatSelections(
selection,
- "pdu._id = matching_addresses.address_msg_id");
+ pduTable + "._id = matching_addresses.address_msg_id");
String finalSmsSelection =
concatSelections(
selection,
@@ -982,14 +1017,14 @@
mmsQueryBuilder.setDistinct(true);
smsQueryBuilder.setDistinct(true);
mmsQueryBuilder.setTables(
- MmsProvider.TABLE_PDU +
+ pduTable +
", (SELECT msg_id AS address_msg_id " +
"FROM addr WHERE (address=" + escapedPhoneNumber +
" OR PHONE_NUMBERS_EQUAL(addr.address, " +
escapedPhoneNumber +
(mUseStrictPhoneNumberComparation ? ", 1))) " : ", 0))) ") +
"AS matching_addresses");
- smsQueryBuilder.setTables(SmsProvider.TABLE_SMS);
+ smsQueryBuilder.setTables(smsTable);
String[] columns = handleNullMessageProjection(projection);
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(
@@ -1033,16 +1068,16 @@
selectionArgs, sortOrder, null, null);
}
- private static String joinPduAndPendingMsgTables() {
- return MmsProvider.TABLE_PDU + " LEFT JOIN " + TABLE_PENDING_MSG
- + " ON pdu._id = pending_msgs.msg_id";
+ private static String joinPduAndPendingMsgTables(String pduTable) {
+ return pduTable + " LEFT JOIN " + TABLE_PENDING_MSG
+ + " ON " + pduTable + "._id = pending_msgs.msg_id";
}
- private static String[] createMmsProjection(String[] old) {
+ private static String[] createMmsProjection(String[] old, String pduTable) {
String[] newProjection = new String[old.length];
for (int i = 0; i < old.length; i++) {
if (old[i].equals(BaseColumns._ID)) {
- newProjection[i] = "pdu._id";
+ newProjection[i] = pduTable + "._id";
} else {
newProjection[i] = old[i];
}
@@ -1052,14 +1087,14 @@
private Cursor getUndeliveredMessages(
String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- String[] mmsProjection = createMmsProjection(projection);
+ String sortOrder, String smsTable, String pduTable) {
+ String[] mmsProjection = createMmsProjection(projection, pduTable);
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
- mmsQueryBuilder.setTables(joinPduAndPendingMsgTables());
- smsQueryBuilder.setTables(SmsProvider.TABLE_SMS);
+ mmsQueryBuilder.setTables(joinPduAndPendingMsgTables(pduTable));
+ smsQueryBuilder.setTables(smsTable);
String finalMmsSelection = concatSelections(
selection, Mms.MESSAGE_BOX + " = " + Mms.MESSAGE_BOX_OUTBOX);
@@ -1076,7 +1111,7 @@
smsColumns, 1);
Set<String> columnsPresentInTable = new HashSet<String>(MMS_COLUMNS);
- columnsPresentInTable.add("pdu._id");
+ columnsPresentInTable.add(pduTable + "._id");
columnsPresentInTable.add(PendingMessages.ERROR_TYPE);
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(
MmsSms.TYPE_DISCRIMINATOR_COLUMN, innerMmsProjection,
@@ -1118,16 +1153,16 @@
}
private static String buildConversationQuery(String[] projection,
- String selection, String sortOrder) {
- String[] mmsProjection = createMmsProjection(projection);
+ String selection, String sortOrder, String smsTable, String pduTable) {
+ String[] mmsProjection = createMmsProjection(projection, pduTable);
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
mmsQueryBuilder.setDistinct(true);
smsQueryBuilder.setDistinct(true);
- mmsQueryBuilder.setTables(joinPduAndPendingMsgTables());
- smsQueryBuilder.setTables(SmsProvider.TABLE_SMS);
+ mmsQueryBuilder.setTables(joinPduAndPendingMsgTables(pduTable));
+ smsQueryBuilder.setTables(smsTable);
String[] smsColumns = handleNullMessageProjection(projection);
String[] mmsColumns = handleNullMessageProjection(mmsProjection);
@@ -1135,7 +1170,7 @@
String[] innerSmsProjection = makeProjectionWithNormalizedDate(smsColumns, 1);
Set<String> columnsPresentInTable = new HashSet<String>(MMS_COLUMNS);
- columnsPresentInTable.add("pdu._id");
+ columnsPresentInTable.add(pduTable + "._id");
columnsPresentInTable.add(PendingMessages.ERROR_TYPE);
String mmsSelection = concatSelections(selection,
@@ -1241,13 +1276,14 @@
public int update(Uri uri, ContentValues values,
String selection, String[] selectionArgs) {
final int callerUid = Binder.getCallingUid();
+ final String callerPkg = getCallingPackage();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int affectedRows = 0;
switch(URI_MATCHER.match(uri)) {
case URI_CONVERSATIONS_MESSAGES:
String threadIdString = uri.getPathSegments().get(1);
affectedRows = updateConversation(threadIdString, values,
- selection, selectionArgs, callerUid);
+ selection, selectionArgs, callerUid, callerPkg);
break;
case URI_PENDING_MSG:
@@ -1285,9 +1321,8 @@
return affectedRows;
}
- private int updateConversation(
- String threadIdString, ContentValues values, String selection,
- String[] selectionArgs, int callerUid) {
+ private int updateConversation(String threadIdString, ContentValues values, String selection,
+ String[] selectionArgs, int callerUid, String callerPkg) {
try {
Long.parseLong(threadIdString);
} catch (NumberFormatException exception) {
@@ -1297,8 +1332,7 @@
}
if (ProviderUtil.shouldRemoveCreator(values, callerUid)) {
// CREATOR should not be changed by non-SYSTEM/PHONE apps
- Log.w(LOG_TAG, ProviderUtil.getPackageNamesByUid(getContext(), callerUid) +
- " tries to update CREATOR");
+ Log.w(LOG_TAG, callerPkg + " tries to update CREATOR");
// Sms.CREATOR and Mms.CREATOR are same. But let's do this
// twice in case the names may differ in the future
values.remove(Sms.CREATOR);
diff --git a/src/com/android/providers/telephony/ProviderUtil.java b/src/com/android/providers/telephony/ProviderUtil.java
index 8abe934..9435409 100644
--- a/src/com/android/providers/telephony/ProviderUtil.java
+++ b/src/com/android/providers/telephony/ProviderUtil.java
@@ -18,10 +18,10 @@
import android.content.ContentValues;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.Process;
import android.provider.Telephony;
-import android.text.TextUtils;
+
+import com.android.internal.telephony.SmsApplication;
/**
* Helpers
@@ -29,28 +29,18 @@
public class ProviderUtil {
/**
- * Get space separated package names associated with a UID
+ * Check if a caller of the provider has restricted access,
+ * i.e. being non-system, non-phone, non-default SMS app
*
- * @param context The context to use
- * @param uid The UID to look up
- * @return The space separated list of package names for UID
+ * @param context the context to use
+ * @param packageName the caller package name
+ * @param uid the caller uid
+ * @return true if the caller is not system, or phone or default sms app, false otherwise
*/
- public static String getPackageNamesByUid(Context context, int uid) {
- final PackageManager pm = context.getPackageManager();
- final String[] packageNames = pm.getPackagesForUid(uid);
- if (packageNames != null) {
- final StringBuilder sb = new StringBuilder();
- for (String name : packageNames) {
- if (!TextUtils.isEmpty(name)) {
- if (sb.length() > 0) {
- sb.append(' ');
- }
- sb.append(name);
- }
- }
- return sb.toString();
- }
- return null;
+ public static boolean isAccessRestricted(Context context, String packageName, int uid) {
+ return (uid != Process.SYSTEM_UID
+ && uid != Process.PHONE_UID
+ && !SmsApplication.isDefaultSmsApplication(context, packageName));
}
/**
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index 03d2aa6..d48f1c6 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -51,6 +51,7 @@
static final String TABLE_RAW = "raw";
private static final String TABLE_SR_PENDING = "sr_pending";
private static final String TABLE_WORDS = "words";
+ static final String VIEW_SMS_RESTRICTED = "sms_restricted";
private static final Integer ONE = Integer.valueOf(1);
@@ -88,48 +89,65 @@
return true;
}
+ /**
+ * Return the proper view of "sms" table for the current access status.
+ *
+ * @param accessRestricted If the access is restricted
+ * @return the table/view name of the "sms" data
+ */
+ public static String getSmsTable(boolean accessRestricted) {
+ return accessRestricted ? VIEW_SMS_RESTRICTED : TABLE_SMS;
+ }
+
@Override
public Cursor query(Uri url, String[] projectionIn, String selection,
String[] selectionArgs, String sort) {
+ // First check if a restricted view of the "sms" table should be used based on the
+ // caller's identity. Only system, phone or the default sms app can have full access
+ // of sms data. For other apps, we present a restricted view which only contains sent
+ // or received messages.
+ final boolean accessRestricted = ProviderUtil.isAccessRestricted(
+ getContext(), getCallingPackage(), Binder.getCallingUid());
+ final String smsTable = getSmsTable(accessRestricted);
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
// Generate the body of the query.
int match = sURLMatcher.match(url);
switch (match) {
case SMS_ALL:
- constructQueryForBox(qb, Sms.MESSAGE_TYPE_ALL);
+ constructQueryForBox(qb, Sms.MESSAGE_TYPE_ALL, smsTable);
break;
case SMS_UNDELIVERED:
- constructQueryForUndelivered(qb);
+ constructQueryForUndelivered(qb, smsTable);
break;
case SMS_FAILED:
- constructQueryForBox(qb, Sms.MESSAGE_TYPE_FAILED);
+ constructQueryForBox(qb, Sms.MESSAGE_TYPE_FAILED, smsTable);
break;
case SMS_QUEUED:
- constructQueryForBox(qb, Sms.MESSAGE_TYPE_QUEUED);
+ constructQueryForBox(qb, Sms.MESSAGE_TYPE_QUEUED, smsTable);
break;
case SMS_INBOX:
- constructQueryForBox(qb, Sms.MESSAGE_TYPE_INBOX);
+ constructQueryForBox(qb, Sms.MESSAGE_TYPE_INBOX, smsTable);
break;
case SMS_SENT:
- constructQueryForBox(qb, Sms.MESSAGE_TYPE_SENT);
+ constructQueryForBox(qb, Sms.MESSAGE_TYPE_SENT, smsTable);
break;
case SMS_DRAFT:
- constructQueryForBox(qb, Sms.MESSAGE_TYPE_DRAFT);
+ constructQueryForBox(qb, Sms.MESSAGE_TYPE_DRAFT, smsTable);
break;
case SMS_OUTBOX:
- constructQueryForBox(qb, Sms.MESSAGE_TYPE_OUTBOX);
+ constructQueryForBox(qb, Sms.MESSAGE_TYPE_OUTBOX, smsTable);
break;
case SMS_ALL_ID:
- qb.setTables(TABLE_SMS);
+ qb.setTables(smsTable);
qb.appendWhere("(_id = " + url.getPathSegments().get(0) + ")");
break;
@@ -138,7 +156,7 @@
case SMS_SENT_ID:
case SMS_DRAFT_ID:
case SMS_OUTBOX_ID:
- qb.setTables(TABLE_SMS);
+ qb.setTables(smsTable);
qb.appendWhere("(_id = " + url.getPathSegments().get(1) + ")");
break;
@@ -158,16 +176,28 @@
return null;
}
- qb.setTables(TABLE_SMS);
+ qb.setTables(smsTable);
qb.appendWhere("thread_id = " + threadID);
break;
case SMS_CONVERSATIONS:
- qb.setTables("sms, (SELECT thread_id AS group_thread_id, MAX(date)AS group_date,"
- + "COUNT(*) AS msg_count FROM sms GROUP BY thread_id) AS groups");
- qb.appendWhere("sms.thread_id = groups.group_thread_id AND sms.date ="
- + "groups.group_date");
- qb.setProjectionMap(sConversationProjectionMap);
+ qb.setTables(smsTable + ", "
+ + "(SELECT thread_id AS group_thread_id, "
+ + "MAX(date) AS group_date, "
+ + "COUNT(*) AS msg_count "
+ + "FROM " + smsTable + " "
+ + "GROUP BY thread_id) AS groups");
+ qb.appendWhere(smsTable + ".thread_id=groups.group_thread_id"
+ + " AND " + smsTable + ".date=groups.group_date");
+ final HashMap<String, String> projectionMap = new HashMap<>();
+ projectionMap.put(Sms.Conversations.SNIPPET,
+ smsTable + ".body AS snippet");
+ projectionMap.put(Sms.Conversations.THREAD_ID,
+ smsTable + ".thread_id AS thread_id");
+ projectionMap.put(Sms.Conversations.MESSAGE_COUNT,
+ "groups.msg_count AS msg_count");
+ projectionMap.put("delta", null);
+ qb.setProjectionMap(projectionMap);
break;
case SMS_RAW_MESSAGE:
@@ -196,7 +226,7 @@
break;
case SMS_STATUS_ID:
- qb.setTables(TABLE_SMS);
+ qb.setTables(smsTable);
qb.appendWhere("(_id = " + url.getPathSegments().get(1) + ")");
break;
@@ -217,7 +247,7 @@
if (!TextUtils.isEmpty(sort)) {
orderBy = sort;
- } else if (qb.getTables().equals(TABLE_SMS)) {
+ } else if (qb.getTables().equals(smsTable)) {
orderBy = Sms.DEFAULT_SORT_ORDER;
}
@@ -314,16 +344,16 @@
return cursor;
}
- private void constructQueryForBox(SQLiteQueryBuilder qb, int type) {
- qb.setTables(TABLE_SMS);
+ private void constructQueryForBox(SQLiteQueryBuilder qb, int type, String smsTable) {
+ qb.setTables(smsTable);
if (type != Sms.MESSAGE_TYPE_ALL) {
qb.appendWhere("type=" + type);
}
}
- private void constructQueryForUndelivered(SQLiteQueryBuilder qb) {
- qb.setTables(TABLE_SMS);
+ private void constructQueryForUndelivered(SQLiteQueryBuilder qb, String smsTable) {
+ qb.setTables(smsTable);
qb.appendWhere("(type=" + Sms.MESSAGE_TYPE_OUTBOX +
" OR type=" + Sms.MESSAGE_TYPE_FAILED +
@@ -356,15 +386,16 @@
@Override
public Uri insert(Uri url, ContentValues initialValues) {
final int callerUid = Binder.getCallingUid();
+ final String callerPkg = getCallingPackage();
long token = Binder.clearCallingIdentity();
try {
- return insertInner(url, initialValues, callerUid);
+ return insertInner(url, initialValues, callerUid, callerPkg);
} finally {
Binder.restoreCallingIdentity(token);
}
}
- private Uri insertInner(Uri url, ContentValues initialValues, int callerUid) {
+ private Uri insertInner(Uri url, ContentValues initialValues, int callerUid, String callerPkg) {
ContentValues values;
long rowID;
int type = Sms.MESSAGE_TYPE_ALL;
@@ -512,7 +543,7 @@
// If caller is not SYSTEM or PHONE, or SYSTEM or PHONE does not set CREATOR
// set CREATOR using the truth on caller.
// Note: Inferring package name from UID may include unrelated package names
- values.put(Sms.CREATOR, ProviderUtil.getPackageNamesByUid(getContext(), callerUid));
+ values.put(Sms.CREATOR, callerPkg);
}
} else {
if (initialValues == null) {
@@ -643,6 +674,7 @@
@Override
public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
final int callerUid = Binder.getCallingUid();
+ final String callerPkg = getCallingPackage();
int count = 0;
String table = TABLE_SMS;
String extraWhere = null;
@@ -704,8 +736,7 @@
if (table.equals(TABLE_SMS) && ProviderUtil.shouldRemoveCreator(values, callerUid)) {
// CREATOR should not be changed by non-SYSTEM/PHONE apps
- Log.w(TAG, ProviderUtil.getPackageNamesByUid(getContext(), callerUid) +
- " tries to update CREATOR");
+ Log.w(TAG, callerPkg + " tries to update CREATOR");
values.remove(Sms.CREATOR);
}
@@ -738,8 +769,6 @@
private final static String VND_ANDROID_DIR_SMS =
"vnd.android.cursor.dir/sms";
- private static final HashMap<String, String> sConversationProjectionMap =
- new HashMap<String, String>();
private static final String[] sIDProjection = new String[] { "_id" };
private static final int SMS_ALL = 0;
@@ -800,13 +829,5 @@
//we keep these for not breaking old applications
sURLMatcher.addURI("sms", "sim", SMS_ALL_ICC);
sURLMatcher.addURI("sms", "sim/#", SMS_ICC);
-
- sConversationProjectionMap.put(Sms.Conversations.SNIPPET,
- "sms.body AS snippet");
- sConversationProjectionMap.put(Sms.Conversations.THREAD_ID,
- "sms.thread_id AS thread_id");
- sConversationProjectionMap.put(Sms.Conversations.MESSAGE_COUNT,
- "groups.msg_count AS msg_count");
- sConversationProjectionMap.put("delta", null);
}
}