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 ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction( 333 true /* failed */, this); 334 return null; 335 } 336 337 final int status = response.getInt(BUNDLE_REQUEST_STATUS); 338 final int rawStatus = response.getInt(BUNDLE_RAW_TELEPHONY_STATUS); 339 final Uri messageUri = response.getParcelable(BUNDLE_MMS_URI); 340 final boolean autoDownload = actionParameters.getBoolean(KEY_AUTO_DOWNLOAD); 341 final String messageId = actionParameters.getString(KEY_MESSAGE_ID); 342 343 // Do post-processing on downloaded message 344 final MessageData message = processResult(status, rawStatus, messageUri); 345 346 final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID); 347 // If we were trying to auto-download but have failed need to send the deferred response 348 final boolean needToSendDeferredResp = 349 autoDownload && (status == MmsUtils.MMS_REQUEST_MANUAL_RETRY); 350 if (needToSendDeferredResp) { 351 final String transactionId = actionParameters.getString(KEY_TRANSACTION_ID); 352 final String contentLocation = actionParameters.getString(KEY_CONTENT_LOCATION); 353 sendDeferredRespStatus(messageId, transactionId, contentLocation, subId); 354 } 355 356 if (autoDownload) { 357 final DatabaseWrapper db = DataModel.get().getDatabase(); 358 MessageData toastMessage = message; 359 if (toastMessage == null) { 360 // If the downloaded failed (message is null), then we should announce the 361 // receiving of the wap push message. Load the wap push message here instead. 362 toastMessage = BugleDatabaseOperations.readMessageData(db, messageId); 363 } 364 if (toastMessage != null) { 365 final ParticipantData sender = ParticipantData.getFromId( 366 db, toastMessage.getParticipantId()); 367 BugleActionToasts.onMessageReceived( 368 toastMessage.getConversationId(), sender, toastMessage); 369 } 370 } else { 371 final boolean success = message != null && status == MmsUtils.MMS_REQUEST_SUCCEEDED; 372 BugleActionToasts.onSendMessageOrManualDownloadActionCompleted( 373 // If download failed, use the wap push message's conversation instead 374 success ? message.getConversationId() 375 : actionParameters.getString(KEY_CONVERSATION_ID), 376 success, status, false/*isSms*/, subId, false /*isSend*/); 377 } 378 379 final boolean failed = (messageUri == null); 380 // Scheduling pending messages. If auto downloading is failed and it needs to send the 381 // deferred response, Skip it here and it will be scheduled after sending the response. 382 if (!needToSendDeferredResp) { 383 ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(failed, this); 384 } 385 if (failed) { 386 BugleNotifications.update(false, BugleNotifications.UPDATE_ERRORS); 387 } 388 389 return message; 390 } 391 392 @Override processBackgroundFailure()393 protected Object processBackgroundFailure() { 394 if (actionParameters.getBoolean(KEY_SEND_DEFERRED_RESP_STATUS)) { 395 // We can early-out for these failures. processResult is only designed to handle 396 // post-processing of MMS downloads (whether successful or not). 397 LogUtil.w(TAG, 398 "ProcessDownloadedMmsAction: Exception while sending deferred NotifyRespInd"); 399 return null; 400 } 401 402 // Background worker threw an exception; require manual retry 403 processResult(MmsUtils.MMS_REQUEST_MANUAL_RETRY, MessageData.RAW_TELEPHONY_STATUS_UNDEFINED, 404 null /* mmsUri */); 405 406 ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(true /* failed */, 407 this); 408 409 return null; 410 } 411 processResult(final int status, final int rawStatus, final Uri mmsUri)412 private MessageData processResult(final int status, final int rawStatus, final Uri mmsUri) { 413 final Context context = Factory.get().getApplicationContext(); 414 final String messageId = actionParameters.getString(KEY_MESSAGE_ID); 415 final Uri mmsNotificationUri = actionParameters.getParcelable(KEY_NOTIFICATION_URI); 416 final String notificationConversationId = actionParameters.getString(KEY_CONVERSATION_ID); 417 final String notificationParticipantId = actionParameters.getString(KEY_PARTICIPANT_ID); 418 final int statusIfFailed = actionParameters.getInt(KEY_STATUS_IF_FAILED); 419 final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID); 420 421 Assert.notNull(messageId); 422 423 LogUtil.i(TAG, "ProcessDownloadedMmsAction: Processed MMS download of message " + messageId 424 + "; status is " + MmsUtils.getRequestStatusDescription(status)); 425 426 DatabaseMessages.MmsMessage mms = null; 427 if (status == MmsUtils.MMS_REQUEST_SUCCEEDED && mmsUri != null) { 428 // Delete the initial M-Notification.ind from telephony 429 SqliteWrapper.delete(context, context.getContentResolver(), 430 mmsNotificationUri, null, null); 431 432 // Read the sent MMS from the telephony provider 433 mms = MmsUtils.loadMms(mmsUri); 434 } 435 436 boolean messageInFocusedConversation = false; 437 boolean messageInObservableConversation = false; 438 String conversationId = null; 439 MessageData message = null; 440 final DatabaseWrapper db = DataModel.get().getDatabase(); 441 db.beginTransaction(); 442 try { 443 if (mms != null) { 444 final ParticipantData self = ParticipantData.getSelfParticipant(mms.getSubId()); 445 final String selfId = 446 BugleDatabaseOperations.getOrCreateParticipantInTransaction(db, self); 447 448 final List<String> recipients = MmsUtils.getRecipientsByThread(mms.mThreadId); 449 String from = MmsUtils.getMmsSender(recipients, mms.getUri()); 450 if (from == null) { 451 LogUtil.w(TAG, 452 "Downloaded an MMS without sender address; using unknown sender."); 453 from = ParticipantData.getUnknownSenderDestination(); 454 } 455 final ParticipantData sender = ParticipantData.getFromRawPhoneBySimLocale(from, 456 subId); 457 final String senderParticipantId = 458 BugleDatabaseOperations.getOrCreateParticipantInTransaction(db, sender); 459 if (!senderParticipantId.equals(notificationParticipantId)) { 460 LogUtil.e(TAG, "ProcessDownloadedMmsAction: Downloaded MMS message " 461 + messageId + " has different sender (participantId = " 462 + senderParticipantId + ") than notification (" 463 + notificationParticipantId + ")"); 464 } 465 final boolean blockedSender = BugleDatabaseOperations.isBlockedDestination( 466 db, sender.getNormalizedDestination()); 467 conversationId = BugleDatabaseOperations.getOrCreateConversationFromThreadId(db, 468 mms.mThreadId, blockedSender, subId); 469 470 messageInFocusedConversation = 471 DataModel.get().isFocusedConversation(conversationId); 472 messageInObservableConversation = 473 DataModel.get().isNewMessageObservable(conversationId); 474 475 // TODO: Also write these values to the telephony provider 476 mms.mRead = messageInFocusedConversation; 477 mms.mSeen = messageInObservableConversation; 478 479 // Translate to our format 480 message = MmsUtils.createMmsMessage(mms, conversationId, senderParticipantId, 481 selfId, MessageData.BUGLE_STATUS_INCOMING_COMPLETE); 482 // Update image sizes. 483 message.updateSizesForImageParts(); 484 // Inform sync that message has been added at local received timestamp 485 final SyncManager syncManager = DataModel.get().getSyncManager(); 486 syncManager.onNewMessageInserted(message.getReceivedTimeStamp()); 487 final MessageData current = BugleDatabaseOperations.readMessageData(db, messageId); 488 if (current == null) { 489 LogUtil.w(TAG, "Message deleted prior to update"); 490 BugleDatabaseOperations.insertNewMessageInTransaction(db, message); 491 } else { 492 // Overwrite existing notification message 493 message.updateMessageId(messageId); 494 // Write message 495 BugleDatabaseOperations.updateMessageInTransaction(db, message); 496 } 497 498 if (!TextUtils.equals(notificationConversationId, conversationId)) { 499 // If this is a group conversation, the message is moved. So the original 500 // 1v1 conversation (as referenced by notificationConversationId) could 501 // be left with no non-draft message. Delete the conversation if that 502 // happens. See the comment for the method below for why we need to do this. 503 if (!BugleDatabaseOperations.deleteConversationIfEmptyInTransaction( 504 db, notificationConversationId)) { 505 BugleDatabaseOperations.maybeRefreshConversationMetadataInTransaction( 506 db, notificationConversationId, messageId, 507 true /*shouldAutoSwitchSelfId*/, blockedSender /*keepArchived*/); 508 } 509 } 510 511 BugleDatabaseOperations.refreshConversationMetadataInTransaction(db, conversationId, 512 true /*shouldAutoSwitchSelfId*/, blockedSender /*keepArchived*/); 513 } else { 514 messageInFocusedConversation = 515 DataModel.get().isFocusedConversation(notificationConversationId); 516 517 // Default to retry status unless status indicates otherwise 518 int bugleStatus = statusIfFailed; 519 if (status == MmsUtils.MMS_REQUEST_MANUAL_RETRY) { 520 bugleStatus = MessageData.BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED; 521 } else if (status == MmsUtils.MMS_REQUEST_NO_RETRY) { 522 bugleStatus = MessageData.BUGLE_STATUS_INCOMING_EXPIRED_OR_NOT_AVAILABLE; 523 } 524 DownloadMmsAction.updateMessageStatus(mmsNotificationUri, messageId, 525 notificationConversationId, bugleStatus, rawStatus); 526 527 // Log MMS download failed 528 final int resultCode = actionParameters.getInt(KEY_RESULT_CODE); 529 final int httpStatusCode = actionParameters.getInt(KEY_HTTP_STATUS_CODE); 530 531 // Just in case this was the latest message update the summary data 532 BugleDatabaseOperations.refreshConversationMetadataInTransaction(db, 533 notificationConversationId, true /*shouldAutoSwitchSelfId*/, 534 false /*keepArchived*/); 535 } 536 537 db.setTransactionSuccessful(); 538 } finally { 539 db.endTransaction(); 540 } 541 542 if (mmsUri != null) { 543 // Update mms table with read status now we know the conversation id 544 final ContentValues values = new ContentValues(1); 545 values.put(Mms.READ, messageInFocusedConversation); 546 SqliteWrapper.update(context, context.getContentResolver(), mmsUri, values, 547 null, null); 548 } 549 550 // Show a notification to let the user know a new message has arrived 551 BugleNotifications.update(false /*silent*/, conversationId, BugleNotifications.UPDATE_ALL); 552 553 // Messages may have changed in two conversations 554 if (conversationId != null) { 555 MessagingContentProvider.notifyMessagesChanged(conversationId); 556 } 557 MessagingContentProvider.notifyMessagesChanged(notificationConversationId); 558 MessagingContentProvider.notifyPartsChanged(); 559 560 return message; 561 } 562 ProcessDownloadedMmsAction(final Parcel in)563 private ProcessDownloadedMmsAction(final Parcel in) { 564 super(in); 565 } 566 567 public static final Parcelable.Creator<ProcessDownloadedMmsAction> CREATOR 568 = new Parcelable.Creator<ProcessDownloadedMmsAction>() { 569 @Override 570 public ProcessDownloadedMmsAction createFromParcel(final Parcel in) { 571 return new ProcessDownloadedMmsAction(in); 572 } 573 574 @Override 575 public ProcessDownloadedMmsAction[] newArray(final int size) { 576 return new ProcessDownloadedMmsAction[size]; 577 } 578 }; 579 580 @Override writeToParcel(final Parcel parcel, final int flags)581 public void writeToParcel(final Parcel parcel, final int flags) { 582 writeActionToParcel(parcel, flags); 583 } 584 } 585