• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.mms.service;
18 
19 import com.google.android.mms.MmsException;
20 import com.google.android.mms.pdu.GenericPdu;
21 import com.google.android.mms.pdu.PduParser;
22 import com.google.android.mms.pdu.PduPersister;
23 import com.google.android.mms.pdu.SendConf;
24 import com.google.android.mms.pdu.SendReq;
25 import com.google.android.mms.util.SqliteWrapper;
26 
27 import com.android.mms.service.exception.MmsHttpException;
28 
29 import android.app.Activity;
30 import android.app.AppOpsManager;
31 import android.app.PendingIntent;
32 import android.content.ContentValues;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.database.sqlite.SQLiteException;
36 import android.net.Uri;
37 import android.os.Binder;
38 import android.os.Bundle;
39 import android.os.UserHandle;
40 import android.provider.Telephony;
41 import android.telephony.SmsManager;
42 import android.telephony.TelephonyManager;
43 import android.text.TextUtils;
44 import android.util.Log;
45 
46 import java.util.List;
47 
48 /**
49  * Request to send an MMS
50  */
51 public class SendRequest extends MmsRequest {
52     private final Uri mPduUri;
53     private byte[] mPduData;
54     private final String mLocationUrl;
55     private final PendingIntent mSentIntent;
56 
SendRequest(RequestManager manager, long subId, Uri contentUri, Uri messageUri, String locationUrl, PendingIntent sentIntent, String creator, Bundle configOverrides)57     public SendRequest(RequestManager manager, long subId, Uri contentUri, Uri messageUri,
58             String locationUrl, PendingIntent sentIntent, String creator,
59             Bundle configOverrides) {
60         super(manager, messageUri, subId, creator, configOverrides);
61         mPduUri = contentUri;
62         mPduData = null;
63         mLocationUrl = locationUrl;
64         mSentIntent = sentIntent;
65     }
66 
67     @Override
doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn)68     protected byte[] doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn)
69             throws MmsHttpException {
70         return doHttpForResolvedAddresses(context,
71                 netMgr,
72                 mLocationUrl != null ? mLocationUrl : apn.getMmscUrl(),
73                 mPduData,
74                 HttpUtils.HTTP_POST_METHOD,
75                 apn);
76     }
77 
78     @Override
getPendingIntent()79     protected PendingIntent getPendingIntent() {
80         return mSentIntent;
81     }
82 
83     @Override
getRunningQueue()84     protected int getRunningQueue() {
85         return MmsService.QUEUE_INDEX_SEND;
86     }
87 
storeInOutbox(Context context)88     public void storeInOutbox(Context context) {
89         final long identity = Binder.clearCallingIdentity();
90         try {
91             // Read message using phone process identity
92             if (!readPduFromContentUri()) {
93                 Log.e(MmsService.TAG, "SendRequest.storeInOutbox: empty PDU");
94                 return;
95             }
96             if (mMessageUri == null) {
97                 // This is a new message to send
98                 final GenericPdu pdu = (new PduParser(mPduData)).parse();
99                 if (pdu == null) {
100                     Log.e(MmsService.TAG, "SendRequest.storeInOutbox: can't parse input PDU");
101                     return;
102                 }
103                 if (!(pdu instanceof SendReq)) {
104                     Log.d(MmsService.TAG, "SendRequest.storeInOutbox: not SendReq");
105                     return;
106                 }
107                 final PduPersister persister = PduPersister.getPduPersister(context);
108                 mMessageUri = persister.persist(
109                         pdu,
110                         Telephony.Mms.Outbox.CONTENT_URI,
111                         true/*createThreadId*/,
112                         true/*groupMmsEnabled*/,
113                         null/*preOpenedFiles*/);
114                 if (mMessageUri == null) {
115                     Log.e(MmsService.TAG, "SendRequest.storeInOutbox: can not persist message");
116                     return;
117                 }
118                 final ContentValues values = new ContentValues(5);
119                 values.put(Telephony.Mms.DATE, System.currentTimeMillis() / 1000L);
120                 values.put(Telephony.Mms.READ, 1);
121                 values.put(Telephony.Mms.SEEN, 1);
122                 if (!TextUtils.isEmpty(mCreator)) {
123                     values.put(Telephony.Mms.CREATOR, mCreator);
124                 }
125                 values.put(Telephony.Mms.SUB_ID, mSubId);
126                 if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values,
127                         null/*where*/, null/*selectionArg*/) != 1) {
128                     Log.e(MmsService.TAG, "SendRequest.storeInOutbox: failed to update message");
129                 }
130             } else {
131                 // This is a stored message, either in FAILED or DRAFT
132                 // Move this to OUTBOX for sending
133                 final ContentValues values = new ContentValues(3);
134                 // Reset the timestamp
135                 values.put(Telephony.Mms.DATE, System.currentTimeMillis() / 1000L);
136                 values.put(Telephony.Mms.MESSAGE_BOX, Telephony.Mms.MESSAGE_BOX_OUTBOX);
137                 values.put(Telephony.Mms.SUB_ID, mSubId);
138                 if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values,
139                         null/*where*/, null/*selectionArg*/) != 1) {
140                     Log.e(MmsService.TAG, "SendRequest.storeInOutbox: failed to update message");
141                 }
142             }
143         } catch (MmsException e) {
144             Log.e(MmsService.TAG, "SendRequest.storeInOutbox: can not persist/update message", e);
145         } catch (RuntimeException e) {
146             Log.e(MmsService.TAG, "SendRequest.storeInOutbox: unexpected parsing failure", e);
147         } finally {
148             Binder.restoreCallingIdentity(identity);
149         }
150     }
151 
152     /**
153      * Read the pdu from the file descriptor and cache pdu bytes in request
154      * @return true if pdu read successfully
155      */
readPduFromContentUri()156     private boolean readPduFromContentUri() {
157         if (mPduData != null) {
158             return true;
159         }
160         final int bytesTobeRead = mMmsConfig.getMaxMessageSize();
161         mPduData = mRequestManager.readPduFromContentUri(mPduUri, bytesTobeRead);
162         return (mPduData != null);
163     }
164 
165     @Override
updateStatus(Context context, int result, byte[] response)166     protected void updateStatus(Context context, int result, byte[] response) {
167         if (mMessageUri == null) {
168             return;
169         }
170         final long identity = Binder.clearCallingIdentity();
171         try {
172             final int messageStatus = result == Activity.RESULT_OK ?
173                     Telephony.Mms.MESSAGE_BOX_SENT : Telephony.Mms.MESSAGE_BOX_FAILED;
174             SendConf sendConf = null;
175             if (response != null && response.length > 0) {
176                 final GenericPdu pdu = (new PduParser(response)).parse();
177                 if (pdu != null && pdu instanceof SendConf) {
178                     sendConf = (SendConf) pdu;
179                 }
180             }
181             final ContentValues values = new ContentValues(3);
182             values.put(Telephony.Mms.MESSAGE_BOX, messageStatus);
183             if (sendConf != null) {
184                 values.put(Telephony.Mms.RESPONSE_STATUS, sendConf.getResponseStatus());
185                 values.put(Telephony.Mms.MESSAGE_ID,
186                         PduPersister.toIsoString(sendConf.getMessageId()));
187             }
188             SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values,
189                     null/*where*/, null/*selectionArg*/);
190         } catch (SQLiteException e) {
191             Log.e(MmsService.TAG, "SendRequest.updateStatus: can not update message", e);
192         } catch (RuntimeException e) {
193             Log.e(MmsService.TAG, "SendRequest.updateStatus: can not parse response", e);
194         } finally {
195             Binder.restoreCallingIdentity(identity);
196         }
197     }
198 
199     /**
200      * Transfer the received response to the caller (for send requests the pdu is small and can
201      *  just include bytes as extra in the "returned" intent).
202      *
203      * @param fillIn the intent that will be returned to the caller
204      * @param response the pdu to transfer
205      */
206     @Override
transferResponse(Intent fillIn, byte[] response)207     protected boolean transferResponse(Intent fillIn, byte[] response) {
208         // SendConf pdus are always small and can be included in the intent
209         if (response != null) {
210             fillIn.putExtra(SmsManager.EXTRA_MMS_DATA, response);
211         }
212         return true;
213     }
214 
215     /**
216      * Read the data from the file descriptor if not yet done
217      * @return whether data successfully read
218      */
prepareForHttpRequest()219     protected boolean prepareForHttpRequest() {
220         return readPduFromContentUri();
221     }
222 
223     /**
224      * Try sending via the carrier app by sending an intent
225      *
226      * @param context The context
227      */
trySendingByCarrierApp(Context context)228     public void trySendingByCarrierApp(Context context) {
229         TelephonyManager telephonyManager =
230                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
231         Intent intent = new Intent(Telephony.Mms.Intents.MMS_SEND_ACTION);
232         List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(
233                 intent);
234 
235         if (carrierPackages == null || carrierPackages.size() != 1) {
236             mRequestManager.addRunning(this);
237         } else {
238             intent.setPackage(carrierPackages.get(0));
239             intent.putExtra(Telephony.Mms.Intents.EXTRA_MMS_CONTENT_URI, mPduUri);
240             intent.putExtra(Telephony.Mms.Intents.EXTRA_MMS_LOCATION_URL, mLocationUrl);
241             intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
242             context.sendOrderedBroadcastAsUser(
243                     intent,
244                     UserHandle.OWNER,
245                     android.Manifest.permission.RECEIVE_MMS,
246                     AppOpsManager.OP_RECEIVE_MMS,
247                     mCarrierAppResultReceiver,
248                     null/*scheduler*/,
249                     Activity.RESULT_CANCELED,
250                     null/*initialData*/,
251                     null/*initialExtras*/);
252         }
253     }
254 
255     @Override
revokeUriPermission(Context context)256     protected void revokeUriPermission(Context context) {
257         context.revokeUriPermission(mPduUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
258     }
259 }
260