• 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.MmsConfig;
29 import com.android.mms.util.DownloadManager;
30 import com.android.mms.util.Recycler;
31 import com.google.android.mms.MmsException;
32 import com.google.android.mms.pdu.GenericPdu;
33 import com.google.android.mms.pdu.NotificationInd;
34 import com.google.android.mms.pdu.NotifyRespInd;
35 import com.google.android.mms.pdu.PduComposer;
36 import com.google.android.mms.pdu.PduHeaders;
37 import com.google.android.mms.pdu.PduParser;
38 import com.google.android.mms.pdu.PduPersister;
39 import com.google.android.mms.util.SqliteWrapper;
40 
41 import android.content.Context;
42 import android.database.Cursor;
43 import android.net.Uri;
44 import android.provider.Telephony.Mms;
45 import android.provider.Telephony.Mms.Inbox;
46 import android.telephony.TelephonyManager;
47 import android.util.Config;
48 import android.util.Log;
49 
50 import 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  */
68 public 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 = DEBUG ? Config.LOGD : Config.LOGV;
72 
73     private Uri mUri;
74     private NotificationInd mNotificationInd;
75     private String mContentLocation;
76 
NotificationTransaction( Context context, int serviceId, TransactionSettings connectionSettings, String uriString)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      */
NotificationTransaction( Context context, int serviceId, TransactionSettings connectionSettings, NotificationInd ind)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.Transaction#process()
122      */
123     @Override
process()124     public void process() {
125         new Thread(this).start();
126     }
127 
run()128     public void run() {
129         DownloadManager downloadManager = DownloadManager.getInstance();
130         boolean autoDownload = downloadManager.isAuto();
131         boolean dataSuspended = (TelephonyManager.getDefault().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                     mTransactionState.setState(FAILED);
169                     status = STATUS_UNRECOGNIZED;
170                 } else {
171                     // Save the received PDU (must be a M-RETRIEVE.CONF).
172                     PduPersister p = PduPersister.getPduPersister(mContext);
173                     Uri uri = p.persist(pdu, Inbox.CONTENT_URI);
174                     // We have successfully downloaded the new MM. Delete the
175                     // M-NotifyResp.ind from Inbox.
176                     SqliteWrapper.delete(mContext, mContext.getContentResolver(),
177                                          mUri, null, null);
178                     // Notify observers with newly received MM.
179                     mUri = uri;
180                     status = STATUS_RETRIEVED;
181                 }
182             }
183 
184             if (LOCAL_LOGV) {
185                 Log.v(TAG, "status=0x" + Integer.toHexString(status));
186             }
187 
188             // Check the status and update the result state of this Transaction.
189             switch (status) {
190                 case STATUS_RETRIEVED:
191                     mTransactionState.setState(SUCCESS);
192                     break;
193                 case STATUS_DEFERRED:
194                     // STATUS_DEFERRED, may be a failed immediate retrieval.
195                     if (mTransactionState.getState() == INITIALIZED) {
196                         mTransactionState.setState(SUCCESS);
197                     }
198                     break;
199             }
200 
201             sendNotifyRespInd(status);
202 
203             // Make sure this thread isn't over the limits in message count.
204             Recycler.getMmsRecycler().deleteOldMessagesInSameThreadAsMessage(mContext, mUri);
205         } catch (Throwable t) {
206             Log.e(TAG, Log.getStackTraceString(t));
207         } finally {
208             mTransactionState.setContentUri(mUri);
209             if (!autoDownload || dataSuspended) {
210                 // Always mark the transaction successful for deferred
211                 // download since any error here doesn't make sense.
212                 mTransactionState.setState(SUCCESS);
213             }
214             if (mTransactionState.getState() != SUCCESS) {
215                 mTransactionState.setState(FAILED);
216                 Log.e(TAG, "NotificationTransaction failed.");
217             }
218             notifyObservers();
219         }
220     }
221 
sendNotifyRespInd(int status)222     private void sendNotifyRespInd(int status) throws MmsException, IOException {
223         // Create the M-NotifyResp.ind
224         NotifyRespInd notifyRespInd = new NotifyRespInd(
225                 PduHeaders.CURRENT_MMS_VERSION,
226                 mNotificationInd.getTransactionId(),
227                 status);
228 
229         // Pack M-NotifyResp.ind and send it
230         if(MmsConfig.getNotifyWapMMSC()) {
231             sendPdu(new PduComposer(mContext, notifyRespInd).make(), mContentLocation);
232         } else {
233             sendPdu(new PduComposer(mContext, notifyRespInd).make());
234         }
235     }
236 
237     @Override
getType()238     public int getType() {
239         return NOTIFICATION_TRANSACTION;
240     }
241 }
242