diff --git a/src/com/android/messaging/datamodel/action/DownloadMmsAction.java b/src/com/android/messaging/datamodel/action/DownloadMmsAction.java index 7a8c907..2852a61 100644 --- a/src/com/android/messaging/datamodel/action/DownloadMmsAction.java +++ b/src/com/android/messaging/datamodel/action/DownloadMmsAction.java @@ -82,6 +82,7 @@ public class DownloadMmsAction extends Action implements Parcelable { private static final String KEY_SUB_PHONE_NUMBER = "sub_phone_number"; private static final String KEY_AUTO_DOWNLOAD = "auto_download"; private static final String KEY_FAILURE_STATUS = "failure_status"; + private static final String KEY_EXPIRY = "expiry"; // Values we attach to the pending intent that's fired when the message is downloaded. // Only applicable when downloading via the platform APIs on L+. @@ -97,6 +98,7 @@ public class DownloadMmsAction extends Action implements Parcelable { public static final String EXTRA_CONVERSATION_ID = "conversation_id"; public static final String EXTRA_PARTICIPANT_ID = "participant_id"; public static final String EXTRA_STATUS_IF_FAILED = "status_if_failed"; + public static final String EXTRA_EXPIRY = "expiry"; private DownloadMmsAction() { super(); @@ -130,6 +132,7 @@ public class DownloadMmsAction extends Action implements Parcelable { actionParameters.putString(KEY_TRANSACTION_ID, message.getMmsTransactionId()); actionParameters.putParcelable(KEY_NOTIFICATION_URI, notificationUri); actionParameters.putBoolean(KEY_AUTO_DOWNLOAD, isAutoDownload(status)); + actionParameters.putLong(KEY_EXPIRY, message.getMmsExpiry()); final long now = System.currentTimeMillis(); if (message.getInDownloadWindow(now)) { @@ -239,6 +242,7 @@ public class DownloadMmsAction extends Action implements Parcelable { final String conversationId = actionParameters.getString(KEY_CONVERSATION_ID); final String participantId = actionParameters.getString(KEY_PARTICIPANT_ID); final int statusIfFailed = actionParameters.getInt(KEY_FAILURE_STATUS); + final long expiry = actionParameters.getLong(KEY_EXPIRY); final long receivedTimestampRoundedToSecond = 1000 * ((System.currentTimeMillis() + 500) / 1000); @@ -256,7 +260,7 @@ public class DownloadMmsAction extends Action implements Parcelable { // Start the download final MmsUtils.StatusPlusUri status = MmsUtils.downloadMmsMessage(context, notificationUri, subId, subPhoneNumber, transactionId, contentLocation, - autoDownload, receivedTimestampRoundedToSecond / 1000L, extras); + autoDownload, receivedTimestampRoundedToSecond / 1000L, expiry / 1000L, extras); if (status == MmsUtils.STATUS_PENDING) { // Async download; no status yet if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { diff --git a/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java b/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java index 757ea05..07e2cfb 100644 --- a/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java +++ b/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java @@ -78,6 +78,7 @@ public class ProcessDownloadedMmsAction extends Action { private static final String KEY_CONTENT_LOCATION = "content_location"; private static final String KEY_AUTO_DOWNLOAD = "auto_download"; private static final String KEY_RECEIVED_TIMESTAMP = "received_timestamp"; + private static final String KEY_EXPIRY = "expiry"; // Set when message downloaded by us (legacy) private static final String KEY_STATUS = "status"; @@ -130,6 +131,7 @@ public class ProcessDownloadedMmsAction extends Action { params.putString(KEY_PARTICIPANT_ID, participantId); params.putInt(KEY_STATUS_IF_FAILED, extras.getInt(DownloadMmsAction.EXTRA_STATUS_IF_FAILED)); + params.putLong(KEY_EXPIRY, extras.getLong(DownloadMmsAction.EXTRA_EXPIRY)); action.start(); } @@ -279,6 +281,7 @@ public class ProcessDownloadedMmsAction extends Action { KEY_AUTO_DOWNLOAD); final long receivedTimestampInSeconds = actionParameters.getLong(KEY_RECEIVED_TIMESTAMP); + final long expiry = actionParameters.getLong(KEY_EXPIRY); // Inform sync we're adding a message to telephony final SyncManager syncManager = DataModel.get().getSyncManager(); @@ -288,7 +291,7 @@ public class ProcessDownloadedMmsAction extends Action { MmsUtils.insertDownloadedMessageAndSendResponse(context, notificationUri, subId, subPhoneNumber, transactionId, contentLocation, autoDownload, receivedTimestampInSeconds, - retrieveConf); + expiry, retrieveConf); status = result.status; rawStatus = result.rawStatus; mmsUri = result.uri; diff --git a/src/com/android/messaging/datamodel/data/MessageData.java b/src/com/android/messaging/datamodel/data/MessageData.java index a3698a9..cb5311e 100644 --- a/src/com/android/messaging/datamodel/data/MessageData.java +++ b/src/com/android/messaging/datamodel/data/MessageData.java @@ -540,6 +540,10 @@ public class MessageData implements Parcelable { return mSeen; } + public final long getMmsExpiry() { + return mMmsExpiry; + } + /** * For incoming MMS messages this returns the retrieve-status value * For sent MMS messages this returns the response-status value diff --git a/src/com/android/messaging/sms/MmsUtils.java b/src/com/android/messaging/sms/MmsUtils.java index 91631b3..fbecd8b 100644 --- a/src/com/android/messaging/sms/MmsUtils.java +++ b/src/com/android/messaging/sms/MmsUtils.java @@ -952,7 +952,7 @@ public class MmsUtils { // Persist a received MMS message in telephony public static Uri insertReceivedMmsMessage(final Context context, final RetrieveConf retrieveConf, final int subId, final String subPhoneNumber, - final long receivedTimestampInSeconds, final String contentLocation) { + final long receivedTimestampInSeconds, final long expiry, final String transactionId) { final PduPersister persister = PduPersister.getPduPersister(context); Uri uri = null; try { @@ -963,12 +963,13 @@ public class MmsUtils { subPhoneNumber, null/*preOpenedFiles*/); - final ContentValues values = new ContentValues(2); + final ContentValues values = new ContentValues(3); // Update mms table with local time instead of PDU time values.put(Mms.DATE, receivedTimestampInSeconds); - // Also update the content location field from NotificationInd so that - // wap push dedup would work even after the wap push is deleted - values.put(Mms.CONTENT_LOCATION, contentLocation); + // Also update the transaction id and the expiry from NotificationInd so that + // wap push dedup would work even after the wap push is deleted. + values.put(Mms.TRANSACTION_ID, transactionId); + values.put(Mms.EXPIRY, expiry); SqliteWrapper.update(context, context.getContentResolver(), uri, values, null, null); if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { LogUtil.d(TAG, "MmsUtils: Inserted MMS message into telephony, uri: " + uri); @@ -1843,7 +1844,7 @@ public class MmsUtils { public static StatusPlusUri downloadMmsMessage(final Context context, final Uri notificationUri, final int subId, final String subPhoneNumber, final String transactionId, final String contentLocation, final boolean autoDownload, - final long receivedTimestampInSeconds, Bundle extras) { + final long receivedTimestampInSeconds, final long expiry, Bundle extras) { if (TextUtils.isEmpty(contentLocation)) { LogUtil.e(TAG, "MmsUtils: Download from empty content location URL"); return new StatusPlusUri( @@ -1894,13 +1895,14 @@ public class MmsUtils { extras.putBoolean(DownloadMmsAction.EXTRA_AUTO_DOWNLOAD, autoDownload); extras.putLong(DownloadMmsAction.EXTRA_RECEIVED_TIMESTAMP, receivedTimestampInSeconds); + extras.putLong(DownloadMmsAction.EXTRA_EXPIRY, expiry); MmsSender.downloadMms(context, subId, contentLocation, extras); return STATUS_PENDING; // Download happens asynchronously; no status to return } return insertDownloadedMessageAndSendResponse(context, notificationUri, subId, subPhoneNumber, transactionId, contentLocation, autoDownload, - receivedTimestampInSeconds, retrieveConf); + receivedTimestampInSeconds, expiry, retrieveConf); } catch (final MmsFailureException e) { LogUtil.e(TAG, "MmsUtils: failed to download message " + notificationUri, e); @@ -1915,7 +1917,7 @@ public class MmsUtils { final Uri notificationUri, final int subId, final String subPhoneNumber, final String transactionId, final String contentLocation, final boolean autoDownload, final long receivedTimestampInSeconds, - final RetrieveConf retrieveConf) { + final long expiry, final RetrieveConf retrieveConf) { final byte[] notificationTransactionId = stringToBytes(transactionId, "UTF-8"); Uri messageUri = null; int status = MMS_REQUEST_MANUAL_RETRY; @@ -1954,7 +1956,7 @@ public class MmsUtils { // Insert downloaded message into telephony final Uri inboxUri = MmsUtils.insertReceivedMmsMessage(context, retrieveConf, subId, - subPhoneNumber, receivedTimestampInSeconds, contentLocation); + subPhoneNumber, receivedTimestampInSeconds, expiry, transactionId); messageUri = ContentUris.withAppendedId(Mms.CONTENT_URI, ContentUris.parseId(inboxUri)); } else if (status == MMS_REQUEST_AUTO_RETRY) { // For a retry do nothing @@ -2171,57 +2173,28 @@ public class MmsUtils { uri, values, null, null); } - // Selection for new dedup algorithm: - // ((m_type<>130) OR (exp>NOW)) AND (date>NOW-7d) AND (dateNOW)) AND (t_id=xxxxxx) + // i.e. If it is NotificationInd or RetrieveConf and not expired + // AND transaction id is the input id private static final String DUP_NOTIFICATION_QUERY_SELECTION = - "((" + Mms.MESSAGE_TYPE + "<>?) OR (" + Mms.EXPIRY + ">?)) AND (" - + Mms.DATE + ">?) AND (" + Mms.DATE + "?) AND (" + Mms.TRANSACTION_ID + "=?)"; private static final int MAX_RETURN = 32; private static String[] getDupNotifications(final Context context, final NotificationInd nInd) { - final byte[] rawLocation = nInd.getContentLocation(); - if (rawLocation != null) { - final String location = new String(rawLocation); - // We can not be sure if the content location of an MMS is globally and historically - // unique. So we limit the dedup time within the last 7 days - // (or configured by gservices remotely). If the same content location shows up after - // that, we will download regardless. Duplicated message is better than no message. - String selection; - String[] selectionArgs; - final long timeLimit = BugleGservices.get().getLong( - BugleGservicesKeys.MMS_WAP_PUSH_DEDUP_TIME_LIMIT_SECS, - BugleGservicesKeys.MMS_WAP_PUSH_DEDUP_TIME_LIMIT_SECS_DEFAULT); - if (timeLimit > 0) { - // New dedup algorithm - selection = DUP_NOTIFICATION_QUERY_SELECTION; - final long nowSecs = System.currentTimeMillis() / 1000; - final long timeLowerBoundSecs = nowSecs - timeLimit; - // Need upper bound to protect against clock change so that a message has a time - // stamp in the future - final long timeUpperBoundSecs = nowSecs + timeLimit; - selectionArgs = new String[] { - Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND), - Long.toString(nowSecs), - Long.toString(timeLowerBoundSecs), - Long.toString(timeUpperBoundSecs), - location - }; - } else { - // If time limit is 0, we revert back to old behavior in case the new - // dedup algorithm behaves badly - selection = DUP_NOTIFICATION_QUERY_SELECTION_OLD; - selectionArgs = new String[] { - Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND), - location - }; - } + final byte[] rawTransactionId = nInd.getTransactionId(); + if (rawTransactionId != null) { + // dedup algorithm + String selection = DUP_NOTIFICATION_QUERY_SELECTION; + final long nowSecs = System.currentTimeMillis() / 1000; + String[] selectionArgs = new String[] { + Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND), + Integer.toString(PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF), + Long.toString(nowSecs), + new String(rawTransactionId) + }; + Cursor cursor = null; try { cursor = SqliteWrapper.query( @@ -2358,7 +2331,7 @@ public class MmsUtils { } else { LogUtil.w(TAG, "Received WAP Push is a dup: " + Joiner.on(',').join(dups)); if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { - LogUtil.w(TAG, "Dup WAP Push url=" + new String(nInd.getContentLocation())); + LogUtil.w(TAG, "Dup Transaction Id=" + new String(nInd.getTransactionId())); } } break;