Files
crowd-funder-for-time-pwa/doc/daily-notification-plugin-android-receiver-issue.md
Jose Olarte III 9902e5fac7 chore: align with daily-notification-plugin 2.0.0 (org.timesafari namespace)
- Update Android manifest, Java imports, and capacitor.plugins.json to use
  org.timesafari.dailynotification (receivers, intent action, plugin classpath)
- Update iOS Info.plist BGTaskSchedulerPermittedIdentifiers to org.timesafari.*
- Bump @timesafari/daily-notification-plugin 1.3.3 → 2.0.0 (package-lock, Podfile.lock)
- Update docs and test-notification-receiver.sh to reference new package/action names
- Lockfile: minor bumps for @expo/cli, @expo/fingerprint, @expo/router-server, babel-preset-expo
2026-03-12 17:20:45 +08:00

8.8 KiB

Daily Notification Plugin - Android Receiver Not Triggered by AlarmManager

Date: 2026-02-02
Status: Resolved (2026-02-06)
Plugin: @timesafari/daily-notification-plugin
Platform: Android
Issue: AlarmManager fires alarms but DailyNotificationReceiver is not receiving broadcasts


Resolution (2026-02-06)

The bug was fixed in the plugin repository. The plugin now:

  • Creates the PendingIntent with the receiver component explicitly set (setComponent(ComponentName(context, DailyNotificationReceiver::class.java))), so AlarmManager delivers the broadcast to the receiver.
  • Adds the schedule ID to the Intent extras (intent.putExtra("id", scheduleId)), resolving the missing_id error.

In this app after pulling the fix:

  1. Run npm install to get the latest plugin from #master.
  2. Run npx cap sync so the Android (and iOS) native projects get the updated plugin code.
  3. Run node scripts/restore-local-plugins.js if you use local plugins (e.g. SafeArea, SharedImage).
  4. Rebuild and run on Android, then verify using the Testing Steps for Plugin Fix below.

Problem Summary

Alarms are being scheduled successfully and fire at the correct time, but the DailyNotificationReceiver is not being triggered when AlarmManager delivers the broadcast. Manual broadcasts to the receiver work correctly, indicating the receiver itself is functional.


What Works

  1. Receiver Registration: The receiver is properly registered in AndroidManifest.xml with exported="true"
  2. Manual Broadcasts: Manually triggering the receiver via adb shell am broadcast successfully triggers it
  3. Alarm Scheduling: Alarms are successfully scheduled via setAlarmClock() and appear in dumpsys alarm
  4. Alarm Firing: Alarms fire at the scheduled time (confirmed by alarm disappearing from dumpsys)

What Doesn't Work

  1. Automatic Receiver Triggering: When AlarmManager fires the alarm, the broadcast PendingIntent does not reach the receiver
  2. No Logs on Alarm Fire: No DN|RECEIVE_START logs appear when alarms fire automatically
  3. Missing ID in Intent: When manually tested, receiver shows DN|RECEIVE_ERR missing_id (separate issue but related)

Technical Details

Receiver Configuration

File: android/app/src/main/AndroidManifest.xml

<receiver
    android:name="org.timesafari.dailynotification.DailyNotificationReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="org.timesafari.daily.NOTIFICATION" />
    </intent-filter>
</receiver>
  • exported="true" is set (required for AlarmManager broadcasts)
  • Intent action matches: org.timesafari.daily.NOTIFICATION
  • Receiver is inside <application> tag

Alarm Scheduling Evidence

From logs when scheduling (23:51:32):

I DNP-SCHEDULE: Scheduling OS alarm: variant=ALARM_CLOCK, action=org.timesafari.daily.NOTIFICATION, triggerTime=1770105300000, requestCode=44490, scheduleId=timesafari_daily_reminder
I DNP-NOTIFY: Alarm clock scheduled (setAlarmClock): triggerAt=1770105300000, requestCode=44490

From dumpsys alarm output:

RTC_WAKEUP #36: Alarm{7a8fb5e type 0 origWhen 1770148800000 whenElapsed 122488536 app.timesafari.app}
  tag=*walarm*:org.timesafari.daily.NOTIFICATION
  type=RTC_WAKEUP origWhen=2026-02-03 12:00:00.000 window=0 exactAllowReason=policy_permission
  operation=PendingIntent{6fce955: PendingIntentRecord{5856f6a app.timesafari.app broadcastIntent}}

Alarm Firing Evidence

  • Alarm scheduled for 23:55:00 (timestamp: 1770105300000)
  • At 23:55:00, alarm is no longer in dumpsys alarm (confirmed it fired)
  • No DN|RECEIVE_START log at 23:55:00 (receiver was not triggered)

Manual Broadcast Test (Works)

adb shell am broadcast -a org.timesafari.daily.NOTIFICATION -n app.timesafari.app/org.timesafari.dailynotification.DailyNotificationReceiver

Result: Receiver triggered successfully

02-02 23:46:07.505  DailyNotificationReceiver  D  DN|RECEIVE_START action=org.timesafari.daily.NOTIFICATION
02-02 23:46:07.506  DailyNotificationReceiver  W  DN|RECEIVE_ERR missing_id

Root Cause Analysis

The issue appears to be in how the PendingIntent is created when scheduling alarms. Possible causes:

Hypothesis 1: PendingIntent Not Targeting Receiver Correctly

The PendingIntent may be created without explicitly specifying the component, causing Android to not match it to the receiver when the alarm fires.

Expected Fix: When creating the PendingIntent for AlarmManager, explicitly set the component:

val intent = Intent("org.timesafari.daily.NOTIFICATION").apply {
    setComponent(ComponentName(context, DailyNotificationReceiver::class.java))
    putExtra("id", scheduleId) // Also fix missing_id issue
}
val pendingIntent = PendingIntent.getBroadcast(
    context,
    requestCode,
    intent,
    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)

Hypothesis 2: PendingIntent Flags Issue

The PendingIntent may be created with incorrect flags that prevent delivery when the app is in certain states.

Check: Ensure flags include:

  • FLAG_UPDATE_CURRENT or FLAG_CANCEL_CURRENT
  • FLAG_IMMUTABLE (required on Android 12+)

Hypothesis 3: Package/Component Mismatch

The PendingIntent may be created with a different package name or component than what's registered in the manifest.

Check: Verify the package name in the Intent matches app.timesafari.app and the component matches the receiver class.


Additional Issue: Missing ID in Intent

When the receiver IS triggered (manually), it shows:

DN|RECEIVE_ERR missing_id

This indicates the Intent extras don't include the scheduleId. The plugin should add the ID to the Intent when creating the PendingIntent:

intent.putExtra("id", scheduleId)
// or
intent.putExtra("scheduleId", scheduleId) // if receiver expects different key

Testing Steps for Plugin Fix

  1. Verify PendingIntent Creation:

    • Check the code that creates PendingIntent for AlarmManager
    • Ensure component is explicitly set
    • Ensure ID is added to Intent extras
  2. Test Alarm Delivery:

    • Schedule an alarm for 1-2 minutes in the future
    • Monitor logs: adb logcat | grep -E "DN|RECEIVE_START|DailyNotification"
    • Verify DN|RECEIVE_START appears when alarm fires
    • Verify no missing_id error
  3. Test Different App States:

    • App in foreground
    • App in background
    • App force-closed
    • Device in doze mode (if possible on emulator)
  4. Compare with Manual Broadcast:

    • Manual broadcast works → receiver is fine
    • Alarm broadcast doesn't work → PendingIntent creation is the issue

Files to Check in Plugin

  1. Alarm Scheduling Code: Where setAlarmClock() or setExact() is called
  2. PendingIntent Creation: Where PendingIntent.getBroadcast() is called
  3. Intent Creation: Where the Intent for the alarm is created
  4. Receiver Code: Verify what Intent extras it expects (for missing_id fix)

AndroidManifest.xml (App Side)

  • Receiver exported="true"
  • Correct intent action
  • Receiver inside application tag

Permissions (App Side)

  • POST_NOTIFICATIONS
  • SCHEDULE_EXACT_ALARM
  • RECEIVE_BOOT_COMPLETED
  • WAKE_LOCK
  • USE_EXACT_ALARM -- must not use; see note below

Note on USE_EXACT_ALARM: The USE_EXACT_ALARM permission is restricted by Google on Android. Apps that declare it must be primarily dedicated to alarm or calendar functionality. Google will reject apps from the Play Store that use this permission for other purposes. This plugin uses SCHEDULE_EXACT_ALARM instead, which is sufficient for scheduling daily notifications.


Expected Behavior After Fix

When an alarm fires:

  1. AlarmManager delivers the broadcast
  2. DailyNotificationReceiver.onReceive() is called
  3. Log shows: DN|RECEIVE_START action=org.timesafari.daily.NOTIFICATION
  4. Receiver finds the ID in Intent extras (no missing_id error)
  5. Notification is displayed

Notes

  • The exported="true" change in the app's manifest was necessary and correct
  • The issue is in the plugin's PendingIntent creation, not the app configuration
  • Manual broadcasts work, proving the receiver registration is correct
  • Alarms fire, proving AlarmManager scheduling is correct
  • The gap is in the PendingIntent → Receiver delivery

Quick Reference: Working Manual Test

# This works - receiver is triggered
adb shell am broadcast \
  -a org.timesafari.daily.NOTIFICATION \
  -n app.timesafari.app/org.timesafari.dailynotification.DailyNotificationReceiver \
  --es "id" "timesafari_daily_reminder"

The plugin's PendingIntent should create an equivalent broadcast that AlarmManager can deliver.