How SmsForwarder Implements MMS Message Handling and Parsing in Android

SmsForwarder handles MMS messages by registering a broadcast receiver for WAP push intents, extracting the raw PDU byte array, and using Java reflection to decode message parts—including text and images—before merging them into a unified message for forwarding.

The SmsForwarder open-source project (pppscn/SmsForwarder) provides a robust solution for automatically forwarding SMS and MMS messages to various channels. Understanding how it implements MMS message handling and parsing reveals a clever use of reflection to access Android's internal MMS APIs, allowing the extraction of multimedia content without relying on undocumented public methods.

Broadcast Reception and Intent Filtering

The MMS handling pipeline begins in app/src/main/kotlin/cn/ppps/forwarder/receiver/SmsReceiver.kt, where the receiver listens for system WAP push broadcasts. Specifically, the receiver registers for WAP_PUSH_RECEIVED_ACTION and WAP_PUSH_DELIVER_ACTION intents at lines 42-44.

When the Android system receives an MMS, it broadcasts these intents containing the raw Protocol Data Unit (PDU). The receiver must be declared in the manifest with appropriate permissions to intercept these system-level broadcasts.

Validating MMS Intents

Before processing, SmsForwarder validates that the incoming intent actually represents an MMS message. At lines 45-48, the code performs two critical checks:

  • The intent's type must equal application/vnd.wap.mms-message
  • The transactionId extra must equal "mms"

This validation ensures the receiver only processes genuine MMS payloads and ignores other WAP push notifications that might use different MIME types.

Extracting Raw MMS Data

Once validated, the receiver extracts the raw byte payload at lines 49-51. The code retrieves the byte array extra named "data" from the intent, which contains the complete MMS PDU. This raw data is then passed to the handleMmsData method for parsing and content extraction.

Reflection-Based MMS Decoding

The core MMS parsing logic resides in the handleMmsData method within SmsReceiver.kt. Since Android does not expose public APIs for parsing MMS PDUs directly, SmsForwarder uses reflection to access internal telephony classes.

At lines 50-57, the implementation loads android.telephony.gsm.SmsMessage via Class.forName() and invokes the createFromPdu(byte[]) method to obtain a message object. This approach allows the app to decode the binary MMS structure without requiring private API access or platform-specific dependencies.

After obtaining the message object, the code calls getParts() at lines 63-64 to retrieve an array of individual MMS parts. Each part represents a segment of the multimedia message, such as text, images, or attachments.

Parsing Text Content

For text extraction, the parser iterates through each part and checks the content type at lines 66-68. When getContentType() returns text/plain, the code extracts the payload using getData() at lines 70-77 and appends the text to the message string (msg). This allows SmsForwarder to capture the textual content of MMS messages alongside traditional SMS.

Handling Image Parts

Binary content such as images follows a similar extraction pattern. At lines 81-86, the code retrieves image data using getData(), though the current implementation treats this as a placeholder for custom handling. The binary data is available for extension, allowing developers to implement image saving or base64 encoding for forwarding services that support multimedia attachments.

Forwarding Parsed Content

After parsing completes, the combined message content—now containing both SMS text and any extracted MMS text—is wrapped in a MsgInfo object. At lines 109-117, this object is dispatched to SendWorker for processing through the configured forwarding channels (webhooks, email, Telegram, etc.).

This architecture ensures that MMS content flows through the same forwarding pipeline as regular SMS, maintaining consistency across all message types.

Required Permissions

To receive MMS broadcasts, the application must declare the appropriate permission in app/src/main/AndroidManifest.xml. Line 38 contains the required declaration:

<uses-permission android:name="android.permission.RECEIVE_MMS" />

Without this permission, the system will not deliver WAP push broadcasts to the application, preventing any MMS processing regardless of the receiver implementation.

Implementation Example

Developers can extend SmsForwarder's approach using the following reflection pattern. This snippet mirrors the library's implementation for extracting text from MMS parts:

// Inside SmsReceiver.handleMmsData()
val mmsClass = Class.forName("android.telephony.gsm.SmsMessage")
val createFromPdu = mmsClass.getDeclaredMethod("createFromPdu", ByteArray::class.java)
val message = createFromPdu.invoke(null, data) ?: return

val parts = message.javaClass.getMethod("getParts")
    .invoke(message) as? Array<*>
parts?.forEach { part ->
    val type = part?.javaClass?.getMethod("getContentType")
        ?.invoke(part) as? String

    if (type?.startsWith("text/plain") == true) {
        val text = part.javaClass.getMethod("getData")
            .invoke(part) as? String
        text?.let { Log.d(TAG, "MMS Text: $it") }
    }
    // Image handling omitted for brevity
}

This code demonstrates how to hook into the same reflection-based parsing used by SmsForwarder, enabling custom processing of MMS content types beyond the default text extraction.

Summary

  • MMS Handling Pipeline: SmsForwarder intercepts WAP push broadcasts via SmsReceiver.kt and validates MIME types before processing.
  • Reflection-Based Parsing: The app uses Class.forName() to access android.telephony.gsm.SmsMessage and decode PDU byte arrays without private API dependencies.
  • Content Extraction: Text parts are extracted via getData() when getContentType() matches text/plain, while image data is available for custom extensions.
  • Unified Forwarding: Parsed MMS content merges into the standard Msg flow before dispatch to SendWorker for channel-specific forwarding.
  • Permission Requirements: RECEIVE_MMS must be declared in AndroidManifest.xml to receive system MMS broadcasts.

Frequently Asked Questions

Why does SmsForwarder use reflection for MMS parsing instead of standard Android APIs?

Android does not expose public APIs for parsing MMS PDU (Protocol Data Unit) data directly. The android.telephony.gsm.SmsMessage class contains the necessary decoding logic, but it is marked as internal or hidden in the SDK. SmsForwarder uses reflection to invoke createFromPdu() and getParts() at runtime, allowing the app to extract multimedia content without requiring platform-specific modifications or private API access that would trigger Google Play restrictions.

What specific MIME type does SmsForwarder check for when handling MMS messages?

According to the source code in SmsReceiver.kt at lines 45-48, SmsForwarder validates that the intent type equals application/vnd.wap.mms-message. This MIME type identifies WAP push messages containing MMS data as defined by the Open Mobile Alliance standards. The code also verifies that the intent's transactionId equals "mms" to ensure it processes only MMS-specific broadcasts rather than other WAP push notifications.

How does SmsForwarder handle image attachments in MMS messages?

The current implementation in SmsReceiver.kt (lines 81-86) extracts binary image data using the same reflection-based getData() method used for text, but treats it as a placeholder for custom handling. While text content is automatically appended to the forwarding message, image processing requires extending the forEach loop to implement base64 encoding, file saving, or direct upload to forwarding channels. The binary data is available through the reflection API for developers who need multimedia forwarding capabilities.

What permission is required to receive MMS broadcasts in SmsForwarder?

The app must declare android.permission.RECEIVE_MMS in app/src/main/AndroidManifest.xml (line 38). This permission allows the application to receive system broadcasts when new MMS messages arrive via WAP push. Without this declaration, the SmsReceiver will not receive the WAP_PUSH_RECEIVED_ACTION or WAP_PUSH_DELIVER_ACTION intents, making MMS forwarding impossible regardless of the parsing implementation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →