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

Commit 4a3e6eb6 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Clean up SMS filter handling." into rvc-qpr-dev am: 83b3cdf5

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/opt/telephony/+/12110413

Change-Id: I041de866a8f722fd16998557b9131ab750c1d0e6
parents 360722ef 83b3cdf5
Loading
Loading
Loading
Loading
+115 −48
Original line number 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.telephony.TelephonyManager.PHONE_TYPE_CDMA;

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

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

    /** 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. */
    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. */
    private boolean mLastSmsWasInjected = false;

    private List<SmsFilter> mSmsFilters;

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

        mSmsFilters = createDefaultSmsFilters();

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

    /**
     * Filters the SMS.
     *
     * <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.
     * Creates the default filters used to filter SMS messages.
     *
     * <p>If the carrier package does not exists, we will let the VisualVoicemailSmsFilter filter
     * it. If the SMS passed the filter, then we will try to find the system package to do the
     * filtering.
     * <p>Currently 3 filters exist: the carrier package, the VisualVoicemailSmsFilter, and the
     * missed incoming call SMS filter.
     *
     * @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,
        InboundSmsTracker tracker, SmsBroadcastReceiver resultReceiver, boolean userUnlocked) {
    private List<SmsFilter> createDefaultSmsFilters() {
        List<SmsFilter> smsFilters = new ArrayList<>(3);
        smsFilters.add(
                (pdus, destPort, tracker, resultReceiver, userUnlocked, remainingFilters) -> {
                    CarrierServicesSmsFilterCallback filterCallback =
                            new CarrierServicesSmsFilterCallback(
                        pdus, destPort, tracker.getFormat(), resultReceiver, userUnlocked,
                        tracker.isClass0(), tracker.getSubId(), tracker.getMessageId());
                                    pdus, destPort, tracker, tracker.getFormat(), resultReceiver,
                                    userUnlocked,
                                    tracker.isClass0(), tracker.getSubId(), tracker.getMessageId(),
                                    remainingFilters);
                    CarrierServicesSmsFilter carrierServicesFilter = new CarrierServicesSmsFilter(
                            mContext, mPhone, pdus, destPort, tracker.getFormat(),
                filterCallback, getName() + "::CarrierServicesSmsFilter", mCarrierServiceLocalLog,
                tracker.getMessageId());
                            filterCallback, getName() + "::CarrierServicesSmsFilter",
                            mCarrierServiceLocalLog, tracker.getMessageId());
                    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;
                    } else {
                        return false;
                    }

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

                    return false;
                });
        smsFilters.add(
                (pdus, destPort, tracker, resultReceiver, userUnlocked, remainingFilters) -> {
                    MissedIncomingCallSmsFilter missedIncomingCallSmsFilter =
                            new MissedIncomingCallSmsFilter(mPhone);
                    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);
                        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;
    }

@@ -1527,7 +1564,8 @@ public abstract class InboundSmsHandler extends StateMachine {
     * 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).
     */
    private final class SmsBroadcastReceiver extends BroadcastReceiver {
    @VisibleForTesting
    public final class SmsBroadcastReceiver extends BroadcastReceiver {
        @UnsupportedAppUsage
        private final String mDeleteWhere;
        @UnsupportedAppUsage
@@ -1612,34 +1650,37 @@ public abstract class InboundSmsHandler extends StateMachine {
            CarrierServicesSmsFilter.CarrierServicesSmsFilterCallbackInterface {
        private final byte[][] mPdus;
        private final int mDestPort;
        private final InboundSmsTracker mTracker;
        private final String mSmsFormat;
        private final SmsBroadcastReceiver mSmsBroadcastReceiver;
        private final boolean mUserUnlocked;
        private final boolean mIsClass0;
        private final int mSubId;
        private final long mMessageId;
        private final List<SmsFilter> mRemainingFilters;

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

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

@@ -1884,6 +1925,20 @@ public abstract class InboundSmsHandler extends StateMachine {
        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
     * 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 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.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.nullable;
import static org.mockito.Mockito.atLeastOnce;
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.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.BroadcastReceiver;
import android.content.ContentValues;
@@ -75,9 +77,13 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
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.util.ArrayList;
import java.util.Collections;
import java.util.List;

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

    private GsmInboundSmsHandler mGsmInboundSmsHandler;

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

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

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

        verifySmsFiltersInvoked(times(1));
    }

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

        verify(mContext, never()).sendBroadcast(any(Intent.class));
        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) {
@@ -402,6 +480,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
        processAllMessages();

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

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

        verifyDataSmsIntentBroadcasts(1);

        verifySmsFiltersInvoked(times(2));
    }

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

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

        verifySmsFiltersInvoked(times(1));
    }

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

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

        // additional copy of part 1 of non-3gpp2wap
        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.
        assertEquals(3, mContentProvider.getNumRows());
        assertEquals(1, mContentProvider.query(sRawUri, null, "deleted=0", null, null).getCount());

        verifySmsFiltersInvoked(times(1));
    }

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

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

        // 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
@@ -603,6 +690,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {

        // verify no additional broadcasts sent
        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
        // 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(mContext, times(2)).sendBroadcast(any(Intent.class));
        verifySmsFiltersInvoked(times(1));

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

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

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

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

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

        verify(mContext, never()).sendBroadcast(any(Intent.class));
        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
@@ -860,6 +1002,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
        processAllMessages();

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

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

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

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

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

        verifySmsFiltersInvoked(never());
    }

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

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

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

        verifySmsIntentBroadcasts(0, mSubId0, true);
        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());
    }
}