• 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.android.mms.widget.MmsWidgetProvider;
33 import com.google.android.mms.MmsException;
34 import com.google.android.mms.pdu.GenericPdu;
35 import com.google.android.mms.pdu.NotificationInd;
36 import com.google.android.mms.pdu.NotifyRespInd;
37 import com.google.android.mms.pdu.PduComposer;
38 import com.google.android.mms.pdu.PduHeaders;
39 import com.google.android.mms.pdu.PduParser;
40 import com.google.android.mms.pdu.PduPersister;
41 import android.database.sqlite.SqliteWrapper;
42 
43 import android.content.ContentValues;
44 import android.content.Context;
45 import android.database.Cursor;
46 import android.net.Uri;
47 import android.provider.Telephony.Mms;
48 import android.provider.Telephony.Mms.Inbox;
49 import android.telephony.TelephonyManager;
50 import android.util.Log;
51 
52 import java.io.IOException;
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  */
70 public 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 
NotificationTransaction( Context context, int serviceId, TransactionSettings connectionSettings, String uriString)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      */
NotificationTransaction( Context context, int serviceId, TransactionSettings connectionSettings, NotificationInd ind)104     public NotificationTransaction(
105             Context context, int serviceId,
106             TransactionSettings connectionSettings, NotificationInd ind) {
107         super(context, serviceId, connectionSettings);
108 
109         try {
110             mUri = PduPersister.getPduPersister(context).persist(
111                         ind, Inbox.CONTENT_URI);
112         } catch (MmsException e) {
113             Log.e(TAG, "Failed to save NotificationInd in constructor.", e);
114             throw new IllegalArgumentException();
115         }
116 
117         mNotificationInd = ind;
118         mId = new String(ind.getTransactionId());
119     }
120 
121     /*
122      * (non-Javadoc)
123      * @see com.google.android.mms.pdu.Transaction#process()
124      */
125     @Override
process()126     public void process() {
127         new Thread(this, "NotificationTransaction").start();
128     }
129 
run()130     public void run() {
131         DownloadManager downloadManager = DownloadManager.getInstance();
132         boolean autoDownload = downloadManager.isAuto();
133         boolean dataSuspended = (MmsApp.getApplication().getTelephonyManager().getDataState() ==
134                 TelephonyManager.DATA_SUSPENDED);
135         try {
136             if (LOCAL_LOGV) {
137                 Log.v(TAG, "Notification transaction launched: " + this);
138             }
139 
140             // By default, we set status to STATUS_DEFERRED because we
141             // should response MMSC with STATUS_DEFERRED when we cannot
142             // download a MM immediately.
143             int status = STATUS_DEFERRED;
144             // Don't try to download when data is suspended, as it will fail, so defer download
145             if (!autoDownload || dataSuspended) {
146                 downloadManager.markState(mUri, DownloadManager.STATE_UNSTARTED);
147                 sendNotifyRespInd(status);
148                 return;
149             }
150 
151             downloadManager.markState(mUri, DownloadManager.STATE_DOWNLOADING);
152 
153             if (LOCAL_LOGV) {
154                 Log.v(TAG, "Content-Location: " + mContentLocation);
155             }
156 
157             byte[] retrieveConfData = null;
158             // We should catch exceptions here to response MMSC
159             // with STATUS_DEFERRED.
160             try {
161                 retrieveConfData = getPdu(mContentLocation);
162             } catch (IOException e) {
163                 mTransactionState.setState(FAILED);
164             }
165 
166             if (retrieveConfData != null) {
167                 GenericPdu pdu = new PduParser(retrieveConfData).parse();
168                 if ((pdu == null) || (pdu.getMessageType() != MESSAGE_TYPE_RETRIEVE_CONF)) {
169                     Log.e(TAG, "Invalid M-RETRIEVE.CONF PDU. " +
170                             (pdu != null ? "message type: " + pdu.getMessageType() : "null pdu"));
171                     mTransactionState.setState(FAILED);
172                     status = STATUS_UNRECOGNIZED;
173                 } else {
174                     // Save the received PDU (must be a M-RETRIEVE.CONF).
175                     PduPersister p = PduPersister.getPduPersister(mContext);
176                     Uri uri = p.persist(pdu, Inbox.CONTENT_URI);
177 
178                     // Use local time instead of PDU time
179                     ContentValues values = new ContentValues(1);
180                     values.put(Mms.DATE, System.currentTimeMillis() / 1000L);
181                     SqliteWrapper.update(mContext, mContext.getContentResolver(),
182                             uri, values, null, null);
183 
184                     // We have successfully downloaded the new MM. Delete the
185                     // M-NotifyResp.ind from Inbox.
186                     SqliteWrapper.delete(mContext, mContext.getContentResolver(),
187                                          mUri, null, null);
188                     // Notify observers with newly received MM.
189                     mUri = uri;
190                     status = STATUS_RETRIEVED;
191                 }
192             }
193 
194             if (LOCAL_LOGV) {
195                 Log.v(TAG, "status=0x" + Integer.toHexString(status));
196             }
197 
198             // Check the status and update the result state of this Transaction.
199             switch (status) {
200                 case STATUS_RETRIEVED:
201                     mTransactionState.setState(SUCCESS);
202                     break;
203                 case STATUS_DEFERRED:
204                     // STATUS_DEFERRED, may be a failed immediate retrieval.
205                     if (mTransactionState.getState() == INITIALIZED) {
206                         mTransactionState.setState(SUCCESS);
207                     }
208                     break;
209             }
210 
211             sendNotifyRespInd(status);
212 
213             // Make sure this thread isn't over the limits in message count.
214             Recycler.getMmsRecycler().deleteOldMessagesInSameThreadAsMessage(mContext, mUri);
215             MmsWidgetProvider.notifyDatasetChanged(mContext);
216         } catch (Throwable t) {
217             Log.e(TAG, Log.getStackTraceString(t));
218         } finally {
219             mTransactionState.setContentUri(mUri);
220             if (!autoDownload || dataSuspended) {
221                 // Always mark the transaction successful for deferred
222                 // download since any error here doesn't make sense.
223                 mTransactionState.setState(SUCCESS);
224             }
225             if (mTransactionState.getState() != SUCCESS) {
226                 mTransactionState.setState(FAILED);
227                 Log.e(TAG, "NotificationTransaction failed.");
228             }
229             notifyObservers();
230         }
231     }
232 
sendNotifyRespInd(int status)233     private void sendNotifyRespInd(int status) throws MmsException, IOException {
234         // Create the M-NotifyResp.ind
235         NotifyRespInd notifyRespInd = new NotifyRespInd(
236                 PduHeaders.CURRENT_MMS_VERSION,
237                 mNotificationInd.getTransactionId(),
238                 status);
239 
240         // Pack M-NotifyResp.ind and send it
241         if(MmsConfig.getNotifyWapMMSC()) {
242             sendPdu(new PduComposer(mContext, notifyRespInd).make(), mContentLocation);
243         } else {
244             sendPdu(new PduComposer(mContext, notifyRespInd).make());
245         }
246     }
247 
248     @Override
getType()249     public int getType() {
250         return NOTIFICATION_TRANSACTION;
251     }
252 }
253