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

Commit 1e6887f4 authored by Amit Mahajan's avatar Amit Mahajan
Browse files

Add timeout for multipart SMS sending code path.

Test: atest GsmSmsDispatcherTest
Bug: 178580734
Change-Id: I6b93cb8b6a2a5ba55340ab3e3ada82806dc23666
parent 16213446
Loading
Loading
Loading
Loading
+92 −46
Original line number Diff line number Diff line
@@ -355,45 +355,60 @@ public abstract class SMSDispatcher extends Handler {
     */
    protected abstract class SmsSender extends Handler {
        private static final int EVENT_TIMEOUT = 1;
        protected final SmsTracker mTracker;
        // Initialized in sendSmsByCarrierApp
        protected volatile SmsSenderCallback mSenderCallback;
        protected volatile CarrierMessagingCallback mSenderCallback;
        protected final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper =
                new CarrierMessagingServiceWrapper();
        private String mCarrierPackageName;

        protected SmsSender(SmsTracker tracker) {
        protected SmsSender() {
            super(Looper.getMainLooper());
            mTracker = tracker;
        }

        /**
         * Bind to carrierPackageName to send message through it
         */
        public void sendSmsByCarrierApp(String carrierPackageName,
                                        SmsSenderCallback senderCallback) {
                CarrierMessagingCallback senderCallback) {
            mCarrierPackageName = carrierPackageName;
            mSenderCallback = senderCallback;
            if (!mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
                    mContext, carrierPackageName, runnable -> runnable.run(),
                    ()->onServiceReady())) {
                Rlog.e(TAG, "bindService() for carrier messaging service failed");
                mSenderCallback.onSendSmsComplete(
                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                        0 /* messageRef */);
                onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
            } else {
                Rlog.d(TAG, "bindService() for carrier messaging service succeeded");
                sendMessageDelayed(obtainMessage(EVENT_TIMEOUT), mCarrierMessagingTimeout);
            }
        }

        /**
         * Callback received from mCarrierPackageName on binding to it is done
         */
        public abstract void onServiceReady();

        /**
         * Method to call message send callback with passed in result and default parameters
         */
        public abstract void onSendComplete(@CarrierMessagingService.SendResult int result);

        /**
         * Used to get the SmsTracker for single part messages
         */
        public abstract SmsTracker getSmsTracker();

        /**
         * Used to get the SmsTrackers for multi part messages
         */
        public abstract SmsTracker[] getSmsTrackers();

        @Override
        public void handleMessage(Message msg) {
            if (msg.what == EVENT_TIMEOUT) {
                logWithLocalLog("handleMessage: did not receive response from "
                        + mCarrierPackageName + " for " + mCarrierMessagingTimeout + " ms");
                mSenderCallback.onSendSmsComplete(
                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                        0 /* messageRef */);
                onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
            } else {
                logWithLocalLog("handleMessage: received unexpected message " + msg.what);
            }
@@ -413,8 +428,10 @@ public abstract class SMSDispatcher extends Handler {
     * Use the carrier messaging service to send a text SMS.
     */
    protected final class TextSmsSender extends SmsSender {
        private final SmsTracker mTracker;
        public TextSmsSender(SmsTracker tracker) {
            super(tracker);
            super();
            mTracker = tracker;
        }

        @Override
@@ -435,24 +452,38 @@ public abstract class SMSDispatcher extends Handler {
                            mSenderCallback);
                } catch (RuntimeException e) {
                    Rlog.e(TAG, "Exception sending the SMS: " + e.getMessage());
                    mSenderCallback.onSendSmsComplete(
                            CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                            0 /* messageRef */);
                    onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
                }
            } else {
                mSenderCallback.onSendSmsComplete(
                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                        0 /* messageRef */);
                onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
            }
        }

        @Override
        public void onSendComplete(int result) {
            mSenderCallback.onSendSmsComplete(result, 0 /* messageRef */);
        }

        @Override
        public SmsTracker getSmsTracker() {
            return mTracker;
        }

        @Override
        public SmsTracker[] getSmsTrackers() {
            Rlog.e(TAG, "getSmsTrackers: Unexpected call for TextSmsSender");
            return null;
        }
    }

    /**
     * Use the carrier messaging service to send a data SMS.
     */
    protected final class DataSmsSender extends SmsSender {
        private final SmsTracker mTracker;
        public DataSmsSender(SmsTracker tracker) {
            super(tracker);
            super();
            mTracker = tracker;
        }

        @Override
@@ -476,16 +507,28 @@ public abstract class SMSDispatcher extends Handler {
                } catch (RuntimeException e) {
                    Rlog.e(TAG, "Exception sending the SMS: " + e
                            + " " + SmsController.formatCrossStackMessageId(mTracker.mMessageId));
                    mSenderCallback.onSendSmsComplete(
                            CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                            0 /* messageRef */);
                    onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
                }
            } else {
                mSenderCallback.onSendSmsComplete(
                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                        0 /* messageRef */);
                onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
            }
        }

        @Override
        public void onSendComplete(int result) {
            mSenderCallback.onSendSmsComplete(result, 0 /* messageRef */);
        }

        @Override
        public SmsTracker getSmsTracker() {
            return mTracker;
        }

        @Override
        public SmsTracker[] getSmsTrackers() {
            Rlog.e(TAG, "getSmsTrackers: Unexpected call for DataSmsSender");
            return null;
        }
    }

    /**
@@ -507,7 +550,7 @@ public abstract class SMSDispatcher extends Handler {
            final long identity = Binder.clearCallingIdentity();
            try {
                mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
                processSendSmsResponse(mSmsSender.mTracker, result, messageRef);
                processSendSmsResponse(mSmsSender.getSmsTracker(), result, messageRef);
                mSmsSender.removeTimeout();
            } finally {
                Binder.restoreCallingIdentity(identity);
@@ -579,15 +622,12 @@ public abstract class SMSDispatcher extends Handler {
    /**
     * Use the carrier messaging service to send a multipart text SMS.
     */
    private final class MultipartSmsSender {
    private final class MultipartSmsSender extends SmsSender {
        private final List<String> mParts;
        public final SmsTracker[] mTrackers;
        // Initialized in sendSmsByCarrierApp
        private volatile MultipartSmsSenderCallback mSenderCallback;
        private final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper =
                new CarrierMessagingServiceWrapper();

        MultipartSmsSender(ArrayList<String> parts, SmsTracker[] trackers) {
            super();
            mParts = parts;
            mTrackers = trackers;
        }
@@ -595,20 +635,11 @@ public abstract class SMSDispatcher extends Handler {
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        void sendSmsByCarrierApp(String carrierPackageName,
                                 MultipartSmsSenderCallback senderCallback) {
            mSenderCallback = senderCallback;
            if (!mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
                    mContext, carrierPackageName, runnable -> runnable.run(),
                    ()->onServiceReady())) {
                Rlog.e(TAG, "bindService() for carrier messaging service failed");
                mSenderCallback.onSendMultipartSmsComplete(
                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                        null /* messageRefs */);
            } else {
                Rlog.d(TAG, "bindService() for carrier messaging service succeeded");
            }
            super.sendSmsByCarrierApp(carrierPackageName, senderCallback);
        }

        private void onServiceReady() {
        @Override
        public void onServiceReady() {
            boolean statusReportRequested = false;
            for (SmsTracker tracker : mTrackers) {
                if (tracker.mDeliveryIntent != null) {
@@ -629,10 +660,24 @@ public abstract class SMSDispatcher extends Handler {
                        mSenderCallback);
            } catch (RuntimeException e) {
                Rlog.e(TAG, "Exception sending the SMS: " + e);
                mSenderCallback.onSendMultipartSmsComplete(
                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
                        null /* messageRefs */);
                onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
            }
        }

        @Override
        public void onSendComplete(int result) {
            mSenderCallback.onSendMultipartSmsComplete(result, null /* messageRefs */);
        }

        @Override
        public SmsTracker getSmsTracker() {
            Rlog.e(TAG, "getSmsTracker: Unexpected call for MultipartSmsSender");
            return null;
        }

        @Override
        public SmsTracker[] getSmsTrackers() {
            return mTrackers;
        }
    }

@@ -657,6 +702,7 @@ public abstract class SMSDispatcher extends Handler {
         */
        @Override
        public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
            mSmsSender.removeTimeout();
            mSmsSender.mCarrierMessagingServiceWrapper.disconnect();

            if (mSmsSender.mTrackers == null) {
+2 −1
Original line number Diff line number Diff line
@@ -1146,7 +1146,8 @@ public class SimulatedCommands extends BaseCommands
     */
    @Override
    public void sendSMSExpectMore (String smscPDU, String pdu, Message result) {
        unimplemented(result);
        SimulatedCommandsVerifier.getInstance().sendSMSExpectMore(smscPDU, pdu, result);
        resultSuccess(result, new SmsResponse(0 /*messageRef*/, null, SmsResponse.NO_ERROR_CODE));
    }

    @Override
+93 −0
Original line number Diff line number Diff line
@@ -347,6 +347,7 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
            throws RemoteException {
        when(stub.queryLocalInterface(anyString())).thenReturn(stub);
        when(stub.asBinder()).thenReturn(stub);
        // for single part
        doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
@@ -360,6 +361,21 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
        }).when(stub).sendTextSms(
                anyString(), anyInt(), anyString(), anyInt(),
                any(ICarrierMessagingCallback.class));

        // for multi part
        doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
                Object[] args = invocation.getArguments();
                ICarrierMessagingCallback callback = (ICarrierMessagingCallback) args[4];
                if (callOnFilterComplete) {
                    callback.onSendMultipartSmsComplete(result, null);
                }
                return null;
            }
        }).when(stub).sendMultipartTextSms(
                any(), anyInt(), anyString(), anyInt(),
                any(ICarrierMessagingCallback.class));
    }

    @Test
@@ -417,4 +433,81 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
        processAllMessages();
        verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(), any(Message.class));
    }

    private void sendMultipartTextSms(boolean withSentIntents) {
        // initiate parameters for a multipart sms
        ArrayList<String> parts = new ArrayList<>();
        parts.add("segment1");
        parts.add("segment2");

        ArrayList<PendingIntent> sentIntents = new ArrayList<>();
        PendingIntent sentIntent1 = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
                new Intent(TEST_INTENT), PendingIntent.FLAG_MUTABLE);
        PendingIntent sentIntent2 = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
                new Intent(TEST_INTENT), PendingIntent.FLAG_MUTABLE);
        sentIntents.add(sentIntent1);
        sentIntents.add(sentIntent2);

        mGsmSmsDispatcher.sendMultipartText("6501002000" /*destAddr*/, "222" /*scAddr*/, parts,
                withSentIntents ? sentIntents : null, null, null, null, false, -1, false, -1, 0L);
    }

    @Test
    @SmallTest
    public void testSendMultipartSmsByCarrierApp() throws Exception {
        mockCarrierApp();
        mockCarrierAppStubResults(CarrierMessagingService.SEND_STATUS_OK,
                mICarrierAppMessagingService, true);
        registerTestIntentReceiver();

        // send SMS and check sentIntent
        mReceivedTestIntent = false;
        sendMultipartTextSms(true);
        processAllMessages();
        synchronized (mLock) {
            if (!mReceivedTestIntent) {
                // long wait since sometimes broadcasts can take a long time if the system is loaded
                mLock.wait(60000);
            }
            assertEquals(true, mReceivedTestIntent);
            int resultCode = mTestReceiver.getResultCode();
            assertTrue("Unexpected result code: " + resultCode,
                    resultCode == SmsManager.RESULT_ERROR_NONE || resultCode == Activity.RESULT_OK);
            verify(mSimulatedCommandsVerifier, times(0)).sendSMS(anyString(), anyString(),
                    any(Message.class));
        }
    }

    @Test
    @SmallTest
    public void testSendMultipartSmsByCarrierAppNoResponse() throws Exception {
        mockCarrierApp();
        // do not mock result, instead reduce the timeout for test
        mGsmSmsDispatcher.mCarrierMessagingTimeout = 100;

        sendMultipartTextSms(false);

        // wait for timeout
        waitForMs(150);
        verify(mSimulatedCommandsVerifier).sendSMSExpectMore(anyString(), anyString(),
                any(Message.class));
        verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(),
                any(Message.class));
    }

    @Test
    @SmallTest
    public void testSendMultipartSmsByCarrierAppBindingFailed() throws Exception {
        mContextFixture.mockBindingFailureForPackage(CARRIER_APP_PACKAGE_NAME);
        // mock presence of carrier app, but do not create a mock service to make binding fail
        mockUiccWithCarrierApp();

        sendMultipartTextSms(false);

        processAllMessages();
        verify(mSimulatedCommandsVerifier).sendSMSExpectMore(anyString(), anyString(),
                any(Message.class));
        verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(),
                any(Message.class));
    }
}