• 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.ContentValues;
43 import android.content.Context;
44 import android.database.Cursor;
45 import android.net.Uri;
46 import android.provider.Telephony.Mms;
47 import android.provider.Telephony.Mms.Inbox;
48 import android.telephony.TelephonyManager;
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 = false;
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                             (pdu != null ? "message type: " + pdu.getMessageType() : "null pdu"));
170                     mTransactionState.setState(FAILED);
171                     status = STATUS_UNRECOGNIZED;
172                 } else {
173                     // Save the received PDU (must be a M-RETRIEVE.CONF).
174                     PduPersister p = PduPersister.getPduPersister(mContext);
175                     Uri uri = p.persist(pdu, Inbox.CONTENT_URI);
176 
177                     // Use local time instead of PDU time
178                     ContentValues values = new ContentValues(1);
179                     values.put(Mms.DATE, System.currentTimeMillis() / 1000L);
180                     SqliteWrapper.update(mContext, mContext.getContentResolver(),
181                             uri, values, null, null);
182 
183                     // We have successfully downloaded the new MM. Delete the
184                     // M-NotifyResp.ind from Inbox.
185                     SqliteWrapper.delete(mContext, mContext.getContentResolver(),
186                                          mUri, null, null);
187                     // Notify observers with newly received MM.
188                     mUri = uri;
189                     status = STATUS_RETRIEVED;
190                 }
191             }
192 
193             if (LOCAL_LOGV) {
194                 Log.v(TAG, "status=0x" + Integer.toHexString(status));
195             }
196 
197             // Check the status and update the result state of this Transaction.
198             switch (status) {
199                 case STATUS_RETRIEVED:
200                     mTransactionState.setState(SUCCESS);
201                     break;
202                 case STATUS_DEFERRED:
203                     // STATUS_DEFERRED, may be a failed immediate retrieval.
204                     if (mTransactionState.getState() == INITIALIZED) {
205                         mTransactionState.setState(SUCCESS);
206                     }
207                     break;
208             }
209 
210             sendNotifyRespInd(status);
211 
212             // Make sure this thread isn't over the limits in message count.
213             Recycler.getMmsRecycler().deleteOldMessagesInSameThreadAsMessage(mContext, mUri);
214         } catch (Throwable t) {
215             Log.e(TAG, Log.getStackTraceString(t));
216         } finally {
217             mTransactionState.setContentUri(mUri);
218             if (!autoDownload || dataSuspended) {
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 
sendNotifyRespInd(int status)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         if(MmsConfig.getNotifyWapMMSC()) {
240             sendPdu(new PduComposer(mContext, notifyRespInd).make(), mContentLocation);
241         } else {
242             sendPdu(new PduComposer(mContext, notifyRespInd).make());
243         }
244     }
245 
246     @Override
getType()247     public int getType() {
248         return NOTIFICATION_TRANSACTION;
249     }
250 }
251