fix(android): serialize Firebase messaging setup
This commit is contained in:
parent
8e4656a725
commit
615aee3d7a
|
|
@ -467,8 +467,18 @@ public final class MainActivity extends ComponentActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
PushTokenStore.setRequested(this, true);
|
PushTokenStore.setRequested(this, true);
|
||||||
FirebaseMessaging.getInstance().setAutoInitEnabled(true);
|
((WhoNeedHelpApplication) getApplication()).runFirebaseTask(() -> {
|
||||||
|
try {
|
||||||
|
FirebaseMessaging.getInstance().setAutoInitEnabled(true);
|
||||||
|
runOnUiThread(this::continuePushRegistration);
|
||||||
|
} catch (RuntimeException error) {
|
||||||
|
Log.w(LOG_TAG, "FCM auto-init failed", error);
|
||||||
|
runOnUiThread(() -> dispatchNativePushError("token_unavailable"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void continuePushRegistration() {
|
||||||
if (
|
if (
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
|
||||||
&& checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
|
&& checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
|
|
@ -509,11 +519,17 @@ public final class MainActivity extends ComponentActivity {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FirebaseMessaging messaging = FirebaseMessaging.getInstance();
|
((WhoNeedHelpApplication) getApplication()).runFirebaseTask(() -> {
|
||||||
messaging.setAutoInitEnabled(false);
|
try {
|
||||||
messaging.unregister().addOnFailureListener(
|
FirebaseMessaging messaging = FirebaseMessaging.getInstance();
|
||||||
error -> Log.w(LOG_TAG, "FCM unregister failed", error)
|
messaging.setAutoInitEnabled(false);
|
||||||
);
|
messaging.unregister().addOnFailureListener(
|
||||||
|
error -> Log.w(LOG_TAG, "FCM unregister failed", error)
|
||||||
|
);
|
||||||
|
} catch (RuntimeException error) {
|
||||||
|
Log.w(LOG_TAG, "FCM disable failed", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispatchPendingPushToken() {
|
private void dispatchPendingPushToken() {
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,18 @@ import android.app.Application;
|
||||||
|
|
||||||
import com.google.firebase.FirebaseApp;
|
import com.google.firebase.FirebaseApp;
|
||||||
import com.google.firebase.FirebaseOptions;
|
import com.google.firebase.FirebaseOptions;
|
||||||
import com.google.firebase.messaging.FirebaseMessaging;
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public final class WhoNeedHelpApplication extends Application {
|
public final class WhoNeedHelpApplication extends Application {
|
||||||
|
private final ExecutorService firebaseExecutor =
|
||||||
|
Executors.newSingleThreadExecutor(runnable -> {
|
||||||
|
Thread thread = new Thread(runnable, "wnh-firebase");
|
||||||
|
thread.setPriority(Thread.NORM_PRIORITY);
|
||||||
|
return thread;
|
||||||
|
});
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
@ -24,9 +33,9 @@ public final class WhoNeedHelpApplication extends Application {
|
||||||
.build();
|
.build();
|
||||||
FirebaseApp.initializeApp(this, options);
|
FirebaseApp.initializeApp(this, options);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (PushTokenStore.requested(this)) {
|
void runFirebaseTask(Runnable task) {
|
||||||
FirebaseMessaging.getInstance().setAutoInitEnabled(true);
|
firebaseExecutor.execute(task);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,18 @@ edit, branch, or tag was made.
|
||||||
`output/android-browser-development-e2e/20260724121700-2530398`; the
|
`output/android-browser-development-e2e/20260724121700-2530398`; the
|
||||||
run-scoped container, AVD volume, image, and database fixture were absent
|
run-scoped container, AVD volume, image, and database fixture were absent
|
||||||
afterward.
|
afterward.
|
||||||
|
- The same cross-client development flow then passed on a connected physical
|
||||||
|
Android device. The signed development app completed magic-link login,
|
||||||
|
registered its FCM token, exchanged one message in each direction with the
|
||||||
|
browser, displayed a real private FCM notification, started the foreground
|
||||||
|
location service, stored one physical location sample, and stopped sharing
|
||||||
|
with zero active sessions and zero retained current positions. The replay
|
||||||
|
exposed a Firebase Messaging main-thread call; auto-init and unregister now
|
||||||
|
run serially on the application Firebase executor. Instrumentation reported
|
||||||
|
`OK (1 test)` in 8.73 seconds. Exact fixture cleanup restored both synthetic
|
||||||
|
users, their request, assignment, messages, push jobs, and tracking session
|
||||||
|
to zero. Evidence is
|
||||||
|
`output/android-physical-development-e2e/physical-20260724-191948-1352510`.
|
||||||
- A separate signed development smoke passed on Android 37.1. Android verified
|
- A separate signed development smoke passed on Android 37.1. Android verified
|
||||||
the exact App Link host, the implicit same-origin deep link opened the app,
|
the exact App Link host, the implicit same-origin deep link opened the app,
|
||||||
the home and `/safety` DOM assertions passed, an unrelated HTTPS origin was
|
the home and `/safety` DOM assertions passed, an unrelated HTTPS origin was
|
||||||
|
|
@ -348,15 +360,15 @@ this audit.
|
||||||
| Privacy settings | Implemented and browser-verified | The profile exposed hidden, approximate public, exact for active match, and explicit exact-public options. Blocking and current-position cleanup have automated tests. | Exact public location remains a user opt-in; legal privacy and retention text still requires jurisdiction-specific review before launch. |
|
| Privacy settings | Implemented and browser-verified | The profile exposed hidden, approximate public, exact for active match, and explicit exact-public options. Blocking and current-position cleanup have automated tests. | Exact public location remains a user opt-in; legal privacy and retention text still requires jurisdiction-specific review before launch. |
|
||||||
| Reputation and anti-abuse | Implemented at MVP level | Handover codes, two-party completion, double-blind reviews, unique-counterpart ranking, optional movement/proximity evidence, reports, blocks, abuse signals, and moderator audit paths have automated tests. | The system is not bot-proof and does not claim identity verification. No punitive numeric policy is enabled without measured and approved thresholds. |
|
| Reputation and anti-abuse | Implemented at MVP level | Handover codes, two-party completion, double-blind reviews, unique-counterpart ranking, optional movement/proximity evidence, reports, blocks, abuse signals, and moderator audit paths have automated tests. | The system is not bot-proof and does not claim identity verification. No punitive numeric policy is enabled without measured and approved thresholds. |
|
||||||
| Account registration and sign-in | Implemented and browser-verified | Email registration is a single passwordless flow: it records the display name and acceptance once, sends a confirmation link, and does not duplicate a user on subsequent sign-in. Confirmed users can keep using magic links or add a password in settings. Google OpenID Connect registration, sign-in, link, unlink, replay prevention, verified-email enforcement, and account-ownership rules are covered by the 353-test suite. Real headed Chrome on the development origin exercised the Google callback, existing-account ownership email, one-time identity connection, and subsequent one-click Google login without creating a duplicate user. The application-generated authentication email was observed in Gmail from the development sender. | Production SMTP delivery and the production Google callback remain unverified. |
|
| Account registration and sign-in | Implemented and browser-verified | Email registration is a single passwordless flow: it records the display name and acceptance once, sends a confirmation link, and does not duplicate a user on subsequent sign-in. Confirmed users can keep using magic links or add a password in settings. Google OpenID Connect registration, sign-in, link, unlink, replay prevention, verified-email enforcement, and account-ownership rules are covered by the 353-test suite. Real headed Chrome on the development origin exercised the Google callback, existing-account ownership email, one-time identity connection, and subsequent one-click Google login without creating a duplicate user. The application-generated authentication email was observed in Gmail from the development sender. | Production SMTP delivery and the production Google callback remain unverified. |
|
||||||
| Notifications and nearby alerts | Implemented and browser-verified | Users can configure push/email preferences, quiet hours, category/urgency/day/time filters, a private matching center, and 1/3/5/10/25 km radii. Durable inbox notifications and Oban delivery jobs are tested; public notification payloads omit chat text, exact coordinates, and the private saved-area label. The focused Chromium replay completed subscription, matching request, inbox delivery, navigation, and export. A real development Web Push subscription and provider delivery completed without a recorded error. | Production Web Push and physical-device Android FCM delivery remain unverified and require isolated deployment credentials. |
|
| Notifications and nearby alerts | Implemented and browser/physical-device verified in development | Users can configure push/email preferences, quiet hours, category/urgency/day/time filters, a private matching center, and 1/3/5/10/25 km radii. Durable inbox notifications and Oban delivery jobs are tested; public notification payloads omit chat text, exact coordinates, and the private saved-area label. The focused Chromium replay completed subscription, matching request, inbox delivery, navigation, and export. Real development Web Push delivery completed without a recorded error, and a real private FCM notification reached the physical Android development app. | Production Web Push and production Android FCM delivery remain unverified and require isolated production credentials. |
|
||||||
| Social profiles | Manual links implemented; optional GitHub verification implemented and automated-tested | Manual links cannot set verification fields. The optional GitHub flow uses state, PKCE, a user-bound one-time session, unique provider ownership, and an audit record. The local protocol drill also performs real HTTP token/user exchanges without returning an access token to the application. | GitHub OAuth credentials are intentionally absent and are not required for registration or the help flow. The real external provider redirect/callback remains disabled and unverified; other providers remain manual/unverified. |
|
| Social profiles | Manual links implemented; optional GitHub verification implemented and automated-tested | Manual links cannot set verification fields. The optional GitHub flow uses state, PKCE, a user-bound one-time session, unique provider ownership, and an audit record. The local protocol drill also performs real HTTP token/user exchanges without returning an access token to the application. | GitHub OAuth credentials are intentionally absent and are not required for registration or the help flow. The real external provider redirect/callback remains disabled and unverified; other providers remain manual/unverified. |
|
||||||
| Support and content removal | Implemented and browser-verified | Public support, account deletion, general removal, and TAKE IT DOWN forms create separate audited queues; private email status links verify public contacts; authenticated submissions use the account email; moderator-only operations can update status and notify verified contacts. TAKE IT DOWN accepts URLs/text only and records a 48-hour review due time. Authenticated users can download an allowlisted JSON export, and moderators can run a read-only deletion relationship preflight. | The current product hosts no user media and does not claim TAKE IT DOWN coverage. Staffing, jurisdiction-specific legal classification, final retention rules, destructive account erasure/anonymisation, and identical-media-copy handling remain operational/legal work. |
|
| Support and content removal | Implemented and browser-verified | Public support, account deletion, general removal, and TAKE IT DOWN forms create separate audited queues; private email status links verify public contacts; authenticated submissions use the account email; moderator-only operations can update status and notify verified contacts. TAKE IT DOWN accepts URLs/text only and records a 48-hour review due time. Authenticated users can download an allowlisted JSON export, and moderators can run a read-only deletion relationship preflight. | The current product hosts no user media and does not claim TAKE IT DOWN coverage. Staffing, jurisdiction-specific legal classification, final retention rules, destructive account erasure/anonymisation, and identical-media-copy handling remain operational/legal work. |
|
||||||
| Voluntary thanks | Implemented as an external optional link | A helper can expose an optional link after completion; the UI states that the platform does not process the payment. | The platform does not provide payments, escrow, refunds, tax reporting, or payment guarantees. |
|
| Voluntary thanks | Implemented as an external optional link | A helper can expose an optional link after completion; the UI states that the platform does not process the payment. | The platform does not provide payments, escrow, refunds, tax reporting, or payment guarantees. |
|
||||||
| Android client | Local, development, test/staging, and production build identities implemented | The native packages `org.whoneedhelp.mobile.debug`, `org.whoneedhelp.mobile.development`, `org.whoneedhelp.mobile.staging`, and `org.whoneedhelp.mobile` are separated by build type and signing identity. Lifecycle, permission, deep-link, foreground tracking, recoverable main-page failure, notification-Stop, and Activity-destruction tests remain in place. Ephemeral signed development and production pipelines verify package IDs, certificates, unit tests, lint, APKs and instrumentation artifacts; production also verifies the signed AAB with Bundletool. The fresh API 37 development smoke verified the online `assetlinks.json`, Android domain state, real implicit same-origin App Link, in-app home and Safety DOM, and external-HTTPS browser isolation with zero load/TLS errors. Evidence is `output/android-development-smoke/20260723232919-3461820`. | Play registration/App Signing, physical-device FCM delivery, unattended/background-permission tracking, and iOS are not complete. |
|
| Android client | Local, development, test/staging, and production build identities implemented | The native packages `org.whoneedhelp.mobile.debug`, `org.whoneedhelp.mobile.development`, `org.whoneedhelp.mobile.staging`, and `org.whoneedhelp.mobile` are separated by build type and signing identity. Lifecycle, permission, deep-link, foreground tracking, recoverable main-page failure, notification-Stop, and Activity-destruction tests remain in place. Ephemeral signed development and production pipelines verify package IDs, certificates, unit tests, lint, APKs and instrumentation artifacts; production also verifies the signed AAB with Bundletool. The API 37 smoke verified the development App Link and WebView boundaries. A physical development device then passed magic-link login, bidirectional browser chat, real FCM delivery, foreground location sampling, Stop cleanup, and exact fixture cleanup. Evidence is `output/android-physical-development-e2e/physical-20260724-191948-1352510`. | Play registration/App Signing, production-device FCM delivery, unattended/background-permission tracking, and iOS are not complete. |
|
||||||
| Multiple web/worker instances | Implemented and locally failure/rollout-verified | The final isolated Compose drill passed BEAM crashes and sequential replacement with 3 web/2 worker replicas: all five nodes joined, PubSub passed, and 744/744 readiness requests succeeded. The project-owned kind cluster replaced all 2 web/2 worker pod UIDs under `maxUnavailable=0`; all four replacement pods joined, PubSub passed, and 363/363 samples ultimately succeeded. | Local PostGIS is a single instance. Production database HA, backups, and recovery are operator work and are not claimed complete. |
|
| Multiple web/worker instances | Implemented and locally failure/rollout-verified | The final isolated Compose drill passed BEAM crashes and sequential replacement with 3 web/2 worker replicas: all five nodes joined, PubSub passed, and 744/744 readiness requests succeeded. The project-owned kind cluster replaced all 2 web/2 worker pod UIDs under `maxUnavailable=0`; all four replacement pods joined, PubSub passed, and 363/363 samples ultimately succeeded. | Local PostGIS is a single instance. Production database HA, backups, and recovery are operator work and are not claimed complete. |
|
||||||
| Local observability | Implemented and protocol-verified | Pinned Prometheus scraped the exact 3 web and 2 worker targets with a file Bearer credential; Grafana provisioned a healthy datasource and ten-panel web/worker/BEAM/Ecto/Oban dashboard; Alertmanager delivered firing and resolved webhooks for an induced scoped replica stop. | Local delivery does not establish production retention, notification-provider reliability, on-call policy, or measured alert thresholds. |
|
| Local observability | Implemented and protocol-verified | Pinned Prometheus scraped the exact 3 web and 2 worker targets with a file Bearer credential; Grafana provisioned a healthy datasource and ten-panel web/worker/BEAM/Ecto/Oban dashboard; Alertmanager delivered firing and resolved webhooks for an induced scoped replica stop. | Local delivery does not establish production retention, notification-provider reliability, on-call policy, or measured alert thresholds. |
|
||||||
| Encrypted local backup | Implemented and failure-verified | Pinned Restic streamed PostgreSQL custom format into pinned local MinIO with no host plaintext dump, passed full-data checking and a fresh-database restore, rejected a corrupted repository, and published no snapshot for an interrupted upload. The one-run MinIO project and volume were removed after retaining the non-secret evidence. | The drill proves the local mechanism, not off-site durability, database HA, or a production RPO/RTO/retention policy. |
|
| Encrypted local backup | Implemented and failure-verified | Pinned Restic streamed PostgreSQL custom format into pinned local MinIO with no host plaintext dump, passed full-data checking and a fresh-database restore, rejected a corrupted repository, and published no snapshot for an interrupted upload. The one-run MinIO project and volume were removed after retaining the non-secret evidence. | The drill proves the local mechanism, not off-site durability, database HA, or a production RPO/RTO/retention policy. |
|
||||||
| External protocol boundaries | Implemented and locally failure-verified | Assent/Req and Swoosh/gen_smtp paths have internal success/rejection/retry/replay/timeout coverage. Google OIDC discovery/authorization/token/JWKS with nonce and PKCE and the optional GitHub boundary are locally exercised through mocks. The development origin additionally exercised the real Google provider, an authenticated Brevo SMTP delivery observed in Gmail, and a real browser Web Push subscription/provider delivery. The current push code includes provider-neutral HTTP delivery plus direct standards-based Web Push and FCM adapters with durable Oban retries and invalid-device cleanup. UniSender observations below are retained only as historical evidence and do not describe current wiring. | The real GitHub provider, production Google callback, production authentication-email delivery, production Web Push, physical-device FCM delivery, and APNs remain unverified. SMTP exactly-once delivery is not claimed. |
|
| External protocol boundaries | Implemented and locally failure-verified | Assent/Req and Swoosh/gen_smtp paths have internal success/rejection/retry/replay/timeout coverage. Google OIDC discovery/authorization/token/JWKS with nonce and PKCE and the optional GitHub boundary are locally exercised through mocks. The development origin additionally exercised the real Google provider, an authenticated Brevo SMTP delivery observed in Gmail, real browser Web Push delivery, and real physical-device Android FCM delivery. The current push code includes provider-neutral HTTP delivery plus direct standards-based Web Push and FCM adapters with durable Oban retries and invalid-device cleanup. UniSender observations below are retained only as historical evidence and do not describe current wiring. | The real GitHub provider, production Google callback, production authentication-email delivery, production Web Push/FCM delivery, and APNs remain unverified. SMTP exactly-once delivery is not claimed. |
|
||||||
|
|
||||||
## Reproducible checks
|
## Reproducible checks
|
||||||
|
|
||||||
|
|
@ -1626,10 +1638,12 @@ None of the observations below describe the current delivery path.
|
||||||
release is explicitly promoted. The test client and callback have already
|
release is explicitly promoted. The test client and callback have already
|
||||||
completed real registration and returning-user login.
|
completed real registration and returning-user login.
|
||||||
- Development VAPID and isolated Firebase/FCM are configured. Real development
|
- Development VAPID and isolated Firebase/FCM are configured. Real development
|
||||||
browser Web Push and Android-emulator FCM delivery both completed
|
browser Web Push, emulator FCM, and physical-device FCM delivery all
|
||||||
successfully. Before a public mobile release, repeat FCM and background
|
completed successfully. The physical development replay also covered
|
||||||
tracking on a physical Android device and repeat browser/Android delivery
|
foreground tracking while the native service was active and verified Stop
|
||||||
against each explicitly promoted origin. APNs and iOS are outside the current
|
cleanup. Before a public mobile release, repeat browser/Android delivery
|
||||||
|
against each explicitly promoted production origin. Unattended background
|
||||||
|
location was not requested or verified. APNs and iOS are outside the current
|
||||||
scope.
|
scope.
|
||||||
- Load-test representative data and traffic, then set measured pool, resource,
|
- Load-test representative data and traffic, then set measured pool, resource,
|
||||||
autoscaling, and action-limit policies.
|
autoscaling, and action-limit policies.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user