Skip to content

Commit 5dc2110

Browse files
thomasbuildsthestinger
authored andcommitted
Fix crash receiving SMS when the local database insert fails
Commit 641e11b dropped the null guard around the received-message log statement while keeping the catch block that swallows database failures; commit 91c7eb7 had introduced the two together. If anything throws inside the transaction before the MessageData is constructed - such as a transient SQLiteException from the participant lookup - message stays null and the unconditional message.getMessageId() call throws an uncaught NullPointerException that kills the process, skipping the notification and pending-message processing even though the message was already written to telephony. Restore the guard so these failures degrade gracefully again.
1 parent da08aaf commit 5dc2110

2 files changed

Lines changed: 119 additions & 3 deletions

File tree

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.android.messaging.datamodel.action
2+
3+
import android.content.ContentValues
4+
import android.database.sqlite.SQLiteException
5+
import android.provider.Telephony.Sms
6+
import androidx.core.content.contentValuesOf
7+
import com.android.messaging.FactoryTestAccess
8+
import com.android.messaging.datamodel.BugleDatabaseOperations
9+
import com.android.messaging.datamodel.BugleNotifications
10+
import com.android.messaging.datamodel.DataModel
11+
import com.android.messaging.datamodel.MessagingContentProvider
12+
import com.android.messaging.datamodel.data.ParticipantData
13+
import com.android.messaging.sms.MmsSmsUtils
14+
import com.android.messaging.testutil.installTestFactory
15+
import io.mockk.every
16+
import io.mockk.just
17+
import io.mockk.mockk
18+
import io.mockk.mockkStatic
19+
import io.mockk.runs
20+
import io.mockk.unmockkAll
21+
import io.mockk.verify
22+
import org.junit.After
23+
import org.junit.Assert.assertNull
24+
import org.junit.Before
25+
import org.junit.Test
26+
import org.junit.runner.RunWith
27+
import org.robolectric.RobolectricTestRunner
28+
import org.robolectric.RuntimeEnvironment
29+
30+
@RunWith(RobolectricTestRunner::class)
31+
class ReceiveSmsMessageActionDatabaseFailureTest {
32+
33+
private val dataModel = mockk<DataModel>(relaxed = true)
34+
35+
@Before
36+
fun setUp() {
37+
installTestFactory(
38+
context = RuntimeEnvironment.getApplication().applicationContext,
39+
dataModel = dataModel,
40+
)
41+
mockkStatic(MmsSmsUtils.Threads::class)
42+
every { MmsSmsUtils.Threads.getOrCreateThreadId(any(), any<String>()) } returns THREAD_ID
43+
mockkStatic(BugleDatabaseOperations::class)
44+
every { BugleDatabaseOperations.isBlockedDestination(any(), any()) } returns false
45+
every {
46+
BugleDatabaseOperations.getOrCreateConversationFromRecipient(any(), any(), any(), any())
47+
} returns CONVERSATION_ID
48+
every {
49+
BugleDatabaseOperations.getOrCreateParticipantInTransaction(any(), any())
50+
} throws SQLiteException("Transient participant lookup failure")
51+
mockkStatic(BugleNotifications::class)
52+
every { BugleNotifications.update(any(), any()) } just runs
53+
mockkStatic(ProcessPendingMessagesAction::class)
54+
every {
55+
ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(any(), any())
56+
} just runs
57+
mockkStatic(MessagingContentProvider::class)
58+
every { MessagingContentProvider.notifyMessagesChanged(any()) } just runs
59+
every { MessagingContentProvider.notifyPartsChanged() } just runs
60+
}
61+
62+
@After
63+
fun tearDown() {
64+
unmockkAll()
65+
FactoryTestAccess.reset()
66+
}
67+
68+
@Test
69+
fun receiveSmsStillNotifiesAndSchedulesProcessingWhenDatabaseInsertFails() {
70+
val action = TestReceiveSmsMessageAction(receivedSmsValues())
71+
72+
val result = action.execute()
73+
74+
assertNull(result)
75+
verify(exactly = 1) {
76+
BugleNotifications.update(CONVERSATION_ID, BugleNotifications.UPDATE_ALL)
77+
}
78+
verify(exactly = 1) {
79+
ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, action)
80+
}
81+
verify(exactly = 1) { MessagingContentProvider.notifyMessagesChanged(CONVERSATION_ID) }
82+
}
83+
84+
private fun receivedSmsValues(): ContentValues {
85+
return contentValuesOf(
86+
Sms.ADDRESS to SENDER,
87+
Sms.BODY to MESSAGE_TEXT,
88+
Sms.DATE to RECEIVED_TIMESTAMP_MILLIS,
89+
Sms.DATE_SENT to SENT_TIMESTAMP_MILLIS,
90+
Sms.REPLY_PATH_PRESENT to 0,
91+
Sms.SUBSCRIPTION_ID to ParticipantData.DEFAULT_SELF_SUB_ID,
92+
Sms.Inbox.READ to 0,
93+
Sms.Inbox.SEEN to 0,
94+
)
95+
}
96+
97+
private class TestReceiveSmsMessageAction(
98+
messageValues: ContentValues,
99+
) : ReceiveSmsMessageAction(messageValues) {
100+
fun execute(): Any? {
101+
return executeAction()
102+
}
103+
}
104+
105+
private companion object {
106+
private const val SENDER = "+15551234567"
107+
private const val MESSAGE_TEXT = "Incoming message"
108+
private const val CONVERSATION_ID = "193"
109+
private const val THREAD_ID = 193L
110+
private const val RECEIVED_TIMESTAMP_MILLIS = 1_780_920_000_000L
111+
private const val SENT_TIMESTAMP_MILLIS = 1_780_919_999_000L
112+
}
113+
}

src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,12 @@ protected Object executeAction() {
159159
db.endTransaction();
160160
}
161161

162-
LogUtil.i(TAG, "ReceiveSmsMessageAction: Received SMS message " + message.getMessageId()
163-
+ " in conversation " + message.getConversationId()
164-
+ ", uri = " + messageUri);
162+
if (message != null) {
163+
LogUtil.i(TAG, "ReceiveSmsMessageAction: Received SMS message "
164+
+ message.getMessageId()
165+
+ " in conversation " + message.getConversationId()
166+
+ ", uri = " + messageUri);
167+
}
165168

166169
actionParameters.putInt(KEY_SUB_ID, subId);
167170
ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, this);

0 commit comments

Comments
 (0)