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