# SABRE protocol v1.2

This codument covers the protocol (SABRE) for using a separate Android application for communication with crowd-sourced data exchange servers.

The following definitions are used in this document:

• Host app: JBV1, Highway Radar, or any other application capable of displaying and submitting alerts
• Proxy app: An application capable of pulling and submitting the data to/from an external server

The following key considerations are assumed:

• All data is sent using standard Android broadcast intents
• Broadcasts to proxy app must be explicit, specifying the package name, and <packageName>.MainBroadcastReceiver as a receiving class (this is needed to overcome broadcast limitations added in Android O)
• Broadcasts to host should not specify the target class
• All data is JSON-encoded
• The data is sent in the data string extra field of an intent
• All unknown fields in JSON’s must be ignored by either side

This document uses YAML representation for messages in examples for easier reading; however, in actual applications, the pure JSON must be used.

### Handshake

In order to be discovered, all proxy apps must have package name starting with app.sabre., followed by a plugin name.

The host application

• lists all installed applications,
• finds out ones starting with app.sabre.,
• if mupltiple found, picks one,
• sends the handshake broadcast to the chosen application.

The handshake broadcast has following contents:

ACTION: app.sabre.HANDSHAKE
CONTENTS:
protocol_version: 1
response_action: "@action_discovery_response"


@package_name is the package name of the host application. The action of intent should look like app.sabre.<plugin_name>.HANDSHAKE.
protocol_version is an integer. 1 must be used for implementations in accordance with this document; this should be changed only on non-bachward-compatible protocol updates. This number must match the major version number in the heading of this document.
@action_discovery_response is generated by the host app and shows, which action should be used by the proxy app to send the response.

Once a proxy app receives a message from this channel, it must response with another broadcast message.

ACTION: @action_discovery_response
CONTENTS:
id: "@proxy_id"
name: "@proxy_name"
version: "@proxy_version"
supported_sources: [
"id": @source_id
"name": @source_name
],
request_action: "@action_fetch_request"
report_action: "@action_submit_report"
confirm_action: "@action_confirm_report"
shutdown_action: "@action_shutdown"
alternative_startup_activity: "@activity_alternative_startup"

• @proxy_id is the ID that should be the same for every launch. This way the host app can store some internal data sprcific for the proxy app. The Android’s installation ID should work perfectly for this.
• @proxy_name is how the host app should present the proxy to the user
• @package_name the name of the proxy package (should start with app.sabre.)
• @proxy_version is also for display purposes
• supported_sources lists the sources that the proxy can work with, the host app may decide (or let user to choose) to leave reports from only some of them
• @source_id is the ID of source that shouldn’t change over time, should be used by the host to identify the source
• @source_name is the name of the source which can be presented to the end user, may change over time
• @action_fetch_request is where messages requesting the alerts data should be sent to
• @action_submit_report, @action_confirm_report, and @action_discard_report are where reporting, confirming, and “not there” messages should be sent to
• @action_shutdown is a channel for the host app to notify proxy that there are no more requests expected, and the proxy app may free caches/resouces, and stop required services.
• @activity_alternative_startup is an activity to be used in the alternative startup procedure. See below for details. (IMPORTANT: this field was introduced in protocol v1.1 and may not be present in the response). Assume that alternative startup isn’t available if the field isn’t presented.

The proxy app shouldn’t assume the discovery broadcast will always be sent (e.g. the host app may cache the data), and must wake up on any incoming broadcast having the correct action.

### Proxy shutdown

On newer Android version, requests has to be processed in a foreground service. It is a good practice for a host app to notify, when the proxy isn’t needed any more, so it can immediately shut down the service and, thus, remove the mandatory notification.

In order to notify the proxy that it can shut down the service, it should send an empty broadcast with action @action_shutdown.

The proxy app shouldn’t assume the shutdown broadcast will always be sent (e.g. the host app may crash), and must shutdown itself after several minutes of no incoming broadcasts.

### Alternative startup method (new in SABRE v1.1)

Some devices have incorrectly configured firmware, blocking background apps from staring foreground service even on an explicit intent. For such cases host app may support and alternative method for waking up the proxy app. It is recommended to let user enabled the alternative startup manually, and have it disabled by default.

Alternative startup sequence:

• The host app launches an activity of the proxy app, using a @activity_alternative_startup. (ComponentName("sabre.app.<appname>, @activity_alternative_startup)) This brings the proxy app to the foreground.
• The intent launching activity should contain two “extra” fields, named callback_app, and callback_component.
• The proxy app, while being foregrounded, starts the foreground service.
• Once the foreground service is started, proxy app should start an activity belonging to the host app, using a component name build from callback_app and callback_component extras from the first step.

Using the alternative startup flow allows the proxy app to start its foreground service while being displayed. This overcomes limitations of some firmwares. The downside of this flow is that the proxy application is temporarily displayed on the screen during the start-up.

Notes

• It is important to add FLAG_ACTIVITY_NEW_TASK when starting activities from other apps.

Alerts fetching works using the request-response model. In order for the host app to fetch an alert, it sends a broadcast with the following contents:

ACTION: @action_fetch_request
CONTENTS:
request_id: "@request_id"
lat: @latitude
lon: @longidute
response_action: "@action_fetch_response"

• @request_id is a unique string for the host app to be able to match requests with responses
• @latitude, @longitude, and @radius are floats. Radius is set in meters.
• response_action is a broadcast action where the response must be sent

The proxy app fetches the required data and sends the following respose back:

ACTION: @action_fetch_response
CONTENTS:
request_id: "@request_id"
error_message: "@error_message"
response:
n_batches: @n_batches
batch_id: @batch_id
lat: @latitude
lon: @longitude
street_name: "@street_name"
report_ts: "@report_ts"
confirm_ts: "@confirm_ts"
n_confirmations: "@n_confirmations",
is_response_accepted: "@is_response_accepted",
speed_limit: "@speed_limit",
]

• @request_id is the same as in request
• error_message and response are nullable fields. Exactly one of them must be set, depending on the outcome of the request
• @error_message is a short human-readable message which can be displayed the the end user
• @n_batches is the total number of messages response for this response; as messages may get large and not fit into the maximum allowed broadcast message size, the proxy app may decide to split the response into multiple messages.
• @batch_id is the ID number of the current batch; there should be total @n_batches batches with IDs from 0 to @n_batches - 1
• @alert_source is the id which must be present in the supported_sources section of the discovery response
• @alert_id is the unique ID of a given alert; it can be used to track the same alerts between different requests, and to confirm/discard reports in further section
• @latitude, @longitude, and @heading_deg are floats. Heading is set in degrees, clockwise from the true north.
• @street_name is an nullable string, containing the street name of an aleert
• @report_ts is a UNIX timestamp, specifying when the alert was reported
• @confirm_ts is an nullable field, containing UNIX timestamp, specifying when the alert was last confirmed by any user
• @n_confirmations is an integer field showing number of times the alert was confirmed. (IMPORTANT: this field was introduced in protocol v1.1 and may not be present in responses)
• @is_response_accepted is a boolean, denoting whether this alert can be used with confirm/discard methods. Normally this evaluate to false for own submissions, and for data sources not supporting confiming/discarding alerts. (IMPORTANT: this field was introduced in protocol v1.1 and may not be present in responses; assume it being equal to true if not present)
• @speed_limit is a nullable float field, that may be set for some alerts (e.g. for mobile cameras). (IMPORTANT: this field was introduced in protocol v1.1 and may not be present in responses)
• @alert_type must be one of predefined alert types, allowed types are:
• POLICE_VISIBLE
• POLICE_HIDING
• ACCIDENT_MINOR
• ACCIDENT_MAJOR
• HAZARD_ON_ROAD
• HAZARD_ON_SHOULDER
• HAZARD_WEATHER
• HAZARD_ON_ROAD_OBJECT
• HAZARD_ON_ROAD_POT_HOLE
• HAZARD_ON_ROAD_ROAD_KILL
• HARARD_ON_ROAD_CAR_STOPPED
• HAZARD_ON_SHOULDER_CAR_STOPPED
• HAZARD_ON_SHOULDER_ANIMALS
• HAZARD_ON_SHOULDER_MISSING_SIGN
• HAZARD_WEATHER_FOG
• HAZARD_WEATHER_HAIL
• HAZARD_WEATHER_HEAVY_RAIN
• HAZARD_WEATHER_HEAVY_SNOW
• HAZARD_WEATHER_FLOOD
• HAZARD_WEATHER_MONSOON
• HAZARD_WEATHER_TORNADO
• HAZARD_WEATHER_HEAT_WAVE
• HAZARD_WEATHER_HURRICANE
• HAZARD_WEATHER_FREEZING_RAIN
• HARARD_ON_ROAD_LANE_CLOSED
• HAZARD_ON_ROAD_OIL
• HAZARD_ON_ROAD_ICE
• HAZRAD_ON_ROAD_CONSTRUCTION
• FIXED_CAMERA (new in SABRE v1.1)
• MOBILE_CAMERA (new in SABRE v1.1)
• END_OF_TAILBACK (new in SABRE v1.1)

The following message is used to report an alert

ACTION: @action_submit_report
CONTENTS:
lat: @latitude
lon: @longitude
altitude_m: @altitude
is_opposite: @is_opposite
time_delta_s: @time_delta
test: @test

• @latitude, @longitude, and @heading follow the same format as in the previous sections of this document; they describe where the report should be placed to
• @altitude_m is a float, representing the GPS driver’s altitude
• @alert_type is the same as in the reporting section of this document
• @is_opposite is a boolean
• @time_delta_s (integer) specifies, how many seconds has passed since the reporting event, until this message
• @test (boolean, new in SABRE v1.2) specifies if the request should actually be sent, if set to false than the SABRE plugin should produce the same interaction, but not actually send the report.

The send action expects no response (fire and forget). If the action has failed, the proxy app may notify the user by showing a toast message.

### Confirming reports of other users

In order to confirm a report the following message should be sent

ACTION: @action_confirm_report
CONTENTS:
lat: @latitude
lon: @longitude
test: @test

• @alert_id must match the one received from the response message.
• @latitude and @longitude represent current driver position
• @test (boolean, new in SABRE v1.2) specifies if the request should actually be sent, if set to false than the SABRE plugin should produce the same interaction, but not actually send the report.

The confirm action expects no response (fire and forget). If the action has failed, the proxy app may choose to notify the user by showing a toast message.

### Marking other reports as “not there”

In order to decline a report the following message should be sent

ACTION: @action_discard_report
CONTENTS:
lat: @latitude
lon: @longitude

• @alert_id must match the one received from the response message.
• @latitude and @longitude represent current driver position
• @test (boolean, new in SABRE v1.2) specifies if the request should actually be sent, if set to false than the SABRE plugin should produce the same interaction, but not actually send the report.