summaryrefslogtreecommitdiffstats
path: root/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java')
-rw-r--r--src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java310
1 files changed, 310 insertions, 0 deletions
diff --git a/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java b/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java
new file mode 100644
index 0000000..f408e47
--- /dev/null
+++ b/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.messaging.datamodel.action;
+
+import android.app.Activity;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SmsManager;
+
+import com.android.messaging.Factory;
+import com.android.messaging.datamodel.BugleDatabaseOperations;
+import com.android.messaging.datamodel.BugleNotifications;
+import com.android.messaging.datamodel.DataModel;
+import com.android.messaging.datamodel.DatabaseWrapper;
+import com.android.messaging.datamodel.MmsFileProvider;
+import com.android.messaging.datamodel.data.MessageData;
+import com.android.messaging.datamodel.data.MessagePartData;
+import com.android.messaging.datamodel.data.ParticipantData;
+import com.android.messaging.mmslib.pdu.SendConf;
+import com.android.messaging.sms.MmsConfig;
+import com.android.messaging.sms.MmsSender;
+import com.android.messaging.sms.MmsUtils;
+import com.android.messaging.util.Assert;
+import com.android.messaging.util.LogUtil;
+
+import java.io.File;
+import java.util.ArrayList;
+
+/**
+* Update message status to reflect success or failure
+* Can also update the message itself if a "final" message is now available from telephony db
+*/
+public class ProcessSentMessageAction extends Action {
+ private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
+
+ // These are always set
+ private static final String KEY_SMS = "is_sms";
+ private static final String KEY_SENT_BY_PLATFORM = "sent_by_platform";
+
+ // These are set when we're processing a message sent by the user. They are null for messages
+ // sent automatically (e.g. a NotifyRespInd/AcknowledgeInd sent in response to a download).
+ private static final String KEY_MESSAGE_ID = "message_id";
+ private static final String KEY_MESSAGE_URI = "message_uri";
+ private static final String KEY_UPDATED_MESSAGE_URI = "updated_message_uri";
+ private static final String KEY_SUB_ID = "sub_id";
+
+ // These are set for messages sent by the platform (L+)
+ public static final String KEY_RESULT_CODE = "result_code";
+ public static final String KEY_HTTP_STATUS_CODE = "http_status_code";
+ private static final String KEY_CONTENT_URI = "content_uri";
+ private static final String KEY_RESPONSE = "response";
+ private static final String KEY_RESPONSE_IMPORTANT = "response_important";
+
+ // These are set for messages we sent ourself (legacy), or which we fast-failed before sending.
+ private static final String KEY_STATUS = "status";
+ private static final String KEY_RAW_STATUS = "raw_status";
+
+ // This is called when MMS lib API returns via PendingIntent
+ public static void processMmsSent(final int resultCode, final Uri messageUri,
+ final Bundle extras) {
+ final ProcessSentMessageAction action = new ProcessSentMessageAction();
+ final Bundle params = action.actionParameters;
+ params.putBoolean(KEY_SMS, false);
+ params.putBoolean(KEY_SENT_BY_PLATFORM, true);
+ params.putString(KEY_MESSAGE_ID, extras.getString(SendMessageAction.EXTRA_MESSAGE_ID));
+ params.putParcelable(KEY_MESSAGE_URI, messageUri);
+ params.putParcelable(KEY_UPDATED_MESSAGE_URI,
+ extras.getParcelable(SendMessageAction.EXTRA_UPDATED_MESSAGE_URI));
+ params.putInt(KEY_SUB_ID,
+ extras.getInt(SendMessageAction.KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID));
+ params.putInt(KEY_RESULT_CODE, resultCode);
+ params.putInt(KEY_HTTP_STATUS_CODE, extras.getInt(SmsManager.EXTRA_MMS_HTTP_STATUS, 0));
+ params.putParcelable(KEY_CONTENT_URI,
+ extras.getParcelable(SendMessageAction.EXTRA_CONTENT_URI));
+ params.putByteArray(KEY_RESPONSE, extras.getByteArray(SmsManager.EXTRA_MMS_DATA));
+ params.putBoolean(KEY_RESPONSE_IMPORTANT,
+ extras.getBoolean(SendMessageAction.EXTRA_RESPONSE_IMPORTANT));
+ action.start();
+ }
+
+ public static void processMessageSentFastFailed(final String messageId,
+ final Uri messageUri, final Uri updatedMessageUri, final int subId, final boolean isSms,
+ final int status, final int rawStatus, final int resultCode) {
+ final ProcessSentMessageAction action = new ProcessSentMessageAction();
+ final Bundle params = action.actionParameters;
+ params.putBoolean(KEY_SMS, isSms);
+ params.putBoolean(KEY_SENT_BY_PLATFORM, false);
+ params.putString(KEY_MESSAGE_ID, messageId);
+ params.putParcelable(KEY_MESSAGE_URI, messageUri);
+ params.putParcelable(KEY_UPDATED_MESSAGE_URI, updatedMessageUri);
+ params.putInt(KEY_SUB_ID, subId);
+ params.putInt(KEY_STATUS, status);
+ params.putInt(KEY_RAW_STATUS, rawStatus);
+ params.putInt(KEY_RESULT_CODE, resultCode);
+ action.start();
+ }
+
+ private ProcessSentMessageAction() {
+ // Callers must use one of the static methods above
+ }
+
+ /**
+ * Update message status to reflect success or failure
+ * Can also update the message itself if a "final" message is now available from telephony db
+ */
+ @Override
+ protected Object executeAction() {
+ final Context context = Factory.get().getApplicationContext();
+ final String messageId = actionParameters.getString(KEY_MESSAGE_ID);
+ final Uri messageUri = actionParameters.getParcelable(KEY_MESSAGE_URI);
+ final Uri updatedMessageUri = actionParameters.getParcelable(KEY_UPDATED_MESSAGE_URI);
+ final boolean isSms = actionParameters.getBoolean(KEY_SMS);
+ final boolean sentByPlatform = actionParameters.getBoolean(KEY_SENT_BY_PLATFORM);
+
+ int status = actionParameters.getInt(KEY_STATUS, MmsUtils.MMS_REQUEST_MANUAL_RETRY);
+ int rawStatus = actionParameters.getInt(KEY_RAW_STATUS,
+ MmsUtils.PDU_HEADER_VALUE_UNDEFINED);
+ final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
+
+ if (sentByPlatform) {
+ // Delete temporary file backing the contentUri passed to MMS service
+ final Uri contentUri = actionParameters.getParcelable(KEY_CONTENT_URI);
+ Assert.isTrue(contentUri != null);
+ final File tempFile = MmsFileProvider.getFile(contentUri);
+ long messageSize = 0;
+ if (tempFile.exists()) {
+ messageSize = tempFile.length();
+ tempFile.delete();
+ if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
+ LogUtil.v(TAG, "ProcessSentMessageAction: Deleted temp file with outgoing "
+ + "MMS pdu: " + contentUri);
+ }
+ }
+
+ final int resultCode = actionParameters.getInt(KEY_RESULT_CODE);
+ final boolean responseImportant = actionParameters.getBoolean(KEY_RESPONSE_IMPORTANT);
+ if (resultCode == Activity.RESULT_OK) {
+ if (responseImportant) {
+ // Get the status from the response PDU and update telephony
+ final byte[] response = actionParameters.getByteArray(KEY_RESPONSE);
+ final SendConf sendConf = MmsSender.parseSendConf(response, subId);
+ if (sendConf != null) {
+ final MmsUtils.StatusPlusUri result =
+ MmsUtils.updateSentMmsMessageStatus(context, messageUri, sendConf);
+ status = result.status;
+ rawStatus = result.rawStatus;
+ }
+ }
+ } else {
+ String errorMsg = "ProcessSentMessageAction: Platform returned error resultCode: "
+ + resultCode;
+ final int httpStatusCode = actionParameters.getInt(KEY_HTTP_STATUS_CODE);
+ if (httpStatusCode != 0) {
+ errorMsg += (", HTTP status code: " + httpStatusCode);
+ }
+ LogUtil.w(TAG, errorMsg);
+ status = MmsSender.getErrorResultStatus(resultCode, httpStatusCode);
+
+ // Check for MMS messages that failed because they exceeded the maximum size,
+ // indicated by an I/O error from the platform.
+ if (resultCode == SmsManager.MMS_ERROR_IO_ERROR) {
+ if (messageSize > MmsConfig.get(subId).getMaxMessageSize()) {
+ rawStatus = MessageData.RAW_TELEPHONY_STATUS_MESSAGE_TOO_BIG;
+ }
+ }
+ }
+ }
+ if (messageId != null) {
+ final int resultCode = actionParameters.getInt(KEY_RESULT_CODE);
+ final int httpStatusCode = actionParameters.getInt(KEY_HTTP_STATUS_CODE);
+ processResult(
+ messageId, updatedMessageUri, status, rawStatus, isSms, this, subId,
+ resultCode, httpStatusCode);
+ } else {
+ if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
+ LogUtil.v(TAG, "ProcessSentMessageAction: No sent message to process (it was "
+ + "probably a notify response for an MMS download)");
+ }
+ }
+ return null;
+ }
+
+ static void processResult(final String messageId, Uri updatedMessageUri, int status,
+ final int rawStatus, final boolean isSms, final Action processingAction,
+ final int subId, final int resultCode, final int httpStatusCode) {
+ final DatabaseWrapper db = DataModel.get().getDatabase();
+ MessageData message = BugleDatabaseOperations.readMessage(db, messageId);
+ final MessageData originalMessage = message;
+ if (message == null) {
+ LogUtil.w(TAG, "ProcessSentMessageAction: Sent message " + messageId
+ + " missing from local database");
+ return;
+ }
+ final String conversationId = message.getConversationId();
+ if (updatedMessageUri != null) {
+ // Update message if we have newly written final message in the telephony db
+ final MessageData update = MmsUtils.readSendingMmsMessage(updatedMessageUri,
+ conversationId, message.getParticipantId(), message.getSelfId());
+ if (update != null) {
+ // Set message Id of final message to that of the existing place holder.
+ update.updateMessageId(message.getMessageId());
+ // Update image sizes.
+ update.updateSizesForImageParts();
+ // Temp attachments are no longer needed
+ for (final MessagePartData part : message.getParts()) {
+ part.destroySync();
+ }
+ message = update;
+ // processResult will rewrite the complete message as part of update
+ } else {
+ updatedMessageUri = null;
+ status = MmsUtils.MMS_REQUEST_MANUAL_RETRY;
+ LogUtil.e(TAG, "ProcessSentMessageAction: Unable to read sending message");
+ }
+ }
+
+ final long timestamp = System.currentTimeMillis();
+ boolean failed;
+ if (status == MmsUtils.MMS_REQUEST_SUCCEEDED) {
+ message.markMessageSent(timestamp);
+ failed = false;
+ } else if (status == MmsUtils.MMS_REQUEST_AUTO_RETRY
+ && message.getInResendWindow(timestamp)) {
+ message.markMessageNotSent(timestamp);
+ message.setRawTelephonyStatus(rawStatus);
+ failed = false;
+ } else {
+ message.markMessageFailed(timestamp);
+ message.setRawTelephonyStatus(rawStatus);
+ message.setMessageSeen(false);
+ failed = true;
+ }
+
+ // We have special handling for when a message to an emergency number fails. In this case,
+ // we notify immediately of any failure (even if we auto-retry), and instruct the user to
+ // try calling the emergency number instead.
+ if (status != MmsUtils.MMS_REQUEST_SUCCEEDED) {
+ final ArrayList<String> recipients =
+ BugleDatabaseOperations.getRecipientsForConversation(db, conversationId);
+ for (final String recipient : recipients) {
+ if (PhoneNumberUtils.isEmergencyNumber(recipient)) {
+ BugleNotifications.notifyEmergencySmsFailed(recipient, conversationId);
+ message.markMessageFailedEmergencyNumber(timestamp);
+ failed = true;
+ break;
+ }
+ }
+ }
+
+ // Update the message status and optionally refresh the message with final parts/values.
+ if (SendMessageAction.updateMessageAndStatus(isSms, message, updatedMessageUri, failed)) {
+ // We shouldn't show any notifications if we're not allowed to modify Telephony for
+ // this message.
+ if (failed) {
+ BugleNotifications.update(false, BugleNotifications.UPDATE_ERRORS);
+ }
+ BugleActionToasts.onSendMessageOrManualDownloadActionCompleted(
+ conversationId, !failed, status, isSms, subId, true/*isSend*/);
+ }
+
+ LogUtil.i(TAG, "ProcessSentMessageAction: Done sending " + (isSms ? "SMS" : "MMS")
+ + " message " + message.getMessageId()
+ + " in conversation " + conversationId
+ + "; status is " + MmsUtils.getRequestStatusDescription(status));
+
+ // Whether we succeeded or failed we will check and maybe schedule some more work
+ ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(
+ status != MmsUtils.MMS_REQUEST_SUCCEEDED, processingAction);
+ }
+
+ private ProcessSentMessageAction(final Parcel in) {
+ super(in);
+ }
+
+ public static final Parcelable.Creator<ProcessSentMessageAction> CREATOR
+ = new Parcelable.Creator<ProcessSentMessageAction>() {
+ @Override
+ public ProcessSentMessageAction createFromParcel(final Parcel in) {
+ return new ProcessSentMessageAction(in);
+ }
+
+ @Override
+ public ProcessSentMessageAction[] newArray(final int size) {
+ return new ProcessSentMessageAction[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(final Parcel parcel, final int flags) {
+ writeActionToParcel(parcel, flags);
+ }
+}