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

Commit 892ca83f authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add timeout to unblock sms handler state machine for missing broadcast." into sc-dev

parents 7f7a9967 d59742b3
Loading
Loading
Loading
Loading
+96 −20
Original line number Diff line number Diff line
@@ -186,6 +186,9 @@ public abstract class InboundSmsHandler extends StateMachine {
    /** Update the sms tracker */
    public static final int EVENT_UPDATE_TRACKER = 8;

    /** BroadcastReceiver timed out waiting for an intent */
    public static final int EVENT_RECEIVER_TIMEOUT = 9;

    /** Wakelock release delay when returning to idle state. */
    private static final int WAKELOCK_TIMEOUT = 3000;

@@ -378,6 +381,9 @@ public abstract class InboundSmsHandler extends StateMachine {
            case EVENT_UPDATE_TRACKER:
                whatString = "EVENT_UPDATE_TRACKER";
                break;
            case EVENT_RECEIVER_TIMEOUT:
                whatString = "EVENT_RECEIVER_TIMEOUT";
                break;
            default:
                whatString = "UNKNOWN EVENT " + what;
        }
@@ -631,6 +637,15 @@ public abstract class InboundSmsHandler extends StateMachine {
                    deferMessage(msg);
                    return HANDLED;

                case EVENT_RECEIVER_TIMEOUT:
                    logeWithLocalLog("WaitingState.processMessage: received "
                            + "EVENT_RECEIVER_TIMEOUT");
                    if (mLastDeliveredSmsTracker != null) {
                        mLastDeliveredSmsTracker.getSmsBroadcastReceiver(InboundSmsHandler.this)
                                .fakeNextAction();
                    }
                    return HANDLED;

                case EVENT_BROADCAST_COMPLETE:
                    mLastDeliveredSmsTracker = null;
                    // return to idle after handling all deferred messages
@@ -1039,7 +1054,7 @@ public abstract class InboundSmsHandler extends StateMachine {
            }
        }

        SmsBroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker);
        SmsBroadcastReceiver resultReceiver = tracker.getSmsBroadcastReceiver(this);

        if (!mUserManager.isUserUnlocked()) {
            log("processMessagePart: !isUserUnlocked; calling processMessagePartWithUserLocked. "
@@ -1288,7 +1303,7 @@ public abstract class InboundSmsHandler extends StateMachine {
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void dispatchIntent(Intent intent, String permission, String appOp,
            Bundle opts, BroadcastReceiver resultReceiver, UserHandle user, int subId) {
            Bundle opts, SmsBroadcastReceiver resultReceiver, UserHandle user, int subId) {
        intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
        final String action = intent.getAction();
        if (Intents.SMS_DELIVER_ACTION.equals(action)
@@ -1319,6 +1334,11 @@ public abstract class InboundSmsHandler extends StateMachine {
            for (UserHandle handle : userHandles) {
                if (mUserManager.isUserRunning(handle)) {
                    runningUserHandles.add(handle);
                } else {
                    if (handle.equals(UserHandle.SYSTEM)) {
                        logeWithLocalLog("dispatchIntent: SYSTEM user is not running",
                                resultReceiver.mInboundSmsTracker.getMessageId());
                    }
                }
            }
            if (runningUserHandles.isEmpty()) {
@@ -1345,6 +1365,9 @@ public abstract class InboundSmsHandler extends StateMachine {
                }
                // Only pass in the resultReceiver when the user SYSTEM is processed.
                try {
                    if (users[i] == UserHandle.SYSTEM.getIdentifier()) {
                        resultReceiver.setWaitingForIntent(intent);
                    }
                    mContext.createPackageContextAsUser(mContext.getPackageName(), 0, targetUser)
                            .sendOrderedBroadcast(intent, Activity.RESULT_OK, permission, appOp,
                                    users[i] == UserHandle.SYSTEM.getIdentifier()
@@ -1355,6 +1378,7 @@ public abstract class InboundSmsHandler extends StateMachine {
            }
        } else {
            try {
                resultReceiver.setWaitingForIntent(intent);
                mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user)
                        .sendOrderedBroadcast(intent, Activity.RESULT_OK, permission, appOp,
                                resultReceiver, getHandler(), null /* initialData */,
@@ -1629,27 +1653,80 @@ public abstract class InboundSmsHandler extends StateMachine {
        return (PHONE_TYPE_CDMA == activePhone);
    }

    @VisibleForTesting
    public static int sTimeoutDurationMillis = 10 * 60 * 1000; // 10 minutes

    /**
     * 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).
     */
    @VisibleForTesting
    public final class SmsBroadcastReceiver extends BroadcastReceiver {
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        private final String mDeleteWhere;
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        private final String[] mDeleteWhereArgs;
        private long mBroadcastTimeNano;
        private long mBroadcastTimeMillis;
        public Intent mWaitingForIntent;
        private final InboundSmsTracker mInboundSmsTracker;

        SmsBroadcastReceiver(InboundSmsTracker tracker) {
        /**
         * This method must be called anytime an ordered broadcast is sent that is expected to be
         * received by this receiver.
         */
        public synchronized void setWaitingForIntent(Intent intent) {
            mWaitingForIntent = intent;
            mBroadcastTimeMillis = System.currentTimeMillis();
            removeMessages(EVENT_RECEIVER_TIMEOUT);
            sendMessageDelayed(EVENT_RECEIVER_TIMEOUT, sTimeoutDurationMillis);
        }

        public SmsBroadcastReceiver(InboundSmsTracker tracker) {
            mDeleteWhere = tracker.getDeleteWhere();
            mDeleteWhereArgs = tracker.getDeleteWhereArgs();
            mBroadcastTimeNano = System.nanoTime();
            mInboundSmsTracker = tracker;
        }

        /**
         * This method is called if the expected intent (mWaitingForIntent) is not received and
         * the timer for it expires. It fakes the receipt of the intent to unblock the state
         * machine.
         */
        public void fakeNextAction() {
            if (mWaitingForIntent != null) {
                logeWithLocalLog("fakeNextAction: " + mWaitingForIntent.getAction(),
                        mInboundSmsTracker.getMessageId());
                handleAction(mWaitingForIntent, false);
            } else {
                logeWithLocalLog("fakeNextAction: mWaitingForIntent is null",
                        mInboundSmsTracker.getMessageId());
            }
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            handleAction(intent, true);
        }

        private synchronized void handleAction(Intent intent, boolean onReceive) {
            String action = intent.getAction();
            if (mWaitingForIntent == null || !mWaitingForIntent.getAction().equals(action)) {
                logeWithLocalLog("handleAction: Received " + action + " when expecting "
                        + mWaitingForIntent == null ? "none" : mWaitingForIntent.getAction(),
                        mInboundSmsTracker.getMessageId());
                return;
            }

            if (onReceive) {
                int durationMillis = (int) (System.currentTimeMillis() - mBroadcastTimeMillis);
                if (durationMillis >= 5000) {
                    loge("Slow ordered broadcast completion time for " + action + ": "
                            + durationMillis + " ms");
                } else if (DBG) {
                    log("Ordered broadcast completed for " + action + " in: "
                            + durationMillis + " ms");
                }
            }

            int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
            if (action.equals(Intents.SMS_DELIVER_ACTION)) {
@@ -1661,6 +1738,7 @@ public abstract class InboundSmsHandler extends StateMachine {
                // All running users will be notified of the received sms.
                Bundle options = handleSmsWhitelisting(null, false /* bgActivityStartAllowed */);

                setWaitingForIntent(intent);
                dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
                        AppOpsManager.OPSTR_RECEIVE_SMS,
                        options, this, UserHandle.ALL, subId);
@@ -1683,6 +1761,8 @@ public abstract class InboundSmsHandler extends StateMachine {
                Bundle options = bopts.toBundle();

                String mimeType = intent.getType();

                setWaitingForIntent(intent);
                dispatchIntent(intent, WapPushOverSms.getPermissionForType(mimeType),
                        WapPushOverSms.getAppOpsStringPermissionForIntent(mimeType), options, this,
                        UserHandle.SYSTEM, subId);
@@ -1690,11 +1770,11 @@ public abstract class InboundSmsHandler extends StateMachine {
                // Now that the intents have been deleted we can clean up the PDU data.
                if (!Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
                        && !Intents.SMS_RECEIVED_ACTION.equals(action)
                        && !Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
                        && !Intents.WAP_PUSH_RECEIVED_ACTION.equals(action)) {
                    loge("unexpected BroadcastReceiver action: " + action);
                }

                if (onReceive) {
                    int rc = getResultCode();
                    if ((rc != Activity.RESULT_OK) && (rc != Intents.RESULT_SMS_HANDLED)) {
                        loge("a broadcast receiver set the result code to " + rc
@@ -1702,16 +1782,12 @@ public abstract class InboundSmsHandler extends StateMachine {
                    } else if (DBG) {
                        log("successful broadcast, deleting from raw table.");
                    }
                }

                deleteFromRawTable(mDeleteWhere, mDeleteWhereArgs, MARK_DELETED);
                mWaitingForIntent = null;
                removeMessages(EVENT_RECEIVER_TIMEOUT);
                sendMessage(EVENT_BROADCAST_COMPLETE);

                int durationMillis = (int) ((System.nanoTime() - mBroadcastTimeNano) / 1000000);
                if (durationMillis >= 5000) {
                    loge("Slow ordered broadcast completion time: " + durationMillis + " ms");
                } else if (DBG) {
                    log("ordered broadcast completed in: " + durationMillis + " ms");
                }
            }
        }
    }
+14 −0
Original line number Diff line number Diff line
@@ -68,6 +68,8 @@ public class InboundSmsTracker {
    private String mDeleteWhere;
    private String[] mDeleteWhereArgs;

    // BroadcastReceiver associated with this tracker
    private InboundSmsHandler.SmsBroadcastReceiver mSmsBroadcastReceiver;
    /**
     * Copied from SmsMessageBase#getDisplayOriginatingAddress used for blocking messages.
     * DisplayAddress could be email address if this message was from an email gateway, otherwise
@@ -509,4 +511,16 @@ public class InboundSmsTracker {
    public @InboundSmsHandler.SmsSource int getSource() {
        return mSmsSource;
    }

    /**
     * Get/create the SmsBroadcastReceiver corresponding to the current tracker.
     */
    public InboundSmsHandler.SmsBroadcastReceiver getSmsBroadcastReceiver(
            InboundSmsHandler handler) {
        // lazy initialization
        if (mSmsBroadcastReceiver == null) {
            mSmsBroadcastReceiver = handler.new SmsBroadcastReceiver(this);
        }
        return mSmsBroadcastReceiver;
    }
}
+2 −3
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import android.app.Activity;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -317,8 +316,8 @@ public class WapPushOverSms implements ServiceConnection {
     *         {@link Activity#RESULT_OK} if the message has been broadcast
     *         to applications
     */
    public int dispatchWapPdu(byte[] pdu, BroadcastReceiver receiver, InboundSmsHandler handler,
            String address, int subId, long messageId) {
    public int dispatchWapPdu(byte[] pdu, InboundSmsHandler.SmsBroadcastReceiver receiver,
            InboundSmsHandler handler, String address, int subId, long messageId) {
        DecodedResult result = decodeWapPdu(pdu, handler);
        if (result.statusCode != Activity.RESULT_OK) {
            return result.statusCode;
+2 −3
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
@@ -92,7 +91,7 @@ public class WapPushOverSmsTest extends TelephonyTest {
                eq(android.Manifest.permission.RECEIVE_WAP_PUSH),
                eq(AppOpsManager.OPSTR_RECEIVE_WAP_PUSH),
                nullable(Bundle.class),
                isNull(BroadcastReceiver.class),
                isNull(InboundSmsHandler.SmsBroadcastReceiver.class),
                eq(UserHandle.SYSTEM),
                anyInt());
        Intent intent = intentArgumentCaptor.getValue();
@@ -147,7 +146,7 @@ public class WapPushOverSmsTest extends TelephonyTest {
                any(String.class),
                any(String.class),
                any(Bundle.class),
                any(BroadcastReceiver.class),
                any(InboundSmsHandler.SmsBroadcastReceiver.class),
                any(UserHandle.class),
                anyInt());
    }
+97 −76
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
@@ -64,12 +63,10 @@ import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.InboundSmsTracker;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SmsBroadcastUndelivered;
import com.android.internal.telephony.SmsConstants;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsStorageMonitor;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
import com.android.internal.util.HexDump;
import com.android.internal.util.IState;
import com.android.internal.util.StateMachine;

@@ -100,15 +97,10 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
    @Mock
    private SmsHeader mSmsHeader;
    private InboundSmsTracker mInboundSmsTracker;
    private InboundSmsTracker mInboundSmsTrackerSub1;
    private InboundSmsTracker mInboundSmsTrackerPart1;
    private InboundSmsTracker mInboundSmsTrackerPart2;
    @Mock
    private InboundSmsTracker mMockInboundSmsTracker;
    private ContentValues mInboundSmsTrackerCV;
    @Mock
    private InboundSmsTracker mMockInboundSmsTrackerSub1;
    private ContentValues mInboundSmsTrackerCVSub1;
    @Mock
    private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
    @Mock
    private InboundSmsHandler.SmsFilter mSmsFilter;
@@ -143,63 +135,25 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
    }

    /**
     * This is used only for InboundSmsTracker constructed through Cursor. For other cases
     * real objects should be used. This should be used only for tests related to
     * SmsBroadcastUndelivered.
     * This is used only for InboundSmsTracker constructed through Cursor. This should be used only
     * for tests related to SmsBroadcastUndelivered. Also, this adds a second tracker for multisim.
     */
    private void createMockInboundSmsTracker() {
        mInboundSmsTrackerCV = new ContentValues();
        mInboundSmsTrackerCV.put("destination_port", InboundSmsTracker.DEST_PORT_FLAG_NO_PORT);
        mInboundSmsTrackerCV.put("pdu", HexDump.toHexString(mSmsPdu));
        mInboundSmsTrackerCV.put("address", "1234567890");
        mInboundSmsTrackerCV.put("reference_number", 1);
        mInboundSmsTrackerCV.put("sequence", 1);
        mInboundSmsTrackerCV.put("count", 1);
        mInboundSmsTrackerCV.put("date", System.currentTimeMillis());
        mInboundSmsTrackerCV.put("message_body", mMessageBody);
        mInboundSmsTrackerCV.put("display_originating_addr", "1234567890");
        mInboundSmsTrackerCV.put("sub_id", mSubId0);

        doReturn(1).when(mMockInboundSmsTracker).getMessageCount();
        doReturn(1).when(mMockInboundSmsTracker).getReferenceNumber();
        doReturn("1234567890").when(mMockInboundSmsTracker).getAddress();
        doReturn(1).when(mMockInboundSmsTracker).getSequenceNumber();
        doReturn(1).when(mMockInboundSmsTracker).getIndexOffset();
        doReturn(-1).when(mMockInboundSmsTracker).getDestPort();
        doReturn(mMessageBody).when(mMockInboundSmsTracker).getMessageBody();
        doReturn(mSmsPdu).when(mMockInboundSmsTracker).getPdu();
        doReturn(mInboundSmsTrackerCV.get("date")).when(mMockInboundSmsTracker).getTimestamp();
        doReturn(SmsConstants.FORMAT_3GPP).when(mMockInboundSmsTracker).getFormat();
        doReturn(mInboundSmsTrackerCV).when(mMockInboundSmsTracker).getContentValues();
        doReturn(mSubId0).when(mMockInboundSmsTracker).getSubId();

        mInboundSmsTrackerCVSub1 = new ContentValues();
        mInboundSmsTrackerCVSub1.put("destination_port", InboundSmsTracker.DEST_PORT_FLAG_NO_PORT);
        mInboundSmsTrackerCVSub1.put("pdu", HexDump.toHexString(mSmsPdu));
        mInboundSmsTrackerCVSub1.put("address", "1234567890");
        mInboundSmsTrackerCVSub1.put("reference_number", 1);
        mInboundSmsTrackerCVSub1.put("sequence", 1);
        mInboundSmsTrackerCVSub1.put("count", 1);
        mInboundSmsTrackerCVSub1.put("date", System.currentTimeMillis());
        mInboundSmsTrackerCVSub1.put("message_body", mMessageBody);
        mInboundSmsTrackerCVSub1.put("display_originating_addr", "1234567890");
        mInboundSmsTrackerCVSub1.put("sub_id", mSubId1);

        doReturn(1).when(mMockInboundSmsTrackerSub1).getMessageCount();
        doReturn(1).when(mMockInboundSmsTrackerSub1).getReferenceNumber();
        doReturn("1234567890").when(mMockInboundSmsTrackerSub1).getAddress();
        doReturn(1).when(mMockInboundSmsTrackerSub1).getSequenceNumber();
        doReturn(1).when(mMockInboundSmsTrackerSub1).getIndexOffset();
        doReturn(-1).when(mMockInboundSmsTrackerSub1).getDestPort();
        doReturn(mMessageBody).when(mMockInboundSmsTrackerSub1).getMessageBody();
        doReturn(mSmsPdu).when(mMockInboundSmsTrackerSub1).getPdu();
        doReturn(mInboundSmsTrackerCVSub1.get("date")).when(mMockInboundSmsTrackerSub1)
                .getTimestamp();
        doReturn(SmsConstants.FORMAT_3GPP).when(mMockInboundSmsTrackerSub1).getFormat();
        doReturn(mInboundSmsTrackerCVSub1).when(mMockInboundSmsTrackerSub1).getContentValues();
        doReturn(mSubId1).when(mMockInboundSmsTrackerSub1).getSubId();

        doReturn(mMockInboundSmsTracker).doReturn(mMockInboundSmsTrackerSub1)
    private void createInboundSmsTrackerMultiSim() {
        mInboundSmsTrackerSub1 = new InboundSmsTracker(
                mContext,
                mSmsPdu, /* pdu */
                System.currentTimeMillis(), /* timestamp */
                -1, /* destPort */
                false, /* is3gpp2 */
                false, /* is3gpp2WapPdu */
                "1234567890", /* address */
                "1234567890", /* displayAddress */
                mMessageBody, /* messageBody */
                false, /* isClass0 */
                mSubId1,
                InboundSmsHandler.SOURCE_NOT_INJECTED);

        doReturn(mInboundSmsTracker).doReturn(mInboundSmsTrackerSub1)
                .when(mTelephonyComponentFactory)
                .makeInboundSmsTracker(any(Context.class), nullable(Cursor.class),
                        anyBoolean());
@@ -241,7 +195,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
                anyBoolean(), nullable(String.class), nullable(String.class),
                nullable(String.class), anyBoolean(), anyInt(), anyInt());

        createMockInboundSmsTracker();
        createInboundSmsTrackerMultiSim();

        mContentProvider = new FakeSmsContentProvider();
        ((MockContentResolver)mContext.getContentResolver()).addProvider(
@@ -259,6 +213,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
        doReturn(mCdmaInboundSmsHandler).when(mPhone).getInboundSmsHandler(true);

        processAllMessages();
        logd("setUp: complete");
    }

    @After
@@ -509,9 +464,10 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
                intentArgumentCaptor.capture());
        assertEquals(Telephony.Sms.Intents.DATA_SMS_RECEIVED_ACTION,
                intentArgumentCaptor.getAllValues().get(numPastBroadcasts).getAction());
        assertNotEquals(0L,
        // TODO mock messageId correctly in InboundSmsTracker
        /* assertNotEquals(0L,
                intentArgumentCaptor.getAllValues().get(numPastBroadcasts)
                        .getLongExtra("messageId", 0L));
                        .getLongExtra("messageId", 0L)); */
        assertEquals("WaitingState", getCurrentState().getName());

        mContextFixture.sendBroadcastToOrderedBroadcastReceivers();
@@ -1069,11 +1025,28 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
    @MediumTest
    public void testBroadcastUndeliveredUserLocked() throws Exception {
        replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null);
        doReturn(0).when(mMockInboundSmsTracker).getDestPort();
        doReturn(2131L).when(mMockInboundSmsTracker).getMessageId();

        mInboundSmsTracker = new InboundSmsTracker(
                mContext,
                mSmsPdu, /* pdu */
                System.currentTimeMillis(), /* timestamp */
                0, /* destPort */
                false, /* is3gpp2 */
                false, /* is3gpp2WapPdu */
                "1234567890", /* address */
                "1234567890", /* displayAddress */
                mMessageBody, /* messageBody */
                false, /* isClass0 */
                mSubId0,
                InboundSmsHandler.SOURCE_NOT_INJECTED);

        doReturn(mInboundSmsTracker)
                .when(mTelephonyComponentFactory)
                .makeInboundSmsTracker(any(Context.class), nullable(Cursor.class),
                        anyBoolean());

        // add a fake entry to db
        mContentProvider.insert(sRawUri, mMockInboundSmsTracker.getContentValues());
        mContentProvider.insert(sRawUri, mInboundSmsTracker.getContentValues());

        // user locked
        UserManager userManager = (UserManager)mContext.getSystemService(Context.USER_SERVICE);
@@ -1110,11 +1083,27 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
    @MediumTest
    public void testBroadcastUndeliveredUserUnlocked() throws Exception {
        replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null);
        doReturn(0).when(mMockInboundSmsTracker).getDestPort();
        doReturn(2131L).when(mMockInboundSmsTracker).getMessageId();
        mInboundSmsTracker = new InboundSmsTracker(
                mContext,
                mSmsPdu, /* pdu */
                System.currentTimeMillis(), /* timestamp */
                0, /* destPort */
                false, /* is3gpp2 */
                false, /* is3gpp2WapPdu */
                "1234567890", /* address */
                "1234567890", /* displayAddress */
                mMessageBody, /* messageBody */
                false, /* isClass0 */
                mSubId0,
                InboundSmsHandler.SOURCE_NOT_INJECTED);

        doReturn(mInboundSmsTracker)
                .when(mTelephonyComponentFactory)
                .makeInboundSmsTracker(any(Context.class), nullable(Cursor.class),
                        anyBoolean());

        // add a fake entry to db
        mContentProvider.insert(sRawUri, mMockInboundSmsTracker.getContentValues());
        mContentProvider.insert(sRawUri, mInboundSmsTracker.getContentValues());

        SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);

@@ -1202,8 +1191,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
        replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null);

        // add SMSs from different subs to db
        mContentProvider.insert(sRawUri, mMockInboundSmsTracker.getContentValues());
        mContentProvider.insert(sRawUri, mMockInboundSmsTrackerSub1.getContentValues());
        mContentProvider.insert(sRawUri, mInboundSmsTracker.getContentValues());
        mContentProvider.insert(sRawUri, mInboundSmsTrackerSub1.getContentValues());

        SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
        // wait for ScanRawTableThread
@@ -1223,4 +1212,36 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
                any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
                anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
    }

    @Test
    @MediumTest
    public void testBroadcastTimeout() {
        InboundSmsHandler.sTimeoutDurationMillis = 100;
        transitionFromStartupToIdle();

        // send new SMS to state machine and verify that triggers SMS_DELIVER_ACTION
        sendNewSms();

        ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
        verify(mContext).sendBroadcast(intentArgumentCaptor.capture());
        Intent intent = intentArgumentCaptor.getAllValues().get(0);
        assertEquals(Telephony.Sms.Intents.SMS_DELIVER_ACTION, intent.getAction());
        assertEquals("WaitingState", getCurrentState().getName());

        // don't send broadcast back to InboundSmsHandler, instead wait for timeout
        waitForMs(300);
        processAllMessages();

        intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
        verify(mContext, times(2)).sendBroadcast(intentArgumentCaptor.capture());
        intent = intentArgumentCaptor.getAllValues().get(1);
        assertEquals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION, intent.getAction());

        // don't send broadcast back to InboundSmsHandler, instead wait for timeout
        waitForMs(300);
        processAllMessages();

        assertEquals("IdleState", getCurrentState().getName());

    }
}