DownloadRequest.java revision 79ab3ac28d02ba5f05d012a63cffa73931070a85
1/*
2 * Copyright (C) 2014 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
17package com.android.mms.service;
18
19import android.app.Activity;
20import android.app.AppOpsManager;
21import android.app.PendingIntent;
22import android.content.ContentValues;
23import android.content.Context;
24import android.content.Intent;
25import android.database.sqlite.SQLiteException;
26import android.net.Uri;
27import android.os.Binder;
28import android.os.Bundle;
29import android.os.UserHandle;
30import android.provider.Telephony;
31import android.telephony.TelephonyManager;
32import android.text.TextUtils;
33import android.util.Log;
34
35import com.android.mms.service.exception.MmsHttpException;
36import com.google.android.mms.MmsException;
37import com.google.android.mms.pdu.GenericPdu;
38import com.google.android.mms.pdu.PduHeaders;
39import com.google.android.mms.pdu.PduParser;
40import com.google.android.mms.pdu.PduPersister;
41import com.google.android.mms.pdu.RetrieveConf;
42import com.google.android.mms.util.SqliteWrapper;
43
44import java.util.List;
45
46/**
47 * Request to download an MMS
48 */
49public class DownloadRequest extends MmsRequest {
50    private static final String LOCATION_SELECTION =
51            Telephony.Mms.MESSAGE_TYPE + "=? AND " + Telephony.Mms.CONTENT_LOCATION + " =?";
52
53    private final String mLocationUrl;
54    private final PendingIntent mDownloadedIntent;
55    private final Uri mContentUri;
56
57    public DownloadRequest(RequestManager manager, int subId, String locationUrl,
58            Uri contentUri, PendingIntent downloadedIntent, String creator,
59            Bundle configOverrides) {
60        super(manager, subId, creator, configOverrides);
61        mLocationUrl = locationUrl;
62        mDownloadedIntent = downloadedIntent;
63        mContentUri = contentUri;
64    }
65
66    @Override
67    protected byte[] doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn)
68            throws MmsHttpException {
69        final MmsHttpClient mmsHttpClient = netMgr.getOrCreateHttpClient();
70        if (mmsHttpClient == null) {
71            Log.e(MmsService.TAG, "MMS network is not ready!");
72            throw new MmsHttpException(0/*statusCode*/, "MMS network is not ready");
73        }
74        return mmsHttpClient.execute(
75                mLocationUrl,
76                null/*pud*/,
77                MmsHttpClient.METHOD_GET,
78                apn.isProxySet(),
79                apn.getProxyAddress(),
80                apn.getProxyPort(),
81                mMmsConfig);
82    }
83
84    @Override
85    protected PendingIntent getPendingIntent() {
86        return mDownloadedIntent;
87    }
88
89    @Override
90    protected int getQueueType() {
91        return MmsService.QUEUE_INDEX_DOWNLOAD;
92    }
93
94    @Override
95    protected Uri persistIfRequired(Context context, int result, byte[] response) {
96        if (!mRequestManager.getAutoPersistingPref()) {
97            return null;
98        }
99        Log.d(MmsService.TAG, "DownloadRequest.persistIfRequired");
100        if (response == null || response.length < 1) {
101            Log.e(MmsService.TAG, "DownloadRequest.persistIfRequired: empty response");
102            return null;
103        }
104        final long identity = Binder.clearCallingIdentity();
105        try {
106            final GenericPdu pdu =
107                    (new PduParser(response, mMmsConfig.getSupportMmsContentDisposition())).parse();
108            if (pdu == null || !(pdu instanceof RetrieveConf)) {
109                Log.e(MmsService.TAG, "DownloadRequest.persistIfRequired: invalid parsed PDU");
110                return null;
111            }
112            final RetrieveConf retrieveConf = (RetrieveConf) pdu;
113            final int status = retrieveConf.getRetrieveStatus();
114            if (status != PduHeaders.RETRIEVE_STATUS_OK) {
115                Log.e(MmsService.TAG, "DownloadRequest.persistIfRequired: retrieve failed "
116                        + status);
117                // Update the retrieve status of the NotificationInd
118                final ContentValues values = new ContentValues(1);
119                values.put(Telephony.Mms.RETRIEVE_STATUS, status);
120                SqliteWrapper.update(
121                        context,
122                        context.getContentResolver(),
123                        Telephony.Mms.CONTENT_URI,
124                        values,
125                        LOCATION_SELECTION,
126                        new String[] {
127                                Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND),
128                                mLocationUrl
129                        });
130                return null;
131            }
132            // Store the downloaded message
133            final PduPersister persister = PduPersister.getPduPersister(context);
134            final Uri messageUri = persister.persist(
135                    pdu,
136                    Telephony.Mms.Inbox.CONTENT_URI,
137                    true/*createThreadId*/,
138                    true/*groupMmsEnabled*/,
139                    null/*preOpenedFiles*/);
140            if (messageUri == null) {
141                Log.e(MmsService.TAG, "DownloadRequest.persistIfRequired: can not persist message");
142                return null;
143            }
144            // Update some of the properties of the message
145            final ContentValues values = new ContentValues();
146            values.put(Telephony.Mms.DATE, System.currentTimeMillis() / 1000L);
147            values.put(Telephony.Mms.READ, 0);
148            values.put(Telephony.Mms.SEEN, 0);
149            if (!TextUtils.isEmpty(mCreator)) {
150                values.put(Telephony.Mms.CREATOR, mCreator);
151            }
152            values.put(Telephony.Mms.SUBSCRIPTION_ID, mSubId);
153            if (SqliteWrapper.update(
154                    context,
155                    context.getContentResolver(),
156                    messageUri,
157                    values,
158                    null/*where*/,
159                    null/*selectionArg*/) != 1) {
160                Log.e(MmsService.TAG, "DownloadRequest.persistIfRequired: can not update message");
161            }
162            // Delete the corresponding NotificationInd
163            SqliteWrapper.delete(context,
164                    context.getContentResolver(),
165                    Telephony.Mms.CONTENT_URI,
166                    LOCATION_SELECTION,
167                    new String[]{
168                            Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND),
169                            mLocationUrl
170                    });
171            return messageUri;
172        } catch (MmsException e) {
173            Log.e(MmsService.TAG, "DownloadRequest.persistIfRequired: can not persist message", e);
174        } catch (SQLiteException e) {
175            Log.e(MmsService.TAG, "DownloadRequest.persistIfRequired: can not update message", e);
176        } catch (RuntimeException e) {
177            Log.e(MmsService.TAG, "DownloadRequest.persistIfRequired: can not parse response", e);
178        } finally {
179            Binder.restoreCallingIdentity(identity);
180        }
181        return null;
182    }
183
184    /**
185     * Transfer the received response to the caller (for download requests write to content uri)
186     *
187     * @param fillIn the intent that will be returned to the caller
188     * @param response the pdu to transfer
189     */
190    @Override
191    protected boolean transferResponse(Intent fillIn, final byte[] response) {
192        return mRequestManager.writePduToContentUri(mContentUri, response);
193    }
194
195    protected boolean prepareForHttpRequest() {
196        return true;
197    }
198
199    /**
200     * Try downloading via the carrier app by sending intent.
201     *
202     * @param context The context
203     */
204    public void tryDownloadingByCarrierApp(Context context) {
205        TelephonyManager telephonyManager =
206                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
207        Intent intent = new Intent(Telephony.Mms.Intents.MMS_DOWNLOAD_ACTION);
208        List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(
209                intent);
210
211        if (carrierPackages == null || carrierPackages.size() != 1) {
212            mRequestManager.addSimRequest(this);
213        } else {
214            intent.setPackage(carrierPackages.get(0));
215            intent.putExtra(Telephony.Mms.Intents.EXTRA_MMS_LOCATION_URL, mLocationUrl);
216            intent.putExtra(Telephony.Mms.Intents.EXTRA_MMS_CONTENT_URI, mContentUri);
217            intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
218            context.sendOrderedBroadcastAsUser(
219                    intent,
220                    UserHandle.OWNER,
221                    android.Manifest.permission.RECEIVE_MMS,
222                    AppOpsManager.OP_RECEIVE_MMS,
223                    mCarrierAppResultReceiver,
224                    null/*scheduler*/,
225                    Activity.RESULT_CANCELED,
226                    null/*initialData*/,
227                    null/*initialExtras*/);
228        }
229    }
230
231    @Override
232    protected void revokeUriPermission(Context context) {
233        context.revokeUriPermission(mContentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
234    }
235}
236