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