/* * 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 cn.edu.tsinghua.hpc.tmms.transaction; import static cn.edu.tsinghua.hpc.google.tmms.pdu.PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF; import static cn.edu.tsinghua.hpc.google.tmms.pdu.PduHeaders.STATUS_DEFERRED; import static cn.edu.tsinghua.hpc.google.tmms.pdu.PduHeaders.STATUS_RETRIEVED; import static cn.edu.tsinghua.hpc.google.tmms.pdu.PduHeaders.STATUS_UNRECOGNIZED; import static cn.edu.tsinghua.hpc.tmms.transaction.TransactionState.FAILED; import static cn.edu.tsinghua.hpc.tmms.transaction.TransactionState.INITIALIZED; import static cn.edu.tsinghua.hpc.tmms.transaction.TransactionState.SUCCESS; import java.io.IOException; import java.lang.reflect.Method; import SQLite.StringEncoder; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.net.Uri; import android.telephony.TelephonyManager; import android.util.Config; import android.util.Log; import cn.edu.tsinghua.hpc.google.tmms.InvalidHeaderValueException; import cn.edu.tsinghua.hpc.google.tmms.MmsException; import cn.edu.tsinghua.hpc.google.tmms.pdu.GenericPdu; import cn.edu.tsinghua.hpc.google.tmms.pdu.NotificationInd; import cn.edu.tsinghua.hpc.google.tmms.pdu.NotifyRespInd; import cn.edu.tsinghua.hpc.google.tmms.pdu.PduComposer; import cn.edu.tsinghua.hpc.google.tmms.pdu.PduHeaders; import cn.edu.tsinghua.hpc.google.tmms.pdu.PduParser; import cn.edu.tsinghua.hpc.google.tmms.pdu.PduPersister; import cn.edu.tsinghua.hpc.google.tmms.util.SqliteWrapper; import cn.edu.tsinghua.hpc.tmms.MmsConfig; import cn.edu.tsinghua.hpc.tmms.util.DownloadManager; import cn.edu.tsinghua.hpc.tmms.util.Recycler; import cn.edu.tsinghua.hpc.tmms.util.TTelephony.TMms; import cn.edu.tsinghua.hpc.tmms.util.TTelephony.TMmsSms; /** * 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 = DEBUG ? Config.LOGD : Config.LOGV; 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 { mUri = PduPersister.getPduPersister(context).persist( ind, TMms.TInbox.CONTENT_URI); } 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.Transaction#process() */ @Override public void process() { new Thread(this).start(); } public void run() { DownloadManager downloadManager = DownloadManager.getInstance(); boolean autoDownload = downloadManager.isAuto(); boolean dataSuspended = false; try { Method getDefaultMethod = Class.forName( "android.telephony.TelephonyManager").getMethod( "getDefault", new Class[] {}); getDefaultMethod.setAccessible(true); dataSuspended = (Integer) getDefaultMethod.invoke(null, new Object[] {}) == TelephonyManager.DATA_SUSPENDED; } catch (Exception e) { Log.d(TAG, e.getMessage()); } // boolean dataSuspended = (TelephonyManager.getDefault().getDataState() ); 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 || dataSuspended) { 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."); 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, TMms.TInbox.CONTENT_URI); // We have successfully downloaded the new MM. Delete the // M-NotifyResp.ind from Inbox. SqliteWrapper.delete(mContext, mContext.getContentResolver(), mUri, null, null); /** * Add by Yangyang Zhao */ int id = (int) ContentUris.parseId(uri); Uri mmsdataUri = Uri.withAppendedPath(TMms.AUTHORITY_URI, "data"); //Uri.parse("content://mms/data/"); ContentValues values = new ContentValues(); values.put("mms_id", id); values.put("data", StringEncoder.encode(retrieveConfData)); mContext.getContentResolver().insert(mmsdataUri, values); //MmsUtils.notifySyncService(mContext); // 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); } catch (Throwable t) { Log.e(TAG, Log.getStackTraceString(t)); } finally { mTransactionState.setContentUri(mUri); if (!autoDownload || dataSuspended) { // 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, InvalidHeaderValueException { // 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; } }