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