Home | History | Annotate | Download | only in mms
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.support.v7.mms;
     18 
     19 import android.app.PendingIntent;
     20 import android.content.ContentResolver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.net.Uri;
     24 import android.os.Bundle;
     25 import android.os.Parcel;
     26 import android.os.ParcelFileDescriptor;
     27 import android.os.Parcelable;
     28 import android.telephony.SmsManager;
     29 import android.text.TextUtils;
     30 import android.util.Log;
     31 
     32 import java.io.IOException;
     33 import java.util.concurrent.Callable;
     34 import java.util.concurrent.Future;
     35 import java.util.concurrent.TimeUnit;
     36 
     37 /**
     38  * Request to send an MMS
     39  */
     40 class SendRequest extends MmsRequest {
     41     // Max send response PDU size in bytes (exceeding this may cause problem with
     42     // system intent delivery).
     43     private static final int MAX_SEND_RESPONSE_SIZE = 1000 * 1024;
     44 
     45     private byte[] mPduData;
     46 
     47     SendRequest(final String locationUrl, final Uri pduUri, final PendingIntent sentIntent) {
     48         super(locationUrl, pduUri, sentIntent);
     49     }
     50 
     51     @Override
     52     protected boolean loadRequest(final Context context, final Bundle mmsConfig) {
     53         mPduData = readPduFromContentUri(
     54                 context,
     55                 mPduUri,
     56                 mmsConfig.getInt(
     57                         CarrierConfigValuesLoader.CONFIG_MAX_MESSAGE_SIZE,
     58                         CarrierConfigValuesLoader.CONFIG_MAX_MESSAGE_SIZE_DEFAULT));
     59         return (mPduData != null);
     60     }
     61 
     62     @Override
     63     protected boolean transferResponse(final Context context, final Intent fillIn,
     64             final byte[] response) {
     65         // SendConf pdus are always small and can be included in the intent
     66         if (response != null && fillIn != null) {
     67             if (response.length > MAX_SEND_RESPONSE_SIZE) {
     68                 // If the response PDU is too large, it won't be able to fit in
     69                 // the PendingIntent to be transferred via system IPC.
     70                 return false;
     71             }
     72             fillIn.putExtra(SmsManager.EXTRA_MMS_DATA, response);
     73         }
     74         return true;
     75     }
     76 
     77     @Override
     78     protected byte[] doHttp(Context context, MmsNetworkManager netMgr, ApnSettingsLoader.Apn apn,
     79             Bundle mmsConfig, String userAgent, String uaProfUrl) throws MmsHttpException {
     80         final MmsHttpClient httpClient = netMgr.getHttpClient();
     81         return httpClient.execute(getHttpRequestUrl(apn), mPduData, MmsHttpClient.METHOD_POST,
     82                 !TextUtils.isEmpty(apn.getMmsProxy()), apn.getMmsProxy(), apn.getMmsProxyPort(),
     83                 mmsConfig, userAgent, uaProfUrl);
     84     }
     85 
     86     @Override
     87     protected String getHttpRequestUrl(final ApnSettingsLoader.Apn apn) {
     88         return !TextUtils.isEmpty(mLocationUrl) ? mLocationUrl : apn.getMmsc();
     89     }
     90 
     91     /**
     92      * Read pdu from content provider uri
     93      *
     94      * @param contentUri content provider uri from which to read
     95      * @param maxSize maximum number of bytes to read
     96      * @return pdu bytes if succeeded else null
     97      */
     98     public byte[] readPduFromContentUri(final Context context, final Uri contentUri,
     99             final int maxSize) {
    100         if (contentUri == null) {
    101             return null;
    102         }
    103         final Callable<byte[]> copyPduToArray = new Callable<byte[]>() {
    104             public byte[] call() {
    105                 ParcelFileDescriptor.AutoCloseInputStream inStream = null;
    106                 try {
    107                     final ContentResolver cr = context.getContentResolver();
    108                     final ParcelFileDescriptor pduFd = cr.openFileDescriptor(contentUri, "r");
    109                     inStream = new ParcelFileDescriptor.AutoCloseInputStream(pduFd);
    110                     // Request one extra byte to make sure file not bigger than maxSize
    111                     final byte[] readBuf = new byte[maxSize+1];
    112                     final int bytesRead = inStream.read(readBuf, 0, maxSize+1);
    113                     if (bytesRead <= 0) {
    114                         Log.e(MmsService.TAG, "Reading PDU from sender: empty PDU");
    115                         return null;
    116                     }
    117                     if (bytesRead > maxSize) {
    118                         Log.e(MmsService.TAG, "Reading PDU from sender: PDU too large");
    119                         return null;
    120                     }
    121                     // Copy and return the exact length of bytes
    122                     final byte[] result = new byte[bytesRead];
    123                     System.arraycopy(readBuf, 0, result, 0, bytesRead);
    124                     return result;
    125                 } catch (IOException e) {
    126                     Log.e(MmsService.TAG, "Reading PDU from sender: IO exception", e);
    127                     return null;
    128                 } finally {
    129                     if (inStream != null) {
    130                         try {
    131                             inStream.close();
    132                         } catch (IOException ex) {
    133                             // Ignore
    134                         }
    135                     }
    136                 }
    137             }
    138         };
    139         final Future<byte[]> pendingResult = mPduTransferExecutor.submit(copyPduToArray);
    140         try {
    141             return pendingResult.get(TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
    142         } catch (Exception e) {
    143             // Typically a timeout occurred - cancel task
    144             pendingResult.cancel(true);
    145         }
    146         return null;
    147     }
    148 
    149     public static final Parcelable.Creator<SendRequest> CREATOR
    150             = new Parcelable.Creator<SendRequest>() {
    151         public SendRequest createFromParcel(Parcel in) {
    152             return new SendRequest(in);
    153         }
    154 
    155         public SendRequest[] newArray(int size) {
    156             return new SendRequest[size];
    157         }
    158     };
    159 
    160     private SendRequest(Parcel in) {
    161         super(in);
    162     }
    163 }
    164