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.content.Context; 20 import android.os.Bundle; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 24 import com.android.messaging.Factory; 25 import com.android.messaging.datamodel.BugleDatabaseOperations; 26 import com.android.messaging.datamodel.BugleNotifications; 27 import com.android.messaging.datamodel.DataModel; 28 import com.android.messaging.datamodel.DataModelException; 29 import com.android.messaging.datamodel.DatabaseWrapper; 30 import com.android.messaging.datamodel.MessagingContentProvider; 31 import com.android.messaging.datamodel.SyncManager; 32 import com.android.messaging.datamodel.data.MessageData; 33 import com.android.messaging.datamodel.data.ParticipantData; 34 import com.android.messaging.mmslib.pdu.PduHeaders; 35 import com.android.messaging.sms.DatabaseMessages; 36 import com.android.messaging.sms.MmsUtils; 37 import com.android.messaging.util.LogUtil; 38 39 import java.util.List; 40 41 /** 42 * Action used to "receive" an incoming message 43 */ 44 public class ReceiveMmsMessageAction extends Action implements Parcelable { 45 private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG; 46 47 private static final String KEY_SUB_ID = "sub_id"; 48 private static final String KEY_PUSH_DATA = "push_data"; 49 private static final String KEY_TRANSACTION_ID = "transaction_id"; 50 private static final String KEY_CONTENT_LOCATION = "content_location"; 51 52 /** 53 * Create a message received from a particular number in a particular conversation 54 */ ReceiveMmsMessageAction(final int subId, final byte[] pushData)55 public ReceiveMmsMessageAction(final int subId, final byte[] pushData) { 56 actionParameters.putInt(KEY_SUB_ID, subId); 57 actionParameters.putByteArray(KEY_PUSH_DATA, pushData); 58 } 59 60 @Override executeAction()61 protected Object executeAction() { 62 final Context context = Factory.get().getApplicationContext(); 63 final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID); 64 final byte[] pushData = actionParameters.getByteArray(KEY_PUSH_DATA); 65 final DatabaseWrapper db = DataModel.get().getDatabase(); 66 67 // Write received message to telephony DB 68 MessageData message = null; 69 final ParticipantData self = BugleDatabaseOperations.getOrCreateSelf(db, subId); 70 71 final long received = System.currentTimeMillis(); 72 // Inform sync that message has been added at local received timestamp 73 final SyncManager syncManager = DataModel.get().getSyncManager(); 74 syncManager.onNewMessageInserted(received); 75 76 // TODO: Should use local time to set received time in MMS message 77 final DatabaseMessages.MmsMessage mms = MmsUtils.processReceivedPdu( 78 context, pushData, self.getSubId(), self.getNormalizedDestination()); 79 80 if (mms != null) { 81 final List<String> recipients = MmsUtils.getRecipientsByThread(mms.mThreadId); 82 String from = MmsUtils.getMmsSender(recipients, mms.getUri()); 83 if (from == null) { 84 LogUtil.w(TAG, "Received an MMS without sender address; using unknown sender."); 85 from = ParticipantData.getUnknownSenderDestination(); 86 } 87 final ParticipantData rawSender = ParticipantData.getFromRawPhoneBySimLocale( 88 from, subId); 89 final boolean blocked = BugleDatabaseOperations.isBlockedDestination( 90 db, rawSender.getNormalizedDestination()); 91 final boolean autoDownload = (!blocked && MmsUtils.allowMmsAutoRetrieve(subId)); 92 final String conversationId = 93 BugleDatabaseOperations.getOrCreateConversationFromThreadId(db, mms.mThreadId, 94 blocked, subId); 95 96 final boolean messageInFocusedConversation = 97 DataModel.get().isFocusedConversation(conversationId); 98 final boolean messageInObservableConversation = 99 DataModel.get().isNewMessageObservable(conversationId); 100 101 // TODO: Also write these values to the telephony provider 102 mms.mRead = messageInFocusedConversation; 103 mms.mSeen = messageInObservableConversation || blocked; 104 105 // Write received placeholder message to our DB 106 db.beginTransaction(); 107 try { 108 final String participantId = 109 BugleDatabaseOperations.getOrCreateParticipantInTransaction(db, rawSender); 110 final String selfId = 111 BugleDatabaseOperations.getOrCreateParticipantInTransaction(db, self); 112 113 message = MmsUtils.createMmsMessage(mms, conversationId, participantId, selfId, 114 (autoDownload ? MessageData.BUGLE_STATUS_INCOMING_RETRYING_AUTO_DOWNLOAD : 115 MessageData.BUGLE_STATUS_INCOMING_YET_TO_MANUAL_DOWNLOAD)); 116 // Write the message 117 BugleDatabaseOperations.insertNewMessageInTransaction(db, message); 118 119 if (!autoDownload) { 120 BugleDatabaseOperations.updateConversationMetadataInTransaction(db, 121 conversationId, message.getMessageId(), message.getReceivedTimeStamp(), 122 blocked, true /* shouldAutoSwitchSelfId */); 123 final ParticipantData sender = ParticipantData .getFromId( 124 db, participantId); 125 BugleActionToasts.onMessageReceived(conversationId, sender, message); 126 } 127 // else update the conversation once we have downloaded final message (or failed) 128 db.setTransactionSuccessful(); 129 } finally { 130 db.endTransaction(); 131 } 132 133 // Update conversation if not immediately initiating a download 134 if (!autoDownload) { 135 MessagingContentProvider.notifyMessagesChanged(message.getConversationId()); 136 MessagingContentProvider.notifyPartsChanged(); 137 138 // Show a notification to let the user know a new message has arrived 139 BugleNotifications.update(false/*silent*/, conversationId, 140 BugleNotifications.UPDATE_ALL); 141 142 // Send the NotifyRespInd with DEFERRED status since no auto download 143 actionParameters.putString(KEY_TRANSACTION_ID, mms.mTransactionId); 144 actionParameters.putString(KEY_CONTENT_LOCATION, mms.mContentLocation); 145 requestBackgroundWork(); 146 } 147 148 LogUtil.i(TAG, "ReceiveMmsMessageAction: Received MMS message " + message.getMessageId() 149 + " in conversation " + message.getConversationId() 150 + ", uri = " + message.getSmsMessageUri()); 151 } else { 152 LogUtil.e(TAG, "ReceiveMmsMessageAction: Skipping processing of incoming PDU"); 153 } 154 155 ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, this); 156 157 return message; 158 } 159 160 @Override doBackgroundWork()161 protected Bundle doBackgroundWork() throws DataModelException { 162 final Context context = Factory.get().getApplicationContext(); 163 final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID); 164 final String transactionId = actionParameters.getString(KEY_TRANSACTION_ID); 165 final String contentLocation = actionParameters.getString(KEY_CONTENT_LOCATION); 166 MmsUtils.sendNotifyResponseForMmsDownload( 167 context, 168 subId, 169 MmsUtils.stringToBytes(transactionId, "UTF-8"), 170 contentLocation, 171 PduHeaders.STATUS_DEFERRED); 172 // We don't need to return anything. 173 return null; 174 } 175 ReceiveMmsMessageAction(final Parcel in)176 private ReceiveMmsMessageAction(final Parcel in) { 177 super(in); 178 } 179 180 public static final Parcelable.Creator<ReceiveMmsMessageAction> CREATOR 181 = new Parcelable.Creator<ReceiveMmsMessageAction>() { 182 @Override 183 public ReceiveMmsMessageAction createFromParcel(final Parcel in) { 184 return new ReceiveMmsMessageAction(in); 185 } 186 187 @Override 188 public ReceiveMmsMessageAction[] newArray(final int size) { 189 return new ReceiveMmsMessageAction[size]; 190 } 191 }; 192 193 @Override writeToParcel(final Parcel parcel, final int flags)194 public void writeToParcel(final Parcel parcel, final int flags) { 195 writeActionToParcel(parcel, flags); 196 } 197 } 198