NotificationTransaction.java revision 99f719d1cb0c6ed68373d73427a816c4e56eb5ba
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 java.io.IOException; 29 30import android.content.ContentValues; 31import android.content.Context; 32import android.database.sqlite.SqliteWrapper; 33import android.net.Uri; 34import android.provider.Telephony.Mms; 35import android.provider.Telephony.Threads; 36import android.provider.Telephony.Mms.Inbox; 37import android.telephony.TelephonyManager; 38import android.util.Log; 39 40import com.android.mms.MmsApp; 41import com.android.mms.MmsConfig; 42import com.android.mms.util.DownloadManager; 43import com.android.mms.util.Recycler; 44import com.android.mms.widget.MmsWidgetProvider; 45import com.google.android.mms.MmsException; 46import com.google.android.mms.pdu.GenericPdu; 47import com.google.android.mms.pdu.NotificationInd; 48import com.google.android.mms.pdu.NotifyRespInd; 49import com.google.android.mms.pdu.PduComposer; 50import com.google.android.mms.pdu.PduHeaders; 51import com.google.android.mms.pdu.PduParser; 52import com.google.android.mms.pdu.PduPersister; 53 54/** 55 * The NotificationTransaction is responsible for handling multimedia 56 * message notifications (M-Notification.ind). It: 57 * 58 * <ul> 59 * <li>Composes the notification response (M-NotifyResp.ind). 60 * <li>Sends the notification response to the MMSC server. 61 * <li>Stores the notification indication. 62 * <li>Notifies the TransactionService about succesful completion. 63 * </ul> 64 * 65 * NOTE: This MMS client handles all notifications with a <b>deferred 66 * retrieval</b> response. The transaction service, upon succesful 67 * completion of this transaction, will trigger a retrieve transaction 68 * in case the client is in immediate retrieve mode. 69 */ 70public class NotificationTransaction extends Transaction implements Runnable { 71 private static final String TAG = "NotificationTransaction"; 72 private static final boolean DEBUG = false; 73 private static final boolean LOCAL_LOGV = false; 74 75 private Uri mUri; 76 private NotificationInd mNotificationInd; 77 private String mContentLocation; 78 79 public NotificationTransaction( 80 Context context, int serviceId, 81 TransactionSettings connectionSettings, String uriString) { 82 super(context, serviceId, connectionSettings); 83 84 mUri = Uri.parse(uriString); 85 86 try { 87 mNotificationInd = (NotificationInd) 88 PduPersister.getPduPersister(context).load(mUri); 89 } catch (MmsException e) { 90 Log.e(TAG, "Failed to load NotificationInd from: " + uriString, e); 91 throw new IllegalArgumentException(); 92 } 93 94 mId = new String(mNotificationInd.getTransactionId()); 95 mContentLocation = new String(mNotificationInd.getContentLocation()); 96 97 // Attach the transaction to the instance of RetryScheduler. 98 attach(RetryScheduler.getInstance(context)); 99 } 100 101 /** 102 * This constructor is only used for test purposes. 103 */ 104 public NotificationTransaction( 105 Context context, int serviceId, 106 TransactionSettings connectionSettings, NotificationInd ind) { 107 super(context, serviceId, connectionSettings); 108 109 try { 110 // Save the pdu. If we can start downloading the real pdu immediately, don't allow 111 // persist() to create a thread for the notificationInd because it causes UI jank. 112 mUri = PduPersister.getPduPersister(context).persist( 113 ind, Inbox.CONTENT_URI, !allowAutoDownload()); 114 } catch (MmsException e) { 115 Log.e(TAG, "Failed to save NotificationInd in constructor.", e); 116 throw new IllegalArgumentException(); 117 } 118 119 mNotificationInd = ind; 120 mId = new String(ind.getTransactionId()); 121 } 122 123 /* 124 * (non-Javadoc) 125 * @see com.google.android.mms.pdu.Transaction#process() 126 */ 127 @Override 128 public void process() { 129 new Thread(this, "NotificationTransaction").start(); 130 } 131 132 public static boolean allowAutoDownload() { 133 DownloadManager downloadManager = DownloadManager.getInstance(); 134 boolean autoDownload = downloadManager.isAuto(); 135 boolean dataSuspended = (MmsApp.getApplication().getTelephonyManager().getDataState() == 136 TelephonyManager.DATA_SUSPENDED); 137 return autoDownload && !dataSuspended; 138 } 139 140 public void run() { 141 DownloadManager downloadManager = DownloadManager.getInstance(); 142 boolean autoDownload = allowAutoDownload(); 143 try { 144 if (LOCAL_LOGV) { 145 Log.v(TAG, "Notification transaction launched: " + this); 146 } 147 148 // By default, we set status to STATUS_DEFERRED because we 149 // should response MMSC with STATUS_DEFERRED when we cannot 150 // download a MM immediately. 151 int status = STATUS_DEFERRED; 152 // Don't try to download when data is suspended, as it will fail, so defer download 153 if (!autoDownload) { 154 downloadManager.markState(mUri, DownloadManager.STATE_UNSTARTED); 155 sendNotifyRespInd(status); 156 return; 157 } 158 159 downloadManager.markState(mUri, DownloadManager.STATE_DOWNLOADING); 160 161 if (LOCAL_LOGV) { 162 Log.v(TAG, "Content-Location: " + mContentLocation); 163 } 164 165 byte[] retrieveConfData = null; 166 // We should catch exceptions here to response MMSC 167 // with STATUS_DEFERRED. 168 try { 169 retrieveConfData = getPdu(mContentLocation); 170 } catch (IOException e) { 171 mTransactionState.setState(FAILED); 172 } 173 174 if (retrieveConfData != null) { 175 GenericPdu pdu = new PduParser(retrieveConfData).parse(); 176 if ((pdu == null) || (pdu.getMessageType() != MESSAGE_TYPE_RETRIEVE_CONF)) { 177 Log.e(TAG, "Invalid M-RETRIEVE.CONF PDU. " + 178 (pdu != null ? "message type: " + pdu.getMessageType() : "null pdu")); 179 mTransactionState.setState(FAILED); 180 status = STATUS_UNRECOGNIZED; 181 } else { 182 // Save the received PDU (must be a M-RETRIEVE.CONF). 183 PduPersister p = PduPersister.getPduPersister(mContext); 184 Uri uri = p.persist(pdu, Inbox.CONTENT_URI); 185 186 // Use local time instead of PDU time 187 ContentValues values = new ContentValues(1); 188 values.put(Mms.DATE, System.currentTimeMillis() / 1000L); 189 SqliteWrapper.update(mContext, mContext.getContentResolver(), 190 uri, values, null, null); 191 192 // We have successfully downloaded the new MM. Delete the 193 // M-NotifyResp.ind from Inbox. 194 SqliteWrapper.delete(mContext, mContext.getContentResolver(), 195 mUri, null, null); 196 if (true) { 197 Log.v(TAG, "NotificationTransaction deleting obsolete threads"); 198 } 199 // Delete obsolete threads 200 SqliteWrapper.delete(mContext, mContext.getContentResolver(), 201 Threads.OBSOLETE_THREADS_URI, null, null); 202 203 // Notify observers with newly received MM. 204 mUri = uri; 205 status = STATUS_RETRIEVED; 206 } 207 } 208 209 if (LOCAL_LOGV) { 210 Log.v(TAG, "status=0x" + Integer.toHexString(status)); 211 } 212 213 // Check the status and update the result state of this Transaction. 214 switch (status) { 215 case STATUS_RETRIEVED: 216 mTransactionState.setState(SUCCESS); 217 break; 218 case STATUS_DEFERRED: 219 // STATUS_DEFERRED, may be a failed immediate retrieval. 220 if (mTransactionState.getState() == INITIALIZED) { 221 mTransactionState.setState(SUCCESS); 222 } 223 break; 224 } 225 226 sendNotifyRespInd(status); 227 228 // Make sure this thread isn't over the limits in message count. 229 Recycler.getMmsRecycler().deleteOldMessagesInSameThreadAsMessage(mContext, mUri); 230 MmsWidgetProvider.notifyDatasetChanged(mContext); 231 } catch (Throwable t) { 232 Log.e(TAG, Log.getStackTraceString(t)); 233 } finally { 234 mTransactionState.setContentUri(mUri); 235 if (!autoDownload) { 236 // Always mark the transaction successful for deferred 237 // download since any error here doesn't make sense. 238 mTransactionState.setState(SUCCESS); 239 } 240 if (mTransactionState.getState() != SUCCESS) { 241 mTransactionState.setState(FAILED); 242 Log.e(TAG, "NotificationTransaction failed."); 243 } 244 notifyObservers(); 245 } 246 } 247 248 private void sendNotifyRespInd(int status) throws MmsException, IOException { 249 // Create the M-NotifyResp.ind 250 NotifyRespInd notifyRespInd = new NotifyRespInd( 251 PduHeaders.CURRENT_MMS_VERSION, 252 mNotificationInd.getTransactionId(), 253 status); 254 255 // Pack M-NotifyResp.ind and send it 256 if(MmsConfig.getNotifyWapMMSC()) { 257 sendPdu(new PduComposer(mContext, notifyRespInd).make(), mContentLocation); 258 } else { 259 sendPdu(new PduComposer(mContext, notifyRespInd).make()); 260 } 261 } 262 263 @Override 264 public int getType() { 265 return NOTIFICATION_TRANSACTION; 266 } 267} 268