-
Notifications
You must be signed in to change notification settings - Fork 5
Fix synchronization issue with android notification #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
| NoWifi, NoInternet, Ok; | ||
|
|
||
| companion object { | ||
| fun fetch(context: Context, wifiOnly: Boolean): Connectivity { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved from UploadWorker.ts no logical change
| } | ||
|
|
||
| // builds the notification required to enable Foreground mode | ||
| fun build(context: Context): Notification { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved from UploadWorker.ts. Not much has changed except for val wifiOnly = getActiveUpload()?.wifiOnly ?: false
| if (this.activeUpload?.id == upload.id) this.activeUpload = null | ||
| } | ||
|
|
||
| fun setOptions(opts: ReadableMap) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these options are moved over from the Upload class, so they are now global options instead of per-Upload options
elliottkember
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
| setProgress(undefined); | ||
| console.log('Upload error!', err); | ||
| }); | ||
| for (let i = 0; i < 100; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a comment explaining the magic number 100 and why we have to perform Upload.startUpload 100 times?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah this is just the test app, but it's good to add a comment still
- Name response variable in use block (UploadUtils.kt) - Fix shadowed it in takeIf lambda (UploadUtils.kt) - Rename set() to setIfNotNull() (UploadProgress.kt) - Add complete() method to Progress class (UploadProgress.kt) - Rename clearIfNeeded() to clearIfCompleted() (UploadProgress.kt) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
bugbot run |
Ensures the notification shows correct "Waiting for WiFi/Internet" title when network degrades after initial foreground setup. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cbc41c0 to
c2c9922
Compare
|
bugbot run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
| channel = opts.getString("notificationChannel") | ||
| ?: throw MissingOptionException("notificationChannel") | ||
| maxRetries = if (opts.hasKey("maxRetries")) opts.getInt("maxRetries") else 5 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thread-safety gap in setOptions vs concurrent readers
Medium Severity
The setOptions() method writes to shared properties (id, title, channel, maxRetries, etc.) without @Synchronized, while build(), getForegroundInfo(), and checkRetry() read these properties from background worker threads. This creates a race condition where a worker resuming from a previous session could call build() while setOptions() is executing, resulting in inconsistent notification content (e.g., old channel ID paired with new title). Other methods in UploadNotification that access activeUpload are correctly synchronized, but this synchronization pattern isn't applied to the configuration properties.
Additional Locations (1)
| val url: String, | ||
| val path: String, | ||
| val method: String, | ||
| val maxRetries: Int, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resumed workers use uninitialized notification settings after app restart
Medium Severity
The notification settings (notificationChannel, notificationId, etc.) were removed from the Upload class, so they're no longer serialized with WorkManager input data. When workers resume after an app restart (process death), they depend on UploadNotification singleton state which hasn't been initialized yet—initialize() is called by JS code that may not have executed. The worker uses the default channel "File Uploads" which likely doesn't exist, causing the foreground notification to fail. This could result in missing notifications or setForeground() failures that prevent the worker from running properly.
Fixes notification race condition where queued uploads could overwrite the active upload's notification settings. The bug occurred because each Upload object carried its own notification config—when a Wi-Fi-only upload was queued while a regular upload was running, the queued upload could touch the shared system notification and apply its wifiOnly=true preference before actually executing.
Centralization solves this by making UploadNotification the single source of truth for notification state. It tracks exactly one activeUpload at a time (synchronized), and notification content is built by reading getActiveUpload()?.wifiOnly rather than arbitrary upload configs. Queued uploads can exist in memory with their settings, but they can't affect the notification until they call setActiveUpload() when they start executing. This eliminates the race condition—only the actively running upload determines what the user sees.
Test Plan:
Note
Android notification handling consolidated and race fixed
UploadNotificationsingleton andConnectivity.fetchto build/update a single foreground notification reflecting active upload or queue needs (WiFi vs internet), with sharedmaxRetriesUpload; addsinitialize(options)to set global notification config (JS/types updated)UploadWorkerto use centralized notification/ connectivity, mark active upload, and update notification on progress/success/error; retries now use globalmaxRetriesUploadProgressto trackwifiOnlyand exposehasNonWifiOnlyUploads()for accurate notification messagesUpload.initializeand starts multiple uploads; bumps package to7.6.0Written by Cursor Bugbot for commit c2c9922. Configure here.