• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 package com.android.mms.transaction;
19 
20 import static com.android.mms.transaction.TransactionState.FAILED;
21 import static com.android.mms.transaction.TransactionState.INITIALIZED;
22 import static com.android.mms.transaction.TransactionState.SUCCESS;
23 import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF;
24 import static com.google.android.mms.pdu.PduHeaders.STATUS_DEFERRED;
25 import static com.google.android.mms.pdu.PduHeaders.STATUS_RETRIEVED;
26 import static com.google.android.mms.pdu.PduHeaders.STATUS_UNRECOGNIZED;
27 
28 import com.android.mms.MmsApp;
29 import com.android.mms.MmsConfig;
30 import com.android.mms.util.DownloadManager;
31 import com.android.mms.util.Recycler;
32 import com.google.android.mms.MmsException;
33 import com.google.android.mms.pdu.GenericPdu;
34 import com.google.android.mms.pdu.NotificationInd;
35 import com.google.android.mms.pdu.NotifyRespInd;
36 import com.google.android.mms.pdu.PduComposer;
37 import com.google.android.mms.pdu.PduHeaders;
38 import com.google.android.mms.pdu.PduParser;
39 import com.google.android.mms.pdu.PduPersister;
40 import android.database.sqlite.SqliteWrapper;
41 
42 import android.content.Context;
43 import android.database.Cursor;
44 import android.net.Uri;
45 import android.provider.Telephony.Mms;
46 import android.provider.Telephony.Mms.Inbox;
47 import android.telephony.TelephonyManager;
48 import android.util.Config;
49 import android.util.Log;
50 
51 import java.io.IOException;
52 
53 /**
54  * The NotificationTransaction is responsible for handling multimedia
55  * message notifications (M-Notification.ind).  It:
56  *
57  * <ul>
58  * <li>Composes the notification response (M-NotifyResp.ind).
59  * <li>Sends the notification response to the MMSC server.
60  * <li>Stores the notification indication.
61  * <li>Notifies the TransactionService about succesful completion.
62  * </ul>
63  *
64  * NOTE: This MMS client handles all notifications with a <b>deferred
65  * retrieval</b> response.  The transaction service, upon succesful
66  * completion of this transaction, will trigger a retrieve transaction
67  * in case the client is in immediate retrieve mode.
68  */
69 public class NotificationTransaction extends Transaction implements Runnable {
70     private static final String TAG = "NotificationTransaction";
71     private static final boolean DEBUG = false;
72     private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
73 
74     private Uri mUri;
75     private NotificationInd mNotificationInd;
76     private String mContentLocation;
77 
NotificationTransaction( Context context, int serviceId, TransactionSettings connectionSettings, String uriString)78     public NotificationTransaction(
79             Context context, int serviceId,
80             TransactionSettings connectionSettings, String uriString) {
81         super(context, serviceId, connectionSettings);
82 
83         mUri = Uri.parse(uriString);
84 
85         try {
86             mNotificationInd = (NotificationInd)
87                     PduPersister.getPduPersister(context).load(mUri);
88         } catch (MmsException e) {
89             Log.e(TAG, "Failed to load NotificationInd from: " + uriString, e);
90             throw new IllegalArgumentException();
91         }
92 
93         mId = new String(mNotificationInd.getTransactionId());
94         mContentLocation = new String(mNotificationInd.getContentLocation());
95 
96         // Attach the transaction to the instance of RetryScheduler.
97         attach(RetryScheduler.getInstance(context));
98     }
99 
100     /**
101      * This constructor is only used for test purposes.
102      */
NotificationTransaction( Context context, int serviceId, TransactionSettings connectionSettings, NotificationInd ind)103     public NotificationTransaction(
104             Context context, int serviceId,
105             TransactionSettings connectionSettings, NotificationInd ind) {
106         super(context, serviceId, connectionSettings);
107 
108         try {
109             mUri = PduPersister.getPduPersister(context).persist(
110                         ind, Inbox.CONTENT_URI);
111         } catch (MmsException e) {
112             Log.e(TAG, "Failed to save NotificationInd in constructor.", e);
113             throw new IllegalArgumentException();
114         }
115 
116         mNotificationInd = ind;
117         mId = new String(ind.getTransactionId());
118     }
119 
120     /*
121      * (non-Javadoc)
122      * @see com.google.android.mms.pdu.Transaction#process()
123      */
124     @Override
process()125     public void process() {
126         new Thread(this).start();
127     }
128 
run()129     public void run() {
130         DownloadManager downloadManager = DownloadManager.getInstance();
131         boolean autoDownload = downloadManager.isAuto();
132         boolean dataSuspended = (MmsApp.getApplication().getTelephonyManager().getDataState() ==
133                 TelephonyManager.DATA_SUSPENDED);
134         try {
135             if (LOCAL_LOGV) {
136                 Log.v(TAG, "Notification transaction launched: " + this);
137             }
138 
139             // By default, we set status to STATUS_DEFERRED because we
140             // should response MMSC with STATUS_DEFERRED when we cannot
141             // download a MM immediately.
142             int status = STATUS_DEFERRED;
143             // Don't try to download when data is suspended, as it will fail, so defer download
144             if (!autoDownload || dataSuspended) {
145                 downloadManager.markState(mUri, DownloadManager.STATE_UNSTARTED);
146                 sendNotifyRespInd(status);
147                 return;
148             }
149 
150             downloadManager.markState(mUri, DownloadManager.STATE_DOWNLOADING);
151 
152             if (LOCAL_LOGV) {
153                 Log.v(TAG, "Content-Location: " + mContentLocation);
154             }
155 
156             byte[] retrieveConfData = null;
157             // We should catch exceptions here to response MMSC
158             // with STATUS_DEFERRED.
159             try {
160                 retrieveConfData = getPdu(mContentLocation);
161             } catch (IOException e) {
162                 mTransactionState.setState(FAILED);
163             }
164 
165             if (retrieveConfData != null) {
166                 GenericPdu pdu = new PduParser(retrieveConfData).parse();
167                 if ((pdu == null) || (pdu.getMessageType() != MESSAGE_TYPE_RETRIEVE_CONF)) {
168                     Log.e(TAG, "Invalid M-RETRIEVE.CONF 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 
sendNotifyRespInd(int status)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
getType()239     public int getType() {
240         return NOTIFICATION_TRANSACTION;
241     }
242 }
243