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 android.app.Activity; 20 import android.app.ActivityManager; 21 import android.app.AppOpsManager; 22 import android.app.PendingIntent; 23 import android.content.ContentValues; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.UserInfo; 27 import android.database.sqlite.SQLiteException; 28 import android.net.Uri; 29 import android.os.Binder; 30 import android.os.Bundle; 31 import android.os.RemoteException; 32 import android.os.UserHandle; 33 import android.os.UserManager; 34 import android.provider.Telephony; 35 import android.service.carrier.CarrierMessagingService; 36 import android.service.carrier.CarrierMessagingServiceWrapper; 37 import android.telephony.SmsManager; 38 import android.text.TextUtils; 39 40 import com.android.mms.service.exception.MmsHttpException; 41 import com.google.android.mms.MmsException; 42 import com.google.android.mms.pdu.GenericPdu; 43 import com.google.android.mms.pdu.PduHeaders; 44 import com.google.android.mms.pdu.PduParser; 45 import com.google.android.mms.pdu.PduPersister; 46 import com.google.android.mms.pdu.RetrieveConf; 47 import com.google.android.mms.util.SqliteWrapper; 48 49 /** 50 * Request to download an MMS 51 */ 52 public class DownloadRequest extends MmsRequest { 53 private static final String LOCATION_SELECTION = 54 Telephony.Mms.MESSAGE_TYPE + "=? AND " + Telephony.Mms.CONTENT_LOCATION + " =?"; 55 56 private final String mLocationUrl; 57 private final PendingIntent mDownloadedIntent; 58 private final Uri mContentUri; 59 DownloadRequest(RequestManager manager, int subId, String locationUrl, Uri contentUri, PendingIntent downloadedIntent, String creator, Bundle configOverrides, Context context, long messageId)60 public DownloadRequest(RequestManager manager, int subId, String locationUrl, 61 Uri contentUri, PendingIntent downloadedIntent, String creator, 62 Bundle configOverrides, Context context, long messageId) { 63 super(manager, subId, creator, configOverrides, context, messageId); 64 mLocationUrl = locationUrl; 65 mDownloadedIntent = downloadedIntent; 66 mContentUri = contentUri; 67 } 68 69 @Override doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn)70 protected byte[] doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn) 71 throws MmsHttpException { 72 final String requestId = getRequestId(); 73 final MmsHttpClient mmsHttpClient = netMgr.getOrCreateHttpClient(); 74 if (mmsHttpClient == null) { 75 LogUtil.e(requestId, "MMS network is not ready! messageId: " + mMessageId); 76 throw new MmsHttpException(0/*statusCode*/, "MMS network is not ready. messageId: " 77 + mMessageId); 78 } 79 return mmsHttpClient.execute( 80 mLocationUrl, 81 null/*pud*/, 82 MmsHttpClient.METHOD_GET, 83 apn.isProxySet(), 84 apn.getProxyAddress(), 85 apn.getProxyPort(), 86 mMmsConfig, 87 mSubId, 88 requestId); 89 } 90 91 @Override getPendingIntent()92 protected PendingIntent getPendingIntent() { 93 return mDownloadedIntent; 94 } 95 96 @Override getQueueType()97 protected int getQueueType() { 98 return MmsService.QUEUE_INDEX_DOWNLOAD; 99 } 100 101 @Override persistIfRequired(Context context, int result, byte[] response)102 protected Uri persistIfRequired(Context context, int result, byte[] response) { 103 final String requestId = getRequestId(); 104 // Let any mms apps running as secondary user know that a new mms has been downloaded. 105 notifyOfDownload(context); 106 107 if (!mRequestManager.getAutoPersistingPref()) { 108 return null; 109 } 110 LogUtil.d(requestId, "persistIfRequired. messageId: " + mMessageId); 111 if (response == null || response.length < 1) { 112 LogUtil.e(requestId, "persistIfRequired: empty response. messageId: " + mMessageId); 113 return null; 114 } 115 final long identity = Binder.clearCallingIdentity(); 116 try { 117 final boolean supportMmsContentDisposition = 118 mMmsConfig.getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION); 119 final GenericPdu pdu = (new PduParser(response, supportMmsContentDisposition)).parse(); 120 if (pdu == null || !(pdu instanceof RetrieveConf)) { 121 LogUtil.e(requestId, "persistIfRequired: invalid parsed PDU. messageId: " 122 + mMessageId); 123 return null; 124 } 125 final RetrieveConf retrieveConf = (RetrieveConf) pdu; 126 final int status = retrieveConf.getRetrieveStatus(); 127 if (status != PduHeaders.RETRIEVE_STATUS_OK) { 128 LogUtil.e(requestId, "persistIfRequired: retrieve failed " + status 129 + ", messageId: " + mMessageId); 130 // Update the retrieve status of the NotificationInd 131 final ContentValues values = new ContentValues(1); 132 values.put(Telephony.Mms.RETRIEVE_STATUS, status); 133 SqliteWrapper.update( 134 context, 135 context.getContentResolver(), 136 Telephony.Mms.CONTENT_URI, 137 values, 138 LOCATION_SELECTION, 139 new String[] { 140 Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND), 141 mLocationUrl 142 }); 143 return null; 144 } 145 // Store the downloaded message 146 final PduPersister persister = PduPersister.getPduPersister(context); 147 final Uri messageUri = persister.persist( 148 pdu, 149 Telephony.Mms.Inbox.CONTENT_URI, 150 true/*createThreadId*/, 151 true/*groupMmsEnabled*/, 152 null/*preOpenedFiles*/); 153 if (messageUri == null) { 154 LogUtil.e(requestId, "persistIfRequired: can not persist message. messageId: " 155 + mMessageId); 156 return null; 157 } 158 // Update some of the properties of the message 159 final ContentValues values = new ContentValues(); 160 values.put(Telephony.Mms.DATE, System.currentTimeMillis() / 1000L); 161 values.put(Telephony.Mms.READ, 0); 162 values.put(Telephony.Mms.SEEN, 0); 163 if (!TextUtils.isEmpty(mCreator)) { 164 values.put(Telephony.Mms.CREATOR, mCreator); 165 } 166 values.put(Telephony.Mms.SUBSCRIPTION_ID, mSubId); 167 if (SqliteWrapper.update( 168 context, 169 context.getContentResolver(), 170 messageUri, 171 values, 172 null/*where*/, 173 null/*selectionArg*/) != 1) { 174 LogUtil.e(requestId, "persistIfRequired: can not update message. messageId: " 175 + mMessageId); 176 } 177 // Delete the corresponding NotificationInd 178 SqliteWrapper.delete(context, 179 context.getContentResolver(), 180 Telephony.Mms.CONTENT_URI, 181 LOCATION_SELECTION, 182 new String[]{ 183 Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND), 184 mLocationUrl 185 }); 186 187 return messageUri; 188 } catch (MmsException e) { 189 LogUtil.e(requestId, "persistIfRequired: can not persist message. messageId: " 190 + mMessageId, e); 191 } catch (SQLiteException e) { 192 LogUtil.e(requestId, "persistIfRequired: can not update message. messageId: " 193 + mMessageId, e); 194 } catch (RuntimeException e) { 195 LogUtil.e(requestId, "persistIfRequired: can not parse response. messageId: " 196 + mMessageId, e); 197 } finally { 198 Binder.restoreCallingIdentity(identity); 199 } 200 return null; 201 } 202 notifyOfDownload(Context context)203 private void notifyOfDownload(Context context) { 204 final Intent intent = new Intent(Telephony.Sms.Intents.MMS_DOWNLOADED_ACTION); 205 intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT); 206 207 // Get a list of currently started users. 208 int[] users = null; 209 try { 210 users = ActivityManager.getService().getRunningUserIds(); 211 } catch (RemoteException re) { 212 } 213 if (users == null) { 214 users = new int[] {UserHandle.ALL.getIdentifier()}; 215 } 216 final UserManager userManager = 217 (UserManager) context.getSystemService(Context.USER_SERVICE); 218 219 // Deliver the broadcast only to those running users that are permitted 220 // by user policy. 221 for (int i = users.length - 1; i >= 0; i--) { 222 UserHandle targetUser = new UserHandle(users[i]); 223 if (users[i] != UserHandle.USER_SYSTEM) { 224 // Is the user not allowed to use SMS? 225 if (userManager.hasUserRestriction(UserManager.DISALLOW_SMS, targetUser)) { 226 continue; 227 } 228 // Skip unknown users and managed profiles as well 229 UserInfo info = userManager.getUserInfo(users[i]); 230 if (info == null || info.isManagedProfile()) { 231 continue; 232 } 233 } 234 context.sendOrderedBroadcastAsUser(intent, targetUser, 235 android.Manifest.permission.RECEIVE_MMS, 236 AppOpsManager.OP_RECEIVE_MMS, 237 null, 238 null, Activity.RESULT_OK, null, null); 239 } 240 } 241 242 /** 243 * Transfer the received response to the caller (for download requests write to content uri) 244 * 245 * @param fillIn the intent that will be returned to the caller 246 * @param response the pdu to transfer 247 */ 248 @Override transferResponse(Intent fillIn, final byte[] response)249 protected boolean transferResponse(Intent fillIn, final byte[] response) { 250 return mRequestManager.writePduToContentUri(mContentUri, response); 251 } 252 253 @Override prepareForHttpRequest()254 protected boolean prepareForHttpRequest() { 255 return true; 256 } 257 258 /** 259 * Try downloading via the carrier app. 260 * 261 * @param context The context 262 * @param carrierMessagingServicePackage The carrier messaging service handling the download 263 */ tryDownloadingByCarrierApp(Context context, String carrierMessagingServicePackage)264 public void tryDownloadingByCarrierApp(Context context, String carrierMessagingServicePackage) { 265 final CarrierDownloadManager carrierDownloadManger = new CarrierDownloadManager(); 266 final CarrierDownloadCompleteCallback downloadCallback = 267 new CarrierDownloadCompleteCallback(context, carrierDownloadManger); 268 carrierDownloadManger.downloadMms(context, carrierMessagingServicePackage, 269 downloadCallback); 270 } 271 272 @Override revokeUriPermission(Context context)273 protected void revokeUriPermission(Context context) { 274 context.revokeUriPermission(mContentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 275 } 276 277 /** 278 * Downloads the MMS through through the carrier app. 279 */ 280 private final class CarrierDownloadManager extends CarrierMessagingServiceWrapper { 281 // Initialized in downloadMms 282 private volatile CarrierDownloadCompleteCallback mCarrierDownloadCallback; 283 downloadMms(Context context, String carrierMessagingServicePackage, CarrierDownloadCompleteCallback carrierDownloadCallback)284 void downloadMms(Context context, String carrierMessagingServicePackage, 285 CarrierDownloadCompleteCallback carrierDownloadCallback) { 286 mCarrierDownloadCallback = carrierDownloadCallback; 287 if (bindToCarrierMessagingService(context, carrierMessagingServicePackage)) { 288 LogUtil.v("bindService() for carrier messaging service succeeded. messageId: " 289 + mMessageId); 290 } else { 291 LogUtil.e("bindService() for carrier messaging service failed. messageId: " 292 + mMessageId); 293 carrierDownloadCallback.onDownloadMmsComplete( 294 CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK); 295 } 296 } 297 298 @Override onServiceReady()299 public void onServiceReady() { 300 try { 301 downloadMms(mContentUri, mSubId, Uri.parse(mLocationUrl), 302 mCarrierDownloadCallback); 303 } catch (RuntimeException e) { 304 LogUtil.e("Exception downloading MMS for messageId " + mMessageId 305 + " using the carrier messaging service: " + e, e); 306 mCarrierDownloadCallback.onDownloadMmsComplete( 307 CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK); 308 } 309 } 310 } 311 312 /** 313 * A callback which notifies carrier messaging app send result. Once the result is ready, the 314 * carrier messaging service connection is disposed. 315 */ 316 private final class CarrierDownloadCompleteCallback extends 317 MmsRequest.CarrierMmsActionCallback { 318 private final Context mContext; 319 private final CarrierDownloadManager mCarrierDownloadManager; 320 CarrierDownloadCompleteCallback(Context context, CarrierDownloadManager carrierDownloadManager)321 public CarrierDownloadCompleteCallback(Context context, 322 CarrierDownloadManager carrierDownloadManager) { 323 mContext = context; 324 mCarrierDownloadManager = carrierDownloadManager; 325 } 326 327 @Override onSendMmsComplete(int result, byte[] sendConfPdu)328 public void onSendMmsComplete(int result, byte[] sendConfPdu) { 329 LogUtil.e("Unexpected onSendMmsComplete call with result: " + result 330 + ", messageId: " + mMessageId); 331 } 332 333 @Override onDownloadMmsComplete(int result)334 public void onDownloadMmsComplete(int result) { 335 LogUtil.d("Carrier app result for download: " + result 336 + ", messageId: " + mMessageId); 337 mCarrierDownloadManager.disposeConnection(mContext); 338 339 if (!maybeFallbackToRegularDelivery(result)) { 340 processResult(mContext, toSmsManagerResult(result), null/* response */, 341 0/* httpStatusCode */); 342 } 343 } 344 } 345 } 346