I can't remove the particular message on Android 8.0
private void foo() {
Uri dummySms = insertDummySms(context, threadId);
removeDummySms(context, dummySms);
}
private Uri insertDummySms(Context context, long threadId) {
ContentValues values = new ContentValues();
values.put("thread_id", threadId);
values.put("body", "Dummy SMS body.");
Uri insert = context.getContentResolver().insert(Uri.parse("content://sms/sent"), values);
Log.i(TAG, "insertDummySms: " + insert);
return insert;
}
private void removeDummySms(Context context, Uri uri) {
Log.i(TAG, "removeDummySms: START: " + uri.toString() + " :: " + context.getContentResolver().getType(uri));
context.getContentResolver().delete(uri, null, null);
Log.i(TAG, "removeDummySms: END!!!");
}
When I run foo()
method the logs shows:
I/Test: insertDummySms: content://sms/sent//8
I/Test: removeDummySms: START: content://sms/sent//8 :: vnd.android.cursor.item/sms,
then it crashes:
java.lang.IllegalArgumentException: Unknown URL
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:165)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
The same code works on android 6.x and 7.x
The funny thing: If I remove every sms like this:
context.getContentResolver().delete(uri, null, null); // where uri is equal: content://sms
it works like a charm.
Any ideas why it fails?
Currently I have only one phone with Android O, so I don't know if it crashes on every Android O device. I have Nokia TA-1004
A relatively recent change to the SmsProvider
class kinda broke the expected functionality there. The Uri
returned from the Provider's insert()
call was previously figured very simply as:
Uri uri = Uri.parse("content://" + table + "/" + rowID);
where rowID
is the return from an insert()
call on an SQLiteDatabase
.
For the insertion of a regular, complete SMS message, as in your case, table
is "sms"
. However, there are several other tables, mostly used by the system and default messaging app, for which the same insert()
method is used; e.g., "raw"
, "attachments"
, etc. Inserts on these tables resulted in invalid authorities on the returned Uri
s; e.g. content://raw/123
.
Newer versions now throw Exceptions on invalid authorities, rather than failing silently, so the Uri
construction was altered to:
Uri uri;
if (table == TABLE_SMS) {
uri = Uri.withAppendedPath(url, "/" + rowID);
} else {
uri = Uri.withAppendedPath(url, "/" + table + "/" + rowID );
}
and later to:
Uri uri = Uri.withAppendedPath(url, String.valueOf(rowID));
to address issues in the first.
In both revisions, url
is the one you passed in the ContentResolver#insert()
call. For the insert()
call in your snippet, this ends up returning a URI similar to content://sms/sent/123
, wherein lies the issue. When that is passed in a delete()
call, it doesn't match any URIs that the Provider's corresponding method deems valid, so it throws an IllegalArgumentException
of Unknown URL
.
To fix this, you could construct your own valid URI from the return, as the ID is really all you need to know. For example:
Uri del = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, dummySms.getLastPathSegment());
However, it is arguably incorrect to do an insert on "content://sms/sent"
(Sms.Sent.CONTENT_URI
) to begin with. That URI, and the other similar "box" URIs - i.e., Sms.Inbox.CONTENT_URI
, Sms.Drafts.CONTENT_URI
, etc. - are meant to be used with queries, rather than write operations.
You should instead insert on the base Sms.CONTENT_URI
("content://sms"
), and set the TYPE
to MESSAGE_TYPE_SENT
in the ContentValues
. For example, the recommended fix:
ContentValues cv = new ContentValues();
cv.put(Telephony.Sms.ADDRESS, ...);
cv.put(Telephony.Sms.BODY, ...);
cv.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_SENT);
Uri uri = getContentResolver().insert(Telephony.Sms.CONTENT_URI, cv);
This will result in a Uri
that SmsProvider
's delete()
method will recognize, and your delete should succeed as before.