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

Commit 8c977c7e authored by Jeff Davidson's avatar Jeff Davidson
Browse files

Clean up SMS filter handling.

Define an interface for filters and perform filtering as a generic
iteration over the list of filters. Lower code duplication by
providing the list of remaining filters to each filter, allowing
an asynchronous filter (like the carrier filter) to invoke any.
remaining filters generically.

This should mostly be a no-op. However, there is one small behavioral
change - the MissedIncomingCallSmsFilter will now be invoked for
messages which are passed through and not dropped by the carrier SMS
filter and the visual voicemail SMS filter. This seems like it should
be the case for consistency.

Also allow overriding SMS filters for testing and add unit tests to
exercise filter invocation.

This is a precursor CL to one which will pass SMSes from blocked
numbers through the filters to prevent vital system messages from
being blocked.

Bug: 136262737
Test: atest GsmInboundSmsHandlerTest
Change-Id: I280d8e4c0847bf261e0497f95231a5063dfa025f
Merged-In: I280d8e4c0847bf261e0497f95231a5063dfa025f
parent 26fcb83f
Loading
Loading
Loading
Loading
+115 −48
Original line number Original line Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.provider.Telephony.Sms.Intents.RESULT_SMS_NULL_PDU;
import static android.service.carrier.CarrierMessagingService.RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE;
import static android.service.carrier.CarrierMessagingService.RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE;
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;


import android.annotation.Nullable;
import android.app.Activity;
import android.app.Activity;
import android.app.AppOpsManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.BroadcastOptions;
@@ -84,6 +85,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashMap;
import java.util.List;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map;


/**
/**
@@ -164,7 +166,7 @@ public abstract class InboundSmsHandler extends StateMachine {
    public static final int EVENT_BROADCAST_SMS = 2;
    public static final int EVENT_BROADCAST_SMS = 2;


    /** Message from resultReceiver notifying {@link WaitingState} of a completed broadcast. */
    /** Message from resultReceiver notifying {@link WaitingState} of a completed broadcast. */
    private static final int EVENT_BROADCAST_COMPLETE = 3;
    public static final int EVENT_BROADCAST_COMPLETE = 3;


    /** Sent on exit from {@link WaitingState} to return to idle after sending all broadcasts. */
    /** Sent on exit from {@link WaitingState} to return to idle after sending all broadcasts. */
    private static final int EVENT_RETURN_TO_IDLE = 4;
    private static final int EVENT_RETURN_TO_IDLE = 4;
@@ -260,6 +262,8 @@ public abstract class InboundSmsHandler extends StateMachine {
        others in order to update metrics. */
        others in order to update metrics. */
    private boolean mLastSmsWasInjected = false;
    private boolean mLastSmsWasInjected = false;


    private List<SmsFilter> mSmsFilters;

    /**
    /**
     * Create a new SMS broadcast helper.
     * Create a new SMS broadcast helper.
     * @param name the class name for logging
     * @param name the class name for logging
@@ -289,6 +293,8 @@ public abstract class InboundSmsHandler extends StateMachine {
                (PowerWhitelistManager) mContext.getSystemService(Context.POWER_WHITELIST_MANAGER);
                (PowerWhitelistManager) mContext.getSystemService(Context.POWER_WHITELIST_MANAGER);
        mCellBroadcastServiceManager = new CellBroadcastServiceManager(context, phone);
        mCellBroadcastServiceManager = new CellBroadcastServiceManager(context, phone);


        mSmsFilters = createDefaultSmsFilters();

        addState(mDefaultState);
        addState(mDefaultState);
        addState(mStartupState, mDefaultState);
        addState(mStartupState, mDefaultState);
        addState(mIdleState, mDefaultState);
        addState(mIdleState, mDefaultState);
@@ -1136,53 +1142,84 @@ public abstract class InboundSmsHandler extends StateMachine {
    }
    }


    /**
    /**
     * Filters the SMS.
     * Creates the default filters used to filter SMS messages.
     *
     * <p>currently 3 filters exists: the carrier package, the system package, and the
     * VisualVoicemailSmsFilter.
     *
     * <p>The filtering process is:
     *
     * <p>If the carrier package exists, the SMS will be filtered with it first. If the carrier
     * package did not drop the SMS, then the VisualVoicemailSmsFilter will filter it in the
     * callback.
     *
     *
     * <p>If the carrier package does not exists, we will let the VisualVoicemailSmsFilter filter
     * <p>Currently 3 filters exist: the carrier package, the VisualVoicemailSmsFilter, and the
     * it. If the SMS passed the filter, then we will try to find the system package to do the
     * missed incoming call SMS filter.
     * filtering.
     *
     *
     * @return true if a filter is invoked and the SMS processing flow is diverted, false otherwise.
     * <p>Since the carrier filter is asynchronous, if a message passes through the carrier filter,
     * the remaining filters will be applied in the callback.
     */
     */
    private boolean filterSms(byte[][] pdus, int destPort,
    private List<SmsFilter> createDefaultSmsFilters() {
        InboundSmsTracker tracker, SmsBroadcastReceiver resultReceiver, boolean userUnlocked) {
        List<SmsFilter> smsFilters = new ArrayList<>(3);
        smsFilters.add(
                (pdus, destPort, tracker, resultReceiver, userUnlocked, remainingFilters) -> {
                    CarrierServicesSmsFilterCallback filterCallback =
                    CarrierServicesSmsFilterCallback filterCallback =
                            new CarrierServicesSmsFilterCallback(
                            new CarrierServicesSmsFilterCallback(
                        pdus, destPort, tracker.getFormat(), resultReceiver, userUnlocked,
                                    pdus, destPort, tracker, tracker.getFormat(), resultReceiver,
                        tracker.isClass0(), tracker.getSubId(), tracker.getMessageId());
                                    userUnlocked,
                                    tracker.isClass0(), tracker.getSubId(), tracker.getMessageId(),
                                    remainingFilters);
                    CarrierServicesSmsFilter carrierServicesFilter = new CarrierServicesSmsFilter(
                    CarrierServicesSmsFilter carrierServicesFilter = new CarrierServicesSmsFilter(
                            mContext, mPhone, pdus, destPort, tracker.getFormat(),
                            mContext, mPhone, pdus, destPort, tracker.getFormat(),
                filterCallback, getName() + "::CarrierServicesSmsFilter", mCarrierServiceLocalLog,
                            filterCallback, getName() + "::CarrierServicesSmsFilter",
                tracker.getMessageId());
                            mCarrierServiceLocalLog, tracker.getMessageId());
                    if (carrierServicesFilter.filter()) {
                    if (carrierServicesFilter.filter()) {
            log("filterSms: SMS is being handled by carrier service", tracker.getMessageId());
                        log("SMS is being handled by carrier service", tracker.getMessageId());
                        return true;
                        return true;
                    } else {
                        return false;
                    }
                    }

                });
        smsFilters.add(
                (pdus, destPort, tracker, resultReceiver, userUnlocked, remainingFilters) -> {
                    if (VisualVoicemailSmsFilter.filter(
                    if (VisualVoicemailSmsFilter.filter(
                            mContext, pdus, tracker.getFormat(), destPort, tracker.getSubId())) {
                            mContext, pdus, tracker.getFormat(), destPort, tracker.getSubId())) {
            logWithLocalLog("filterSms: Visual voicemail SMS dropped", tracker.getMessageId());
                        logWithLocalLog("Visual voicemail SMS dropped", tracker.getMessageId());
                        dropSms(resultReceiver);
                        dropSms(resultReceiver);
                        return true;
                        return true;
                    }
                    }

                    return false;
                });
        smsFilters.add(
                (pdus, destPort, tracker, resultReceiver, userUnlocked, remainingFilters) -> {
                    MissedIncomingCallSmsFilter missedIncomingCallSmsFilter =
                    MissedIncomingCallSmsFilter missedIncomingCallSmsFilter =
                            new MissedIncomingCallSmsFilter(mPhone);
                            new MissedIncomingCallSmsFilter(mPhone);
                    if (missedIncomingCallSmsFilter.filter(pdus, tracker.getFormat())) {
                    if (missedIncomingCallSmsFilter.filter(pdus, tracker.getFormat())) {
            logWithLocalLog("filterSms: Missed incoming call SMS received", tracker.getMessageId());
                        logWithLocalLog("Missed incoming call SMS received",
                                tracker.getMessageId());
                        dropSms(resultReceiver);
                        dropSms(resultReceiver);
                        return true;
                        return true;
                    }
                    }
                    return false;
                });
        return smsFilters;
    }


    /**
     * Filters the SMS.
     *
     * <p>Each filter in {@link #mSmsFilters} is invoked sequentially. If any filter returns true,
     * this method returns true and subsequent filters are ignored.
     *
     * @return true if a filter is invoked and the SMS processing flow is diverted, false otherwise.
     */
    private boolean filterSms(byte[][] pdus, int destPort,
            InboundSmsTracker tracker, SmsBroadcastReceiver resultReceiver, boolean userUnlocked) {
        return filterSms(pdus, destPort, tracker, resultReceiver, userUnlocked, mSmsFilters);
    }

    private static boolean filterSms(byte[][] pdus, int destPort,
            InboundSmsTracker tracker, SmsBroadcastReceiver resultReceiver, boolean userUnlocked,
            List<SmsFilter> filters) {
        ListIterator<SmsFilter> iterator = filters.listIterator();
        while (iterator.hasNext()) {
            SmsFilter smsFilter = iterator.next();
            if (smsFilter.filterSms(pdus, destPort, tracker, resultReceiver, userUnlocked,
                    filters.subList(iterator.nextIndex(), filters.size()))) {
                return true;
            }
        }
        return false;
        return false;
    }
    }


@@ -1527,7 +1564,8 @@ public abstract class InboundSmsHandler extends StateMachine {
     * Handler for an {@link InboundSmsTracker} broadcast. Deletes PDUs from the raw table and
     * Handler for an {@link InboundSmsTracker} broadcast. Deletes PDUs from the raw table and
     * logs the broadcast duration (as an error if the other receivers were especially slow).
     * logs the broadcast duration (as an error if the other receivers were especially slow).
     */
     */
    private final class SmsBroadcastReceiver extends BroadcastReceiver {
    @VisibleForTesting
    public final class SmsBroadcastReceiver extends BroadcastReceiver {
        @UnsupportedAppUsage
        @UnsupportedAppUsage
        private final String mDeleteWhere;
        private final String mDeleteWhere;
        @UnsupportedAppUsage
        @UnsupportedAppUsage
@@ -1612,34 +1650,37 @@ public abstract class InboundSmsHandler extends StateMachine {
            CarrierServicesSmsFilter.CarrierServicesSmsFilterCallbackInterface {
            CarrierServicesSmsFilter.CarrierServicesSmsFilterCallbackInterface {
        private final byte[][] mPdus;
        private final byte[][] mPdus;
        private final int mDestPort;
        private final int mDestPort;
        private final InboundSmsTracker mTracker;
        private final String mSmsFormat;
        private final String mSmsFormat;
        private final SmsBroadcastReceiver mSmsBroadcastReceiver;
        private final SmsBroadcastReceiver mSmsBroadcastReceiver;
        private final boolean mUserUnlocked;
        private final boolean mUserUnlocked;
        private final boolean mIsClass0;
        private final boolean mIsClass0;
        private final int mSubId;
        private final int mSubId;
        private final long mMessageId;
        private final long mMessageId;
        private final List<SmsFilter> mRemainingFilters;


        CarrierServicesSmsFilterCallback(byte[][] pdus, int destPort, String smsFormat,
        CarrierServicesSmsFilterCallback(byte[][] pdus, int destPort, InboundSmsTracker tracker,
                SmsBroadcastReceiver smsBroadcastReceiver,  boolean userUnlocked,
                String smsFormat, SmsBroadcastReceiver smsBroadcastReceiver, boolean userUnlocked,
                boolean isClass0, int subId, long messageId) {
                boolean isClass0, int subId, long messageId, List<SmsFilter> remainingFilters) {
            mPdus = pdus;
            mPdus = pdus;
            mDestPort = destPort;
            mDestPort = destPort;
            mTracker = tracker;
            mSmsFormat = smsFormat;
            mSmsFormat = smsFormat;
            mSmsBroadcastReceiver = smsBroadcastReceiver;
            mSmsBroadcastReceiver = smsBroadcastReceiver;
            mUserUnlocked = userUnlocked;
            mUserUnlocked = userUnlocked;
            mIsClass0 = isClass0;
            mIsClass0 = isClass0;
            mSubId = subId;
            mSubId = subId;
            mMessageId = messageId;
            mMessageId = messageId;
            mRemainingFilters = remainingFilters;
        }
        }


        @Override
        @Override
        public void onFilterComplete(int result) {
        public void onFilterComplete(int result) {
            log("onFilterComplete: result is " + result, mMessageId);
            log("onFilterComplete: result is " + result, mMessageId);
            if ((result & CarrierMessagingService.RECEIVE_OPTIONS_DROP) == 0) {
            if ((result & CarrierMessagingService.RECEIVE_OPTIONS_DROP) == 0) {
                if (VisualVoicemailSmsFilter.filter(mContext, mPdus,
                // Message isn't dropped, so run it through the remaining filters.
                        mSmsFormat, mDestPort, mSubId)) {
                if (filterSms(mPdus, mDestPort, mTracker, mSmsBroadcastReceiver, mUserUnlocked,
                    logWithLocalLog("Visual voicemail SMS dropped", mMessageId);
                        mRemainingFilters)) {
                    dropSms(mSmsBroadcastReceiver);
                    return;
                    return;
                }
                }


@@ -1884,6 +1925,20 @@ public abstract class InboundSmsHandler extends StateMachine {
        mWakeLockTimeout = timeOut;
        mWakeLockTimeout = timeOut;
    }
    }


    /**
     * Set the SMS filters used by {@link #filterSms} for testing purposes.
     *
     * @param smsFilters List of SMS filters, or null to restore the default filters.
     */
    @VisibleForTesting
    public void setSmsFiltersForTesting(@Nullable List<SmsFilter> smsFilters) {
        if (smsFilters == null) {
            mSmsFilters = createDefaultSmsFilters();
        } else {
            mSmsFilters = smsFilters;
        }
    }

    /**
    /**
     * Handler for the broadcast sent when the new message notification is clicked. It launches the
     * Handler for the broadcast sent when the new message notification is clicked. It launches the
     * default SMS app.
     * default SMS app.
@@ -1963,4 +2018,16 @@ public abstract class InboundSmsHandler extends StateMachine {
            }
            }
        }
        }
    }
    }

    /** A filter for incoming messages allowing the normal processing flow to be skipped. */
    @VisibleForTesting
    public interface SmsFilter {
        /**
         * Returns true if a filter is invoked and the SMS processing flow should be diverted, false
         * otherwise.
         */
        boolean filterSms(byte[][] pdus, int destPort, InboundSmsTracker tracker,
                SmsBroadcastReceiver resultReceiver, boolean userUnlocked,
                List<SmsFilter> remainingFilters);
    }
}
}
+156 −1
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.nullable;
import static org.mockito.Matchers.nullable;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doReturn;
@@ -34,6 +35,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.ContentValues;
@@ -75,9 +77,13 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.stubbing.Answer;
import org.mockito.verification.VerificationMode;


import java.lang.reflect.Method;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.List;


@RunWith(AndroidTestingRunner.class)
@RunWith(AndroidTestingRunner.class)
@@ -102,6 +108,11 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
    private ContentValues mInboundSmsTrackerCVSub1;
    private ContentValues mInboundSmsTrackerCVSub1;
    @Mock
    @Mock
    private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
    private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
    @Mock
    private InboundSmsHandler.SmsFilter mSmsFilter;
    @Mock
    private InboundSmsHandler.SmsFilter mSmsFilter2;
    private List<InboundSmsHandler.SmsFilter> mSmsFilters;


    private GsmInboundSmsHandler mGsmInboundSmsHandler;
    private GsmInboundSmsHandler mGsmInboundSmsHandler;


@@ -235,6 +246,10 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(mContext,
        mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(mContext,
                mSmsStorageMonitor, mPhone);
                mSmsStorageMonitor, mPhone);
        mSmsFilters = new ArrayList<>();
        mSmsFilters.add(mSmsFilter);
        mSmsFilters.add(mSmsFilter2);
        mGsmInboundSmsHandler.setSmsFiltersForTesting(mSmsFilters);
        monitorTestableLooper(new TestableLooper(mGsmInboundSmsHandler.getHandler().getLooper()));
        monitorTestableLooper(new TestableLooper(mGsmInboundSmsHandler.getHandler().getLooper()));
        processAllMessages();
        processAllMessages();
    }
    }
@@ -342,6 +357,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        verify(mContext, times(2)).sendBroadcast(any(Intent.class));
        verify(mContext, times(2)).sendBroadcast(any(Intent.class));
        assertEquals("IdleState", getCurrentState().getName());
        assertEquals("IdleState", getCurrentState().getName());

        verifySmsFiltersInvoked(times(1));
    }
    }


    @Test
    @Test
@@ -356,6 +373,67 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        verify(mContext, never()).sendBroadcast(any(Intent.class));
        verify(mContext, never()).sendBroadcast(any(Intent.class));
        assertEquals("IdleState", getCurrentState().getName());
        assertEquals("IdleState", getCurrentState().getName());

        // verify no filter was invoked.
        // TODO(b/136262737): Adjust test once blocked SMSes are passed through filters too.
        verifySmsFiltersInvoked(never());
    }

    @Test
    @MediumTest
    public void testNewSms_filterInvoked_noBroadcastsSent() {
        // Configure the first filter to drop the SMS.
        when(mSmsFilter.filterSms(any(byte[][].class), anyInt(),
                any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
                anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any()))
                .thenAnswer((Answer<Boolean>) invocation -> {
                    mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_COMPLETE);
                    return true;
                });

        transitionFromStartupToIdle();

        sendNewSms();

        verify(mContext, never()).sendBroadcast(any(Intent.class));
        assertEquals("IdleState", getCurrentState().getName());

        // verify second filter was never invoked.
        verify(mSmsFilter2, never()).filterSms(any(byte[][].class), anyInt(),
                any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
                anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
    }

    @Test
    @MediumTest
    public void testNewSms_filterChaining_noBroadcastsSent() {
        // Have the first filter indicate it matched without completing the flow.
        when(mSmsFilter.filterSms(any(byte[][].class), anyInt(),
                any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
                anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any())).thenReturn(true);

        transitionFromStartupToIdle();

        sendNewSms();

        verify(mContext, never()).sendBroadcast(any(Intent.class));
        // Now waiting for the first filter to complete.
        assertEquals("WaitingState", getCurrentState().getName());

        // Verify the first filter was invoked with the right set of remaining filters.
        verify(mSmsFilter).filterSms(any(byte[][].class), anyInt(),
                any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
                anyBoolean(), eq(Collections.singletonList(mSmsFilter2)));

        // Verify second filter was never invoked.
        verify(mSmsFilter2, never()).filterSms(any(byte[][].class), anyInt(),
                any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
                anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());

        // Clean up by completing the broadcast, as an asynchronous filter must do.
        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_COMPLETE);
        processAllMessages();
        assertEquals("IdleState", getCurrentState().getName());
    }
    }


    private void verifyDataSmsIntentBroadcasts(int numPastBroadcasts) {
    private void verifyDataSmsIntentBroadcasts(int numPastBroadcasts) {
@@ -402,6 +480,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
        processAllMessages();
        processAllMessages();


        verifySmsIntentBroadcasts(0, true /* allowBgActivityStarts */);
        verifySmsIntentBroadcasts(0, true /* allowBgActivityStarts */);
        verifySmsFiltersInvoked(times(1));
    }
    }


    @Test
    @Test
@@ -439,6 +518,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
        processAllMessages();
        processAllMessages();


        verifyDataSmsIntentBroadcasts(1);
        verifyDataSmsIntentBroadcasts(1);

        verifySmsFiltersInvoked(times(2));
    }
    }


    @FlakyTest
    @FlakyTest
@@ -460,6 +541,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        verify(mContext, times(2)).sendBroadcast(any(Intent.class));
        verify(mContext, times(2)).sendBroadcast(any(Intent.class));
        assertEquals("IdleState", getCurrentState().getName());
        assertEquals("IdleState", getCurrentState().getName());

        verifySmsFiltersInvoked(times(1));
    }
    }


    private void prepareMultiPartSms(boolean is3gpp2WapPush) {
    private void prepareMultiPartSms(boolean is3gpp2WapPush) {
@@ -539,6 +622,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        // verify no broadcast sent.
        // verify no broadcast sent.
        verify(mContext, times(0)).sendBroadcast(any(Intent.class));
        verify(mContext, times(0)).sendBroadcast(any(Intent.class));
        verifySmsFiltersInvoked(never());


        // additional copy of part 1 of non-3gpp2wap
        // additional copy of part 1 of non-3gpp2wap
        prepareMultiPartSms(false);
        prepareMultiPartSms(false);
@@ -555,6 +639,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
        // verify there are three segments in the db and only one of them is not marked as deleted.
        // verify there are three segments in the db and only one of them is not marked as deleted.
        assertEquals(3, mContentProvider.getNumRows());
        assertEquals(3, mContentProvider.getNumRows());
        assertEquals(1, mContentProvider.query(sRawUri, null, "deleted=0", null, null).getCount());
        assertEquals(1, mContentProvider.query(sRawUri, null, "deleted=0", null, null).getCount());

        verifySmsFiltersInvoked(times(1));
    }
    }


    @FlakyTest
    @FlakyTest
@@ -588,6 +674,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        // verify broadcast intents
        // verify broadcast intents
        verifySmsIntentBroadcasts(0);
        verifySmsIntentBroadcasts(0);
        verifySmsFiltersInvoked(times(1));


        // if an additional copy of one of the segments above is received, it should not be kept in
        // if an additional copy of one of the segments above is received, it should not be kept in
        // the db and should not be combined with any subsequent messages received from the same
        // the db and should not be combined with any subsequent messages received from the same
@@ -603,6 +690,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        // verify no additional broadcasts sent
        // verify no additional broadcasts sent
        verify(mContext, times(2)).sendBroadcast(any(Intent.class));
        verify(mContext, times(2)).sendBroadcast(any(Intent.class));
        verifySmsFiltersInvoked(times(1));


        // part 1 of new sms recieved from same sender with same parameters, just different
        // part 1 of new sms recieved from same sender with same parameters, just different
        // timestamps, should not be combined with the additional part 2 received above
        // timestamps, should not be combined with the additional part 2 received above
@@ -620,6 +708,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        // verify no additional broadcasts sent
        // verify no additional broadcasts sent
        verify(mContext, times(2)).sendBroadcast(any(Intent.class));
        verify(mContext, times(2)).sendBroadcast(any(Intent.class));
        verifySmsFiltersInvoked(times(1));


        assertEquals("IdleState", getCurrentState().getName());
        assertEquals("IdleState", getCurrentState().getName());
    }
    }
@@ -682,6 +771,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
        assertEquals(mMessageBodyPart2, c.getString(c.getColumnIndex("message_body")));
        assertEquals(mMessageBodyPart2, c.getString(c.getColumnIndex("message_body")));
        // State machine should go back to idle
        // State machine should go back to idle
        assertEquals("IdleState", getCurrentState().getName());
        assertEquals("IdleState", getCurrentState().getName());
        verifySmsFiltersInvoked(never());
    }
    }


    @Test
    @Test
@@ -737,6 +827,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
        verify(mContext, never()).sendBroadcast(any(Intent.class));
        verify(mContext, never()).sendBroadcast(any(Intent.class));
        // State machine should go back to idle
        // State machine should go back to idle
        assertEquals("IdleState", getCurrentState().getName());
        assertEquals("IdleState", getCurrentState().getName());
        verifySmsFiltersInvoked(never());
    }
    }


    @Test
    @Test
@@ -771,6 +862,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        verify(mContext, never()).sendBroadcast(any(Intent.class));
        verify(mContext, never()).sendBroadcast(any(Intent.class));
        assertEquals("IdleState", getCurrentState().getName());
        assertEquals("IdleState", getCurrentState().getName());
        // TODO(b/136262737): Adjust test once blocked SMSes are passed through filters too.
        verifySmsFiltersInvoked(never());
    }
    }


    @Test
    @Test
@@ -821,6 +914,55 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        verify(mContext, never()).sendBroadcast(any(Intent.class));
        verify(mContext, never()).sendBroadcast(any(Intent.class));
        assertEquals("IdleState", getCurrentState().getName());
        assertEquals("IdleState", getCurrentState().getName());
        // TODO(b/136262737): Adjust test once blocked SMSes are passed through filters too.
        verifySmsFiltersInvoked(never());
    }

    @Test
    @MediumTest
    public void testMultipartSms_filterInvoked_noBroadcastsSent() {
        // Configure the first filter to drop the SMS.
        when(mSmsFilter.filterSms(any(byte[][].class), anyInt(),
                any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
                anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any()))
                .thenAnswer((Answer<Boolean>) invocation -> {
                    mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_COMPLETE);
                    return true;
                });

        transitionFromStartupToIdle();

        // prepare SMS part 1 and part 2
        prepareMultiPartSms(false);

        mSmsHeader.concatRef = new SmsHeader.ConcatRef();
        doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();

        doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
                .makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
                        anyInt(), anyBoolean(),
                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                        anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
        sendNewSms();

        // State machine should go back to idle and wait for second part
        assertEquals("IdleState", getCurrentState().getName());

        doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
                .makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
                        anyInt(), anyBoolean(),
                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                        anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
        sendNewSms();

        // verify no broadcasts sent
        verify(mContext, never()).sendBroadcast(any(Intent.class));
        assertEquals("IdleState", getCurrentState().getName());

        // verify second filter was never invoked.
        verify(mSmsFilter2, never()).filterSms(any(byte[][].class), anyInt(),
                any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
                anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
    }
    }


    @Test
    @Test
@@ -860,6 +1002,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
        processAllMessages();
        processAllMessages();


        verifyDataSmsIntentBroadcasts(1);
        verifyDataSmsIntentBroadcasts(1);
        verifySmsFiltersInvoked(times(1));
    }
    }


    @Test
    @Test
@@ -880,6 +1023,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        // user is unlocked; intent should be broadcast right away
        // user is unlocked; intent should be broadcast right away
        verifyDataSmsIntentBroadcasts(0);
        verifyDataSmsIntentBroadcasts(0);
        verifySmsFiltersInvoked(times(1));
    }
    }


    @Test
    @Test
@@ -918,7 +1062,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        verify(mContext, times(1)).sendBroadcast(any(Intent.class));
        verify(mContext, times(1)).sendBroadcast(any(Intent.class));
        assertEquals("IdleState", getCurrentState().getName());
        assertEquals("IdleState", getCurrentState().getName());

        verifySmsFiltersInvoked(never());
    }
    }


    @FlakyTest
    @FlakyTest
@@ -945,6 +1089,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
        processAllMessages();
        processAllMessages();


        verifySmsIntentBroadcasts(0);
        verifySmsIntentBroadcasts(0);
        verifySmsFiltersInvoked(times(1));
    }
    }


    @Test
    @Test
@@ -963,5 +1108,15 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {


        verifySmsIntentBroadcasts(0, mSubId0, true);
        verifySmsIntentBroadcasts(0, mSubId0, true);
        verifySmsIntentBroadcasts(2, mSubId1, false);
        verifySmsIntentBroadcasts(2, mSubId1, false);
        verifySmsFiltersInvoked(times(2));
    }

    private void verifySmsFiltersInvoked(VerificationMode verificationMode) {
        verify(mSmsFilter, verificationMode).filterSms(any(byte[][].class), anyInt(),
                any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
                anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
        verify(mSmsFilter2, verificationMode).filterSms(any(byte[][].class), anyInt(),
                any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
                anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
    }
    }
}
}