1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.messaging.datamodel.action; 18 19 import android.app.Activity; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.net.Uri; 23 import android.os.Bundle; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.provider.Telephony.Mms; 27 import android.telephony.SmsManager; 28 import android.text.TextUtils; 29 30 import com.android.messaging.Factory; 31 import com.android.messaging.datamodel.BugleDatabaseOperations; 32 import com.android.messaging.datamodel.BugleNotifications; 33 import com.android.messaging.datamodel.DataModel; 34 import com.android.messaging.datamodel.DataModelException; 35 import com.android.messaging.datamodel.DatabaseWrapper; 36 import com.android.messaging.datamodel.MessagingContentProvider; 37 import com.android.messaging.datamodel.MmsFileProvider; 38 import com.android.messaging.datamodel.SyncManager; 39 import com.android.messaging.datamodel.data.MessageData; 40 import com.android.messaging.datamodel.data.ParticipantData; 41 import com.android.messaging.mmslib.SqliteWrapper; 42 import com.android.messaging.mmslib.pdu.PduHeaders; 43 import com.android.messaging.mmslib.pdu.RetrieveConf; 44 import com.android.messaging.sms.DatabaseMessages; 45 import com.android.messaging.sms.MmsSender; 46 import com.android.messaging.sms.MmsUtils; 47 import com.android.messaging.util.Assert; 48 import com.android.messaging.util.LogUtil; 49 import com.google.common.io.Files; 50 51 import java.io.File; 52 import java.io.FileNotFoundException; 53 import java.io.IOException; 54 import java.util.List; 55 56 /** 57 * Processes an MMS message after it has been downloaded. 58 * NOTE: This action must queue a ProcessPendingMessagesAction when it is done (success or failure). 59 */ 60 public class ProcessDownloadedMmsAction extends Action { 61 private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG; 62 63 // Always set when message downloaded 64 private static final String KEY_DOWNLOADED_BY_PLATFORM = "downloaded_by_platform"; 65 private static final String KEY_MESSAGE_ID = "message_id"; 66 private static final String KEY_NOTIFICATION_URI = "notification_uri"; 67 private static final String KEY_CONVERSATION_ID = "conversation_id"; 68 private static final String KEY_PARTICIPANT_ID = "participant_id"; 69 private static final String KEY_STATUS_IF_FAILED = "status_if_failed"; 70 71 // Set when message downloaded by platform (L+) 72 private static final String KEY_RESULT_CODE = "result_code"; 73 private static final String KEY_HTTP_STATUS_CODE = "http_status_code"; 74 private static final String KEY_CONTENT_URI = "content_uri"; 75 private static final String KEY_SUB_ID = "sub_id"; 76 private static final String KEY_SUB_PHONE_NUMBER = "sub_phone_number"; 77 private static final String KEY_TRANSACTION_ID = "transaction_id"; 78 private static final String KEY_CONTENT_LOCATION = "content_location"; 79 private static final String KEY_AUTO_DOWNLOAD = "auto_download"; 80 private static final String KEY_RECEIVED_TIMESTAMP = "received_timestamp"; 81 private static final String KEY_EXPIRY = "expiry"; 82 83 // Set when message downloaded by us (legacy) 84 private static final String KEY_STATUS = "status"; 85 private static final String KEY_RAW_STATUS = "raw_status"; 86 private static final String KEY_MMS_URI = "mms_uri"; 87 88 // Used to send a deferred response in response to auto-download failure 89 private static final String KEY_SEND_DEFERRED_RESP_STATUS = "send_deferred_resp_status"; 90 91 // Results passed from background worker to processCompletion 92 private static final String BUNDLE_REQUEST_STATUS = "request_status"; 93 private static final String BUNDLE_RAW_TELEPHONY_STATUS = "raw_status"; 94 private static final String BUNDLE_MMS_URI = "mms_uri"; 95 96 // This is called when MMS lib API returns via PendingIntent processMessageDownloaded(final int resultCode, final Bundle extras)97 public static void processMessageDownloaded(final int resultCode, final Bundle extras) { 98 final String messageId = extras.getString(DownloadMmsAction.EXTRA_MESSAGE_ID); 99 final Uri contentUri = extras.getParcelable(DownloadMmsAction.EXTRA_CONTENT_URI); 100 final Uri notificationUri = extras.getParcelable(DownloadMmsAction.EXTRA_NOTIFICATION_URI); 101 final String conversationId = extras.getString(DownloadMmsAction.EXTRA_CONVERSATION_ID); 102 final String participantId = extras.getString(DownloadMmsAction.EXTRA_PARTICIPANT_ID); 103 Assert.notNull(messageId); 104 Assert.notNull(contentUri); 105 Assert.notNull(notificationUri); 106 Assert.notNull(conversationId); 107 Assert.notNull(participantId); 108 109 final ProcessDownloadedMmsAction action = new ProcessDownloadedMmsAction(); 110 final Bundle params = action.actionParameters; 111 params.putBoolean(KEY_DOWNLOADED_BY_PLATFORM, true); 112 params.putString(KEY_MESSAGE_ID, messageId); 113 params.putInt(KEY_RESULT_CODE, resultCode); 114 params.putInt(KEY_HTTP_STATUS_CODE, 115 extras.getInt(SmsManager.EXTRA_MMS_HTTP_STATUS, 0)); 116 params.putParcelable(KEY_CONTENT_URI, contentUri); 117 params.putParcelable(KEY_NOTIFICATION_URI, notificationUri); 118 params.putInt(KEY_SUB_ID, 119 extras.getInt(DownloadMmsAction.EXTRA_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID)); 120 params.putString(KEY_SUB_PHONE_NUMBER, 121 extras.getString(DownloadMmsAction.EXTRA_SUB_PHONE_NUMBER)); 122 params.putString(KEY_TRANSACTION_ID, 123 extras.getString(DownloadMmsAction.EXTRA_TRANSACTION_ID)); 124 params.putString(KEY_CONTENT_LOCATION, 125 extras.getString(DownloadMmsAction.EXTRA_CONTENT_LOCATION)); 126 params.putBoolean(KEY_AUTO_DOWNLOAD, 127 extras.getBoolean(DownloadMmsAction.EXTRA_AUTO_DOWNLOAD)); 128 params.putLong(KEY_RECEIVED_TIMESTAMP, 129 extras.getLong(DownloadMmsAction.EXTRA_RECEIVED_TIMESTAMP)); 130 params.putString(KEY_CONVERSATION_ID, conversationId); 131 params.putString(KEY_PARTICIPANT_ID, participantId); 132 params.putInt(KEY_STATUS_IF_FAILED, 133 extras.getInt(DownloadMmsAction.EXTRA_STATUS_IF_FAILED)); 134 params.putLong(KEY_EXPIRY, extras.getLong(DownloadMmsAction.EXTRA_EXPIRY)); 135 action.start(); 136 } 137 138 // This is called for fast failing downloading (due to airplane mode or mobile data ) processMessageDownloadFastFailed(final String messageId, final Uri notificationUri, final String conversationId, final String participantId, final String contentLocation, final int subId, final String subPhoneNumber, final int statusIfFailed, final boolean autoDownload, final String transactionId, final int resultCode)139 public static void processMessageDownloadFastFailed(final String messageId, 140 final Uri notificationUri, final String conversationId, final String participantId, 141 final String contentLocation, final int subId, final String subPhoneNumber, 142 final int statusIfFailed, final boolean autoDownload, final String transactionId, 143 final int resultCode) { 144 Assert.notNull(messageId); 145 Assert.notNull(notificationUri); 146 Assert.notNull(conversationId); 147 Assert.notNull(participantId); 148 149 final ProcessDownloadedMmsAction action = new ProcessDownloadedMmsAction(); 150 final Bundle params = action.actionParameters; 151 params.putBoolean(KEY_DOWNLOADED_BY_PLATFORM, true); 152 params.putString(KEY_MESSAGE_ID, messageId); 153 params.putInt(KEY_RESULT_CODE, resultCode); 154 params.putParcelable(KEY_NOTIFICATION_URI, notificationUri); 155 params.putInt(KEY_SUB_ID, subId); 156 params.putString(KEY_SUB_PHONE_NUMBER, subPhoneNumber); 157 params.putString(KEY_CONTENT_LOCATION, contentLocation); 158 params.putBoolean(KEY_AUTO_DOWNLOAD, autoDownload); 159 params.putString(KEY_CONVERSATION_ID, conversationId); 160 params.putString(KEY_PARTICIPANT_ID, participantId); 161 params.putInt(KEY_STATUS_IF_FAILED, statusIfFailed); 162 params.putString(KEY_TRANSACTION_ID, transactionId); 163 action.start(); 164 } 165 processDownloadActionFailure(final String messageId, final int status, final int rawStatus, final String conversationId, final String participantId, final int statusIfFailed, final int subId, final String transactionId)166 public static void processDownloadActionFailure(final String messageId, final int status, 167 final int rawStatus, final String conversationId, final String participantId, 168 final int statusIfFailed, final int subId, final String transactionId) { 169 Assert.notNull(messageId); 170 Assert.notNull(conversationId); 171 Assert.notNull(participantId); 172 173 final ProcessDownloadedMmsAction action = new ProcessDownloadedMmsAction(); 174 final Bundle params = action.actionParameters; 175 params.putBoolean(KEY_DOWNLOADED_BY_PLATFORM, false); 176 params.putString(KEY_MESSAGE_ID, messageId); 177 params.putInt(KEY_STATUS, status); 178 params.putInt(KEY_RAW_STATUS, rawStatus); 179 params.putString(KEY_CONVERSATION_ID, conversationId); 180 params.putString(KEY_PARTICIPANT_ID, participantId); 181 params.putInt(KEY_STATUS_IF_FAILED, statusIfFailed); 182 params.putInt(KEY_SUB_ID, subId); 183 params.putString(KEY_TRANSACTION_ID, transactionId); 184 action.start(); 185 } 186 sendDeferredRespStatus(final String messageId, final String transactionId, final String contentLocation, final int subId)187 public static void sendDeferredRespStatus(final String messageId, final String transactionId, 188 final String contentLocation, final int subId) { 189 final ProcessDownloadedMmsAction action = new ProcessDownloadedMmsAction(); 190 final Bundle params = action.actionParameters; 191 params.putString(KEY_MESSAGE_ID, messageId); 192 params.putString(KEY_TRANSACTION_ID, transactionId); 193 params.putString(KEY_CONTENT_LOCATION, contentLocation); 194 params.putBoolean(KEY_SEND_DEFERRED_RESP_STATUS, true); 195 params.putInt(KEY_SUB_ID, subId); 196 action.start(); 197 } 198 ProcessDownloadedMmsAction()199 private ProcessDownloadedMmsAction() { 200 // Callers must use one of the static methods above 201 } 202 203 @Override executeAction()204 protected Object executeAction() { 205 // Fire up the background worker 206 requestBackgroundWork(); 207 return null; 208 } 209 210 @Override doBackgroundWork()211 protected Bundle doBackgroundWork() throws DataModelException { 212 final Context context = Factory.get().getApplicationContext(); 213 final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID); 214 final String messageId = actionParameters.getString(KEY_MESSAGE_ID); 215 final String transactionId = actionParameters.getString(KEY_TRANSACTION_ID); 216 final String contentLocation = actionParameters.getString(KEY_CONTENT_LOCATION); 217 final boolean sendDeferredRespStatus = 218 actionParameters.getBoolean(KEY_SEND_DEFERRED_RESP_STATUS, false); 219 220 // Send a response indicating that auto-download failed 221 if (sendDeferredRespStatus) { 222 if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { 223 LogUtil.v(TAG, "DownloadMmsAction: Auto-download of message " + messageId 224 + " failed; sending DEFERRED NotifyRespInd"); 225 } 226 MmsUtils.sendNotifyResponseForMmsDownload( 227 context, 228 subId, 229 MmsUtils.stringToBytes(transactionId, "UTF-8"), 230 contentLocation, 231 PduHeaders.STATUS_DEFERRED); 232 return null; 233 } 234 235 // Processing a real MMS download 236 final boolean downloadedByPlatform = actionParameters.getBoolean( 237 KEY_DOWNLOADED_BY_PLATFORM); 238 239 final int status; 240 int rawStatus = MmsUtils.PDU_HEADER_VALUE_UNDEFINED; 241 Uri mmsUri = null; 242 243 if (downloadedByPlatform) { 244 final int resultCode = actionParameters.getInt(KEY_RESULT_CODE); 245 if (resultCode == Activity.RESULT_OK) { 246 final Uri contentUri = actionParameters.getParcelable(KEY_CONTENT_URI); 247 final File downloadedFile = MmsFileProvider.getFile(contentUri); 248 byte[] downloadedData = null; 249 try { 250 downloadedData = Files.toByteArray(downloadedFile); 251 } catch (final FileNotFoundException e) { 252 LogUtil.e(TAG, "ProcessDownloadedMmsAction: MMS download file not found: " 253 + downloadedFile.getAbsolutePath()); 254 } catch (final IOException e) { 255 LogUtil.e(TAG, "ProcessDownloadedMmsAction: Error reading MMS download file: " 256 + downloadedFile.getAbsolutePath(), e); 257 } 258 259 // Can delete the temp file now 260 if (downloadedFile.exists()) { 261 downloadedFile.delete(); 262 if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 263 LogUtil.d(TAG, "ProcessDownloadedMmsAction: Deleted temp file with " 264 + "downloaded MMS pdu: " + downloadedFile.getAbsolutePath()); 265 } 266 } 267 268 if (downloadedData != null) { 269 final RetrieveConf retrieveConf = 270 MmsSender.parseRetrieveConf(downloadedData, subId); 271 if (MmsUtils.isDumpMmsEnabled()) { 272 MmsUtils.dumpPdu(downloadedData, retrieveConf); 273 } 274 if (retrieveConf != null) { 275 // Insert the downloaded MMS into telephony 276 final Uri notificationUri = actionParameters.getParcelable( 277 KEY_NOTIFICATION_URI); 278 final String subPhoneNumber = actionParameters.getString( 279 KEY_SUB_PHONE_NUMBER); 280 final boolean autoDownload = actionParameters.getBoolean( 281 KEY_AUTO_DOWNLOAD); 282 final long receivedTimestampInSeconds = 283 actionParameters.getLong(KEY_RECEIVED_TIMESTAMP); 284 final long expiry = actionParameters.getLong(KEY_EXPIRY); 285 286 // Inform sync we're adding a message to telephony 287 final SyncManager syncManager = DataModel.get().getSyncManager(); 288 syncManager.onNewMessageInserted(receivedTimestampInSeconds * 1000L); 289 290 final MmsUtils.StatusPlusUri result = 291 MmsUtils.insertDownloadedMessageAndSendResponse(context, 292 notificationUri, subId, subPhoneNumber, transactionId, 293 contentLocation, autoDownload, receivedTimestampInSeconds, 294 expiry, retrieveConf); 295 status = result.status; 296 rawStatus = result.rawStatus; 297 mmsUri = result.uri; 298 } else { 299 // Invalid response PDU 300 status = MmsUtils.MMS_REQUEST_MANUAL_RETRY; 301 } 302 } else { 303 // Failed to read download file 304 status = MmsUtils.MMS_REQUEST_MANUAL_RETRY; 305 } 306 } else { 307 LogUtil.w(TAG, "ProcessDownloadedMmsAction: Platform returned error resultCode: " 308 + resultCode); 309 final int httpStatusCode = actionParameters.getInt(KEY_HTTP_STATUS_CODE); 310 status = MmsSender.getErrorResultStatus(resultCode, httpStatusCode); 311 } 312 } else { 313 // Message was already processed by the internal API, or the download action failed. 314 // In either case, we just need to copy the status to the response bundle. 315 status = actionParameters.getInt(KEY_STATUS); 316 rawStatus = actionParameters.getInt(KEY_RAW_STATUS); 317 mmsUri = actionParameters.getParcelable(KEY_MMS_URI); 318 } 319 320 final Bundle response = new Bundle(); 321 response.putInt(BUNDLE_REQUEST_STATUS, status); 322 response.putInt(BUNDLE_RAW_TELEPHONY_STATUS, rawStatus); 323 response.putParcelable(BUNDLE_MMS_URI, mmsUri); 324 return response; 325 } 326 327 @Override processBackgroundResponse(final Bundle response)328 protected Object processBackgroundResponse(final Bundle response) { 329 if (response == null) { 330 // No message download to process; doBackgroundWork sent a notify deferred response 331 Assert.isTrue(actionParameters.getBoolean(KEY_SEND_DEFERRED_RESP_STATUS)); 332 return null; 333 } 334 335 final int status = response.getInt(BUNDLE_REQUEST_STATUS); 336 final int rawStatus = response.getInt(BUNDLE_RAW_TELEPHONY_STATUS); 337 final Uri messageUri = response.getParcelable(BUNDLE_MMS_URI); 338 final boolean autoDownload = actionParameters.getBoolean(KEY_AUTO_DOWNLOAD); 339 final String messageId = actionParameters.getString(KEY_MESSAGE_ID); 340 341 // Do post-processing on downloaded message 342 final MessageData message = processResult(status, rawStatus, messageUri); 343 344 final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID); 345 // If we were trying to auto-download but have failed need to send the deferred response 346 if (autoDownload && message == null && status == MmsUtils.MMS_REQUEST_MANUAL_RETRY) { 347 final String transactionId = actionParameters.getString(KEY_TRANSACTION_ID); 348 final String contentLocation = actionParameters.getString(KEY_CONTENT_LOCATION); 349 sendDeferredRespStatus(messageId, transactionId, contentLocation, subId); 350 } 351 352 if (autoDownload) { 353 final DatabaseWrapper db = DataModel.get().getDatabase(); 354 MessageData toastMessage = message; 355 if (toastMessage == null) { 356 // If the downloaded failed (message is null), then we should announce the 357 // receiving of the wap push message. Load the wap push message here instead. 358 toastMessage = BugleDatabaseOperations.readMessageData(db, messageId); 359 } 360 if (toastMessage != null) { 361 final ParticipantData sender = ParticipantData.getFromId( 362 db, toastMessage.getParticipantId()); 363 BugleActionToasts.onMessageReceived( 364 toastMessage.getConversationId(), sender, toastMessage); 365 } 366 } else { 367 final boolean success = message != null && status == MmsUtils.MMS_REQUEST_SUCCEEDED; 368 BugleActionToasts.onSendMessageOrManualDownloadActionCompleted( 369 // If download failed, use the wap push message's conversation instead 370 success ? message.getConversationId() 371 : actionParameters.getString(KEY_CONVERSATION_ID), 372 success, status, false/*isSms*/, subId, false /*isSend*/); 373 } 374 375 final boolean failed = (messageUri == null); 376 ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(failed, this); 377 if (failed) { 378 BugleNotifications.update(false, BugleNotifications.UPDATE_ERRORS); 379 } 380 381 return message; 382 } 383 384 @Override processBackgroundFailure()385 protected Object processBackgroundFailure() { 386 if (actionParameters.getBoolean(KEY_SEND_DEFERRED_RESP_STATUS)) { 387 // We can early-out for these failures. processResult is only designed to handle 388 // post-processing of MMS downloads (whether successful or not). 389 LogUtil.w(TAG, 390 "ProcessDownloadedMmsAction: Exception while sending deferred NotifyRespInd"); 391 return null; 392 } 393 394 // Background worker threw an exception; require manual retry 395 processResult(MmsUtils.MMS_REQUEST_MANUAL_RETRY, MessageData.RAW_TELEPHONY_STATUS_UNDEFINED, 396 null /* mmsUri */); 397 398 ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(true /* failed */, 399 this); 400 401 return null; 402 } 403 processResult(final int status, final int rawStatus, final Uri mmsUri)404 private MessageData processResult(final int status, final int rawStatus, final Uri mmsUri) { 405 final Context context = Factory.get().getApplicationContext(); 406 final String messageId = actionParameters.getString(KEY_MESSAGE_ID); 407 final Uri mmsNotificationUri = actionParameters.getParcelable(KEY_NOTIFICATION_URI); 408 final String notificationConversationId = actionParameters.getString(KEY_CONVERSATION_ID); 409 final String notificationParticipantId = actionParameters.getString(KEY_PARTICIPANT_ID); 410 final int statusIfFailed = actionParameters.getInt(KEY_STATUS_IF_FAILED); 411 final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID); 412 413 Assert.notNull(messageId); 414 415 LogUtil.i(TAG, "ProcessDownloadedMmsAction: Processed MMS download of message " + messageId 416 + "; status is " + MmsUtils.getRequestStatusDescription(status)); 417 418 DatabaseMessages.MmsMessage mms = null; 419 if (status == MmsUtils.MMS_REQUEST_SUCCEEDED && mmsUri != null) { 420 // Delete the initial M-Notification.ind from telephony 421 SqliteWrapper.delete(context, context.getContentResolver(), 422 mmsNotificationUri, null, null); 423 424 // Read the sent MMS from the telephony provider 425 mms = MmsUtils.loadMms(mmsUri); 426 } 427 428 boolean messageInFocusedConversation = false; 429 boolean messageInObservableConversation = false; 430 String conversationId = null; 431 MessageData message = null; 432 final DatabaseWrapper db = DataModel.get().getDatabase(); 433 db.beginTransaction(); 434 try { 435 if (mms != null) { 436 final ParticipantData self = ParticipantData.getSelfParticipant(mms.getSubId()); 437 final String selfId = 438 BugleDatabaseOperations.getOrCreateParticipantInTransaction(db, self); 439 440 final List<String> recipients = MmsUtils.getRecipientsByThread(mms.mThreadId); 441 String from = MmsUtils.getMmsSender(recipients, mms.getUri()); 442 if (from == null) { 443 LogUtil.w(TAG, 444 "Downloaded an MMS without sender address; using unknown sender."); 445 from = ParticipantData.getUnknownSenderDestination(); 446 } 447 final ParticipantData sender = ParticipantData.getFromRawPhoneBySimLocale(from, 448 subId); 449 final String senderParticipantId = 450 BugleDatabaseOperations.getOrCreateParticipantInTransaction(db, sender); 451 if (!senderParticipantId.equals(notificationParticipantId)) { 452 LogUtil.e(TAG, "ProcessDownloadedMmsAction: Downloaded MMS message " 453 + messageId + " has different sender (participantId = " 454 + senderParticipantId + ") than notification (" 455 + notificationParticipantId + ")"); 456 } 457 final boolean blockedSender = BugleDatabaseOperations.isBlockedDestination( 458 db, sender.getNormalizedDestination()); 459 conversationId = BugleDatabaseOperations.getOrCreateConversationFromThreadId(db, 460 mms.mThreadId, blockedSender, subId); 461 462 messageInFocusedConversation = 463 DataModel.get().isFocusedConversation(conversationId); 464 messageInObservableConversation = 465 DataModel.get().isNewMessageObservable(conversationId); 466 467 // TODO: Also write these values to the telephony provider 468 mms.mRead = messageInFocusedConversation; 469 mms.mSeen = messageInObservableConversation; 470 471 // Translate to our format 472 message = MmsUtils.createMmsMessage(mms, conversationId, senderParticipantId, 473 selfId, MessageData.BUGLE_STATUS_INCOMING_COMPLETE); 474 // Update image sizes. 475 message.updateSizesForImageParts(); 476 // Inform sync that message has been added at local received timestamp 477 final SyncManager syncManager = DataModel.get().getSyncManager(); 478 syncManager.onNewMessageInserted(message.getReceivedTimeStamp()); 479 final MessageData current = BugleDatabaseOperations.readMessageData(db, messageId); 480 if (current == null) { 481 LogUtil.w(TAG, "Message deleted prior to update"); 482 BugleDatabaseOperations.insertNewMessageInTransaction(db, message); 483 } else { 484 // Overwrite existing notification message 485 message.updateMessageId(messageId); 486 // Write message 487 BugleDatabaseOperations.updateMessageInTransaction(db, message); 488 } 489 490 if (!TextUtils.equals(notificationConversationId, conversationId)) { 491 // If this is a group conversation, the message is moved. So the original 492 // 1v1 conversation (as referenced by notificationConversationId) could 493 // be left with no non-draft message. Delete the conversation if that 494 // happens. See the comment for the method below for why we need to do this. 495 if (!BugleDatabaseOperations.deleteConversationIfEmptyInTransaction( 496 db, notificationConversationId)) { 497 BugleDatabaseOperations.maybeRefreshConversationMetadataInTransaction( 498 db, notificationConversationId, messageId, 499 true /*shouldAutoSwitchSelfId*/, blockedSender /*keepArchived*/); 500 } 501 } 502 503 BugleDatabaseOperations.refreshConversationMetadataInTransaction(db, conversationId, 504 true /*shouldAutoSwitchSelfId*/, blockedSender /*keepArchived*/); 505 } else { 506 messageInFocusedConversation = 507 DataModel.get().isFocusedConversation(notificationConversationId); 508 509 // Default to retry status unless status indicates otherwise 510 int bugleStatus = statusIfFailed; 511 if (status == MmsUtils.MMS_REQUEST_MANUAL_RETRY) { 512 bugleStatus = MessageData.BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED; 513 } else if (status == MmsUtils.MMS_REQUEST_NO_RETRY) { 514 bugleStatus = MessageData.BUGLE_STATUS_INCOMING_EXPIRED_OR_NOT_AVAILABLE; 515 } 516 DownloadMmsAction.updateMessageStatus(mmsNotificationUri, messageId, 517 notificationConversationId, bugleStatus, rawStatus); 518 519 // Log MMS download failed 520 final int resultCode = actionParameters.getInt(KEY_RESULT_CODE); 521 final int httpStatusCode = actionParameters.getInt(KEY_HTTP_STATUS_CODE); 522 523 // Just in case this was the latest message update the summary data 524 BugleDatabaseOperations.refreshConversationMetadataInTransaction(db, 525 notificationConversationId, true /*shouldAutoSwitchSelfId*/, 526 false /*keepArchived*/); 527 } 528 529 db.setTransactionSuccessful(); 530 } finally { 531 db.endTransaction(); 532 } 533 534 if (mmsUri != null) { 535 // Update mms table with read status now we know the conversation id 536 final ContentValues values = new ContentValues(1); 537 values.put(Mms.READ, messageInFocusedConversation); 538 SqliteWrapper.update(context, context.getContentResolver(), mmsUri, values, 539 null, null); 540 } 541 542 // Show a notification to let the user know a new message has arrived 543 BugleNotifications.update(false /*silent*/, conversationId, BugleNotifications.UPDATE_ALL); 544 545 // Messages may have changed in two conversations 546 if (conversationId != null) { 547 MessagingContentProvider.notifyMessagesChanged(conversationId); 548 } 549 MessagingContentProvider.notifyMessagesChanged(notificationConversationId); 550 MessagingContentProvider.notifyPartsChanged(); 551 552 return message; 553 } 554 ProcessDownloadedMmsAction(final Parcel in)555 private ProcessDownloadedMmsAction(final Parcel in) { 556 super(in); 557 } 558 559 public static final Parcelable.Creator<ProcessDownloadedMmsAction> CREATOR 560 = new Parcelable.Creator<ProcessDownloadedMmsAction>() { 561 @Override 562 public ProcessDownloadedMmsAction createFromParcel(final Parcel in) { 563 return new ProcessDownloadedMmsAction(in); 564 } 565 566 @Override 567 public ProcessDownloadedMmsAction[] newArray(final int size) { 568 return new ProcessDownloadedMmsAction[size]; 569 } 570 }; 571 572 @Override writeToParcel(final Parcel parcel, final int flags)573 public void writeToParcel(final Parcel parcel, final int flags) { 574 writeActionToParcel(parcel, flags); 575 } 576 } 577