Class ClientSessionPushAPI
- All Implemented Interfaces:
IPushNotificationAPI, IPushNotificationCommonAPI, PushEvents.ISink
IClientSessionGyro.getPushNotificationAPI() (P4-04).The engine-bound members are PushSessionAPI's. The client-bound ones need the channel of PUSH-PROPS-DESIGN section 3, PushChannelProps under $env, which the context hands over: queryDeviceState(BiFunction) goes down it as QUERY_STATE and completes on the client's REPLY (P4-06); sendToClient(IPushNotification, String, PushSendOptions, BiFunction) targets the device by the UUID the channel reports, on the device tier through its subscription and on the in-session tier through this session's channel (P4-10); subscribe(String, BiFunction) goes down as SUBSCRIBE and stores what the client answers (P4-07); unsubscribe(String, BiFunction), resetDevice(BiFunction) and resetDeviceEverywhere(BiFunction) remove rows first and then go down as UNSUBSCRIBE or RESET with drop (P4-08). A call the channel cannot carry - no channel (a Designer, Design Viewer or VSViewer session, a client built before P4), no device UUID reported yet, or a disposed channel - fails with PushNotificationException.NO_DEVICE, posted to the client session's worker and never thrown (design 3.2), after the argument checks its declaration makes.
subscribe (design 4.2, 8.5): the client performs the device's part - a Browser subscribes its pushManager under the application's key when the permission is already granted, and answers gesture_required without prompting otherwise (RR-8 A, RR-9 A); a Cordova device asks FirebaseX - and answers the subscription or the token. The server stores it as the session's user, this application, this device and the Topic, through the engine's store directly, with no privilege check (RR-5 A): the application decided. Before anything goes down, what is known here is refused at once: no user (no_user), a device that cannot receive PN (unsupported), an application without the transport the device uses (not_configured: no VAPID key written into the channel for a Browser, no Firebase project for Cordova). A reply that fits its operation badly - a subscription without its endpoint or keys, an empty token, neither or both members - completes bad_request and nothing is stored (design 6.4).
Inside iiziRun Developer (PUSH-CLUSTER-DESIGN 3.3, B-10b, ruled PC-9 with RR-21 C; until B-10b refused by CC-PUSH-ROADMAP row 4 A): a row records the credentials it was made under and is signed with them, so a subscription made under the server's own * credentials is stored for this application too. An FCM token of the server's * Firebase project - a device built as iiziRun Developer - is stored recording that project; one of any other project than the application's is refused key_conflict and nothing is stored. One whose device names no project - iiziRun builds before CC-FIX-PUSHDEV-PROJECT name none - is checked with FCM itself (CC-PUSHDEV-VALIDATE-ONLY): a validate_only request to the token with the application's credentials, off the worker, and when FCM answers SENDER_ID_MISMATCH, a second with *'s; a match stores it under the project that matched, a mismatch of both refuses it key_conflict as a named mismatch is, and a check that cannot tell - no credential, FCM unreachable - stores it with a warning. A Browser already subscribed under the server's * key - the channel's skey - answers that subscription with the key it was made under, vapid_public_key, which is stored when it is the application's or *'s and refused key_conflict otherwise; one subscribed under any other key answers key_conflict itself (RR-21 A).
unsubscribe, resetDevice, resetDeviceEverywhere (design 4.2, RR-15 B, P4-b): the store first, off the worker - the row of the Topic, this application's rows on this device, or every application's rows of the user on this device, the HTTP reset {} - then, from the worker, the request down the channel with drop: whether any row of this application remains for the device, always true after a reset. The device gives up a Browser subscription accordingly and never an FCM token, and no FCM Topic is touched (R-5 A). The callback gets the number of rows removed once the device has answered; a timeout leaves the rows gone. Listeners get onSubscriptionRemoved per row from the store, with unsubscribed or reset.
setSubscribeAction, removeSubscribeAction (design 8.2-8.4, P4-09, RR-6 B, RR-7 A, RR-20 A): the action is written into the channel's act as the button's atom path from the root and the Topic, and the callback stays in the channel's action table; the client subscribes inside the button's DOM click - pushManager.subscribe synchronously, as P4-01 measured on 2026-09-26 - and reports ACTION_RESULT once per click, which is validated and stored exactly as a SUBSCRIBE reply is, through store(String, String, String, String, String, String, PropCnr, BiFunction). The application's VAPID public key is refreshed into the channel when an action is configured (CH-4). A result whose action was removed meanwhile still stores the subscription the device holds, with an INFO line and no callback (design 8.4). Only a UIButton is accepted, and only one of this session's tree.
No user needed by sendToClient(IPushNotification, String, PushSendOptions, BiFunction) (ruled 2026-09-26, CC-PUSH-ROADMAP row 3 B): it names this session, not a user, so it works in a session without an authenticated user - in-session down this session's channel, while the device tier matches no stored subscription, a subscription belonging to a user. The fan-out sends of the application session API stay user-bound. The filter carries the session's user ID as IPushSessionContext.getUserID() answers it, -1 for none: the enumerator reports the same -1 for such a session, and no stored row carries it.
Threading (design 5): the channel calls back on the connection's thread or on the timer's; this class runs the store lookup off it and posts the outcome to the worker, so no callback ever runs on either. Deadlines (RR-11 A): 30 seconds, 5 minutes for a subscribe; setDeadlines(long, long) exists for tests.
- Author:
- Christopher Mindus
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe Topic a subscribe at sign-in stores: the application-level subscription,""(PUSH-WIRE-CONTRACT section 2.4); the.iiziPushmodel names no Topic (CC-PUSH-SUBSCRIBE-AT-SIGNIN).Fields inherited from class PushSessionAPI
ctxConstructor Summary
ConstructorsMethod Summary
Modifier and TypeMethodDescriptionlongGets the deadline of a client-bound call.Gets what the last subscribe at sign-in of this session came to, for the rigs and the tests: the text logged, without the application, the user and the session.longGets the deadline of a subscribe.voidqueryDeviceState(BiFunction<IPushDeviceState, Exception, Void> callback) Queries what this device can do: whether it supports Push Notification at all, which transport it would use, what the user has granted, and whether it is subscribed for this application.voidremoveSubscribeAction(IGProp<?> component) Removes the subscribe action of a button configured withIPushNotificationAPI.setSubscribeAction(IGProp, String, BiFunction): the next click on it subscribes nothing.voidresetDevice(BiFunction<Integer, Exception, Void> callback) Removes every subscription of the session's user on this device for this application, every topic (ruled 2026-09-24, RR-15 B), on this server and then on the device asIPushNotificationAPI.unsubscribe(String, BiFunction)does once no row remains: a Browser gives up the subscription it holds under this application's key, a Cordova device keeps its shared FCM token.voidresetDeviceEverywhere(BiFunction<Integer, Exception, Void> callback) Removes every subscription of the session's user on this device, for every application and topic, iiziRun's own rows (*) included: the Java form of the HTTPresetwith an empty body (PUSH-WIRE-CONTRACT section 5.4), what iiziRun's Reset does on the server (ruled 2026-09-25, P4-b).voidsendToClient(IPushNotification notification, String topic, PushSendOptions options, BiFunction<IPushSendResult, Exception, Void> callback) --- sendToClient (P4-10) ---voidsetDeadlines(long deadlineMillis, long subscribeDeadlineMillis) Sets the deadlines of the client-bound calls, for tests: the ruled values are the defaults.voidsetSubscribeAction(IGProp<?> component, String topic, BiFunction<IPushSubscribeResult, Exception, Void> callback) Configures the subscribe action of a button (PUSH-PROPS-DESIGN section 8.2, ruled 2026-09-24 RR-6 B and RR-7 A): the click oncomponent, on the client and inside the user's gesture, subscribes this device for this application andtopic- the one place where Push Notification is not driven from the server (PUSH-WIRE-CONTRACT section 9.4a) - and this server then stores the subscription as this session's user, this application, this device and the topic, exactly asIPushNotificationAPI.subscribe(String, BiFunction)does, with no privilege checked (RR-5 A), and callscallbackwith the outcome.voidsubscribe(String topic, BiFunction<IPushSubscribeResult, Exception, Void> callback) --- subscribe (P4-07) ---voidSubscribes this session's device to the application-level Topic (SIGNIN_TOPIC) once a user has signed in, when the application's.iiziPushasks for it (PushProps.isSubscribeAtSignIn(), ruled 2026-10-22 LV-D-2).voidunsubscribe(String topic, BiFunction<Integer, Exception, Void> callback) Removes this device's subscription for this application and a topic: on this server the row of the session's user, this application, the topic and this device - the Java form of the HTTPunsubscribe(PUSH-WIRE-CONTRACT section 5.3), no privilege checked - and then, on the device, the push subscription a Browser holds under this application's key, but only when no row of this application remains for this device: a Browser holds one subscription for every topic of an application (PUSH-PROPS-DESIGN section 4.2, 8.7), and one it holds under another key - the server's own, or another application's - is left alone.Methods inherited from class PushSessionAPI
addPushNotificationListener, complete, context, dispose, engine, fail, getPendingCount, isDisposed, isFor, newMessageBuilder, onNotificationAction, onNotificationReceived, onSendFailed, onSubscriptionCreated, onSubscriptionRemoved, queryAvailableTransports, querySubscriptions, removePushNotificationListener, runOff, topics, userMethods inherited from class Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface IPushNotificationCommonAPI
addPushNotificationListener, newMessageBuilder, queryAvailableTransports, querySubscriptions, removePushNotificationListener
Field Details
SIGNIN_TOPIC
The Topic a subscribe at sign-in stores: the application-level subscription,""(PUSH-WIRE-CONTRACT section 2.4); the.iiziPushmodel names no Topic (CC-PUSH-SUBSCRIBE-AT-SIGNIN).- See Also:
Constructor Details
ClientSessionPushAPI
Constructor.- Parameters:
ctx- The client session.
Method Details
setDeadlines
public void setDeadlines(long deadlineMillis, long subscribeDeadlineMillis) Sets the deadlines of the client-bound calls, for tests: the ruled values are the defaults.- Parameters:
deadlineMillis- The deadline of every client-bound call but a subscribe.subscribeDeadlineMillis- The deadline of a subscribe.
getDeadlineMillis
public long getDeadlineMillis()Gets the deadline of a client-bound call.- Returns:
- The deadline in milliseconds.
getSubscribeDeadlineMillis
public long getSubscribeDeadlineMillis()Gets the deadline of a subscribe.- Returns:
- The deadline in milliseconds.
queryDeviceState
Description copied from interface:IPushNotificationAPIQueries what this device can do: whether it supports Push Notification at all, which transport it would use, what the user has granted, and whether it is subscribed for this application.- Specified by:
queryDeviceStatein interfaceIPushNotificationAPI- Parameters:
callback- Called on the client session's worker thread with the state, or with the failure.
subscribe
--- subscribe (P4-07) ---- Specified by:
subscribein interfaceIPushNotificationAPI- Parameters:
topic- The topic, the empty string for the application-level subscription; a name is validated as the HTTP API validates one (PUSH-WIRE-CONTRACT section 2.4).callback- Called on the client session's worker thread with the result - whether a new row was stored, and the subscription as stored - or with the failure.
subscribeAtSignIn
public void subscribeAtSignIn()Subscribes this session's device to the application-level Topic (SIGNIN_TOPIC) once a user has signed in, when the application's.iiziPushasks for it (PushProps.isSubscribeAtSignIn(), ruled 2026-10-22 LV-D-2). Called byGyroPushContextsat the client session's creation with a signed-in user and at an in-app sign-in (AppSessionGyro.signInAuthenticatedUser); nothing without the property, a running engine or a signed-in user. Once per session and user: a second call for the same user does nothing, and the store is asked first, off the worker - a device the user already subscribed for this application and Topic is sent no secondSUBSCRIBE. Otherwisesubscribe(String, BiFunction)is called as the application's Java would call it, with every check it makes.A session without a channel to a device (a Designer, Design Viewer or VSViewer session, a client built before P4), a device that reports no transport (a desktop without a Service Worker), an application without the transport the device uses (
not_configured) and a Browser whose permission is not granted - the client answersgesture_requiredand prompts nothing (RR-8 A) - are logged once at FINE and never as an error; a Cordova device asks FirebaseX for the permission when it is undecided (the system dialog, at the first sign-in). A success is logged at INFO; a failure of the store or the engine at WARNING (signInLevel(Exception)).A sign-in that comes before the device half has reported - a session created with its user while the client's
uuidandtransportare still on their way - waits on the channel's events for the report: the client writesuuidandtransportin one transaction,uuidfirst (report()of iz/PushChannelProps.js), and the permission afterwards, so the wait ends at thetransportor thepermvalue event, at which both are in; a report that lands between the check and the listener is caught by a second check. Nothing but the lookups runs on the caller's thread.getSignInOutcome
Gets what the last subscribe at sign-in of this session came to, for the rigs and the tests: the text logged, without the application, the user and the session.- Returns:
- The outcome, or null before any.
unsubscribe
Description copied from interface:IPushNotificationAPIRemoves this device's subscription for this application and a topic: on this server the row of the session's user, this application, the topic and this device - the Java form of the HTTPunsubscribe(PUSH-WIRE-CONTRACT section 5.3), no privilege checked - and then, on the device, the push subscription a Browser holds under this application's key, but only when no row of this application remains for this device: a Browser holds one subscription for every topic of an application (PUSH-PROPS-DESIGN section 4.2, 8.7), and one it holds under another key - the server's own, or another application's - is left alone. A Cordova device keeps its FCM token, which every application on the device shares, and no FCM Topic is unsubscribed (ruled 2026-09-26, R-5 A: the server's rows are the subscription). Listeners of the user's sessions getonSubscriptionRemovedwith the reasonunsubscribedfor each row removed.The server's part comes first and stands whatever the device answers: a
timeoutreported here means the rows are gone and the device may still hold its subscription, which the nextsubscribeanswers as it is. Zero removed is a success: the state the caller wanted already holds.no_userwhen the session has no authenticated user;bad_requestfor a topic that is not a valid name.- Specified by:
unsubscribein interfaceIPushNotificationAPI- Parameters:
topic- The topic, the empty string for the application-level subscription; a name is validated as the HTTP API validates one.callback- Called on the client session's worker thread with the number of subscriptions removed from the server, zero when there was none, or with the failure.
resetDevice
Description copied from interface:IPushNotificationAPIRemoves every subscription of the session's user on this device for this application, every topic (ruled 2026-09-24, RR-15 B), on this server and then on the device asIPushNotificationAPI.unsubscribe(String, BiFunction)does once no row remains: a Browser gives up the subscription it holds under this application's key, a Cordova device keeps its shared FCM token. Other applications' rows on this device, and iiziRun's own (*), survive; listeners of the user's sessions getonSubscriptionRemovedwith the reasonresetfor each row removed. Removing every application's rows isIPushNotificationAPI.resetDeviceEverywhere(BiFunction), the HTTPresetwith an empty body (PUSH-WIRE-CONTRACT section 5.4), and iiziRun's Reset.The server's part comes first and stands whatever the device answers; zero removed is a success;
no_userwhen the session has no authenticated user.- Specified by:
resetDevicein interfaceIPushNotificationAPI- Parameters:
callback- Called on the client session's worker thread with the number of subscriptions removed from the server, or with the failure.
resetDeviceEverywhere
Description copied from interface:IPushNotificationAPIRemoves every subscription of the session's user on this device, for every application and topic, iiziRun's own rows (*) included: the Java form of the HTTPresetwith an empty body (PUSH-WIRE-CONTRACT section 5.4), what iiziRun's Reset does on the server (ruled 2026-09-25, P4-b). The one member of this API that reaches beyond this application's subscriptions. On the device a Browser then gives up its push subscription whatever key it was made under - nothing of this user on this device remains to deliver to - and a Cordova device keeps its FCM token, which is never unregistered by the Java API. Listeners of the user's sessions getonSubscriptionRemovedwith the reasonresetfor each row of their own application.The server's part comes first and stands whatever the device answers; zero removed is a success;
no_userwhen the session has no authenticated user. Stopping this application's notifications alone isIPushNotificationAPI.resetDevice(BiFunction).- Specified by:
resetDeviceEverywherein interfaceIPushNotificationAPI- Parameters:
callback- Called on the client session's worker thread with the number of subscriptions removed from the server, or with the failure.
setSubscribeAction
public void setSubscribeAction(IGProp<?> component, String topic, BiFunction<IPushSubscribeResult, Exception, Void> callback) Description copied from interface:IPushNotificationAPIConfigures the subscribe action of a button (PUSH-PROPS-DESIGN section 8.2, ruled 2026-09-24 RR-6 B and RR-7 A): the click oncomponent, on the client and inside the user's gesture, subscribes this device for this application andtopic- the one place where Push Notification is not driven from the server (PUSH-WIRE-CONTRACT section 9.4a) - and this server then stores the subscription as this session's user, this application, this device and the topic, exactly asIPushNotificationAPI.subscribe(String, BiFunction)does, with no privilege checked (RR-5 A), and callscallbackwith the outcome. On a Browser the click is what shows the permission prompt:pushManager.subscribe()is called synchronously inside the click, as measured on 2026-09-26 (P4-01) - Firefox, Safari and an iPadOS Home Screen web app show the prompt only there and refuse without a gesture, Chrome prompts either way. On Cordova FirebaseX asks the system's permission when it is undecided, and no gesture is needed.A session without an authenticated user answers
no_userbefore any prompt (CC-FIX-PWA-FOLLOWUPS, PWA-D-6): the row a click stores is the user's, so the client refuses the click at once from the channel'sauthand no permission is spent on it. Configure the action all the same: the first click after the sign-in asks and subscribes.The callback is called once per click, not once per configuration, and may be called several times in one session: the user may click, refuse, and click again. It runs on the client session's worker with the result - whether a new row was stored, and the subscription as stored - or with the failure:
permission_denied(the prompt's Block, or a permission already denied: final until the user changes it in the Browser's or the operating system's settings, no code can ask again),dismissed(the prompt closed without an answer: the permission stays undecided and the next click asks again),key_conflict(the Browser already holds a push subscription under another VAPID key, never replaced, RR-21 A; or a Cordova device's FCM token belongs to another Firebase project, CC-PUSH-ROADMAP row 4 A),not_configured(this application offers no transport the device uses),unsupported(the device cannot receive Push Notification at all: on iOS a web page receives it only as a Home Screen web app whose manifest says"display": "standalone", and an ordinary Safari tab reports it),no_user(the session has no authenticated user to store the row for),bad_request(the client answered with a subscription lacking its endpoint or keys, and nothing was stored).gesture_requiredis not expected from a click and is reported if a Browser refuses inside one. Every failure is aPushNotificationExceptionwhosePushNotificationException.getUserText()is a text the user can be shown for it (seeIPushNotificationAPI.subscribe(String, BiFunction)).The component's own action handler (
@OnUIAction) still runs, at once, before the user has answered the prompt (RR-20 A): do not decide anything about the subscription there. The client disables the button while the outcome is pending and enables it again after; every other follow-up - a message, hiding the button, changing the screen - is this callback's (RR-6 B). Setting an action on a component that has one replaces it. A successful click stores the subscription even if you removed the action while the prompt was open: the device holds it, and a row nobody stores is a device the server cannot reach; the callback is then not called and the outcome is logged at INFO.Configure it when the panel is created, and show the button only where it can work (
IPushNotificationAPI.queryDeviceState(BiFunction)). The application's VAPID public key is refreshed into the client when the action is configured, so a key rotated since the session was created reaches the click. With no channel - a Designer, Design Viewer or VSViewer session - nothing is configured andno_deviceis posted to the callback once; an invalid topic postsbad_requestonce.- Specified by:
setSubscribeActionin interfaceIPushNotificationAPI- Parameters:
component- The button: aUIButtonof this client session's screen (P4 hooks the click ofUIButtononly), named to the client by its property path (RR-7 A).topic- The topic, the empty string for the application-level subscription; a name is validated as the HTTP API validates one (PUSH-WIRE-CONTRACT section 2.4).callback- Called on the client session's worker thread, once per click, with the result or with the failure.
removeSubscribeAction
Description copied from interface:IPushNotificationAPIRemoves the subscribe action of a button configured withIPushNotificationAPI.setSubscribeAction(IGProp, String, BiFunction): the next click on it subscribes nothing. A no-op on a component that has none, and in a session without a channel. A click whose prompt was open when the action was removed still stores the subscription the user allowed (seeIPushNotificationAPI.setSubscribeAction(IGProp, String, BiFunction)).- Specified by:
removeSubscribeActionin interfaceIPushNotificationAPI- Parameters:
component- The button.
sendToClient
public void sendToClient(IPushNotification notification, String topic, PushSendOptions options, BiFunction<IPushSendResult, Exception, Void> callback) --- sendToClient (P4-10) ---- Specified by:
sendToClientin interfaceIPushNotificationAPI- Parameters:
notification- The notification, built withIPushNotificationMessageBuilder.topic- The topic, the empty string for the application-level subscription.options- The time-to-live, urgency and delivery mode, ornullfor every default.callback- Called on the client session's worker thread with the result, whose target outcomes name this device, or with the failure.