How to Troubleshoot SmsForwarder Message Forwarding Failures Using Logs

SmsForwarder logs every forwarding step via Android WorkManager classes (e.g., SendWorker, UpdateLogsWorker) to both a local Room database (Logs table) and Logcat, allowing you to diagnose failures by inspecting the Logs UI or filtering Logcat for worker-specific tags like SendWorker:E.

SmsForwarder (available at pppscn/SmsForwarder) is an open-source Android application that automates SMS, call, and notification forwarding to multiple channels. When messages fail to reach their configured destinations, the application provides diagnostic visibility through a custom Log wrapper and persistent database entries. Understanding how to access and interpret these logs is essential for resolving silent-period blocks, rule mismatches, and network configuration errors.

Understanding the SmsForwarder Logging Architecture

The Worker-Based Processing Pipeline

SmsForwarder processes incoming events through dedicated Worker classes in the cn.ppps.forwarder.workers package. Each worker handles a specific phase of the forwarding lifecycle and emits detailed debug information via the custom Log utility (wrapping Log.d and Log.e calls).

The primary workers responsible for logging include:

  • SendWorker: Handles core forwarding logic, rule matching, silent-period validation, and duplicate filtering
  • UpdateLogsWorker: Manages uploading local log entries to remote servers
  • NetworkWorker: Validates network state conditions against task requirements
  • SimWorker: Verifies SIM card slot and state requirements
  • LocationWorker: Checks location-based forwarding conditions

The Logs Database and UI Layer

Every forwarding attempt creates a persistent record in the Logs table defined in cn.ppps.forwarder.database.entity.Logs.kt. The LogsFragment (app/src/main/kotlin/cn/ppps/forwarder/fragment/LogsFragment.kt) provides the main interface for browsing these entries, reading data through the LogsViewModel → LogsRepository → LogsDao chain.

Step-by-Step Troubleshooting Workflow

Follow this systematic approach to identify why a message failed to forward:

  1. Enable verbose logging – The app logs at DEBUG level by default. For additional detail, ensure Log.isDebug = true is set via the app's settings (this flag is managed by SettingUtils).

  2. Reproduce the failure – Trigger the specific SMS, call, or app notification that should be forwarded to generate fresh log entries.

  3. Open the Logs view – Navigate to the Logs screen from the main UI (listed under Message History). This fragment queries the database through LogsViewModel to display timestamps, sender IDs, and rule associations.

  4. Filter by tag or task – Each worker logs with a tag equal to its class name. Use the built-in filter UI or search Logcat for specific tags like SendWorker or UpdateLogsWorker to isolate the relevant processing step.

  5. Interpret key log entries – Examine the specific worker logs for error signatures (detailed in the next section).

  6. Check the database entry – Each forward attempt inserts a Logs record via Core.logs.insert(log). Open the entry in the UI to see the exact failure reason, including optional comments like ResUtils.getString(R.string.silent_time_period) for silent-period blocks.

  7. Use Logcat for low-level issues – If the UI shows no entry (indicating a crash before DB insertion), attach Android Studio or run adb logcat filtered by the package name cn.ppps.forwarder.

Interpreting Worker Log Entries

SendWorker Logs

The SendWorker class (app/src/main/kotlin/cn/ppps/forwarder/workers/SendWorker.kt) logs the complete forwarding pipeline:

  • Start of forwarding: Log.d(TAG, "SendWorker start...")
  • Silent-period checks: Logs when the current time falls within SettingUtils.silentPeriodStart/End
  • Duplicate filtering: Validates against SettingUtils.duplicateMessagesLimits
  • Rule matching: Logs rule.toString() for each evaluated rule before attempting sends

Errors appear as Log.e(TAG, "SendWorker error: …") with specific failure contexts.

UpdateLogsWorker Logs

Located in app/src/main/kotlin/cn/ppps/forwarder/workers/UpdateLogsWorker.kt, this worker handles remote log synchronization:

  • Success: Log.d("UpdateLogsWorker", "UpdateLogsWorker sendResponseJson: …")
  • Failure: Log.e("UpdateLogsWorker", "UpdateLogsWorker error: …") (indicates server unreachability or authentication issues)

Condition Workers

NetworkWorker, SimWorker, and LocationWorker log condition validation failures with structured messages like:


Log.d(TAG, "TASK-123:networkState is not match…")

Common Failure Patterns and Log Signatures

Failure scenario Log messages Likely cause
Silent-period blocking Log.e(TAG, "免打扰(禁用转发)时间段") SettingUtils.silentPeriodStart/End covers the current time and enableSilentPeriodLogs is disabled
Duplicate-message filtering Log.e(TAG, "过滤重复消息机制") SettingUtils.duplicateMessagesLimits is too low; identical payload arrived within the deduplication window
No matching rule Log.d(TAG, rule.toString()) followed by Result.failure without Log.e Incoming message type or SIM slot does not satisfy any active rule's conditions
Condition mismatch Log.d(TAG, "TASK-…:networkState is not match…") The task's network, SIM, or location prerequisites are not met
Remote log upload failure Log.e("UpdateLogsWorker", "UpdateLogsWorker error: …") Remote server unreachable or API authentication failure

Querying Logs Programmatically

When debugging advanced scenarios, you can inspect logs directly via the database layer:

Retrieve Recent Log Entries

// Query the last 20 log entries from the DB
val recentLogs = Core.logs.query(limit = 20, orderDesc = true)
recentLogs.forEach {
    Log.d("DebugHelper", "Log ${it.id}: ${it.msg} – sender ${it.senderId}, rule ${it.ruleId}")
}

Manually Trigger Log Upload

Useful when the UI shows "Pending" status for remote log synchronization:

val work = OneTimeWorkRequestBuilder<UpdateLogsWorker>()
    .setInputData(workDataOf("logId" to logId))
    .build()
WorkManager.getInstance(context).enqueue(work)

Filter Logcat for Specific Workers


# Show only error-level logs from SendWorker

adb logcat -s SendWorker:E

# Filter for all SmsForwarder workers

adb logcat -s SendWorker:D -s UpdateLogsWorker:D -s NetworkWorker:D

Summary

  • SmsForwarder uses Worker classes (SendWorker, UpdateLogsWorker, etc.) to process messages, with each step logged via a custom Log wrapper
  • Failed forwards persist in the Logs database table (cn.ppps.forwarder.database.entity.Logs) and are viewable through LogsFragment
  • Enable Log.isDebug = true via SettingUtils for verbose output before reproducing the issue
  • Filter Logcat by worker tags (e.g., SendWorker:E) to isolate specific failure points like silent-period blocks or rule mismatches
  • Common failures include duplicate-message filtering, condition mismatches (network/SIM/location), and remote server upload errors

Frequently Asked Questions

Where are SmsForwarder logs stored?

SmsForwarder stores forwarding history in a local Room database table named Logs (defined in app/src/main/kotlin/cn/ppps/forwarder/database/entity/Logs.kt). The UI accesses this data through LogsRepository to display entries in LogsFragment. Additionally, debug logs are emitted to Android Logcat with worker-specific tags like SendWorker and UpdateLogsWorker.

How do I enable debug logging in SmsForwarder?

Debug logging is controlled by the Log.isDebug flag, which is read from SettingUtils in the app's settings. When enabled, workers emit detailed Log.d statements showing rule evaluation, condition checks, and HTTP request/response details. For command-line debugging, use adb logcat -s SendWorker:D to capture debug-level output from the forwarding worker.

Why does my message show "Silent period" in the logs?

This indicates the current time falls within the silent period configured in SettingUtils.silentPeriodStart and SettingUtils.silentPeriodEnd. When active and enableSilentPeriodLogs is disabled, SendWorker logs Log.e(TAG, "免打扰(禁用转发)时间段") and aborts forwarding. Adjust the time window in settings or disable the silent period feature to allow forwarding during those hours.

How can I check why a specific rule didn't match?

SendWorker logs each rule evaluation via Log.d(TAG, rule.toString()) before attempting to match. If no rule matches, you will see the rule dump followed by a Result.failure without an accompanying Log.e. Compare the logged rule conditions (SIM slot, sender pattern, content regex) against the incoming message metadata visible in the Logs UI to identify the mismatch.

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 →