/* * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "util/attributes.h" #include #include #define PEBBLE_BT_PAIRING_SERVICE_CONNECTION_STATUS_UUID PEBBLE_BT_UUID_EXPAND(1) #define PEBBLE_BT_PAIRING_SERVICE_TRIGGER_PAIRING_UUID PEBBLE_BT_UUID_EXPAND(2) #define PEBBLE_BT_PAIRING_SERVICE_GATT_MTU_UUID PEBBLE_BT_UUID_EXPAND(3) // Note: UUID 4 was used by the 3.14-rc Android App for V0 of the Connection Param characteristic // but never shipped externally #define PEBBLE_BT_PAIRING_SERVICE_CONNECTION_PARAMETERS_UUID PEBBLE_BT_UUID_EXPAND(5) typedef enum { PebblePairingServiceGATTError_UnknownCommandID = BLEGATTErrorApplicationSpecificErrorStart, PebblePairingServiceGATTError_ConnParamsInvalidRemoteDesiredState, PebblePairingServiceGATTError_ConnParamsMinSlotsTooSmall, PebblePairingServiceGATTError_ConnParamsMinSlotsTooLarge, PebblePairingServiceGATTError_ConnParamsMaxSlotsTooLarge, PebblePairingServiceGATTError_ConnParamsSupervisionTimeoutTooSmall, PebblePairingServiceGATTError_DeviceDoesNotSupportPLE, } PebblePairingServiceGATTError; //! The connectivity status, with respect to the device reading it. typedef struct PACKED { union { struct { //! true if the device that is reading the status is connected (always true) bool ble_is_connected:1; //! true if the device that is reading the status is bonded, false if not bool ble_is_bonded:1; //! true if the current LE link is encrypted, false if not bool ble_is_encrypted:1; //! true if the watch has a bonding to a gateway (LE-based). bool has_bonded_gateway:1; //! true if the watch supports writing the "Don't send slave security request" bit. //! See https://pebbletechnology.atlassian.net/wiki/display/DEV/Pebble+GATT+Services bool supports_pinning_without_security_request:1; //! true if the reversed ppogatt was enabled at the time of bonding bool is_reversed_ppogatt_enabled:1; //! Reserved, leave zero for future use. uint32_t rsvd:18; //! The error of the last pairing process or all zeroes, if no pairing process has completed //! or when there were no errors. Also see BT Spec 4.2, Vol 3, Part H, 3.5.5 Pairing Failed. uint8_t last_pairing_result; }; uint8_t bytes[4]; }; } PebblePairingServiceConnectivityStatus; _Static_assert(sizeof(PebblePairingServiceConnectivityStatus) == 4, ""); typedef struct PACKED { bool should_pin_address:1; //! @note Not available in Bluetopia/cc2564x implementation //! This flag and should_force_slave_security_request are mutually exclusive! bool no_slave_security_request:1; //! @note Not available in Bluetopia/cc2564x implementation //! This flag and no_slave_security_request are mutually exclusive! bool should_force_slave_security_request:1; //! @note Not available in Bluetopia/cc2564x implementation //! Flag to indicate that when re-pairing this device, the re-pairing should be accepted //! automatically for this remote device (matching IRK or matching identity address). //! @note This is a workaround for an Android 4.4.x bug. This opens up a security hole :( where //! a phone could pretend to be the "trusted" phone and pair w/o the user even knowing about it. //! @see https://pebbletechnology.atlassian.net/browse/PBL-39369 bool should_auto_accept_re_pairing:1; //! @note Not available in Bluetopia/cc2564x implementation //! Flag to indicate that the PPoGATT server/client roles should be reversed to support the //! connected phone. Some older Android phones' GATT service API is completely busted. For those //! poor phones, this bit is set before pairing. The Pebble includes a "reversed" PPoGATT service //! that the phone app can connect to as GATT client, but this service only works if this bit //! gets set *before pairing*. This is a security measure: 1. to prevent non-paired devices from //! talking to the "reversed" PPoGATT service. 2. to prevent non-Pebble apps on paired phone that //! does support normal PPoGATT from connecting to the "reversed" PPoGATT service. //! @see ppogatt_emulated_server_wa.c //! @see https://pebbletechnology.atlassian.net/browse/PBL-39634 bool is_reversed_ppogatt_enabled:1; } PairingTriggerRequestData; typedef struct PACKED { //! interval_min_ms / 1.25 msec – valid range: 7.5 msec to 4 seconds uint16_t interval_min_1_25ms; //! (interval_max_ms - interval_min_ms) / 1.25 msec //! @note To fit the parent struct in the minimum GATT MTU, this field is a delta and only one //! byte instead of the uint16_t that the BT spec uses. uint8_t interval_max_delta_1_25ms; //! Slave latency (in number of connection events) //! @note To fit the parent struct in the minimum GATT MTU, this field is only one byte instead //! of the uint16_t that the BT spec uses. uint8_t slave_latency_events; //! Supervision Timeout / 30 msec – valid range: 100 msec to 32 seconds. To fit this into one //! byte and to fit the parent struct in the minimum GATT MTU, the increments is not the standard //! 10msec! uint8_t supervision_timeout_30ms; } PebblePairingServiceConnParamSet; //! The connection parameters settings, with respect to connection to the device reading them. typedef struct PACKED PebblePairingServiceConnParamsReadNotif { //! Capability bits. Reserved for future use. uint8_t packet_length_extension_supported:1; uint8_t rsvd:7; //! Current interval / 1.25 msec – valid range: 7.5 msec to 4 seconds uint16_t current_interval_1_25ms; //! Current Slave latency (in number of connection events) – actual max is 0x01F3, but in //! practice values are much lower. uint16_t current_slave_latency_events; //! Current Supervision Timeout / 10 msec – valid range: 100 msec to 32 seconds. uint16_t current_supervision_timeout_10ms; } PebblePairingServiceConnParamsReadNotif; typedef enum PebblePairingServiceConnParamsWriteCmd { //! Allows phone to change connection parameter set and take over control of parameter management PebblePairingServiceConnParamsWriteCmd_SetRemoteParamMgmtSettings = 0x00, //! Issues a connection parameter change request if the watch is not in the desired state PebblePairingServiceConnParamsWriteCmd_SetRemoteDesiredState = 0x01, //! Controls settings for BLE 4.2 Packet Length Extension feature PebblePairingServiceConnParamsWriteCmd_EnablePacketLengthExtension = 0x02, //! If written to disables Dialog BLE sleep mode (safeguard against PBL-39777 in case it affects //! more watches in the future) PebblePairingServiceConnParamsWriteCmd_InhibitBLESleep = 0x03, PebblePairingServiceConnParamsWriteCmdCount, } PebblePairingServiceConnParamsWriteCmd; typedef struct PACKED PebblePairingServiceRemoteParamMgmtSettings { //! If false/zero, Pebble should manage the connection parameters. If true/one, Pebble should //! NOT manage the connection parameters. In this mode, Pebble will never request a //! connection parameter change. bool is_remote_device_managing_connection_parameters:1; uint8_t rsvd:7; //! Optional. Current parameters sets used by Pebble's Connection Parameter manager. PebblePairingServiceConnParamSet connection_parameter_sets[]; } PebblePairingServiceRemoteParamMgmtSettings; typedef struct PACKED PebblePairingServiceRemoteDesiredState { //! The desired ResponseTime as desired by the remote device. The remote end can set this //! value to a faster mode when it's about to transfer/receive a lot of data. For example, //! when a lot of BlobDB operations are queued up, the watch doesn't know how much data is //! queued up on the remote end. In this case, the remote could write "ResponseTimeMin" so //! increase the speed temporarily. It's the remote end's responsibility to reset this to //! ResponseTimeMax when the bulk transfer is done. As a safety measure, the watch is will //! reset it back to ResponseTimeMax after 5 minutes. In case the phone app still wants to //! keep a particular desired ResponseTime, the phone app is responsible for making sure to //! write the value again before the 5 minute timer expires. uint8_t state:2; uint8_t rsvd:6; } PebblePairingServiceRemoteDesiredState; typedef struct PACKED PebblePairingServicePacketLengthExtension { uint8_t trigger_ll_length_req:1; uint8_t rsvd:7; } PebblePairingServicePacketLengthExtension; typedef struct PACKED PebblePairingServiceInhibitBLESleep { uint8_t rsvd; // for future use } PebblePairingServiceInhibitBLESleep; //! The connection parameters settings, with respect to connection to the device writing them. typedef struct PACKED PebblePairingServiceConnParamsWrite { PebblePairingServiceConnParamsWriteCmd cmd:8; union PACKED { //! Valid iff cmd == PebblePairingServiceConnParamsWriteCmd_SetRemoteParamMgmtSettings PebblePairingServiceRemoteParamMgmtSettings remote_param_mgmt_settings; //! Valid iff cmd == PebblePairingServiceConnParamsWriteCmd_SetRemoteDesiredState PebblePairingServiceRemoteDesiredState remote_desired_state; //! Valid iff cmd == PebblePairingServiceConnParamsWriteCmd_EnablePacketLengthExtension PebblePairingServicePacketLengthExtension ple_req; //! Valid iff cmd == PebblePairingServiceConnParamsWriteCmd_InhibitBLESleep PebblePairingServiceInhibitBLESleep ble_sleep; }; } PebblePairingServiceConnParamsWrite; #define PEBBLE_PAIRING_SERVICE_REMOTE_PARAM_MGTM_SETTINGS_SIZE_WITH_PARAM_SETS \ (sizeof(PebblePairingServiceRemoteParamMgmtSettings) + \ (sizeof(PebblePairingServiceConnParamSet) * NumResponseTimeState)) #define PEBBLE_PAIRING_SERVICE_CONN_PARAMS_WRITE_SIZE_WITH_PARAM_SETS \ (offsetof(PebblePairingServiceConnParamsWrite, remote_param_mgmt_settings) + \ PEBBLE_PAIRING_SERVICE_REMOTE_PARAM_MGTM_SETTINGS_SIZE_WITH_PARAM_SETS) _Static_assert(NumResponseTimeState == 3, ""); _Static_assert(sizeof(PebblePairingServiceConnParamsReadNotif) <= 20, "Larger than minimum MTU!"); _Static_assert(PEBBLE_PAIRING_SERVICE_CONN_PARAMS_WRITE_SIZE_WITH_PARAM_SETS <= 20, "Larger than minimum MTU!"); _Static_assert(sizeof(PebblePairingServiceConnParamsWrite) <= 20, "Larger than minimum MTU!"); _Static_assert(sizeof(PebblePairingServiceConnectivityStatus) <= 20, "Larger than minimum MTU!"); typedef struct GAPLEConnection GAPLEConnection; //! Signals to the Pebble GATT service that status change has occurred (pairing, encryption, ...), //! allowing it to notify any BLE devices that are subscribed to connectivity status updates of the //! change. //! @param connection The connection for which the status was changed. void bt_driver_pebble_pairing_service_handle_status_change(const GAPLEConnection *connection); //! Signals to the Pebble GATT service the GATT MTU has changed, allowing it to notify any BLE //! devices that are subscribed to GATT MTU characteristic. //! @param connection The connection for which the GATT MTU has changed. //! @note the caller is responsible for taking bt_lock! void bt_driver_pebble_pairing_service_handle_gatt_mtu_change(const GAPLEConnection *connection); //! Indicate to the FW that Connectivity Status characteristic has been unsubscribed from. //! This is used to detect that the Pebble iOS app has been terminated. extern void bt_driver_cb_pebble_pairing_service_handle_ios_app_termination_detected(void); //! Indicate to the FW that the Connection Parameters characteristic has been written to with a new //! values. //! @param conn_params The value as written to the Connection Parameters characteristic. The BT //! driver lib is expected to validate any written values and only call this function with valid //! values. //! @param conn_params_length The length of conn_params in bytes. extern void bt_driver_cb_pebble_pairing_service_handle_connection_parameter_write( const BTDeviceInternal *device, const PebblePairingServiceConnParamsWrite *conn_params, size_t conn_params_length);