NotificationTransaction.java revision 368cefca7f9b1c3049cdc5d804dc5f10bce91607
1/* 2 * Copyright (C) 2007-2008 Esmertec AG. 3 * Copyright (C) 2007-2008 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package com.android.mms.transaction; 19 20import static com.android.mms.transaction.TransactionState.FAILED; 21import static com.android.mms.transaction.TransactionState.INITIALIZED; 22import static com.android.mms.transaction.TransactionState.SUCCESS; 23import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF; 24import static com.google.android.mms.pdu.PduHeaders.STATUS_DEFERRED; 25import static com.google.android.mms.pdu.PduHeaders.STATUS_RETRIEVED; 26import static com.google.android.mms.pdu.PduHeaders.STATUS_UNRECOGNIZED; 27 28import com.android.mms.MmsApp; 29import com.android.mms.MmsConfig; 30import com.android.mms.util.DownloadManager; 31import com.android.mms.util.Recycler; 32import com.google.android.mms.MmsException; 33import com.google.android.mms.pdu.GenericPdu; 34import com.google.android.mms.pdu.NotificationInd; 35import com.google.android.mms.pdu.NotifyRespInd; 36import com.google.android.mms.pdu.PduComposer; 37import com.google.android.mms.pdu.PduHeaders; 38import com.google.android.mms.pdu.PduParser; 39import com.google.android.mms.pdu.PduPersister; 40import android.database.sqlite.SqliteWrapper; 41 42import android.content.Context; 43import android.database.Cursor; 44import android.net.Uri; 45import android.provider.Telephony.Mms; 46import android.provider.Telephony.Mms.Inbox; 47import android.telephony.TelephonyManager; 48import android.util.Log; 49 50import java.io.IOException; 51 52/** 53 * The NotificationTransaction is responsible for handling multimedia 54 * message notifications (M-Notification.ind). It: 55 * 56 * <ul> 57 * <li>Composes the notification response (M-NotifyResp.ind). 58 * <li>Sends the notification response to the MMSC server. 59 * <li>Stores the notification indication. 60 * <li>Notifies the TransactionService about succesful completion. 61 * </ul> 62 * 63 * NOTE: This MMS client handles all notifications with a <b>deferred 64 * retrieval</b> response. The transaction service, upon succesful 65 * completion of this transaction, will trigger a retrieve transaction 66 * in case the client is in immediate retrieve mode. 67 */ 68public class NotificationTransaction extends Transaction implements Runnable { 69 private static final String TAG = "NotificationTransaction"; 70 private static final boolean DEBUG = false; 71 private static final boolean LOCAL_LOGV = false; 72 73 private Uri mUri; 74 private NotificationInd mNotificationInd; 75 private String mContentLocation; 76 77 public NotificationTransaction( 78 Context context, int serviceId, 79 TransactionSettings connectionSettings, String uriString) { 80 super(context, serviceId, connectionSettings); 81 82 mUri = Uri.parse(uriString); 83 84 try { 85 mNotificationInd = (NotificationInd) 86 PduPersister.getPduPersister(context).load(mUri); 87 } catch (MmsException e) { 88 Log.e(TAG, "Failed to load NotificationInd from: " + uriString, e); 89 throw new IllegalArgumentException(); 90 } 91 92 mId = new String(mNotificationInd.getTransactionId()); 93 mContentLocation = new String(mNotificationInd.getContentLocation()); 94 95 // Attach the transaction to the instance of RetryScheduler. 96 attach(RetryScheduler.getInstance(context)); 97 } 98 99 /** 100 * This constructor is only used for test purposes. 101 */ 102 public NotificationTransaction( 103 Context context, int serviceId, 104 TransactionSettings connectionSettings, NotificationInd ind) { 105 super(context, serviceId, connectionSettings); 106 107 try { 108 mUri = PduPersister.getPduPersister(context).persist( 109 ind, Inbox.CONTENT_URI); 110 } catch (MmsException e) { 111 Log.e(TAG, "Failed to save NotificationInd in constructor.", e); 112 throw new IllegalArgumentException(); 113 } 114 115 mNotificationInd = ind; 116 mId = new String(ind.getTransactionId()); 117 } 118 119 /* 120 * (non-Javadoc) 121 * @see com.google.android.mms.pdu.Transaction#process() 122 */ 123 @Override 124 public void process() { 125 new Thread(this).start(); 126 } 127 128 public void run() { 129 DownloadManager downloadManager = DownloadManager.getInstance(); 130 boolean autoDownload = downloadManager.isAuto(); 131 boolean dataSuspended = (MmsApp.getApplication().getTelephonyManager().getDataState() == 132 TelephonyManager.DATA_SUSPENDED); 133 try { 134 if (LOCAL_LOGV) { 135 Log.v(TAG, "Notification transaction launched: " + this); 136 } 137 138 // By default, we set status to STATUS_DEFERRED because we 139 // should response MMSC with STATUS_DEFERRED when we cannot 140 // download a MM immediately. 141 int status = STATUS_DEFERRED; 142 // Don't try to download when data is suspended, as it will fail, so defer download 143 if (!autoDownload || dataSuspended) { 144 downloadManager.markState(mUri, DownloadManager.STATE_UNSTARTED); 145 sendNotifyRespInd(status); 146 return; 147 } 148 149 downloadManager.markState(mUri, DownloadManager.STATE_DOWNLOADING); 150 151 if (LOCAL_LOGV) { 152 Log.v(TAG, "Content-Location: " + mContentLocation); 153 } 154 155 byte[] retrieveConfData = null; 156 // We should catch exceptions here to response MMSC 157 // with STATUS_DEFERRED. 158 try { 159 retrieveConfData = getPdu(mContentLocation); 160 } catch (IOException e) { 161 mTransactionState.setState(FAILED); 162 } 163 164 if (retrieveConfData != null) { 165 GenericPdu pdu = new PduParser(retrieveConfData).parse(); 166 if ((pdu == null) || (pdu.getMessageType() != MESSAGE_TYPE_RETRIEVE_CONF)) { 167 Log.e(TAG, "Invalid M-RETRIEVE.CONF PDU. " + 168 (pdu != null ? "message type: " + pdu.getMessageType() : "null pdu")); 169 mTransactionState.setState(FAILED); 170 status = STATUS_UNRECOGNIZED; 171 } else { 172 // Save the received PDU (must be a M-RETRIEVE.CONF). 173 PduPersister p = PduPersister.getPduPersister(mContext); 174 Uri uri = p.persist(pdu, Inbox.CONTENT_URI); 175 // We have successfully downloaded the new MM. Delete the 176 // M-NotifyResp.ind from Inbox. 177 SqliteWrapper.delete(mContext, mContext.getContentResolver(), 178 mUri, null, null); 179 // Notify observers with newly received MM. 180 mUri = uri; 181 status = STATUS_RETRIEVED; 182 } 183 } 184 185 if (LOCAL_LOGV) { 186 Log.v(TAG, "status=0x" + Integer.toHexString(status)); 187 } 188 189 // Check the status and update the result state of this Transaction. 190 switch (status) { 191 case STATUS_RETRIEVED: 192 mTransactionState.setState(SUCCESS); 193 break; 194 case STATUS_DEFERRED: 195 // STATUS_DEFERRED, may be a failed immediate retrieval. 196 if (mTransactionState.getState() == INITIALIZED) { 197 mTransactionState.setState(SUCCESS); 198 } 199 break; 200 } 201 202 sendNotifyRespInd(status); 203 204 // Make sure this thread isn't over the limits in message count. 205 Recycler.getMmsRecycler().deleteOldMessagesInSameThreadAsMessage(mContext, mUri); 206 } catch (Throwable t) { 207 Log.e(TAG, Log.getStackTraceString(t)); 208 } finally { 209 mTransactionState.setContentUri(mUri); 210 if (!autoDownload || dataSuspended) { 211 // Always mark the transaction successful for deferred 212 // download since any error here doesn't make sense. 213 mTransactionState.setState(SUCCESS); 214 } 215 if (mTransactionState.getState() != SUCCESS) { 216 mTransactionState.setState(FAILED); 217 Log.e(TAG, "NotificationTransaction failed."); 218 } 219 notifyObservers(); 220 } 221 } 222 223 private void sendNotifyRespInd(int status) throws MmsException, IOException { 224 // Create the M-NotifyResp.ind 225 NotifyRespInd notifyRespInd = new NotifyRespInd( 226 PduHeaders.CURRENT_MMS_VERSION, 227 mNotificationInd.getTransactionId(), 228 status); 229 230 // Pack M-NotifyResp.ind and send it 231 if(MmsConfig.getNotifyWapMMSC()) { 232 sendPdu(new PduComposer(mContext, notifyRespInd).make(), mContentLocation); 233 } else { 234 sendPdu(new PduComposer(mContext, notifyRespInd).make()); 235 } 236 } 237 238 @Override 239 public int getType() { 240 return NOTIFICATION_TRANSACTION; 241 } 242} 243