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