From 1b2066c9c8f40e2cdabdedfe873a4c960ec7b7d6 Mon Sep 17 00:00:00 2001 From: Abhijith Shastry Date: Fri, 31 Oct 2014 19:02:28 -0700 Subject: Use AIDL based API's to comunicate with the carrier app for MMS sending and downloading. BUG: 18005911 Change-Id: I2d874bac71ee531c41bf9417444d41c45f179dc0 --- src/com/android/mms/service/DownloadRequest.java | 105 +++++++++++++++------ src/com/android/mms/service/MmsRequest.java | 100 +++++++++++--------- src/com/android/mms/service/MmsService.java | 88 +++++++----------- src/com/android/mms/service/SendRequest.java | 111 +++++++++++++++++------ 4 files changed, 254 insertions(+), 150 deletions(-) (limited to 'src') diff --git a/src/com/android/mms/service/DownloadRequest.java b/src/com/android/mms/service/DownloadRequest.java index 3f2fbe7..f5ff1ce 100644 --- a/src/com/android/mms/service/DownloadRequest.java +++ b/src/com/android/mms/service/DownloadRequest.java @@ -26,8 +26,13 @@ import android.database.sqlite.SQLiteException; import android.net.Uri; import android.os.Binder; import android.os.Bundle; +import android.os.RemoteException; import android.os.UserHandle; import android.provider.Telephony; +import android.service.carrier.CarrierMessagingService; +import android.service.carrier.ICarrierMessagingCallback; +import android.service.carrier.ICarrierMessagingService; +import android.telephony.CarrierMessagingServiceManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -192,44 +197,92 @@ public class DownloadRequest extends MmsRequest { return mRequestManager.writePduToContentUri(mContentUri, response); } + @Override protected boolean prepareForHttpRequest() { return true; } /** - * Try downloading via the carrier app by sending intent. + * Try downloading via the carrier app. * * @param context The context + * @param carrierMessagingServicePackage The carrier messaging service handling the download */ - public void tryDownloadingByCarrierApp(Context context) { - TelephonyManager telephonyManager = - (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - Intent intent = new Intent(Telephony.Mms.Intents.MMS_DOWNLOAD_ACTION); - List carrierPackages = telephonyManager.getCarrierPackageNamesForIntent( - intent); - - if (carrierPackages == null || carrierPackages.size() != 1) { - mRequestManager.addSimRequest(this); - } else { - intent.setPackage(carrierPackages.get(0)); - intent.putExtra(Telephony.Mms.Intents.EXTRA_MMS_LOCATION_URL, mLocationUrl); - intent.putExtra(Telephony.Mms.Intents.EXTRA_MMS_CONTENT_URI, mContentUri); - intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT); - context.sendOrderedBroadcastAsUser( - intent, - UserHandle.OWNER, - android.Manifest.permission.RECEIVE_MMS, - AppOpsManager.OP_RECEIVE_MMS, - mCarrierAppResultReceiver, - null/*scheduler*/, - Activity.RESULT_CANCELED, - null/*initialData*/, - null/*initialExtras*/); - } + public void tryDownloadingByCarrierApp(Context context, String carrierMessagingServicePackage) { + final CarrierDownloadManager carrierDownloadManger = new CarrierDownloadManager(); + final CarrierDownloadCompleteCallback downloadCallback = + new CarrierDownloadCompleteCallback(context, carrierDownloadManger); + carrierDownloadManger.downloadMms(context, carrierMessagingServicePackage, + downloadCallback); } @Override protected void revokeUriPermission(Context context) { context.revokeUriPermission(mContentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } + + /** + * Downloads the MMS through through the carrier app. + */ + private final class CarrierDownloadManager extends CarrierMessagingServiceManager { + // Initialized in downloadMms + private volatile CarrierDownloadCompleteCallback mCarrierDownloadCallback; + + void downloadMms(Context context, String carrierMessagingServicePackage, + CarrierDownloadCompleteCallback carrierDownloadCallback) { + mCarrierDownloadCallback = carrierDownloadCallback; + if (bindToCarrierMessagingService(context, carrierMessagingServicePackage)) { + Log.v(MmsService.TAG, "bindService() for carrier messaging service succeeded"); + } else { + Log.e(MmsService.TAG, "bindService() for carrier messaging service failed"); + carrierDownloadCallback.onDownloadMmsComplete( + CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK); + } + } + + @Override + protected void onServiceReady(ICarrierMessagingService carrierMessagingService) { + try { + carrierMessagingService.downloadMms(mContentUri, mSubId, Uri.parse(mLocationUrl), + mCarrierDownloadCallback); + } catch (RemoteException e) { + Log.e(MmsService.TAG, + "Exception downloading MMS using the carrier messaging service: " + e); + mCarrierDownloadCallback.onDownloadMmsComplete( + CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK); + } + } + } + + /** + * A callback which notifies carrier messaging app send result. Once the result is ready, the + * carrier messaging service connection is disposed. + */ + private final class CarrierDownloadCompleteCallback extends + MmsRequest.CarrierMmsActionCallback { + private final Context mContext; + private final CarrierDownloadManager mCarrierDownloadManager; + + public CarrierDownloadCompleteCallback(Context context, + CarrierDownloadManager carrierDownloadManager) { + mContext = context; + mCarrierDownloadManager = carrierDownloadManager; + } + + @Override + public void onSendMmsComplete(int result, byte[] sendConfPdu) { + Log.e(MmsService.TAG, "Unexpected onSendMmsComplete call with result: " + result); + } + + @Override + public void onDownloadMmsComplete(int result) { + Log.d(MmsService.TAG, "Carrier app result for download: " + result); + mCarrierDownloadManager.disposeConnection(mContext); + + if (!maybeFallbackToRegularDelivery(result)) { + processResult(mContext, toSmsManagerResult(result), null/* response */, + 0/* httpStatusCode */); + } + } + } } diff --git a/src/com/android/mms/service/MmsRequest.java b/src/com/android/mms/service/MmsRequest.java index 6d44174..5314e67 100644 --- a/src/com/android/mms/service/MmsRequest.java +++ b/src/com/android/mms/service/MmsRequest.java @@ -16,6 +16,7 @@ package com.android.mms.service; +import android.annotation.Nullable; import android.app.Activity; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -24,6 +25,8 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.provider.Telephony; +import android.service.carrier.CarrierMessagingService; +import android.service.carrier.ICarrierMessagingCallback; import android.telephony.SmsManager; import android.util.Log; @@ -31,26 +34,18 @@ import com.android.mms.service.exception.ApnException; import com.android.mms.service.exception.MmsHttpException; import com.android.mms.service.exception.MmsNetworkException; +import java.util.List; + /** * Base class for MMS requests. This has the common logic of sending/downloading MMS. */ public abstract class MmsRequest { private static final int RETRY_TIMES = 3; - protected static final String EXTRA_MESSAGE_REF = "messageref"; - /** * Interface for certain functionalities from MmsService */ public static interface RequestManager { - /** - * Add a request to be executed by carrier app - * - * @param key The message ref key from carrier app - * @param request The request in pending - */ - public void addCarrierAppRequest(int key, MmsRequest request); - /** * Enqueue an MMS request * @@ -91,39 +86,6 @@ public abstract class MmsRequest { // MMS config overrides protected Bundle mMmsConfigOverrides; - // Intent result receiver for carrier app - protected final BroadcastReceiver mCarrierAppResultReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (action.equals(Telephony.Mms.Intents.MMS_SEND_ACTION) || - action.equals(Telephony.Mms.Intents.MMS_DOWNLOAD_ACTION)) { - Log.d(MmsService.TAG, "Carrier app result for " + action); - final int rc = getResultCode(); - if (rc == Activity.RESULT_OK) { - // Handled by carrier app, waiting for result - Log.d(MmsService.TAG, "Sending/downloading MMS by IP pending."); - final Bundle resultExtras = getResultExtras(false); - if (resultExtras != null && resultExtras.containsKey(EXTRA_MESSAGE_REF)) { - final int ref = resultExtras.getInt(EXTRA_MESSAGE_REF); - Log.d(MmsService.TAG, "messageref = " + ref); - mRequestManager.addCarrierAppRequest(ref, MmsRequest.this); - } else { - // Bad, no message ref provided - Log.e(MmsService.TAG, "Can't find messageref in result extras."); - } - } else { - // No carrier app present, sending normally - Log.d(MmsService.TAG, "Sending/downloading MMS by IP failed."); - mRequestManager.addSimRequest(MmsRequest.this); - } - } else { - Log.e(MmsService.TAG, "unexpected BroadcastReceiver action: " + action); - } - - } - }; - public MmsRequest(RequestManager requestManager, int subId, String creator, Bundle configOverrides) { mRequestManager = requestManager; @@ -246,6 +208,37 @@ public abstract class MmsRequest { revokeUriPermission(context); } + /** + * Returns true if sending / downloading using the carrier app has failed and completes the + * action using platform API's, otherwise false. + */ + protected boolean maybeFallbackToRegularDelivery(int carrierMessagingAppResult) { + if (carrierMessagingAppResult + == CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK + || carrierMessagingAppResult + == CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK) { + Log.d(MmsService.TAG, "Sending/downloading MMS by IP failed."); + mRequestManager.addSimRequest(MmsRequest.this); + return true; + } else { + return false; + } + } + + /** + * Converts from {@code carrierMessagingAppResult} to a platform result code. + */ + protected static int toSmsManagerResult(int carrierMessagingAppResult) { + switch (carrierMessagingAppResult) { + case CarrierMessagingService.SEND_STATUS_OK: + return Activity.RESULT_OK; + case CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK: + return SmsManager.MMS_ERROR_RETRY; + default: + return SmsManager.MMS_ERROR_UNSPECIFIED; + } + } + /** * Making the HTTP request to MMSC * @@ -300,4 +293,25 @@ public abstract class MmsRequest { * @param context The context */ protected abstract void revokeUriPermission(Context context); + + /** + * Base class for handling carrier app send / download result. + */ + protected abstract class CarrierMmsActionCallback extends ICarrierMessagingCallback.Stub { + @Override + public void onSendSmsComplete(int result, int messageRef) { + Log.e(MmsService.TAG, "Unexpected onSendSmsComplete call with result: " + result); + } + + @Override + public void onSendMultipartSmsComplete(int result, int[] messageRefs) { + Log.e(MmsService.TAG, "Unexpected onSendMultipartSmsComplete call with result: " + + result); + } + + @Override + public void onFilterComplete(boolean keepMessage) { + Log.e(MmsService.TAG, "Unexpected onFilterComplete call with result: " + keepMessage); + } + } } diff --git a/src/com/android/mms/service/MmsService.java b/src/com/android/mms/service/MmsService.java index 424a978..0246583 100644 --- a/src/com/android/mms/service/MmsService.java +++ b/src/com/android/mms/service/MmsService.java @@ -16,11 +16,13 @@ package com.android.mms.service; +import android.annotation.Nullable; import android.app.PendingIntent; import android.app.Service; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteException; @@ -36,8 +38,10 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.provider.Telephony; +import android.service.carrier.CarrierMessagingService; import android.telephony.SmsManager; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; @@ -57,6 +61,7 @@ import com.google.android.mms.util.SqliteWrapper; import java.io.IOException; import java.util.ArrayDeque; import java.util.Arrays; +import java.util.List; import java.util.Queue; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; @@ -84,11 +89,6 @@ public class MmsService extends Service implements MmsRequest.RequestManager { // specific size limit should not be used (as it could be lower on some carriers). private static final int MAX_MMS_FILE_SIZE = 8 * 1024 * 1024; - // Pending requests that are currently executed by carrier app - // TODO: persist this in case MmsService crashes - private final ConcurrentHashMap mPendingCarrierAppRequests = - new ConcurrentHashMap<>(); - // Pending requests that are waiting for the SIM to be available // If a different SIM is currently used by previous requests, the following // requests will stay in this queue until that SIM finishes its current requests in @@ -109,11 +109,6 @@ public class MmsService extends Service implements MmsRequest.RequestManager { // The current running MmsRequest count. private int mRunningRequestCount; - @Override - public void addCarrierAppRequest(int key, MmsRequest request) { - mPendingCarrierAppRequests.put(key, request); - } - /** * A thread-based request queue for executing the MMS requests in serial order */ @@ -169,6 +164,20 @@ public class MmsService extends Service implements MmsRequest.RequestManager { return subId; } + @Nullable + private String getCarrierMessagingServicePackageIfExists() { + Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE); + TelephonyManager telephonyManager = + (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE); + List carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(intent); + + if (carrierPackages == null || carrierPackages.size() != 1) { + return null; + } else { + return carrierPackages.get(0); + } + } + private IMms.Stub mStub = new IMms.Stub() { @Override public void sendMessage(int subId, String callingPkg, Uri contentUri, @@ -180,8 +189,15 @@ public class MmsService extends Service implements MmsRequest.RequestManager { subId = checkSubId(subId); final SendRequest request = new SendRequest(MmsService.this, subId, contentUri, locationUrl, sentIntent, callingPkg, configOverrides); - // Try sending via carrier app - request.trySendingByCarrierApp(MmsService.this); + + final String carrierMessagingServicePackage = + getCarrierMessagingServicePackageIfExists(); + if (carrierMessagingServicePackage != null) { + Log.d(TAG, "sending message by carrier app"); + request.trySendingByCarrierApp(MmsService.this, carrierMessagingServicePackage); + } else { + addSimRequest(request); + } } @Override @@ -194,52 +210,16 @@ public class MmsService extends Service implements MmsRequest.RequestManager { subId = checkSubId(subId); final DownloadRequest request = new DownloadRequest(MmsService.this, subId, locationUrl, contentUri, downloadedIntent, callingPkg, configOverrides); - // Try downloading via carrier app - request.tryDownloadingByCarrierApp(MmsService.this); - } - - @Override - public void updateMmsSendStatus(int messageRef, byte[] pdu, int status) { - Log.d(TAG, "updateMmsSendStatus: ref=" + messageRef - + ", pdu=" + (pdu == null ? null : pdu.length) + ", status=" + status); - enforceSystemUid(); - final MmsRequest request = mPendingCarrierAppRequests.remove(messageRef); - if (request != null) { - if (status != SmsManager.MMS_ERROR_RETRY) { - // Sent completed (maybe success or fail) by carrier app, finalize the request. - request.processResult(MmsService.this, status, pdu, 0/*httpStatusCode*/); - } else { - // Failed, try sending via carrier network - addSimRequest(request); - } + final String carrierMessagingServicePackage = + getCarrierMessagingServicePackageIfExists(); + if (carrierMessagingServicePackage != null) { + Log.d(TAG, "downloading message by carrier app"); + request.tryDownloadingByCarrierApp(MmsService.this, carrierMessagingServicePackage); } else { - // Really wrong here: can't find the request to update - Log.e(TAG, "Failed to find the request to update send status"); + addSimRequest(request); } } - @Override - public void updateMmsDownloadStatus(int messageRef, int status) { - Log.d(TAG, "updateMmsDownloadStatus: ref=" + messageRef + ", status=" + status); - enforceSystemUid(); - final MmsRequest request = mPendingCarrierAppRequests.remove(messageRef); - if (request != null) { - if (status != SmsManager.MMS_ERROR_RETRY) { - // Downloaded completed (maybe success or fail) by carrier app, finalize the - // request. - request.processResult( - MmsService.this, status, null/*response*/, 0/*httpStatusCode*/); - } else { - // Failed, try downloading via the carrier network - addSimRequest(request); - } - } else { - // Really wrong here: can't find the request to update - Log.e(TAG, "Failed to find the request to update download status"); - } - } - - @Override public Bundle getCarrierConfigValues(int subId) { Log.d(TAG, "getCarrierConfigValues"); // Make sure the subId is correct diff --git a/src/com/android/mms/service/SendRequest.java b/src/com/android/mms/service/SendRequest.java index 2ded6e4..8ec0538 100644 --- a/src/com/android/mms/service/SendRequest.java +++ b/src/com/android/mms/service/SendRequest.java @@ -25,8 +25,12 @@ import android.content.Intent; import android.net.Uri; import android.os.Binder; import android.os.Bundle; +import android.os.RemoteException; import android.os.UserHandle; import android.provider.Telephony; +import android.service.carrier.CarrierMessagingService; +import android.service.carrier.ICarrierMessagingService; +import android.telephony.CarrierMessagingServiceManager; import android.telephony.SmsManager; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -34,6 +38,7 @@ import android.util.Log; import com.android.internal.telephony.SmsApplication; import com.android.mms.service.exception.MmsHttpException; + import com.google.android.mms.MmsException; import com.google.android.mms.pdu.GenericPdu; import com.google.android.mms.pdu.PduHeaders; @@ -205,44 +210,96 @@ public class SendRequest extends MmsRequest { * Read the data from the file descriptor if not yet done * @return whether data successfully read */ + @Override protected boolean prepareForHttpRequest() { return readPduFromContentUri(); } /** - * Try sending via the carrier app by sending an intent + * Try sending via the carrier app * - * @param context The context + * @param context the context + * @param carrierMessagingServicePackage the carrier messaging service sending the MMS */ - public void trySendingByCarrierApp(Context context) { - TelephonyManager telephonyManager = - (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - Intent intent = new Intent(Telephony.Mms.Intents.MMS_SEND_ACTION); - List carrierPackages = telephonyManager.getCarrierPackageNamesForIntent( - intent); - - if (carrierPackages == null || carrierPackages.size() != 1) { - mRequestManager.addSimRequest(this); - } else { - intent.setPackage(carrierPackages.get(0)); - intent.putExtra(Telephony.Mms.Intents.EXTRA_MMS_CONTENT_URI, mPduUri); - intent.putExtra(Telephony.Mms.Intents.EXTRA_MMS_LOCATION_URL, mLocationUrl); - intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT); - context.sendOrderedBroadcastAsUser( - intent, - UserHandle.OWNER, - android.Manifest.permission.RECEIVE_MMS, - AppOpsManager.OP_RECEIVE_MMS, - mCarrierAppResultReceiver, - null/*scheduler*/, - Activity.RESULT_CANCELED, - null/*initialData*/, - null/*initialExtras*/); - } + public void trySendingByCarrierApp(Context context, String carrierMessagingServicePackage) { + final CarrierSendManager carrierSendManger = new CarrierSendManager(); + final CarrierSendCompleteCallback sendCallback = new CarrierSendCompleteCallback( + context, carrierSendManger); + carrierSendManger.sendMms(context, carrierMessagingServicePackage, sendCallback); } @Override protected void revokeUriPermission(Context context) { context.revokeUriPermission(mPduUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); } + + /** + * Sends the MMS through through the carrier app. + */ + private final class CarrierSendManager extends CarrierMessagingServiceManager { + // Initialized in sendMms + private volatile CarrierSendCompleteCallback mCarrierSendCompleteCallback; + + void sendMms(Context context, String carrierMessagingServicePackage, + CarrierSendCompleteCallback carrierSendCompleteCallback) { + mCarrierSendCompleteCallback = carrierSendCompleteCallback; + if (bindToCarrierMessagingService(context, carrierMessagingServicePackage)) { + Log.v(MmsService.TAG, "bindService() for carrier messaging service succeeded"); + } else { + Log.e(MmsService.TAG, "bindService() for carrier messaging service failed"); + carrierSendCompleteCallback.onSendMmsComplete( + CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK, + null /* no sendConfPdu */); + } + } + + @Override + protected void onServiceReady(ICarrierMessagingService carrierMessagingService) { + try { + Uri locationUri = null; + if (mLocationUrl != null) { + locationUri = Uri.parse(mLocationUrl); + } + carrierMessagingService.sendMms(mPduUri, mSubId, locationUri, + mCarrierSendCompleteCallback); + } catch (RemoteException e) { + Log.e(MmsService.TAG, + "Exception sending MMS using the carrier messaging service: " + e); + mCarrierSendCompleteCallback.onSendMmsComplete( + CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK, + null /* no sendConfPdu */); + } + } + } + + /** + * A callback which notifies carrier messaging app send result. Once the result is ready, the + * carrier messaging service connection is disposed. + */ + private final class CarrierSendCompleteCallback extends + MmsRequest.CarrierMmsActionCallback { + private final Context mContext; + private final CarrierSendManager mCarrierSendManager; + + public CarrierSendCompleteCallback(Context context, CarrierSendManager carrierSendManager) { + mContext = context; + mCarrierSendManager = carrierSendManager; + } + + @Override + public void onSendMmsComplete(int result, byte[] sendConfPdu) { + Log.d(MmsService.TAG, "Carrier app result for send: " + result); + mCarrierSendManager.disposeConnection(mContext); + + if (!maybeFallbackToRegularDelivery(result)) { + processResult(mContext, toSmsManagerResult(result), sendConfPdu, + 0/* httpStatusCode */); + } + } + + @Override + public void onDownloadMmsComplete(int result) { + Log.e(MmsService.TAG, "Unexpected onDownloadMmsComplete call with result: " + result); + } + } } -- cgit v1.2.3