blob: 647f6e242f1f5ca78c2b37ff3da6812f8ad55d1f [file] [log] [blame]
/*
* Copyright (C) 2007-2008 Esmertec AG.
* Copyright (C) 2007-2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mms.transaction;
import static com.android.mms.transaction.TransactionState.FAILED;
import static com.android.mms.transaction.TransactionState.INITIALIZED;
import static com.android.mms.transaction.TransactionState.SUCCESS;
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF;
import static com.google.android.mms.pdu.PduHeaders.STATUS_DEFERRED;
import static com.google.android.mms.pdu.PduHeaders.STATUS_RETRIEVED;
import static com.google.android.mms.pdu.PduHeaders.STATUS_UNRECOGNIZED;
import java.io.IOException;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SqliteWrapper;
import android.net.Uri;
import android.provider.Telephony.Mms;
import android.provider.Telephony.Threads;
import android.provider.Telephony.Mms.Inbox;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.mms.MmsApp;
import com.android.mms.MmsConfig;
import com.android.mms.ui.MessagingPreferenceActivity;
import com.android.mms.util.DownloadManager;
import com.android.mms.util.Recycler;
import com.android.mms.widget.MmsWidgetProvider;
import com.google.android.mms.MmsException;
import com.google.android.mms.pdu.GenericPdu;
import com.google.android.mms.pdu.NotificationInd;
import com.google.android.mms.pdu.NotifyRespInd;
import com.google.android.mms.pdu.PduComposer;
import com.google.android.mms.pdu.PduHeaders;
import com.google.android.mms.pdu.PduParser;
import com.google.android.mms.pdu.PduPersister;
/**
* The NotificationTransaction is responsible for handling multimedia
* message notifications (M-Notification.ind). It:
*
* <ul>
* <li>Composes the notification response (M-NotifyResp.ind).
* <li>Sends the notification response to the MMSC server.
* <li>Stores the notification indication.
* <li>Notifies the TransactionService about succesful completion.
* </ul>
*
* NOTE: This MMS client handles all notifications with a <b>deferred
* retrieval</b> response. The transaction service, upon succesful
* completion of this transaction, will trigger a retrieve transaction
* in case the client is in immediate retrieve mode.
*/
public class NotificationTransaction extends Transaction implements Runnable {
private static final String TAG = "NotificationTransaction";
private static final boolean DEBUG = false;
private static final boolean LOCAL_LOGV = false;
private Uri mUri;
private NotificationInd mNotificationInd;
private String mContentLocation;
public NotificationTransaction(
Context context, int serviceId,
TransactionSettings connectionSettings, String uriString) {
super(context, serviceId, connectionSettings);
mUri = Uri.parse(uriString);
try {
mNotificationInd = (NotificationInd)
PduPersister.getPduPersister(context).load(mUri);
} catch (MmsException e) {
Log.e(TAG, "Failed to load NotificationInd from: " + uriString, e);
throw new IllegalArgumentException();
}
mId = new String(mNotificationInd.getTransactionId());
mContentLocation = new String(mNotificationInd.getContentLocation());
// Attach the transaction to the instance of RetryScheduler.
attach(RetryScheduler.getInstance(context));
}
/**
* This constructor is only used for test purposes.
*/
public NotificationTransaction(
Context context, int serviceId,
TransactionSettings connectionSettings, NotificationInd ind) {
super(context, serviceId, connectionSettings);
try {
// Save the pdu. If we can start downloading the real pdu immediately, don't allow
// persist() to create a thread for the notificationInd because it causes UI jank.
mUri = PduPersister.getPduPersister(context).persist(
ind, Inbox.CONTENT_URI, !allowAutoDownload(),
MessagingPreferenceActivity.getIsGroupMmsEnabled(context), null);
} catch (MmsException e) {
Log.e(TAG, "Failed to save NotificationInd in constructor.", e);
throw new IllegalArgumentException();
}
mNotificationInd = ind;
mId = new String(ind.getTransactionId());
}
/*
* (non-Javadoc)
* @see com.google.android.mms.pdu.Transaction#process()
*/
@Override
public void process() {
new Thread(this, "NotificationTransaction").start();
}
public static boolean allowAutoDownload() {
DownloadManager downloadManager = DownloadManager.getInstance();
boolean autoDownload = downloadManager.isAuto();
boolean dataSuspended = (MmsApp.getApplication().getTelephonyManager().getDataState() ==
TelephonyManager.DATA_SUSPENDED);
return autoDownload && !dataSuspended;
}
public void run() {
DownloadManager downloadManager = DownloadManager.getInstance();
boolean autoDownload = allowAutoDownload();
try {
if (LOCAL_LOGV) {
Log.v(TAG, "Notification transaction launched: " + this);
}
// By default, we set status to STATUS_DEFERRED because we
// should response MMSC with STATUS_DEFERRED when we cannot
// download a MM immediately.
int status = STATUS_DEFERRED;
// Don't try to download when data is suspended, as it will fail, so defer download
if (!autoDownload) {
downloadManager.markState(mUri, DownloadManager.STATE_UNSTARTED);
sendNotifyRespInd(status);
return;
}
downloadManager.markState(mUri, DownloadManager.STATE_DOWNLOADING);
if (LOCAL_LOGV) {
Log.v(TAG, "Content-Location: " + mContentLocation);
}
byte[] retrieveConfData = null;
// We should catch exceptions here to response MMSC
// with STATUS_DEFERRED.
try {
retrieveConfData = getPdu(mContentLocation);
} catch (IOException e) {
mTransactionState.setState(FAILED);
}
if (retrieveConfData != null) {
GenericPdu pdu = new PduParser(retrieveConfData).parse();
if ((pdu == null) || (pdu.getMessageType() != MESSAGE_TYPE_RETRIEVE_CONF)) {
Log.e(TAG, "Invalid M-RETRIEVE.CONF PDU. " +
(pdu != null ? "message type: " + pdu.getMessageType() : "null pdu"));
mTransactionState.setState(FAILED);
status = STATUS_UNRECOGNIZED;
} else {
// Save the received PDU (must be a M-RETRIEVE.CONF).
PduPersister p = PduPersister.getPduPersister(mContext);
Uri uri = p.persist(pdu, Inbox.CONTENT_URI, true,
MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext), null);
// Use local time instead of PDU time
ContentValues values = new ContentValues(1);
values.put(Mms.DATE, System.currentTimeMillis() / 1000L);
SqliteWrapper.update(mContext, mContext.getContentResolver(),
uri, values, null, null);
// We have successfully downloaded the new MM. Delete the
// M-NotifyResp.ind from Inbox.
SqliteWrapper.delete(mContext, mContext.getContentResolver(),
mUri, null, null);
Log.v(TAG, "NotificationTransaction received new mms message: " + uri);
// Delete obsolete threads
SqliteWrapper.delete(mContext, mContext.getContentResolver(),
Threads.OBSOLETE_THREADS_URI, null, null);
// Notify observers with newly received MM.
mUri = uri;
status = STATUS_RETRIEVED;
}
}
if (LOCAL_LOGV) {
Log.v(TAG, "status=0x" + Integer.toHexString(status));
}
// Check the status and update the result state of this Transaction.
switch (status) {
case STATUS_RETRIEVED:
mTransactionState.setState(SUCCESS);
break;
case STATUS_DEFERRED:
// STATUS_DEFERRED, may be a failed immediate retrieval.
if (mTransactionState.getState() == INITIALIZED) {
mTransactionState.setState(SUCCESS);
}
break;
}
sendNotifyRespInd(status);
// Make sure this thread isn't over the limits in message count.
Recycler.getMmsRecycler().deleteOldMessagesInSameThreadAsMessage(mContext, mUri);
MmsWidgetProvider.notifyDatasetChanged(mContext);
} catch (Throwable t) {
Log.e(TAG, Log.getStackTraceString(t));
} finally {
mTransactionState.setContentUri(mUri);
if (!autoDownload) {
// Always mark the transaction successful for deferred
// download since any error here doesn't make sense.
mTransactionState.setState(SUCCESS);
}
if (mTransactionState.getState() != SUCCESS) {
mTransactionState.setState(FAILED);
Log.e(TAG, "NotificationTransaction failed.");
}
notifyObservers();
}
}
private void sendNotifyRespInd(int status) throws MmsException, IOException {
// Create the M-NotifyResp.ind
NotifyRespInd notifyRespInd = new NotifyRespInd(
PduHeaders.CURRENT_MMS_VERSION,
mNotificationInd.getTransactionId(),
status);
// Pack M-NotifyResp.ind and send it
if(MmsConfig.getNotifyWapMMSC()) {
sendPdu(new PduComposer(mContext, notifyRespInd).make(), mContentLocation);
} else {
sendPdu(new PduComposer(mContext, notifyRespInd).make());
}
}
@Override
public int getType() {
return NOTIFICATION_TRANSACTION;
}
}