Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 61c8d238 authored by Ye Wen's avatar Ye Wen
Browse files

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
parent ad0eacb9
Loading
Loading
Loading
Loading
+137 −29
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.os.RemoteException;
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 @@ public class MmsServiceBroker extends SystemService {
        }
    };

    // 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,9 +246,11 @@ public class MmsServiceBroker extends SystemService {
        }
    }

    private void ensureService() {
    private IMms getOrConnectService() {
        synchronized (this) {
            if (mService == null) {
            if (mService != null) {
                return mService;
            }
            // Service is not connected. Try blocking connecting.
            Slog.w(TAG, "MmsService not connected. Try connecting...");
            mConnectionHandler.sendMessage(
@@ -164,25 +267,30 @@ public class MmsServiceBroker extends SystemService {
                }
                if (mService != null) {
                    // Success
                        return;
                    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)");
                throw new RuntimeException("Timed out in connecting to MmsService");
            }
            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() {