Exposure Notifications API

The Exposure Notifications API is a joint effort between Apple and Google to provide the core functionality for building Android apps to notify users of possible exposure to confirmed COVID-19 cases.

For an overview of the goals of the system, see the COVID-19 information & resources page.

This guide shows you how to use the Exposure Notifications API to build Android apps that notify users of possible exposure to confirmed COVID-19 cases.

Note: This guide is released in advance of the SDK and is updated in parallel with subsequent SDK versions. Prepare your implementation cycle accordingly.

This guide contains the following sections:

  • Architecture: This section describes how the system is distributed across different subsystems.
  • Exposure Notifications API modes: This section describes the ExposureWindow and Legacy v1 modes of the API.
  • Data structures and Methods: These sections describe how to interact with the API from your app.
  • Broadcast receivers: This section describes how to define broadcast receivers in your Android manifest and how to respond to received broadcasts.
  • Error handling: This section describes the error codes to check for when you call methods, provides suggestions about how to respond to the error codes, and how to handle other issues.
  • Reference design: This section provides links to the project on GitHub, which provides examples about how to interface with the API.
  • Glossary: This section provides definitions of terms specific to the Exposure Notifications API.

These sections provide overview material. If you need more detailed information about specific classes and methods, see the Exposure Notifications reference.

Architecture

The Exposure Notifications system spans several subsystems. This section describes this distribution.

Google Play services

Bluetooth functionality happens within on-device Google Play services. Bluetooth functionality includes all broadcasts and scans for BLE beacons, along with local database storage. Your app must have the BLUETOOTH and INTERNET permission in its manifest, but your app doesn't require and can't include ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, nor BLUETOOTH_ADMIN. For more information about restrictions on your app, see the API’s Terms of Service.

For your app to work on all devices, set your minSdkVersion to version 6.0 (API level 23) or higher. The framework works on some devices as low as version 5.0 (API level 21), so using this version is an option to increase the number of devices eligible. If you decide to set the minSdkVersion to API level 21, you should test your app on the most popular Android 5.0 devices in your country to ensure that your app works as intended.

The complete list of the parts of the system that Google Play services handles is as follows:

  • Management of daily random keys:

  • Management of Bluetooth broadcast and collection:

    • Enables broadcast beacons.
    • Scans for other beacons.
    • Stores observed RPIs in an on-device data store.
    • Identifies whether the user was in close contact with a confirmed case.
    • Calculates and provides an exposure risk level to the app.
  • Presentation of consent requests to users at the following points:

    • Performs this action the first time an app activates the system, before scanning and broadcasting begins.
    • Performs this action before user keys are provided to the app for upload to the internet-accessible server, after the user has been positively diagnosed with COVID-19.

Mobile app

Your app does the following:

  • Enable users to start and stop broadcast and scan functions.

  • Provide the files with the diagnosis keys and associated metadata (following the file format) from your internet-accessible server to Google Play services.

  • Retrieve keys from the on-device data store and submit them to your internet-accessible server after one of the following conditions is present:

    • A user has been confirmed by a medical provider to have tested positive.

    • A health authority authorized a user-initiated self-report based on symptoms (if your app supports this use case).

    In both situations, the user must provide consent before the keys are provided to the app.

  • Schedule polls of your internet-accessible server for keys.

  • Register a receiver to receive broadcasts of the com.google.android.gms.exposurenotification.ACTION_EXPOSURE_STATE_UPDATED and com.google.android.gms.exposurenotification.ACTION_EXPOSURE_NOT_FOUND intents, and respond with a presentation of information to the user. See Broadcast receivers for more information.

  • Show a notification to the user with instructions about what to do next when the user has been exposed to another user who has tested positive for COVID-19.

Authenticated medical provider validation mechanism

Your app must provide functionality that confirms that the user has been positively diagnosed with COVID-19. This functionality controls the release of Temporary Exposure Keys stored on a device whose user is positively diagnosed. The released keys are sent to your app, which then uploads them to your internet-accessible server.

For more information about this part of the subsystem, see Exposure Notifications verification server.

Internet-accessible server

Your app must be able to communicate with an internet-accessible server that you own and that performs the following functions:

  • Collect diagnosis keys from users who have been diagnosed with COVID-19.
  • When polled, distribute diagnosis keys of confirmed cases to devices.

Exposure Notifications API modes

The Exposure Notifications API has changed significantly and now supports the following modes:

  • ExposureWindow mode: supports new ways of working with data introduced with v1.5 and higher. We recommend migrating to this mode.

  • Legacy v1 mode: supports all v1 functionality, even when running on a device with a higher version of the API installed.

Select the mode based on the provideDiagnosisKeys method used:

  • To enable the ExposureWindow feature (thereby deactivating the ExposureSummary and ExposureInformation features), call provideDiagnosisKeys(files) with only the list of files.

  • To enable the ExposureSummary and ExposureInformation features (thereby deactivating the ExposureWindow feature), call provideDiagnosisKeys(files, token, config) with a token.

  • Alternatively, you can enable the ExposureWindow feature while using the legacy v1 method by calling provideDiagnosisKeys(file, token, config) with token=TOKEN_A.

The API is backward compatible with all versions: it activates non-breaking changes when a new version is available on the device, even when using legacy v1 mode. Such changes would include, for example, the same-day release of keys and the ability to provide keys from multiple files with different signatures in one call.

.
ExposureWindow mode Legacy v1 mode
Token concept Do not use tokens

Or TOKEN_A when using the legacy method.

Use any token != TOKEN_A.

Using different tokens groups keys in different groups.

Obtain a summary risk score getDailySummaries()

(available in v1.6)

getExposureSummary().
Get detailed exposure information getExposureWindows(). getExposureInformation().
provideDiagnosisKeys quota
  • Six per day (counter resets at 00:00 UTC)
  • 1,000,000 per day for allowlisted accounts
  • 20 per day over all tokens (counter resets at 00:00 UTC)
  • 1,000,000 per day for allowlisted accounts
Exposure Notifications API versions supporting this mode Starting in v1.5. Starting in v1.

Data structures

The API contains the data classes described in the following list.

Name and description
Status This class contains codes that represent the state of the service on the device. For more information on how to use the information in this class, see Error handling.
ExposureConfiguration

(Legacy v1 mode)

In legacy v1 mode, the ExposureConfiguration class configures the behaviors of getExposureSummary() and ExposureInformation(). If relying on those functions, populate this configuration object and pass it to provideDiagnosisKeys().

Note: This configuration has no impact on ExposureWindows reported through getExposureWindows().

Configuration fields are as follows.

minimumRiskScore

ExposureSummary.getMaximumRiskScore(), ExposureSummary.getSummationRiskScore(), and ExposureInformation.getTotalRiskScore() do not use exposure incidents with scores lower than the value set in this field. Other returned fields are unaffected by this setting.

If a match occurs and the exposure's calculated risk score is less than or equal to the minimumRiskScore, the exposure's totalRiskScore is set to 0. Note that for the purposes of calculating ExposureSummary aggregate functions, the exposure is still considered a "match," and so is still included in the calculations of daysSinceLastExposure, matchedKeyCount, and so on.

attenuationScores An array in which you specify how much risk to associate with the Bluetooth attenuation value from an exposure.
daysSinceLastExposureScores An array in which you specify how much risk to associate to an exposure based on the number of days since the exposure occurred.
daysSinceLastExposureWeight The daysSinceLastExposureWeight field is reserved for future use.
durationScores An array in which you specify how much risk to associate with an exposure based on the duration of the exposure.
durationWeight The durationWeight field is reserved for future use
transmissionRiskScores An array of custom values that specify how much risk to associate with a risk factor you designate. For example, you might associate a custom value called highestRisk with a user who has recently tested positive, and a custom value called mediumRisk with a user who came into contact with a positively-diagnosed person.
durationAtAttenuationThresholds A two-value array representing the low and high attenuation thresholds to apply when calculating:
  • ExposureSummary.getAttenuationDurationsInMinutes
  • ExposureInformation.getAttenuationDurationsInMinutes

In legacy v1 mode, the following fields are used in the following calculation to determine a RiskScore between 0 and 4096, inclusive:

RiskScore = attenuationScore * daysSinceLastExposureScore * durationScore * transmissionRiskScore

TemporaryExposureKey

The TemporaryExposureKey class contains the following fields:

keyData Used to generate broadcasts that are collected on the other devices. These connect to provide a record of the interaction between two devices. This information remains on a device until and unless the user tests positive, at which point the user can choose to share that information with the internet-accessible server.
rollingStartNumber The time at which the key was generated, in 10-minute intervals since UTC epoch. This time will align to UTC midnight.
rollingPeriod The number of 10-minute intervals that a key is valid for. The expiration date for a key can be calculated by adding rollingPeriod to rollingStartNumber. For keys that are valid for a full day, this value will be equal to 144. If a key was expired early, the value will be less than 144.
reportType

(v1.5 and higher only)

Type of diagnosis that this key was uploaded with. We recommend that the app set this value before uploading each key to the server. The values range from 0 to 5 and have the following definitions. The app must handle all of these values without crashing if received from the server.
  • 0: UNKNOWN. Report type metadata is missing from the diagnosis key.
  • 1: CONFIRMED_TEST. A medical provider has confirmed the user had a positive diagnostic test for COVID-19.
  • 2: CONFIRMED_CLINICAL_DIAGNOSIS. A medical provider has confirmed the user had symptoms consistent with a COVID-19 diagnosis.
  • 3: SELF_REPORT. The user has self-reported symptoms consistent with COVID-19 without confirmation from a medical provider.
  • 4: RECURSIVE. This value is reserved for future use.
  • 5: REVOKED. In v1.5, REVOKED is not used. In v1.6 and higher, REVOKED eliminates exposures associated with that key from the detected exposures.

v1.6 supports changing the reportType value associated with a particular Temporary Exposure Key (TEK), allowing public health authorities (PHAs) to update reported cases to a more accurate state as more information is gathered. TEKs can go through a single report type transition, additional transitions will be rejected if more than one is attempted. The following transitions are supported:

  • SELF_REPORT -> CONFIRMED_TEST, CONFIRMED_CLINICAL_DIAGNOSIS, REVOKED
  • CONFIRMED_TEST -> CONFIRMED_CLINICAL_DIAGNOSIS, REVOKED

transmissionRiskLevel

(Legacy v1 mode)

In legacy v1 mode, specifies the level of risk of cross-exposure for the duration of the interaction between devices.

The following are suggested uses. If used, your app should set this value before uploading each key to the server.

  • 0: Unused
  • 1: Confirmed test - Low transmission risk level
  • 2: Confirmed test - Standard transmission risk level
  • 3: Confirmed test - High transmission risk level
  • 4: Confirmed clinical diagnosis
  • 5: Self report
  • 6: Negative case
  • 7: Recursive case
  • 8: Unused/custom

This value is unused in ExposureWindows. See Transmission risk for more information.

ExposureSummary

(Legacy v1 mode)

In legacy v1 mode, the ExposureSummary class summarizes the exposure to the set of keys associated with the same token. (This data is unavailable in ExposureWindow mode. Your app should instead compute this data from the ExposureWindow information.)

The ExposureSummary class contain the following fields.

daysSinceLastExposure The number of days since the last match to a diagnosis key.
matchedKeyCount The number of matched diagnosis keys.
maximumRiskScore The highest transmission risk level of all exposure incidents.
attenuationDurations

An array that contains the sum of all durations in minutes for all exposures for three ranges of radio signal attenuation:

  • Index 0: sum of durations for all exposures when attenuation was less than the low threshold.
  • Index 1: sum of durations for all exposures when attenuation was greater than or equal to the low threshold and less than the high threshold.
  • Index 2: sum of durations for all exposures when attenuation was greater than or equal to the high threshold.

The threshold values (low and high) are defined in the ExposureConfiguration object.

For more information on how attenuations are calculated, see the Bluetooth LE attenuation Overview.

summationRiskScore A summation of all risk scores of all exposures to keys provided with a given token.

Your app can present this information to users.

ExposureInformation

(Legacy v1 mode)

In legacy v1 mode, the ExposureInformation class stores information in the following fields about an interaction with another device:

dateMillisSinceEpoch The day that the interaction occurred.
durationMinutes The duration of the exposure in five-minute increments.
attenuationValue The time-weighted average of the attenuation.

For more information on how attenuations are calculated, see Bluetooth LE attenuation Overview.

transmissionRiskLevel A transmission risk value.
totalRiskScore

The total risk calculated for the exposure, represented as an integer between 0 and 4096, inclusive. See ExposureConfiguration for more information about the risk score.

Note that if the totalRiskScore is less than the ExposureConfiguration's minimumRiskScore, then totalRiskScore is zero.

You can use this information to gauge a level of risk of the exposure to filter out low-risk interactions, such as sub-five-minute interactions with a low signal strength attenuation that occurred 13 days earlier.

Each time this information is requested using getExposureInformation(), Google Play services displays a notification to the user.

ExposureWindow

(ExposureWindow mode)

The ExposureWindow class stores a duration of up to 30 minutes, during which beacons from a diagnosis key were observed. Each ExposureWindow corresponds to a single diagnosis key. However, one key can lead to several ExposureWindows due to the 30-minute cutoffs between exposure windows in a longer exposure, and the key itself is not exposed. See getExposureWindows() for more information.

ExposureWindows contain the following fields:

day Day of the exposure, using UTC, encapsulated as the time of the beginning of that day.
scanInstances Sightings of this ExposureWindow, time-ordered.

Each sighting corresponds to a scan of a few seconds, during which a beacon with the diagnosis key causing this exposure was observed.

reportType

Report type of the diagnosis key that caused this exposure. The app must handle the following values without crashing if received from the server.:

  • Keys with report type set to CONFIRMED_CLINICAL_DIAGNOSIS or SELF_REPORT are returned as is.
  • Keys with no report type set are returned with reportType=CONFIRMED_TEST.
  • Keys with a RECURSIVE report type may be dropped because this report type is reserved for future use.
  • Keys with REVOKED or invalid report types do not lead to exposures.

    Note: As of v1.5, REVOKED is not used.

infectiousness

This field is present in v1.6 and higher.

Infectiousness of the diagnosis key that caused this exposure (NONE,STANDARD or HIGH), computed from its days since onset of symptoms using a configurable mapping.

In v1.6, ExposureWindow objects with infectiousness=NONE may be returned.

Note:Because this value of infectiousness indicates no risk, a future version of the API may drop ExposureWindow objects with infectiousness=NONE.

calibrationConfidence

This field is present in v1.6 and higher.

Confidence of the BLE TX_power calibration of the transmitting device, defined as follows:
  • LOWEST: Used an Android-wide average calibration.
  • LOW: Used an average calibration over devices from the same manufacturer, or the beacon originates from a version of the Android Exposure Notifications API lower than 1.5.
  • MEDIUM: Used a calibration based on a single-antenna orientation for a similar device model, or the beacon comes from a version of the iOS Exposure Notification API lower than 1.5.
  • HIGH: Used a calibration determined using significant calibration data for this device model.

    This field is present in v1.6 and higher.

ScanInstance

(ExposureWindow mode)

In ExposureWindow mode, the ScanInstance class stores information about the sighting of beacons from a diagnosis key within a BLE scan (of a few seconds). The key itself is not exposed.

ScanInstances contain the following fields:

typicalAttenuation

Aggregation of the attenuations of all of a given diagnosis key's beacons received during the scan, in dB.

Currently, the aggregation applied is an average of the measurements in the dB domain.

minAttenuation Minimum attenuation of all of a given diagnosis key's beacons received during the scan, in dB.
secondsSinceLastScan

Seconds elapsed since the previous scan, coarsened to 60-second increments and capped at five minutes.

This value is typically used as a weight. For example:

  • Summing those values over all ScanInstances of an ExposureWindow provides the duration of that ExposureWindow.
  • Summing those values over all ScanInstances in a given attenuation range and over all exposures recreates the attenuationDurations of v1.

The previous scan may not have led to a sighting of that diagnosis key, or may not be part of the same ExposureWindow.

The coarsening is implemented in a way such that scans separated by 2.5 minutes will typically have secondsSinceLastScan alternating between 120 and 180.

DailySummariesConfig

(ExposureWindow mode)

The DailySummariesConfig provides the weights and thresholds to be applied to ExposureWindow data to compute the associated risk score. In this implementation, the computation depends on three factors: weighted minutes-at-attenuation, infectiousness, and reportType:

reportTypeWeights A map that stores a weight for each possible value of reportType.
infectiousnessWeights

A map that stores a weight for each possible value of infectiousness.

In v1.6, if a weight is specified for infectiousness=NONE, it should be 0 because NONE indicates no risk of exposure.

Note: A future version of the API may disregard a mapping value for infectiousness=NONE, because ExposureWindow with infectiousness=NONE may be dropped before computing the risk scores.

attenuationBucketThresholdDb Thresholds defining the BLE attenuation buckets edges. This list must have 3 elements: the immediate, near, and medium thresholds.
attenuationBucketWeights

Duration weights to associate with ScanInstances depending on the attenuation bucket in which their typicalAttenuation falls.

This list must have four elements, corresponding to the weights for the following buckets:
  • Immediate bucket: -infinity < attenuation <= immediate threshold
  • Near bucket: immediate threshold < attenuation <= near threshold
  • Medium bucket: near threshold < attenuation <= medium threshold
  • Other bucket: medium threshold < attenuation < +infinity

Each element must be between 0 and 2.5.

daysSinceExposureThreshold

(Planned for a future release but not implemented in v1.6)

Reserved for future use, behavior might change in future revisions. No value should be set, or else 0 should be used.
minimumWindowScore Minimum score that ExposureWindows must reach in order to be included in the ExposureSummaryData.
DailySummary

(ExposureWindow mode)

The DailySummary class stores a daily summary of the level of exposure to other devices in the following fields:

daysSinceEpoch UTC day of the ExposureWindow objects that went into this summary.
summaryData Summary of all exposures on this day.
summaryDataForReportType Summary of all exposures with a given report type on this day. The function takes the report type as an argument.

The data in a DailySummary is filtered based on the parameters in the DailySummariesConfig object provided when it was retrieved. You can use this information to gauge the level of risk of exposure for that day.

ExposureSummaryData

(ExposureWindow mode)

The ExposureSummaryData class represents a summary of the level of exposure to other devices associated with a set of ExposureWindow:

maximumScore Highest score of all ExposureWindow objects aggregated into this summary.
scoreSum Sum of scores for all ExposureWindow objects aggregated into this summary.
weightedDurationSum Sum of weighted durations for all ExposureWindow objects aggregated into this summary.

See DailySummariesConfig for more information about how the score for each ExposureWindow and weightedDurationSum are computed.

DiagnosisKeysDataMapping

(ExposureWindow mode)

Mappings from diagnosis keys data to exposure data concepts returned by the Exposure Notification API: infectiousness and reportType.

DiagnosisKeysDataMappings contain the following fields:

daysSinceOnsetToInfectiousness

A map from number of days since onset to an Infectiousness value.

infectiousnessWhenDaysSinceOnsetMissing Default Infectiousness value when the diagnosis keys don't include the daysSinceOnset field.
reportTypeWhenMissing Default ReportType value when the diagnosis keys don't include it.

Methods

The API provides functionality with the use of the methods shown in the following list. These are provided in a roughly chronological order of usage by an app.

To enable asynchronous operations, return-values from methods are wrapped in a Task object.

These methods might produce errors. Be sure to follow best practices and address errors as they occur. For more information about errors, see Error handling.

Name and description
start() Tells Google Play services to start the broadcast and scan process. The first time that this method is called after installation of the app or each time after stop() is called, it prompts Google Play services to display a dialog box, where the user is asked to give permission to broadcast and scan.
isEnabled() Indicates whether exposure notifications are enabled on the device. Note that this does not check whether Bluetooth or location settings are on.
stop() Disables the broadcast and scan process. You can call this directly. It's also called when a user uninstalls the app. When it’s called as part of the uninstallation process, the database and keys are deleted from the device.
deviceSupportsLocationlessScanning() Indicates whether the device supports locationless scanning.
getVersion() Returns a Long integer representing the version of the Exposure Notification module installed on the user device.
setDiagnosisKeysDataMapping() This function takes a DiagnosisKeysDataMapping object and sets the diagnosis keys data mapping if it wasn't already changed recently. If called twice within 7 days, the second call has no effect and raises an exception with status code FAILED_RATE_LIMITED.

In v1.6, these settings are applied at matching time, when calling provideDiagnosisKeys(). Changing the configuration via setDiagnosisKeysDataMapping() won't modify ExposureWindow objects for keys provided in past calls. Note: This behavior may change in a future version of the API.

getDiagnosisKeysDataMapping() This function returns the current DiagnosisKeysDataMapping.
getCalibrationConfidence() This function returns an int representing the calibration confidence value for rssi_offset. Higher value means higher confidence (see calibrationConfidence for possible values).
getTemporaryExposureKeyHistory()

Retrieves key history from the data store on the device to upload to your internet-accessible server. Calling this method prompts Google Play services to display a dialog that requests consent from the user to gather and upload their exposure keys.

The keys that are returned depend on the API version. These key include the past 14 days, but not the current day’s key. (This is different for allowlisted accounts, which also return the current day’s key.)

The consent granted by the user lasts for 24 hours. The dialog prompting for consent appears only once for every 24-hour period, regardless of how many times the app calls the method.

Planned for a future release but not currently activated in v1.5: The keys returned include the current day’s key, which is tagged with an expiration date (rollingPeriod). When the current key is released, the device stops using it to generate BLE beacons, and replaces it with a new key. Calling this causes a new key to be selected for the remainder of the current day. As a result, if this method is called multiple times, several keys with different rollingPeriod and identical rollingStartNumber can be released for those days it was called on.

provideDiagnosisKeys()

The provideDiagnosisKeys() method inserts one batch of files (legacy v1 mode) or one or more batches of files (ExposureWindow mode) containing key information into the on-device database.

These keys must correspond to confirmed cases retrieved from your internet-accessible server. Each batch (same start, country, and batch_count) must include the complete set of files in the batch, otherwise it discards all of them. Information about the file format is in Exposure Key export file format and verification.

When using the legacy v1 mode, this method accepts a token string used to identify a set of keys in future calls to the API. It effectively determines how keys accumulate over calls to provideDiagnosisKeys():

  • Keys provided with the same token accumulate into the same set, and are aged out of those sets as they pass out of the 14-day window.
  • Keys with different tokens are treated independently.

When using the ExposureWindow mode, all provided keys are accumulated under the ExposureWindows.

Calls to this method are limited to six per day for token=TOKEN_A and 20 per day when token != TOKEN_A (Allowlisted accounts are allowed 1,000,000 calls per day.)

This quota applies per call, not per file or batch of files uploaded. In other words, a single call with multiple files or multiple batches counts as only one call. Days are defined as midnight to midnight of a device's UTC.

Note: When downloading the files from the internet-accessible server, store them inside the app-specific directory on internal storage. Optionally, you can create a subdirectory under the filesDir and delete the subdirectory after you've finish processing.
getExposureSummary()

(Legacy v1 mode)

In legacy v1 mode, the getExposureSummary() method retrieves the ExposureSummary object that matches the token from provideDiagnosisKeys() that you provide to the method. The ExposureSummary object provides a high-level overview of the exposure that a user has experienced.

This method does not support token=TOKEN_A. Use getExposureWindows() instead.

There are no quotas for this method.

getExposureInformation()

(Legacy v1 mode)

In legacy v1 mode, the getExposureInformation() method provides a more in-depth version of the information provided by getExposureSummary(). If you pass in a token from provideDiagnosisKeys() for an exposure key, getExposureInformation() provides a list of ExposureInformation objects, from which you can gauge the level of risk of the exposure with the user.

Google Play services displays a notification to the user each time this method is invoked.

This method does not support token=TOKEN_A. Use getExposureWindows() instead.

There are no quotas for this method.

getExposureWindows()

(ExposureWindow mode)

The getExposureWindows() method retrieves the list of exposure windows corresponding to the keys given to provideDiagnosisKeys().

Because long exposures to one key are split into windows of up to 30 minutes of scans, a given key may lead to several exposure windows if beacon sightings for it spanned more than 30 minutes. The link between them (in other words, the fact that they all correspond to the same key) is lost because those windows are shuffled before being returned and the underlying keys are not exposed.

This method has no quota, and calling this method does NOT by itself trigger a user notification. However, information about how frequently the app called this function is included in the weekly summary notification.

getDailySummaries()

(ExposureWindow mode)

The getDailySummaries() method retrieves the daily summaries of exposure data.

This function returns a list of DailySummary objects for the last 14 days (or less,if specified in the DailySummariesConfig).

This function accepts a DailySummariesConfig object, which must contain the weights and thresholds desired to apply to the exposure data.

This method has no quota, and calling this method does NOT by itself trigger a user notification.

Broadcast receivers

Your app must register a receiver to receive broadcasts of the ACTION_EXPOSURE_STATE_UPDATED and ACTION_EXPOSURE_NOT_FOUND intents. After provideDiagnosisKeys() is called, if a match is found, the ACTION_EXPOSURE_STATE_UPDATED broadcast is sent; if no match is found, the ACTION_EXPOSURE_NOT_FOUND broadcast is sent. Present information to the user in response to the broadcasts as needed. The following code sample shows how to do this:

public class ExposureNotificationBroadcastReceiver extends BroadcastReceiver
{
  @Override
  public void onReceive(Context context, Intent intent)
  {
    String action = intent.getAction();
    if (ExposureNotificationClient.ACTION_EXPOSURE_STATE_UPDATED
        .equals(action))
      {
        // Handle exposure found action
      }
    else if (ExposureNotificationClient.ACTION_EXPOSURE_NOT_FOUND
        .equals(action))
      {
        // Handle exposure not found action
      }
  }
}

When you declare the receiver, you must add the com.google.android.gms.nearby.exposurenotification.EXPOSURE_CALLBACK permission to the receiver declaration in your Android manifest.

The following code snippet shows the entry for your manifest:

<receiver
    android:name=".exposurenotification.app.ExposureNotificationBroadcast"
    android:permission="com.google.android.gms.nearby.exposurenotification.EXPOSURE_CALLBACK">
    <intent-filter>
        <action android:name="com.google.android.gms.exposurenotification.ACTION_EXPOSURE_STATE_UPDATED" />
        <action android:name="com.google.android.gms.exposurenotification.ACTION_EXPOSURE_NOT_FOUND" />
    </intent-filter>
</receiver>

For more information about broadcasts, see Broadcasts overview.

Error handling

To handle the errors of method calls, do the following:

  1. Add a failure callback to the returned Task using Task.addOnFailureListener().
  2. Cast the returned object to the APIException class.
  3. Get the Status using the getStatus() method, which provides information used to troubleshoot.

The Status provides:

For example, the status might indicate that the user has not opted-in (startResolutionForResult() would trigger the consent dialog), the service isn’t running, or that there's insufficient storage on the device to complete an operation.

client.start().addOnFailureListener(new OnFailureListener() {
  @Override
  public void onFailure(@NonNull Exception exception) {
    if (exception instanceof ApiException) {
      Status status = ((ApiException) exception).getStatus();
      if (status.hasResolution()) {
        status.startResolutionForResult(activity, REQUEST_START_CODE);
      } else {
        // Consider handling the different ExposureNotificationStatusCodes
        handleStatus(status.getConnectionResult().getErrorCode());
      }
    }
  }
});

In the preceding code, if there is an ApiException with a possible resolution. You launch it and handle the onActivityResult() of the given activity, as shown in the following code:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  switch (requestCode) {
    case REQUEST_START_CODE:
      if (resultCode == Activity.RESULT_OK) {
        // resolution solved (for example, the user gave consent)
        // retry the action, for example start EN API again.
        startClient();
      } else {
        // resolution rejected or cancelled
      }
      break;
  }
}

In the case that the status was not recoverable, you could consider handling the connection result error code that can be mapped to the ExposureNotificationStatusCodes.

private void handleStatus(int errorCode) {
  switch (errorCode) {
    case ExposureNotificationStatusCodes.FAILED_RATE_LIMITED:
      // The client has been rate-limited for access to this API.
      break;
    case ExposureNotificationStatusCodes.FAILED_UNAUTHORIZED:
      // The client is unauthorized to access the APIs.
      break;
    case ExposureNotificationStatusCodes.FAILED_SERVICE_DISABLED:
      // The functionality was disabled by the user or the phone.
      break;
    case ExposureNotificationStatusCodes.FAILED_NOT_SUPPORTED:
      // The hardware capability of the device is not supported.
      break;
    case ExposureNotificationStatusCodes.FAILED_BLUETOOTH_DISABLED:
      // Bluetooth is disabled.
      break;
    default:
      // more status codes in ExposureNotificationStatusCodes class.
      break;
  }
}

Force-stop handling

On devices that have version v1.5 or higher of the Exposure Notifications framework, the framework can automatically start the active Exposure Notifications process by binding to a service when it is force-stopped. While the app remains in the background, this action ensures that the app can execute any periodic tasks or receive broadcast events.

To handle this functionality, rebuild your app against the v1.5 or higher API and ensure that you are rescheduling any periodic tasks when the application process starts.

Reference design

For examples that show how to integrate with the Exposure Notifications API, see the reference design source code hosted on GitHub.

Glossary

Allowlisted accounts
We have partnered with public health agencies for purposes of collaboration and testing of Exposure Notifications apps while they are in development. An allowlist grants specific permissions for these Google partners.
BLE beacons
Bluetooth Low Energy (BLE) beacons enable Bluetooth-equipped devices to share information when they're within range of each other.
Diagnosis key
The Temporary Exposure Key provided by the server that have been confirmed to have a positive diagnosis of COVID-19. It's used by the app to check for exposure.
Rotating Proximity Identifiers (RPIs)
A random ID derived from the device's Temporary Exposure Key. The RPI is generated on the device and a new RPI is generated every 10-20 minutes.
Temporary Exposure Key (TEK)
A key that's randomly generated once every 24 hours. It remains on the device for up to 14 days. In the event that there's a positive diagnosis of COVID-19, and upon permission granted from the user, these keys are provided to the app.
Report type (reportType)
Classifies the method of COVID-19 diagnosis for a given diagnosis key.
Transmission Risk (Legacy v1 mode)
In legacy v1 mode, a value defined by your app that specifies how much risk is associated with a given exposure based on a value of importance to your health authority. This enables you to add an additional, non-API-specified value to the computation of total risk.