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

252 lines
8.8 KiB
Markdown

# 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](#testing-steps-for-plugin-fix) below.
</think>
---
## 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`
```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)
```bash
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:
```kotlin
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:
```kotlin
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)
---
## Related Configuration
### 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
```bash
# 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.