am b4ac04f7: Add more logging to track down a monkey bug
Merge commit 'b4ac04f7bd9d4f16ec181f368c42f89c96f83f55'
* commit 'b4ac04f7bd9d4f16ec181f368c42f89c96f83f55':
Add more logging to track down a monkey bug
diff --git a/src/com/android/providers/telephony/MmsProvider.java b/src/com/android/providers/telephony/MmsProvider.java
index 37d1787..9c3a446 100644
--- a/src/com/android/providers/telephony/MmsProvider.java
+++ b/src/com/android/providers/telephony/MmsProvider.java
@@ -176,6 +176,9 @@
qb.setTables(TABLE_DRM);
qb.appendWhere(BaseColumns._ID + "=" + uri.getLastPathSegment());
break;
+ case MMS_THREADS:
+ qb.setTables("pdu group by thread_id");
+ break;
default:
Log.e(TAG, "Invalid request: " + uri);
return null;
@@ -363,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);
}
}
@@ -588,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);
}
@@ -751,10 +763,11 @@
private static final int MMS_PART_ID = 12;
private static final int MMS_MSG_ADDR = 13;
private static final int MMS_SENDING_RATE = 14;
- private static final int MMS_REPORT_STATUS = 15;
- private static final int MMS_REPORT_REQUEST = 16;
+ private static final int MMS_REPORT_STATUS = 15;
+ private static final int MMS_REPORT_REQUEST = 16;
private static final int MMS_DRM_STORAGE = 17;
private static final int MMS_DRM_STORAGE_ID = 18;
+ private static final int MMS_THREADS = 19;
private static final UriMatcher
sURLMatcher = new UriMatcher(UriMatcher.NO_MATCH);
@@ -779,6 +792,7 @@
sURLMatcher.addURI("mms", "report-request/#", MMS_REPORT_REQUEST);
sURLMatcher.addURI("mms", "drm", MMS_DRM_STORAGE);
sURLMatcher.addURI("mms", "drm/#", MMS_DRM_STORAGE_ID);
+ sURLMatcher.addURI("mms", "threads", MMS_THREADS);
}
private SQLiteOpenHelper mOpenHelper;
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index 05de829..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 = 44;
+ 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.
@@ -345,7 +353,9 @@
Mms.CONTENT_CLASS + " INTEGER," +
Mms.RESPONSE_TEXT + " TEXT," +
Mms.DELIVERY_TIME + " INTEGER," +
- Mms.DELIVERY_REPORT + " INTEGER);");
+ Mms.DELIVERY_REPORT + " INTEGER," +
+ Mms.LOCKED + " INTEGER DEFAULT 0" +
+ ");");
db.execSQL("CREATE TABLE " + MmsProvider.TABLE_ADDR + " (" +
Addr._ID + " INTEGER PRIMARY KEY," +
@@ -368,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);");
@@ -404,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);
@@ -431,7 +442,9 @@
"reply_path_present INTEGER," +
"subject TEXT," +
"body TEXT," +
- "service_center TEXT);");
+ "service_center TEXT," +
+ "locked INTEGER DEFAULT 0" +
+ ");");
/**
* This table is used by the SMS dispatcher to hold
@@ -738,7 +751,7 @@
if (currentVersion <= 41) {
return;
}
-
+
db.beginTransaction();
try {
upgradeDatabaseToVersion42(db);
@@ -754,7 +767,7 @@
if (currentVersion <= 42) {
return;
}
-
+
db.beginTransaction();
try {
upgradeDatabaseToVersion43(db);
@@ -782,8 +795,41 @@
db.endTransaction();
}
return;
+ case 44:
+ if (currentVersion <= 44) {
+ return;
+ }
+
+ db.beginTransaction();
+ try {
+ upgradeDatabaseToVersion45(db);
+ db.setTransactionSuccessful();
+ } catch (Throwable ex) {
+ Log.e(TAG, ex.getMessage(), ex);
+ break;
+ } finally {
+ 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);
@@ -821,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");
@@ -855,4 +901,70 @@
// add the update trigger for keeping the threads up to date.
db.execSQL(PART_UPDATE_THREADS_ON_UPDATE_TRIGGER);
}
+
+ private void upgradeDatabaseToVersion45(SQLiteDatabase db) {
+ // Add 'locked' column to sms table.
+ db.execSQL("ALTER TABLE sms ADD COLUMN locked INTEGER DEFAULT 0");
+
+ // Add 'locked' column to pdu table.
+ 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 81de1af..d55c4fe 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -16,7 +16,11 @@
package com.android.providers.telephony;
-import com.google.android.mms.pdu.PduHeaders;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import android.content.ContentProvider;
import android.content.ContentValues;
@@ -40,10 +44,7 @@
import android.text.TextUtils;
import android.util.Log;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import com.google.android.mms.pdu.PduHeaders;
/**
* This class provides the ability to query the MMS and SMS databases
@@ -87,6 +88,8 @@
private static final int URI_NOTIFICATIONS = 10;
private static final int URI_OBSOLETE_THREADS = 11;
private static final int URI_DRAFT = 12;
+ private static final int URI_CANONICAL_ADDRESSES = 13;
+ private static final int URI_SEARCH = 14;
/**
* the name of the table that is used to store the queue of
@@ -100,7 +103,7 @@
// These are the columns that appear in both the MMS ("pdu") and
// SMS ("sms") message tables.
private static final String[] MMS_SMS_COLUMNS =
- { BaseColumns._ID, Mms.DATE, Mms.READ, Mms.THREAD_ID };
+ { BaseColumns._ID, Mms.DATE, Mms.READ, Mms.THREAD_ID, Mms.LOCKED };
// These are the columns that appear only in the MMS message
// table.
@@ -188,6 +191,11 @@
// Use this pattern to query the canonical address by given ID.
URI_MATCHER.addURI(AUTHORITY, "canonical-address/#", URI_CANONICAL_ADDRESS);
+ // 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:
// "protocol" and "message." For example:
// content://mms-sms/pending?
@@ -278,6 +286,56 @@
null, null, sortOrder);
break;
}
+ case URI_CANONICAL_ADDRESSES:
+ cursor = db.query("canonical_addresses",
+ new String[] {"_id", "address"}, selection, selectionArgs,
+ null, null, sortOrder);
+ break;
+ case URI_SEARCH:
+ if ( sortOrder != null
+ || selection != null
+ || selectionArgs != null
+ || projection != null) {
+ throw new IllegalArgumentException(
+ "do not specify sortOrder, selection, selectionArgs, or projection" +
+ "with this query");
+ }
+
+ // 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.
+
+ 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(
+ "%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: {
String protoName = uri.getQueryParameter("protocol");
String msgId = uri.getQueryParameter("message");
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index 39d7013..cd2182c 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -394,6 +394,8 @@
return null;
}
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
if (table.equals(TABLE_SMS)) {
boolean addDate = false;
boolean addType = false;
@@ -432,6 +434,17 @@
getContext(), address));
}
+ // If this message is going in as a draft, it should replace any
+ // other draft messages in the thread. Just delete all draft
+ // messages with this thread ID. We could add an OR REPLACE to
+ // the insert below, but we'd have to query to find the old _id
+ // to produce a conflict anyway.
+ if (values.getAsInteger(Sms.TYPE) == Sms.MESSAGE_TYPE_DRAFT) {
+ db.delete(TABLE_SMS, "thread_id=? AND type=?",
+ new String[] { values.getAsString(Sms.THREAD_ID),
+ Integer.toString(Sms.MESSAGE_TYPE_DRAFT) });
+ }
+
if (type == Sms.MESSAGE_TYPE_INBOX) {
// Look up the person if not already filled in.
if ((values.getAsLong(Sms.PERSON) == null)
@@ -462,7 +475,6 @@
}
}
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
rowID = db.insert(table, "body", values);
if (rowID > 0) {
Uri uri = Uri.parse("content://" + table + "/" + rowID);
@@ -625,7 +637,7 @@
where = DatabaseUtils.concatenateWhere(where, extraWhere);
count = db.update(table, values, where, whereArgs);
-
+
if (count > 0) {
notifyChange(url);
}
@@ -712,7 +724,11 @@
sURLMatcher.addURI("sms", "sim/#", SMS_ICC);
sConversationProjectionMap.put(Sms.Conversations.SNIPPET,
- "body AS 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);
}
}