Return pending intent to service caller if MmsService can't be connected
If MmsService can not be connected, broker throws a runtime exception
which does not return to service caller via PendingIntent. MMS app would
wait forever for the return of sent/downloaded PendingIntent. This
change makes sure PendingIntent is always returned so MMS app can be
unblocked.
b/19732709
Change-Id: I694a0fc29bb431b48c7077e7a323844709ae695f
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 0de6a03..9409615 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -36,6 +36,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.service.carrier.CarrierMessagingService;
+import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.util.Slog;
@@ -111,6 +112,106 @@
}
};
+ // Instance of IMms for returning failure to service API caller,
+ // used when MmsService cannot be connected.
+ private final IMms mServiceStubForFailure = new IMms() {
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+
+ @Override
+ public void sendMessage(int subId, String callingPkg, Uri contentUri, String locationUrl,
+ Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
+ returnPendingIntentWithError(sentIntent);
+ }
+
+ @Override
+ public void downloadMessage(int subId, String callingPkg, String locationUrl,
+ Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent)
+ throws RemoteException {
+ returnPendingIntentWithError(downloadedIntent);
+ }
+
+ @Override
+ public Bundle getCarrierConfigValues(int subId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public Uri importTextMessage(String callingPkg, String address, int type, String text,
+ long timestampMillis, boolean seen, boolean read) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public Uri importMultimediaMessage(String callingPkg, Uri contentUri, String messageId,
+ long timestampSecs, boolean seen, boolean read) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean deleteStoredConversation(String callingPkg, long conversationId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
+ ContentValues statusValues) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean archiveStoredConversation(String callingPkg, long conversationId,
+ boolean archived) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public Uri addTextMessageDraft(String callingPkg, String address, String text)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void sendStoredMessage(int subId, String callingPkg, Uri messageUri,
+ Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
+ returnPendingIntentWithError(sentIntent);
+ }
+
+ @Override
+ public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
+ // Do nothing
+ }
+
+ @Override
+ public boolean getAutoPersisting() throws RemoteException {
+ return false;
+ }
+
+ private void returnPendingIntentWithError(PendingIntent pendingIntent) {
+ try {
+ pendingIntent.send(mContext, SmsManager.MMS_ERROR_UNSPECIFIED, null);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.e(TAG, "Failed to return pending intent result", e);
+ }
+ }
+ };
+
public MmsServiceBroker(Context context) {
super(context);
mContext = context;
@@ -145,44 +246,51 @@
}
}
- private void ensureService() {
+ private IMms getOrConnectService() {
synchronized (this) {
- if (mService == null) {
- // Service is not connected. Try blocking connecting.
- Slog.w(TAG, "MmsService not connected. Try connecting...");
- mConnectionHandler.sendMessage(
- mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
- final long shouldEnd =
- SystemClock.elapsedRealtime() + SERVICE_CONNECTION_WAIT_TIME_MS;
- long waitTime = SERVICE_CONNECTION_WAIT_TIME_MS;
- while (waitTime > 0) {
- try {
- // TODO: consider using Java concurrent construct instead of raw object wait
- this.wait(waitTime);
- } catch (InterruptedException e) {
- Slog.w(TAG, "Connection wait interrupted", e);
- }
- if (mService != null) {
- // Success
- return;
- }
- // Calculate remaining waiting time to make sure we wait the full timeout period
- waitTime = shouldEnd - SystemClock.elapsedRealtime();
- }
- // Timed out. Something's really wrong.
- Slog.e(TAG, "Can not connect to MmsService (timed out)");
- throw new RuntimeException("Timed out in connecting to MmsService");
+ if (mService != null) {
+ return mService;
}
+ // Service is not connected. Try blocking connecting.
+ Slog.w(TAG, "MmsService not connected. Try connecting...");
+ mConnectionHandler.sendMessage(
+ mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
+ final long shouldEnd =
+ SystemClock.elapsedRealtime() + SERVICE_CONNECTION_WAIT_TIME_MS;
+ long waitTime = SERVICE_CONNECTION_WAIT_TIME_MS;
+ while (waitTime > 0) {
+ try {
+ // TODO: consider using Java concurrent construct instead of raw object wait
+ this.wait(waitTime);
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "Connection wait interrupted", e);
+ }
+ if (mService != null) {
+ // Success
+ return mService;
+ }
+ // Calculate remaining waiting time to make sure we wait the full timeout period
+ waitTime = shouldEnd - SystemClock.elapsedRealtime();
+ }
+ // Timed out. Something's really wrong.
+ Slog.e(TAG, "Can not connect to MmsService (timed out)");
+ return null;
}
}
/**
- * Making sure when we obtain the mService instance it is always valid.
- * Throws {@link RuntimeException} when it is empty.
+ * Make sure to return a non-empty service instance. Return the connected MmsService
+ * instance, if not connected, try connecting. If fail to connect, return a fake service
+ * instance which returns failure to service caller.
+ *
+ * @return a non-empty service instance, real or fake
*/
private IMms getServiceGuarded() {
- ensureService();
- return mService;
+ final IMms service = getOrConnectService();
+ if (service != null) {
+ return service;
+ }
+ return mServiceStubForFailure;
}
private AppOpsManager getAppOpsManager() {