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 androidx.appcompat.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 SendRequest(final String locationUrl, final Uri pduUri, final PendingIntent sentIntent)47 SendRequest(final String locationUrl, final Uri pduUri, final PendingIntent sentIntent) { 48 super(locationUrl, pduUri, sentIntent); 49 } 50 51 @Override loadRequest(final Context context, final Bundle mmsConfig)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 transferResponse(final Context context, final Intent fillIn, final byte[] response)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 doHttp(Context context, MmsNetworkManager netMgr, ApnSettingsLoader.Apn apn, Bundle mmsConfig, String userAgent, String uaProfUrl)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 getHttpRequestUrl(final ApnSettingsLoader.Apn apn)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 */ readPduFromContentUri(final Context context, final Uri contentUri, final int maxSize)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 SendRequest(Parcel in)160 private SendRequest(Parcel in) { 161 super(in); 162 } 163 } 164