MmsRequest.java revision 18aabe2742cbaffc3c8293cfb3ce2841fe82326d
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 com.android.mms.service.exception.ApnException;
20import com.android.mms.service.exception.MmsHttpException;
21import com.android.mms.service.exception.MmsNetworkException;
22
23import android.app.Activity;
24import android.app.PendingIntent;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.Intent;
28import android.net.Uri;
29import android.os.Bundle;
30import android.provider.Telephony;
31import android.telephony.SmsManager;
32import android.util.Log;
33
34/**
35 * Base class for MMS requests. This has the common logic of sending/downloading MMS.
36 */
37public abstract class MmsRequest {
38    private static final int RETRY_TIMES = 3;
39
40    protected static final String EXTRA_MESSAGE_REF = "messageref";
41
42    /**
43     * Interface for certain functionalities from MmsService
44     */
45    public static interface RequestManager {
46        /**
47         * Add a request to pending queue when it is executed by carrier app
48         *
49         * @param key The message ref key from carrier app
50         * @param request The request in pending
51         */
52        public void addPending(int key, MmsRequest request);
53
54        /**
55         * Enqueue an MMS request for running
56         *
57         * @param request the request to enqueue
58         */
59        public void addRunning(MmsRequest request);
60    }
61
62    // The URI of persisted message
63    protected Uri mMessageUri;
64    // The reference to the pending requests manager (i.e. the MmsService)
65    protected RequestManager mRequestManager;
66    // The SIM id
67    protected long mSubId;
68    // The creator app
69    protected String mCreator;
70
71    // Intent result receiver for carrier app
72    protected final BroadcastReceiver mCarrierAppResultReceiver = new BroadcastReceiver() {
73        @Override
74        public void onReceive(Context context, Intent intent) {
75            final String action = intent.getAction();
76            if (action.equals(Telephony.Mms.Intents.MMS_SEND_ACTION) ||
77                    action.equals(Telephony.Mms.Intents.MMS_DOWNLOAD_ACTION)) {
78                Log.d(MmsService.TAG, "Carrier app result for " + action);
79                final int rc = getResultCode();
80                if (rc == Activity.RESULT_OK) {
81                    // Handled by carrier app, waiting for result
82                    Log.d(MmsService.TAG, "Sending/downloading MMS by IP pending.");
83                    final Bundle resultExtras = getResultExtras(false);
84                    if (resultExtras != null && resultExtras.containsKey(EXTRA_MESSAGE_REF)) {
85                        final int ref = resultExtras.getInt(EXTRA_MESSAGE_REF);
86                        Log.d(MmsService.TAG, "messageref = " + ref);
87                        mRequestManager.addPending(ref, MmsRequest.this);
88                    } else {
89                        // Bad, no message ref provided
90                        Log.e(MmsService.TAG, "Can't find messageref in result extras.");
91                    }
92                } else {
93                    // No carrier app present, sending normally
94                    Log.d(MmsService.TAG, "Sending/downloading MMS by IP failed.");
95                    mRequestManager.addRunning(MmsRequest.this);
96                }
97            } else {
98                Log.e(MmsService.TAG, "unexpected BroadcastReceiver action: " + action);
99            }
100
101        }
102    };
103
104    public MmsRequest(RequestManager requestManager, Uri messageUri, long subId, String creator) {
105        mRequestManager = requestManager;
106        mMessageUri = messageUri;
107        mSubId = subId;
108        mCreator = creator;
109    }
110
111    /**
112     * Execute the request
113     *
114     * @param context The context
115     * @param networkManager The network manager to use
116     */
117    public void execute(Context context, MmsNetworkManager networkManager) {
118        int result = Activity.RESULT_OK;
119        byte[] response = null;
120        long retryDelay = 2;
121        // Try multiple times of MMS HTTP request
122        for (int i = 0; i < RETRY_TIMES; i++) {
123            try {
124                networkManager.acquireNetwork();
125                try {
126                    final ApnSettings apn = ApnSettings.load(context, null/*apnName*/, mSubId);
127                    response = doHttp(context, apn);
128                    result = Activity.RESULT_OK;
129                    // Success
130                    break;
131                } finally {
132                    networkManager.releaseNetwork();
133                }
134            } catch (ApnException e) {
135                Log.e(MmsService.TAG, "MmsRequest: APN failure", e);
136                result = SmsManager.MMS_ERROR_INVALID_APN;
137                break;
138            } catch (MmsNetworkException e) {
139                Log.e(MmsService.TAG, "MmsRequest: MMS network acquiring failure", e);
140                result = SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS;
141                // Retry
142            } catch (MmsHttpException e) {
143                Log.e(MmsService.TAG, "MmsRequest: HTTP or network I/O failure", e);
144                result = SmsManager.MMS_ERROR_HTTP_FAILURE;
145                // Retry
146            } catch (Exception e) {
147                Log.e(MmsService.TAG, "MmsRequest: unexpected failure", e);
148                result = SmsManager.MMS_ERROR_UNSPECIFIED;
149                break;
150            }
151            try {
152                Thread.sleep(retryDelay * 1000, 0/*nano*/);
153            } catch (InterruptedException e) {}
154            retryDelay <<= 1;
155        }
156        processResult(context, result, response);
157    }
158
159    /**
160     * Process the result of the completed request, including updating the message status
161     * in database and sending back the result via pending intents.
162     *
163     * @param context The context
164     * @param result The result code of execution
165     * @param response The response body
166     */
167    public void processResult(Context context, int result, byte[] response) {
168        updateStatus(context, result, response);
169
170        // Return MMS HTTP request result via PendingIntent
171        final PendingIntent pendingIntent = getPendingIntent();
172        if (pendingIntent != null) {
173            // Extra information to send back with the pending intent
174            Intent fillIn = new Intent();
175            if (response != null) {
176                fillIn.putExtra(SmsManager.MMS_EXTRA_DATA, response);
177            }
178            if (mMessageUri != null) {
179                fillIn.putExtra("uri", mMessageUri.toString());
180            }
181            try {
182                pendingIntent.send(context, result, fillIn);
183            } catch (PendingIntent.CanceledException e) {
184                Log.e(MmsService.TAG, "MmsRequest: sending pending intent canceled", e);
185            }
186        }
187    }
188
189    /**
190     * Making the HTTP request to MMSC
191     *
192     * @param context The context
193     * @param apn The APN setting
194     * @return The HTTP response data
195     * @throws MmsHttpException If any network error happens
196     */
197    protected abstract byte[] doHttp(Context context, ApnSettings apn) throws MmsHttpException;
198
199    /**
200     * @return The PendingIntent associate with the MMS sending invocation
201     */
202    protected abstract PendingIntent getPendingIntent();
203
204    /**
205     * @return The running queue should be used by this request
206     */
207    protected abstract int getRunningQueue();
208
209    /**
210     * Update database status of the message represented by this request
211     *
212     * @param context The context
213     * @param result The result code of execution
214     * @param response The response body
215     */
216    protected abstract void updateStatus(Context context, int result, byte[] response);
217}
218