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