mods to support searching of mms messages
diff --git a/src/com/android/providers/telephony/MmsProvider.java b/src/com/android/providers/telephony/MmsProvider.java
index fc20ee2..9c3a446 100644
--- a/src/com/android/providers/telephony/MmsProvider.java
+++ b/src/com/android/providers/telephony/MmsProvider.java
@@ -366,24 +366,30 @@
finalValues.put(Part.MSG_ID, uri.getPathSegments().get(0));
}
- // Generate the '_data' field of the part with default
- // permission settings.
- String path = getContext().getDir("parts", 0).getPath()
- + "/PART_" + System.currentTimeMillis();
+ String contentType = values.getAsString("ct");
+
+ // text/plain and app application/smil store their "data" inline in the
+ // table so there's no need to create the file
+ if (!"text/plain".equals(contentType) && !"application/smil".equals(contentType)) {
+ // Generate the '_data' field of the part with default
+ // permission settings.
+ String path = getContext().getDir("parts", 0).getPath()
+ + "/PART_" + System.currentTimeMillis();
- finalValues.put(Part._DATA, path);
+ finalValues.put(Part._DATA, path);
- File partFile = new File(path);
- if (!partFile.exists()) {
- try {
- if (!partFile.createNewFile()) {
+ File partFile = new File(path);
+ if (!partFile.exists()) {
+ try {
+ if (!partFile.createNewFile()) {
+ throw new IllegalStateException(
+ "Unable to create new partFile: " + path);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "createNewFile", e);
throw new IllegalStateException(
"Unable to create new partFile: " + path);
}
- } catch (IOException e) {
- Log.e(TAG, "createNewFile", e);
- throw new IllegalStateException(
- "Unable to create new partFile: " + path);
}
}
@@ -591,7 +597,10 @@
while (cursor.moveToNext()) {
try {
// Delete the associated files saved on file-system.
- new File(cursor.getString(0)).delete();
+ String path = cursor.getString(0);
+ if (path != null) {
+ new File(path).delete();
+ }
} catch (Throwable ex) {
Log.e(TAG, ex.getMessage(), ex);
}
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index 6a17f04..42f8b2f 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -23,6 +23,14 @@
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF;
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_SEND_REQ;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.File;
+import java.util.ArrayList;
+
+import com.google.android.mms.pdu.EncodedStringValue;
+
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
@@ -176,11 +184,11 @@
" ELSE 1 " +
" END; " +
" END";
-
+
private static MmsSmsDatabaseHelper mInstance = null;
static final String DATABASE_NAME = "mmssms.db";
- static final int DATABASE_VERSION = 45;
+ static final int DATABASE_VERSION = 46;
private MmsSmsDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -202,7 +210,7 @@
updateAllThreads(db, null, null);
return;
}
-
+
// Delete the row for this thread in the threads table if
// there are no more messages attached to it in either
// the sms or pdu tables.
@@ -234,24 +242,24 @@
// the threads table to be that of the most recent message in
// the thread.
db.execSQL(
- " UPDATE threads" +
- " SET" +
- " date =" +
- " (SELECT date FROM" +
- " (SELECT date * 1000 AS date, thread_id FROM pdu" +
- " UNION SELECT date, thread_id FROM sms)" +
- " WHERE thread_id = " + thread_id + " ORDER BY date DESC LIMIT 1)," +
- " snippet =" +
- " (SELECT snippet FROM" +
- " (SELECT date * 1000 AS date, sub AS snippet, thread_id FROM pdu" +
- " UNION SELECT date, body AS snippet, thread_id FROM sms)" +
- " WHERE thread_id = " + thread_id + " ORDER BY date DESC LIMIT 1)," +
- " snippet_cs =" +
- " (SELECT snippet_cs FROM" +
- " (SELECT date * 1000 AS date, sub_cs AS snippet_cs, thread_id FROM pdu" +
- " UNION SELECT date, 0 AS snippet_cs, thread_id FROM sms)" +
- " WHERE thread_id = " + thread_id + " ORDER BY date DESC LIMIT 1)" +
- " WHERE threads._id = " + thread_id + ";");
+ " UPDATE threads" +
+ " SET" +
+ " date =" +
+ " (SELECT date FROM" +
+ " (SELECT date * 1000 AS date, thread_id FROM pdu" +
+ " UNION SELECT date, thread_id FROM sms)" +
+ " WHERE thread_id = " + thread_id + " ORDER BY date DESC LIMIT 1)," +
+ " snippet =" +
+ " (SELECT snippet FROM" +
+ " (SELECT date * 1000 AS date, sub AS snippet, thread_id FROM pdu" +
+ " UNION SELECT date, body AS snippet, thread_id FROM sms)" +
+ " WHERE thread_id = " + thread_id + " ORDER BY date DESC LIMIT 1)," +
+ " snippet_cs =" +
+ " (SELECT snippet_cs FROM" +
+ " (SELECT date * 1000 AS date, sub_cs AS snippet_cs, thread_id FROM pdu" +
+ " UNION SELECT date, 0 AS snippet_cs, thread_id FROM sms)" +
+ " WHERE thread_id = " + thread_id + " ORDER BY date DESC LIMIT 1)" +
+ " WHERE threads._id = " + thread_id + ";");
// Update the error column of the thread to indicate if there
// are any messages in it that have failed to send.
@@ -261,7 +269,7 @@
" AND thread_id = " + thread_id + " LIMIT 1)" +
" WHERE threads._id = " + thread_id + ";");
}
-
+
public static void updateAllThreads(SQLiteDatabase db, String where, String[] whereArgs) {
if (where == null) {
where = "";
@@ -282,7 +290,7 @@
"_id NOT IN (SELECT DISTINCT thread_id FROM sms " +
"UNION SELECT DISTINCT thread_id FROM pdu)", null);
}
-
+
public static int deleteOneSms(SQLiteDatabase db, int message_id) {
int thread_id = -1;
// Find the thread ID that the specified SMS belongs to.
@@ -370,7 +378,8 @@
Part.CONTENT_LOCATION + " TEXT," +
Part.CT_START + " INTEGER," +
Part.CT_TYPE + " TEXT," +
- Part._DATA + " TEXT);");
+ Part._DATA + " TEXT," +
+ Part.TEXT + " TEXT);");
db.execSQL("CREATE TABLE " + MmsProvider.TABLE_RATE + " (" +
Rate.SENT_TIME + " INTEGER);");
@@ -406,7 +415,7 @@
" OR " + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_READ_ORIG_IND + ")" +
" AND " + Mms.MESSAGE_ID + "=old." + Mms.MESSAGE_ID + "; " +
"END;");
-
+
// Update threads table to indicate whether attachments exist when
// parts are inserted or deleted.
db.execSQL(PART_UPDATE_THREADS_ON_INSERT_TRIGGER);
@@ -742,7 +751,7 @@
if (currentVersion <= 41) {
return;
}
-
+
db.beginTransaction();
try {
upgradeDatabaseToVersion42(db);
@@ -758,7 +767,7 @@
if (currentVersion <= 42) {
return;
}
-
+
db.beginTransaction();
try {
upgradeDatabaseToVersion43(db);
@@ -802,8 +811,25 @@
db.endTransaction();
}
return;
+
+ case 45:
+ if (currentVersion <= 45) {
+ return;
+ }
+ db.beginTransaction();
+ try {
+ upgradeDatabaseToVersion46(db);
+ db.setTransactionSuccessful();
+ } catch (Throwable ex) {
+ Log.e(TAG, ex.getMessage(), ex);
+ break;
+ } finally {
+ db.endTransaction();
+ }
+ return;
}
+
Log.e(TAG, "Destroying all old data.");
dropAll(db);
onCreate(db);
@@ -841,13 +867,13 @@
" WHERE _id = OLD.thread_id; " +
"END;");
}
-
+
private void upgradeDatabaseToVersion42(SQLiteDatabase db) {
db.execSQL("DROP TRIGGER IF EXISTS sms_update_thread_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS delete_obsolete_threads_sms");
db.execSQL("DROP TRIGGER IF EXISTS update_threads_error_on_delete_sms");
}
-
+
private void upgradeDatabaseToVersion43(SQLiteDatabase db) {
// Add 'has_attachment' column to threads table.
db.execSQL("ALTER TABLE threads ADD COLUMN has_attachment INTEGER DEFAULT 0");
@@ -884,4 +910,61 @@
db.execSQL("ALTER TABLE pdu ADD COLUMN " + Mms.LOCKED + " INTEGER DEFAULT 0");
}
+ private void upgradeDatabaseToVersion46(SQLiteDatabase db) {
+ // add the "text" column for caching inline text (e.g. strings) instead of
+ // putting them in an external file
+ db.execSQL("ALTER TABLE part ADD COLUMN " + Part.TEXT + " TEXT");
+
+ Cursor textRows = db.query(
+ "part",
+ new String[] { Part._ID, Part._DATA, Part.TEXT},
+ "ct = 'text/plain' OR ct == 'application/smil'",
+ null,
+ null,
+ null,
+ null);
+ ArrayList<String> filesToDelete = new ArrayList<String>();
+ try {
+ if (textRows != null) {
+ int partIdColumn = textRows.getColumnIndex(Part._ID);
+ int partDataColumn = textRows.getColumnIndex(Part._DATA);
+ int partTextColumn = textRows.getColumnIndex(Part.TEXT);
+
+ // This code is imperfect in that we can't guarantee that all the
+ // backing files get deleted. For example if the system aborts after
+ // the database is updated but before we complete the process of
+ // deleting files.
+ while (textRows.moveToNext()) {
+ String path = textRows.getString(partDataColumn);
+ if (path != null) {
+ try {
+ InputStream is = new FileInputStream(path);
+ byte [] data = new byte[is.available()];
+ is.read(data);
+ EncodedStringValue v = new EncodedStringValue(data);
+ textRows.updateString(partTextColumn, v.getString());
+ textRows.updateToNull(partDataColumn);
+ is.close();
+ filesToDelete.add(path);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ } finally {
+ textRows.commitUpdates();
+ for (String pathToDelete : filesToDelete) {
+ try {
+ (new File(pathToDelete)).delete();
+ } catch (SecurityException ex) {
+ Log.e(TAG, "unable to clean up old mms file for " + pathToDelete, ex);
+ }
+ }
+ if (textRows != null) {
+ textRows.close();
+ }
+ }
+ }
}
diff --git a/src/com/android/providers/telephony/MmsSmsProvider.java b/src/com/android/providers/telephony/MmsSmsProvider.java
index d9a1089..4382c99 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -192,7 +192,7 @@
// Use this pattern to query all canonical addresses.
URI_MATCHER.addURI(AUTHORITY, "canonical-addresses", URI_CANONICAL_ADDRESSES);
-
+
URI_MATCHER.addURI(AUTHORITY, "search", URI_SEARCH);
// In this pattern, two query parameters may be supplied:
@@ -291,27 +291,48 @@
null, null, sortOrder);
break;
case URI_SEARCH:
- if (sortOrder != null || selection != null || selectionArgs != null) {
- throw new IllegalArgumentException("do not specify sortOrder, selection, selectionArgs with this query");
+ if ( sortOrder != null
+ || selection != null
+ || selectionArgs != null
+ || projection != null) {
+ throw new IllegalArgumentException(
+ "do not specify sortOrder, selection, selectionArgs, or projection" +
+ "with this query");
}
- String searchString = "%" + uri.getQueryParameter("pattern") + "%";
-
- // compute the projection as a merge of the passed in projection
- // and the required projection
- ArrayList<String> projectionToUse = new ArrayList<String>();
- projectionToUse.addAll(Arrays.asList(projection));
+ // 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 that we're
+ // using a UNION so we have to have the same number of result columns from
+ // both queries.
- for (String r : new String[] {"_id","thread_id","address","body","subject","date"}) {
- if (!projectionToUse.contains(r)) {
- projectionToUse.add(r);
- }
- }
+ String searchString = "%" + uri.getQueryParameter("pattern") + "%";
+ String smsProjection = "_id,thread_id,address,body,date";
+ String mmsProjection = "pdu._id,thread_id,addr.address,part.text as body,pdu.date";
+
+ String smsQuery = String.format(
+ "SELECT %s FROM sms WHERE (body LIKE ?) ",
+ smsProjection);
+
+ // TODO consider whether we're really getting the right addr here (for example, if
+ // I send a message to a given phone number do I want the search result to
+ // show a match on "me" or on that phone number. I suspect the latter.
+ String mmsQuery = String.format(
+ "SELECT %s FROM pdu,part,addr WHERE ((part.mid=pdu._id) AND " +
+ "(addr.msg_id=pdu._id) AND " +
+ "(addr.type=%d) AND " +
+ "(part.ct='text/plain') AND " +
+ "(body like ?))",
+ mmsProjection,
+ PduHeaders.TO);
String rawQuery = String.format(
- "select %s from sms where (body like ? or subject like ?) " +
- "group by thread_id order by thread_id ASC,date DESC",
- TextUtils.join(",", projectionToUse));
+ "%s UNION %s GROUP BY %s ORDER BY %s",
+ smsQuery,
+ mmsQuery,
+ "thread_id",
+ "thread_id ASC, date DESC");
+
cursor = db.rawQuery(rawQuery, new String[] { searchString, searchString });
break;
case URI_PENDING_MSG: {
@@ -929,7 +950,7 @@
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Context context = getContext();
int affectedRows = 0;
-
+
switch(URI_MATCHER.match(uri)) {
case URI_CONVERSATIONS_MESSAGES:
long threadId;