/*
* Copyright (C) 2015 Jacob Klinker
*
* 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.service_alt;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.provider.Telephony;
import android.telephony.SmsManager;
import android.text.TextUtils;
import com.klinker.android.logger.Log;
import com.android.mms.service_alt.exception.MmsHttpException;
import com.google.android.mms.MmsException;
import com.google.android.mms.pdu_alt.GenericPdu;
import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.android.mms.pdu_alt.PduParser;
import com.google.android.mms.pdu_alt.PduPersister;
import com.google.android.mms.pdu_alt.SendConf;
import com.google.android.mms.pdu_alt.SendReq;
import com.google.android.mms.util_alt.SqliteWrapper;
/**
* Request to send an MMS
*/
public class SendRequest extends MmsRequest {
private static final String TAG = "SendRequest";
private final Uri mPduUri;
private byte[] mPduData;
private final String mLocationUrl;
private final PendingIntent mSentIntent;
public SendRequest(RequestManager manager, int subId, Uri contentUri, String locationUrl,
PendingIntent sentIntent, String creator, Bundle configOverrides) {
super(manager, subId, creator, configOverrides);
mPduUri = contentUri;
mPduData = null;
mLocationUrl = locationUrl;
mSentIntent = sentIntent;
}
@Override
protected byte[] doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn)
throws MmsHttpException {
final MmsHttpClient mmsHttpClient = netMgr.getOrCreateHttpClient();
if (mmsHttpClient == null) {
Log.e(TAG, "MMS network is not ready!");
throw new MmsHttpException(0/*statusCode*/, "MMS network is not ready");
}
return mmsHttpClient.execute(
mLocationUrl != null ? mLocationUrl : apn.getMmscUrl(),
mPduData,
MmsHttpClient.METHOD_POST,
apn.isProxySet(),
apn.getProxyAddress(),
apn.getProxyPort(),
mMmsConfig);
}
@Override
protected PendingIntent getPendingIntent() {
return mSentIntent;
}
@Override
protected int getQueueType() {
return 0;
}
@Override
protected Uri persistIfRequired(Context context, int result, byte[] response) {
Log.d(TAG, "SendRequest.persistIfRequired");
if (mPduData == null) {
Log.e(TAG, "SendRequest.persistIfRequired: empty PDU");
return null;
}
final long identity = Binder.clearCallingIdentity();
try {
final boolean supportContentDisposition = mMmsConfig.getSupportMmsContentDisposition();
// Persist the request PDU first
GenericPdu pdu = (new PduParser(mPduData, supportContentDisposition)).parse();
if (pdu == null) {
Log.e(TAG, "SendRequest.persistIfRequired: can't parse input PDU");
return null;
}
if (!(pdu instanceof SendReq)) {
Log.d(TAG, "SendRequest.persistIfRequired: not SendReq");
return null;
}
// final PduPersister persister = PduPersister.getPduPersister(context);
// final Uri messageUri = persister.persist(
// pdu,
// Telephony.Mms.Sent.CONTENT_URI,
// true/*createThreadId*/,
// true/*groupMmsEnabled*/,
// null/*preOpenedFiles*/);
// if (messageUri == null) {
// Log.e(TAG, "SendRequest.persistIfRequired: can not persist message");
// return null;
// }
// Update the additional columns based on the send result
final ContentValues values = new ContentValues();
SendConf sendConf = null;
if (response != null && response.length > 0) {
pdu = (new PduParser(response, supportContentDisposition)).parse();
if (pdu != null && pdu instanceof SendConf) {
sendConf = (SendConf) pdu;
}
}
if (result != Activity.RESULT_OK
|| sendConf == null
|| sendConf.getResponseStatus() != PduHeaders.RESPONSE_STATUS_OK) {
// Since we can't persist a message directly into FAILED box,
// we have to update the column after we persist it into SENT box.
// The gap between the state change is tiny so I would not expect
// it to cause any serious problem
// TODO: we should add a "failed" URI for this in MmsProvider?
values.put(Telephony.Mms.MESSAGE_BOX, Telephony.Mms.MESSAGE_BOX_FAILED);
} else {
values.put(Telephony.Mms.MESSAGE_BOX, Telephony.Mms.MESSAGE_BOX_SENT);
}
if (sendConf != null) {
values.put(Telephony.Mms.RESPONSE_STATUS, sendConf.getResponseStatus());
Log.v(TAG, "response status: " + sendConf.getResponseStatus());
values.put(Telephony.Mms.MESSAGE_ID,
PduPersister.toIsoString(sendConf.getMessageId()));
}
values.put(Telephony.Mms.DATE, System.currentTimeMillis() / 1000L);
values.put(Telephony.Mms.READ, 1);
values.put(Telephony.Mms.SEEN, 1);
if (!TextUtils.isEmpty(mCreator)) {
values.put(Telephony.Mms.CREATOR, mCreator);
}
if (SubscriptionIdChecker.getInstance(context).canUseSubscriptionId()) {
values.put(Telephony.Mms.SUBSCRIPTION_ID, mSubId);
}
if (SqliteWrapper.update(context, context.getContentResolver(), mPduUri, values,
null/*where*/, null/*selectionArg*/) != 1) {
Log.e(TAG, "SendRequest.persistIfRequired: failed to update message");
}
return mPduUri;
// } catch (MmsException e) {
// Log.e(TAG, "SendRequest.persistIfRequired: can not persist message", e);
} catch (RuntimeException e) {
Log.e(TAG, "SendRequest.persistIfRequired: unexpected parsing failure", e);
} finally {
Binder.restoreCallingIdentity(identity);
}
return null;
}
/**
* Read the pdu from the file descriptor and cache pdu bytes in request
* @return true if pdu read successfully
*/
private boolean readPduFromContentUri() {
if (mPduData != null) {
return true;
}
final int bytesTobeRead = mMmsConfig.getMaxMessageSize();
mPduData = mRequestManager.readPduFromContentUri(mPduUri, bytesTobeRead);
return (mPduData != null);
}
/**
* Transfer the received response to the caller (for send requests the pdu is small and can
* just include bytes as extra in the "returned" intent).
*
* @param fillIn the intent that will be returned to the caller
* @param response the pdu to transfer
*/
@Override
protected boolean transferResponse(Intent fillIn, byte[] response) {
// SendConf pdus are always small and can be included in the intent
if (response != null) {
fillIn.putExtra(SmsManager.EXTRA_MMS_DATA, response);
}
return true;
}
/**
* Read the data from the file descriptor if not yet done
* @return whether data successfully read
*/
@Override
protected boolean prepareForHttpRequest() {
return readPduFromContentUri();
}
/**
* Try sending via the carrier app
*
* @param context the context
* @param carrierMessagingServicePackage the carrier messaging service sending the MMS
*/
public void trySendingByCarrierApp(Context context, String carrierMessagingServicePackage) {
// final CarrierSendManager carrierSendManger = new CarrierSendManager();
// final CarrierSendCompleteCallback sendCallback = new CarrierSendCompleteCallback(
// context, carrierSendManger);
// carrierSendManger.sendMms(context, carrierMessagingServicePackage, sendCallback);
}
@Override
protected void revokeUriPermission(Context context) {
try {
context.revokeUriPermission(mPduUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
} catch (NullPointerException e) {
Log.e(TAG, "error revoking permissions", e);
}
}
}