Skip to content

[gateway] explicit registration, unregister feature - #430

Open
capcom6 wants to merge 1 commit into
masterfrom
gateway/explicit-registration
Open

capcom6 wants to merge 1 commit into
masterfrom
gateway/explicit-registration

Conversation

@capcom6

@capcom6 capcom6 commented Aug 21, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds explicit cloud register / unregister controls in cloud server settings and reworks the SSE/FCM lifecycle coordination so realtime delivery stays correct across registration, account replacement, and unregister.

Key changes:

  • Cloud settings now expose explicit Register (sign-up / sign-in / sign-in by code) and Unregister actions.
  • GatewayService.registerDevice persists the new identity, then emits DeviceRegisteredEvent.Success; SSE credentials are resolved fresh on every SSEManager.connect (no cached stale token).
  • SSE manager gains synchronous teardown, duplicate-connection prevention, and reconnect suppression via an isDisconnecting flag checked in every callback.
  • RegistrationWorker is narrowed to updating an existing device with the FCM token (background refresh only).
  • CloudServerSettingsFragment stops the gateway pipeline before registering, restores it after, and swallows non-fatal post-success UI errors.
  • Fix: lifecycle cancellation no longer rolls back an already-persisted registration (outer catch now rethrows CancellationException).

Why

Previously registration was implicit/automatic; users could not intentionally (re)register or unregister, and several lifecycle races could leave SSE bound to a stale account or drop the registration event for SSE-only devices.

Known limitation (deferred)

GatewayService.start() revives the workers and the events receiver but does not start SSE. DeviceRegisteredEvent.Success is emitted while the receiver is stopped, so it has no collector at that moment. SSE therefore resumes only indirectly, when the FCM RegistrationWorker re-runs updateDevice() and re-emits the event to the already-restarted receiver.

This is left as-is for now. The interim workaround is the restart-app hint in device_registered_successfully; the gap is recorded in a comment on the CancellationException branch of registerDeviceInternal and is being tracked separately. Explicitly starting SSEForegroundService on both the success and rollback paths would close it.

Test plan

  • SSEManagerTest - not written yet. The concurrency rework in SSEManager currently has no unit coverage, and CI only builds an APK without running unit tests, so nothing guards this path today.
  • Manual: register (SSE-only and FCM) -> verify stream starts; replace account -> verify old stream closed; unregister -> verify SSE stops and no cloud events arrive; destroy fragment mid-registration -> verify credentials are NOT reverted.

RetriggerConfidence Score: 1/5

Fix unregister’s remaining FCM command access and Home signup’s request and cancellation problems before merging.

Findings

  1. P1 Security Unregister still accepts cloud commands ▶
  2. P1 Signup requests overwrite each other ▶
  3. P1 Leaving signup can crash ▶
  4. P2 Password control stays disabled ▶
Fix with agent prompt
### Issue 1
app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt:305-308
`unregisterDevice()` clears local credentials and stops the gateway, but leaves the server registration active. The [server still sends commands to the device’s FCM token](https://github.com/android-sms-gateway/server/blob/HEAD/internal/sms-gateway/modules/events/service.go). `PushService.onMessageReceived()` routes them without checking registration, and the separate receiver for `MessagesExportRequested` remains running. The former cloud account can therefore still request SMS exports after unregister.

Remove or revoke the server registration, and reject commands from the old registration.

**How this was verified:** The server retains the push token, incoming FCM messages are routed without a registration check, and the separate export receiver remains active after the gateway stops.

### Issue 2
app/src/main/java/me/capcom/smsgateway/ui/HomeFragment.kt:79-86
The new `SignUp` path leaves Start available while `registerDevice()` waits for the server. Tapping Start again opens another signup dialog because `registrationInfo` is still null. Confirming it launches another registration. Each anonymous request creates a separate account and device, and whichever response finishes last overwrites the saved credentials-even if an earlier signup already appeared successful.

Block repeated starts until registration finishes.

### Issue 3
app/src/main/java/me/capcom/smsgateway/ui/HomeFragment.kt:87-92
The new `SignUp` handler catches lifecycle cancellation as `Throwable` and calls `requireContext()` to show an error. If the activity is destroyed while registration waits for the network, the fragment can detach before this catch runs. `requireContext()` then throws outside the handler and crashes the app.

Rethrow `CancellationException` and avoid requiring an attached fragment after the network call.

### Issue 4
app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt:313-314
When this page opens without a registration, the password preference is disabled. After registration succeeds, `refreshRegistrationUi()` updates the related controls but never updates the password preference's `isEnabled` value. Users must leave and reopen the page before they can change their password. Set `isEnabled` from `settings.username != null` during this refresh.

```suggestion
    private fun refreshRegistrationUi() {
        findPreference<EditTextPreference>("gateway.password")?.isEnabled = settings.username != null
        findPreference<Preference>("gateway.clear_password")?.isVisible = settings.hasPassword
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds explicit cloud registration and unregister controls, moves background registration work to device updates, and changes SSE teardown and transport switching.

  • Cloud settings let people register, sign in, and unregister a device.
  • Live event delivery follows the current account and active transport.
  • Background registration work only updates an existing device.
  • Long translated strings wrap across lines without changing their wording.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[User unregisters] --> B[Clear local credentials]
  B --> C[Stop gateway receiver and workers]
  C --> D[Server still holds FCM token]
  D --> E[PushService receives command]
  E --> F[EventsRouter]
  F --> G[Separate export receiver remains active]
  G --> H[ReceiverService exports SMS]
Loading

Reviews (41) · Last reviewed commit: "[gateway] explicit registration, unregis..." · Reviewed by Greptile

Comment thread app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt Outdated
@github-actions

github-actions Bot commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor

🤖 Pull request artifacts

file commit
app-release.apk 7f46a89
app-release.aab 7f46a89
app-insecure.apk 7f46a89
app-insecure.aab 7f46a89

@capcom6
capcom6 force-pushed the gateway/explicit-registration branch 2 times, most recently from 8c7f581 to f541c0f Compare August 21, 2026 12:00
Comment thread app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt Outdated
Comment thread app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt Outdated
Comment thread app/src/main/java/me/capcom/smsgateway/helpers/SSEManager.kt Outdated
Comment thread app/src/main/java/me/capcom/smsgateway/helpers/SSEManager.kt Outdated
@capcom6
capcom6 force-pushed the gateway/explicit-registration branch 2 times, most recently from 87c23d4 to 565a464 Compare August 25, 2026 02:00
Comment thread app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt Outdated
capcom6 added a commit that referenced this pull request Aug 26, 2026
When explicit registration completes with a null FCM token, SSE starts as the
fallback transport. The later FCM token update via RegistrationWorker set the
token but never tore down the SSE stream, leaving both transports active and
causing duplicate cloud-event delivery. Tear down SSE in updateDevice when a
non-null push token is set.

Fixes Greptile review blocker on PR #430.
capcom6 added a commit that referenced this pull request Aug 26, 2026
…M token

Symmetrize SSE management in EventsReceiver: when a DeviceRegisteredEvent is
processed after the FCM token has been recorded, stop the SSE foreground
service instead of leaving it running alongside FCM. Prevents duplicate
cloud-event delivery reported by Greptile on PR #430.
Comment thread app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt Outdated
capcom6 added a commit that referenced this pull request Aug 26, 2026
Capture the application context up front and use nullable activity access for
the progress bar so an in-flight explicit registration no longer throws from
requireContext()/requireActivity() if the user navigates away, which would
abort SSE startup and service.start() for an already-registered device.

Addresses Greptile P1 'Fragment detachment aborts SSE startup' on PR #430.
@capcom6
capcom6 force-pushed the gateway/explicit-registration branch from 78258ec to 60d3114 Compare August 26, 2026 07:05
Comment thread app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt Outdated
Comment thread app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt Outdated
Comment thread app/src/main/java/me/capcom/smsgateway/helpers/SSEManager.kt Outdated
Comment thread app/src/main/java/me/capcom/smsgateway/helpers/SSEManager.kt Outdated
Comment thread app/src/main/java/me/capcom/smsgateway/helpers/SSEManager.kt Outdated
@capcom6
capcom6 force-pushed the gateway/explicit-registration branch from 0c955cf to ded47f7 Compare August 29, 2026 00:02
Comment thread app/src/main/java/me/capcom/smsgateway/helpers/SSEManager.kt Outdated
@capcom6
capcom6 force-pushed the gateway/explicit-registration branch 2 times, most recently from f470387 to 4fd3ab9 Compare August 31, 2026 07:44
Comment thread app/src/main/java/me/capcom/smsgateway/helpers/SSEManager.kt Outdated
Comment thread app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt Outdated
@capcom6
capcom6 force-pushed the gateway/explicit-registration branch from f409d7b to cde7b68 Compare September 2, 2026 01:02
Comment thread app/src/main/java/me/capcom/smsgateway/helpers/SSEManager.kt Outdated
@capcom6
capcom6 force-pushed the gateway/explicit-registration branch 2 times, most recently from 66ddd2b to 98643d2 Compare September 4, 2026 00:08
@capcom6
capcom6 force-pushed the gateway/explicit-registration branch from 98643d2 to 7b4ea1b Compare September 8, 2026 01:17
Comment thread app/src/main/java/me/capcom/smsgateway/helpers/SSEManager.kt Outdated
@capcom6
capcom6 force-pushed the gateway/explicit-registration branch from 737b291 to e91c1fd Compare September 10, 2026 03:05
@capcom6 capcom6 added the ready label Sep 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for 7 days with no activity.

@github-actions github-actions Bot added the stale label Sep 20, 2026
@capcom6 capcom6 removed the stale label Sep 21, 2026
@capcom6
capcom6 force-pushed the gateway/explicit-registration branch from e91c1fd to e6f3568 Compare September 25, 2026 01:45
@github-actions github-actions Bot removed the ready label Sep 25, 2026
@greptile-apps

This comment has been minimized.

@capcom6
capcom6 force-pushed the gateway/explicit-registration branch 3 times, most recently from 2a863f2 to 3de2a03 Compare October 1, 2026 07:57
@capcom6 capcom6 added the ready label Oct 2, 2026
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for 7 days with no activity.

@github-actions github-actions Bot added the stale label Oct 9, 2026
@capcom6
capcom6 force-pushed the gateway/explicit-registration branch from 3de2a03 to f939ba7 Compare October 9, 2026 03:36
@github-actions github-actions Bot removed the ready label Oct 9, 2026
Comment on lines +313 to +314
private fun refreshRegistrationUi() {
findPreference<Preference>("gateway.clear_password")?.isVisible = settings.hasPassword

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Password control stays disabled

When this page opens without a registration, the password preference is disabled. After registration succeeds, refreshRegistrationUi() updates the related controls but never updates the password preference's isEnabled value. Users must leave and reopen the page before they can change their password. Set isEnabled from settings.username != null during this refresh.

Suggested change
private fun refreshRegistrationUi() {
findPreference<Preference>("gateway.clear_password")?.isVisible = settings.hasPassword
private fun refreshRegistrationUi() {
findPreference<EditTextPreference>("gateway.password")?.isEnabled = settings.username != null
findPreference<Preference>("gateway.clear_password")?.isVisible = settings.hasPassword
Prompt To Fix With AI
This is a comment left during a code review.
Path: app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt
Line: 313-314

Comment:
**Password control stays disabled**

When this page opens without a registration, the password preference is disabled. After registration succeeds, `refreshRegistrationUi()` updates the related controls but never updates the password preference's `isEnabled` value. Users must leave and reopen the page before they can change their password. Set `isEnabled` from `settings.username != null` during this refresh.

```suggestion
    private fun refreshRegistrationUi() {
        findPreference<EditTextPreference>("gateway.password")?.isEnabled = settings.username != null
        findPreference<Preference>("gateway.clear_password")?.isVisible = settings.hasPassword
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@github-actions github-actions Bot removed the stale label Oct 10, 2026
@capcom6
capcom6 force-pushed the gateway/explicit-registration branch from f939ba7 to 7f46a89 Compare October 12, 2026 00:52
Comment on lines +305 to +308
settings.registrationInfo = null
// Stop SSE session and cloud workers so they do not keep using
// credentials of the just-unregistered device.
service.stop(requireContext())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Unregister still accepts cloud commands

unregisterDevice() clears local credentials and stops the gateway, but leaves the server registration active. The server still sends commands to the device’s FCM token. PushService.onMessageReceived() routes them without checking registration, and the separate receiver for MessagesExportRequested remains running. The former cloud account can therefore still request SMS exports after unregister.

Remove or revoke the server registration, and reject commands from the old registration.

How this was verified: The server retains the push token, incoming FCM messages are routed without a registration check, and the separate export receiver remains active after the gateway stops.

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/src/main/java/me/capcom/smsgateway/ui/settings/CloudServerSettingsFragment.kt
Line: 305-308

Comment:
**Unregister still accepts cloud commands**

`unregisterDevice()` clears local credentials and stops the gateway, but leaves the server registration active. The [server still sends commands to the device’s FCM token](https://github.com/android-sms-gateway/server/blob/HEAD/internal/sms-gateway/modules/events/service.go). `PushService.onMessageReceived()` routes them without checking registration, and the separate receiver for `MessagesExportRequested` remains running. The former cloud account can therefore still request SMS exports after unregister.

Remove or revoke the server registration, and reject commands from the old registration.

**How this was verified:** The server retains the push token, incoming FCM messages are routed without a registration check, and the separate export receiver remains active after the gateway stops.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +79 to +86
lifecycleScope.launch {
try {
gatewaySvc.registerDevice(
requireContext(),
null,
GatewayService.RegistrationMode.Anonymous
)
requestPermissionsAndStart()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Signup requests overwrite each other

The new SignUp path leaves Start available while registerDevice() waits for the server. Tapping Start again opens another signup dialog because registrationInfo is still null. Confirming it launches another registration. Each anonymous request creates a separate account and device, and whichever response finishes last overwrites the saved credentials—even if an earlier signup already appeared successful.

Block repeated starts until registration finishes.

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/src/main/java/me/capcom/smsgateway/ui/HomeFragment.kt
Line: 79-86

Comment:
**Signup requests overwrite each other**

The new `SignUp` path leaves Start available while `registerDevice()` waits for the server. Tapping Start again opens another signup dialog because `registrationInfo` is still null. Confirming it launches another registration. Each anonymous request creates a separate account and device, and whichever response finishes last overwrites the saved credentials—even if an earlier signup already appeared successful.

Block repeated starts until registration finishes.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +87 to +92
} catch (th: Throwable) {
Toast.makeText(
requireContext(),
"Failed to register device: ${th.message}",
Toast.LENGTH_LONG
).show()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Leaving signup can crash

The new SignUp handler catches lifecycle cancellation as Throwable and calls requireContext() to show an error. If the activity is destroyed while registration waits for the network, the fragment can detach before this catch runs. requireContext() then throws outside the handler and crashes the app.

Rethrow CancellationException and avoid requiring an attached fragment after the network call.

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/src/main/java/me/capcom/smsgateway/ui/HomeFragment.kt
Line: 87-92

Comment:
**Leaving signup can crash**

The new `SignUp` handler catches lifecycle cancellation as `Throwable` and calls `requireContext()` to show an error. If the activity is destroyed while registration waits for the network, the fragment can detach before this catch runs. `requireContext()` then throws outside the handler and crashes the app.

Rethrow `CancellationException` and avoid requiring an attached fragment after the network call.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant