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