| /****************************************************************************** |
| |
| Copyright (c) 2020, The Linux Foundation. All rights reserved. |
| * |
| *****************************************************************************/ |
| |
| /****************************************************************************** |
| * |
| |
| * Copyright 2009-2013 Broadcom Corporation |
| * |
| * 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. |
| * |
| ******************************************************************************/ |
| |
| /****************************************************************************** |
| * |
| * This file contains the CSIP Client action functions. |
| * |
| ******************************************************************************/ |
| |
| #include <log/log.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| #include <base/bind.h> |
| #include <base/callback.h> |
| #include <vector> |
| #include <string> |
| |
| #include "bta_csip_int.h" |
| #include "bta_csip_api.h" |
| #include "bta_gatt_api.h" |
| #include "bta_gatt_queue.h" |
| #include "btm_api.h" |
| #include "btm_ble_api.h" |
| #include "btm_int.h" |
| #include "osi/include/osi.h" |
| #include "osi/include/properties.h" |
| #include "bta_dm_api.h" |
| #include "bta_dm_adv_audio.h" |
| |
| /* CSIS Service UUID */ |
| Uuid CSIS_SERVICE_UUID = Uuid::FromString("1846"); |
| /* CSIS Characteristic UUID's */ |
| Uuid CSIS_SERVICE_SIRK_UUID = Uuid::FromString("2B84"); |
| Uuid CSIS_SERVICE_SIZE_UUID = Uuid::FromString("2B85"); |
| Uuid CSIS_SERVICE_LOCK_UUID = Uuid::FromString("2B86"); |
| Uuid CSIS_SERVICE_RANK_UUID = Uuid::FromString("2B87"); |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_api_enable |
| * |
| * Description This function completes tasks to be done on BT ON |
| * |
| * Parameters: p_cback - callbacks from btif layer |
| * |
| ******************************************************************************/ |
| void bta_csip_api_enable(tBTA_CSIP_CBACK *p_cback) { |
| APPL_TRACE_DEBUG("%s", __func__); |
| |
| bta_csip_cb = tBTA_CSIP_CB(); |
| bta_csip_cb.p_cback = p_cback; |
| |
| // register with GATT CLient interface |
| bta_csip_gattc_register(); |
| |
| bta_csip_load_coordinated_sets_from_storage(); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_api_disable |
| * |
| * Description This function completes tasks to be done on BT OFF |
| * |
| * Parameters: None |
| * |
| ******************************************************************************/ |
| void bta_csip_api_disable() { |
| std::vector<tBTA_CSIP_DEV_CB> &dev_cb = bta_csip_cb.dev_cb; |
| |
| /* close all active GATT Connections */ |
| for (tBTA_CSIP_DEV_CB& p_cb: dev_cb) { |
| if (p_cb.state == BTA_CSIP_CONN_ST) { |
| BTA_GATTC_Close(p_cb.conn_id); |
| } |
| } |
| |
| /* Deregister GATT Interface */ |
| BTA_GATTC_AppDeregister(bta_csip_cb.gatt_if); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_app_register |
| * |
| * Description API used to register App/Module for CSIP callbacks. |
| * operation |
| * |
| * Parameters: app_uuid - Application UUID. |
| * p_cback - Application callbacks. |
| * cb - callback after registration. |
| ******************************************************************************/ |
| void bta_csip_app_register (const Uuid& app_uuid, tBTA_CSIP_CBACK* p_cback, |
| BtaCsipAppRegisteredCb cb) { |
| uint8_t i; |
| tBTA_CSIP_STATUS status = BTA_CSIP_FAILURE; |
| |
| for (i = 0; i < BTA_CSIP_MAX_SUPPORTED_APPS; i++) { |
| if (!bta_csip_cb.app_rcb[i].in_use) { |
| bta_csip_cb.app_rcb[i].in_use = true; |
| bta_csip_cb.app_rcb[i].app_id = i; |
| bta_csip_cb.app_rcb[i].p_cback = p_cback; |
| status = BTA_CSIP_SUCCESS; |
| break; |
| } |
| } |
| |
| if (status == BTA_CSIP_SUCCESS) { |
| LOG(INFO) << "CSIP App Registered Succesfully. App ID: " << +i; |
| } else { |
| LOG(ERROR) << "CSIP App Registration failed. App Limit reached"; |
| } |
| |
| // Give callback to registering App/Module |
| if (!cb.is_null()) cb.Run(status, i); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_app_unregister |
| * |
| * Description API used to unregister App/Module for CSIP callbacks. |
| * |
| * Parameters: app_id: ID of the application to be unregistered. |
| * |
| ******************************************************************************/ |
| void bta_csip_app_unregister(uint8_t app_id) { |
| if (app_id >= BTA_CSIP_MAX_SUPPORTED_APPS) { |
| LOG(ERROR) << __func__ << " Invalid App ID: " << +app_id; |
| return; |
| } |
| |
| bta_csip_cb.app_rcb[app_id].in_use = false; |
| bta_csip_cb.app_rcb[app_id].p_cback = NULL; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_gattc_callback |
| * |
| * Description This is GATT client callback function used in BTA CSIP. |
| * |
| * Parameters: event - received from GATT |
| * p_data - data associated with the event |
| * |
| ******************************************************************************/ |
| static void bta_csip_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) { |
| tBTA_CSIP_DEV_CB* p_dev_cb; |
| |
| APPL_TRACE_DEBUG("bta_csip_gattc_callback event = %d", event); |
| |
| if (p_data == NULL) return; |
| |
| switch (event) { |
| case BTA_GATTC_OPEN_EVT: |
| p_dev_cb = bta_csip_find_dev_cb_by_bda(p_data->open.remote_bda); |
| if (p_dev_cb) { |
| bta_csip_sm_execute(p_dev_cb, BTA_CSIP_GATT_OPEN_EVT, |
| (tBTA_CSIP_REQ_DATA*)&p_data->open); |
| } |
| break; |
| |
| case BTA_GATTC_CLOSE_EVT: |
| p_dev_cb = bta_csip_find_dev_cb_by_bda(p_data->close.remote_bda); |
| if (p_dev_cb) { |
| APPL_TRACE_DEBUG("BTA_GATTC_CLOSE_EVT state = %d", p_dev_cb->state); |
| bta_csip_sm_execute(p_dev_cb, BTA_CSIP_GATT_CLOSE_EVT, |
| (tBTA_CSIP_REQ_DATA*)&p_data->close); |
| } |
| break; |
| |
| case BTA_GATTC_SEARCH_CMPL_EVT: { |
| tBTA_GATTC_SEARCH_CMPL* p_srch_data = &p_data->search_cmpl; |
| tBTA_CSIP_DISC_SET disc_params = {.conn_id = p_srch_data->conn_id, |
| .status = p_srch_data->status |
| }; |
| p_dev_cb = bta_csip_get_dev_cb_by_cid(p_srch_data->conn_id); |
| if (p_dev_cb) { |
| disc_params.addr = p_dev_cb->addr; |
| p_dev_cb->is_disc_external = true; |
| } |
| bta_csip_gatt_disc_cmpl_act(&disc_params); |
| bta_csip_sm_execute(p_dev_cb, BTA_CSIP_OPEN_CMPL_EVT, NULL); |
| } |
| break; |
| |
| case BTA_GATTC_NOTIF_EVT: { |
| bta_csip_handle_notification(&p_data->notify); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_gattc_register |
| * |
| * Description API used to register GATT interface for CSIP operations. |
| * |
| * Parameters: None |
| * |
| ******************************************************************************/ |
| void bta_csip_gattc_register() { |
| APPL_TRACE_DEBUG("%s", __func__); |
| |
| BTA_GATTC_AppRegister(bta_csip_gattc_callback, |
| base::Bind([](uint8_t client_id, uint8_t status) { |
| tBTA_CSIP_STATUS csip_status = BTA_CSIP_FAILURE; |
| if (status == GATT_SUCCESS) { |
| bta_csip_cb.gatt_if = client_id; |
| csip_status = BTA_CSIP_SUCCESS; |
| } else { |
| bta_csip_cb.gatt_if = BTA_GATTS_INVALID_IF; |
| } |
| |
| /* BTA_GATTC_AppRegister is done */ |
| if (bta_csip_cb.p_cback) { |
| LOG(INFO) << "CSIP GATT IF : " |
| << +bta_csip_cb.gatt_if; |
| } |
| }), true); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_process_set_lock_act |
| * |
| * Description This function processes lock/unlock request. |
| * |
| * Parameters: lock_param: params used in LOCK/UNLOCK request. |
| * |
| ******************************************************************************/ |
| void bta_csip_process_set_lock_act(tBTA_SET_LOCK_PARAMS lock_param) { |
| LOG(INFO) << __func__ << ": App ID = " << +lock_param.app_id |
| << ", Set ID = " << +lock_param.set_id |
| << ", Value = " << +lock_param.lock_value; |
| |
| tBTA_CSET_CB* cset_cb = bta_csip_get_cset_cb_by_id (lock_param.set_id); |
| if (!cset_cb || !bta_csip_is_valid_lock_request(&lock_param)) { |
| tBTA_LOCK_STATUS_CHANGED res = {.app_id = lock_param.app_id, |
| .set_id = lock_param.set_id, |
| .status = INVALID_REQUEST_PARAMS}; |
| bta_csip_send_lock_req_cmpl_cb(res); |
| return; |
| } |
| |
| // Add request in the queue if one is already in progress for this set |
| if (cset_cb->request_in_progress) { |
| cset_cb->lock_req_queue.push(lock_param); |
| LOG(INFO) << __func__ << " pending lock requests in queue for Set:" |
| << +lock_param.set_id |
| << " Pending Requests = " << +(int)cset_cb->lock_req_queue.size(); |
| return; |
| } |
| |
| bta_csip_form_lock_request(lock_param, cset_cb); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_process_set_lock_act |
| * |
| * Description This function forms request (LOCK/UNLOCK and order of the |
| * set members). |
| * |
| * Parameters: lock_param: params used in LOCK/UNLOCK request. |
| * cset_cb: current set control block. |
| * |
| ******************************************************************************/ |
| void bta_csip_form_lock_request(tBTA_SET_LOCK_PARAMS lock_param, |
| tBTA_CSET_CB* cset_cb) { |
| cset_cb->request_in_progress = true; |
| |
| std::vector<RawAddress> ordered_members; |
| |
| if (lock_param.lock_value == LOCK_VALUE) { |
| ordered_members = bta_csip_arrange_set_members_by_order(cset_cb->set_id, |
| lock_param.members_addr, true); |
| } else { |
| ordered_members = bta_csip_arrange_set_members_by_order(cset_cb->set_id, |
| lock_param.members_addr, false); |
| } |
| |
| //debug log |
| for (int i = 0; i < (int)ordered_members.size(); i++) { |
| APPL_TRACE_DEBUG("%s: Member %d = %s", __func__, (i+1), |
| ordered_members[i].ToString().c_str()); |
| } |
| |
| // update current request in CB |
| cset_cb->cur_lock_req = {lock_param.app_id, lock_param.set_id, lock_param.lock_value, |
| 0, ordered_members}; |
| |
| // update current response in CB |
| cset_cb->cur_lock_res = {}; |
| cset_cb->cur_lock_res.app_id = lock_param.app_id; |
| cset_cb->cur_lock_res.set_id = lock_param.set_id; |
| cset_cb->cur_lock_res.value = UNLOCK_VALUE; |
| |
| /* LOCK Request */ |
| if (cset_cb->cur_lock_req.value == LOCK_VALUE) { |
| cset_cb->cur_lock_res.status = ALL_LOCKS_ACQUIRED; |
| /* check if lock request for this set was denied earlier */ |
| if (bta_csip_validate_req_for_denied_sm(cset_cb)) { |
| bta_csip_get_next_lock_request(cset_cb); |
| |
| /* proceed with request otherwise*/ |
| } else { |
| bta_csip_send_lock_req_act(cset_cb); |
| } |
| /* UNLOCK Request*/ |
| } else { |
| bta_csip_send_unlock_req_act(cset_cb); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_validate_req_for_denied_sm |
| * |
| * Description This function validates request if received for denied set |
| * member |
| * |
| * Parameters: cset_cb: current set control block. |
| * |
| ******************************************************************************/ |
| bool bta_csip_validate_req_for_denied_sm (tBTA_CSET_CB* cset_cb) { |
| bool is_denied = false; |
| tBTA_LOCK_REQUEST& lock_req = cset_cb->cur_lock_req; |
| |
| for (RawAddress& addr: lock_req.members_addr) { |
| tBTA_CSIP_DEV_CB* p_cb = bta_csip_find_dev_cb_by_bda(addr); |
| if (!p_cb) { |
| APPL_TRACE_ERROR("%s: Device CB not found for %s", __func__, |
| addr.ToString().c_str()); |
| continue; |
| } |
| |
| tBTA_CSIS_SRVC_INFO* srvc = bta_csip_get_csis_instance(p_cb, lock_req.set_id); |
| if (!srvc) { |
| APPL_TRACE_ERROR("%s: CSIS instance not found for %s", __func__, |
| addr.ToString().c_str()); |
| continue; |
| } |
| |
| if (!srvc->denied_applist.empty()) { |
| is_denied = true; |
| // add this app_id in the denied_app_list |
| srvc->denied_applist.push_back(lock_req.app_id); |
| cset_cb->cur_lock_res.status = LOCK_DENIED; |
| cset_cb->cur_lock_res.addr.push_back(addr); |
| } |
| } |
| |
| if (is_denied) { |
| bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res); |
| } |
| |
| return is_denied; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_lock_release_by_denial_cb |
| * |
| * Description This callback function is called when set member is unlocked |
| * after unlock request is sent post lock denial. |
| * |
| * Parameters: GATT operation callback params. |
| * |
| ******************************************************************************/ |
| void bta_csip_lock_release_by_denial_cb(uint16_t conn_id, tGATT_STATUS status, |
| uint16_t handle, void* data) { |
| tBTA_CSET_CB* cset_cb = (tBTA_CSET_CB *)data; |
| tBTA_CSIP_DEV_CB* dev_cb = cset_cb->cur_dev_cb; |
| tBTA_CSIS_SRVC_INFO* srvc = |
| bta_csip_get_csis_instance(cset_cb->cur_dev_cb, cset_cb->cur_lock_req.set_id); |
| |
| LOG(INFO) << __func__ << " Released lock for device: " << dev_cb->addr |
| << ", Set ID: " << +cset_cb->set_id; |
| |
| if (srvc && status == GATT_SUCCESS) { |
| srvc->lock = UNLOCK_VALUE; |
| // remove app id from applist |
| srvc->lock_applist.erase(std::remove(srvc->lock_applist.begin(), srvc->lock_applist.end(), |
| cset_cb->cur_lock_req.app_id), srvc->lock_applist.end()); |
| } |
| |
| // release next set member with lower rank |
| bta_csip_handle_lock_denial(cset_cb); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_handle_lock_denial |
| * |
| * Description This function is called when lock has been denied by one of |
| * the set members. |
| * |
| * Parameters: cset_cb: current set control block. |
| * |
| ******************************************************************************/ |
| void bta_csip_handle_lock_denial(tBTA_CSET_CB* cset_cb) { |
| // start lock release procedure for acquired locks |
| int8_t cur_idx = cset_cb->cur_lock_req.cur_idx - 1; |
| cset_cb->cur_lock_req.cur_idx--; |
| |
| if (cur_idx >= 0) { |
| RawAddress bd_addr = cset_cb->cur_lock_req.members_addr[cur_idx]; |
| cset_cb->cur_dev_cb = bta_csip_find_dev_cb_by_bda(bd_addr); |
| tBTA_CSIS_SRVC_INFO* srvc = |
| bta_csip_get_csis_instance(cset_cb->cur_dev_cb, cset_cb->cur_lock_req.set_id); |
| |
| // check if locked by other app |
| if (!cset_cb->cur_dev_cb || !srvc || |
| bta_csip_is_locked_by_other_apps(srvc, cset_cb->cur_lock_req.app_id)) { |
| LOG(INFO) << "Invalid device or service CB or" |
| << " other apps have locked this set member(" << bd_addr << "). Skip."; |
| bta_csip_handle_lock_denial(cset_cb); |
| return; |
| } |
| |
| // Lock value in vector format (one uint8_t size element with ) |
| std::vector<uint8_t> unlock_value(1, UNLOCK_VALUE); |
| BtaGattQueue::WriteCharacteristic(cset_cb->cur_dev_cb->conn_id, |
| srvc->lock_handle, unlock_value, GATT_WRITE, bta_csip_lock_release_by_denial_cb, cset_cb); |
| |
| } else { |
| bta_csip_get_next_lock_request(cset_cb); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_lock_req_cb |
| * |
| * Description This callback function is called when set member is locked |
| * by remote device after LOCK Request |
| * |
| * Parameters: GATT operation callback params. |
| * |
| ******************************************************************************/ |
| void bta_csip_lock_req_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| void* data) { |
| LOG(INFO) << __func__ << " status = " << +status; |
| tBTA_CSET_CB* cset_cb = (tBTA_CSET_CB *)data; |
| tBTA_CSIP_DEV_CB* dev_cb = bta_csip_get_dev_cb_by_cid(conn_id); |
| tBTA_CSIS_SRVC_INFO* srvc = |
| bta_csip_get_csis_instance(dev_cb, cset_cb->set_id); |
| |
| /* Device control block or corresponding CSIS service instance not found */ |
| if (!dev_cb || !srvc) { |
| APPL_TRACE_ERROR("%s: Device CB not found for conn_id = %d", __func__, conn_id); |
| cset_cb->cur_lock_req.cur_idx++; |
| alarm_cancel(cset_cb->unresp_timer); |
| bta_csip_send_lock_req_act(cset_cb); |
| return; |
| } |
| |
| /*check if this response is received from unresponsive set member */ |
| if (dev_cb->unresponsive) { |
| LOG(INFO) << __func__ << " unresponsive remote: " << dev_cb->addr; |
| bta_csip_handle_unresponsive_sm_res(srvc, status); |
| dev_cb->unresponsive = false; |
| return; |
| } |
| // cancel alarm (used for unresponsive set member) |
| alarm_cancel(cset_cb->unresp_timer); |
| |
| if (status == CSIP_LOCK_DENIED) { |
| LOG(INFO) << __func__ << " Locked Denied by " << dev_cb->addr; |
| srvc->lock = UNLOCK_VALUE; |
| cset_cb->cur_lock_res.value = UNLOCK_VALUE; |
| cset_cb->cur_lock_res.status = LOCK_DENIED; |
| |
| // add member to the response list for which lock is denied and clear others |
| cset_cb->cur_lock_res.addr.clear(); |
| cset_cb->cur_lock_res.addr.push_back(dev_cb->addr); |
| |
| // add app_id in the denied applist |
| srvc->denied_applist.push_back(cset_cb->cur_lock_req.app_id); |
| // Give callback to upper layer that lock is denied |
| bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res); |
| // release all the acquired locks till now |
| bta_csip_handle_lock_denial(cset_cb); |
| |
| /* PTS: Remote responding with invalid value */ |
| } else if (status == CSIP_INVALID_LOCK_VALUE) { |
| LOG(ERROR) << __func__ << " remote " << dev_cb->addr |
| << " responded with INVALID Value"; |
| /* for PTS to ensure set coordinator is working fine */ |
| BtaGattQueue::ReadCharacteristic(cset_cb->cur_dev_cb->conn_id, |
| srvc->lock_handle, NULL, NULL); |
| |
| /* Stop locking remaining set members and inform requesting app */ |
| bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res); |
| /* Process next lock request from pending queue */ |
| bta_csip_get_next_lock_request(cset_cb); |
| } else { |
| if (status == GATT_SUCCESS || status == CSIP_LOCK_ALREADY_GRANTED) { |
| LOG(INFO) << __func__ << " successfully locked " << dev_cb->addr; |
| cset_cb->cur_lock_res.addr.push_back(dev_cb->addr); |
| cset_cb->cur_lock_res.value = LOCK_VALUE; |
| srvc->lock = LOCK_VALUE; |
| // add app_id against this device entry |
| srvc->lock_applist.push_back(cset_cb->cur_lock_req.app_id); |
| } |
| |
| //proceed with next set member |
| cset_cb->cur_lock_req.cur_idx++; |
| bta_csip_send_lock_req_act(cset_cb); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_send_lock_req_act |
| * |
| * Description This function is is used to send LOCK request to set member. |
| * It validates if it is required to send lock request based on |
| * connection state and current lock value. |
| * |
| * Parameters: cset_cb: current set control block. |
| * |
| ******************************************************************************/ |
| void bta_csip_send_lock_req_act(tBTA_CSET_CB* cset_cb) { |
| RawAddress bd_addr; |
| uint8_t cur_index = cset_cb->cur_lock_req.cur_idx; |
| |
| if (cur_index == (uint8_t)cset_cb->cur_lock_req.members_addr.size()) { |
| LOG(INFO) << __func__ << " lock operation completed for all set members"; |
| bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res); |
| bta_csip_get_next_lock_request(cset_cb); |
| return; |
| } |
| |
| bd_addr = cset_cb->cur_lock_req.members_addr[cur_index]; |
| |
| // get device control block and corresponding csis service details |
| cset_cb->cur_dev_cb = bta_csip_find_dev_cb_by_bda(bd_addr); |
| tBTA_CSIS_SRVC_INFO* srvc = |
| bta_csip_get_csis_instance(cset_cb->cur_dev_cb, cset_cb->cur_lock_req.set_id); |
| |
| // Skip device if it is not in connected state |
| if (!cset_cb->cur_dev_cb || !srvc || |
| cset_cb->cur_dev_cb->state != BTA_CSIP_CONN_ST) { |
| LOG(INFO) << __func__ << ": Set Member (" << bd_addr.ToString() |
| << ") is not connected. Skip this Set member"; |
| |
| cset_cb->cur_lock_req.cur_idx++; |
| cset_cb->cur_lock_res.status = SOME_LOCKS_ACQUIRED_REASON_DISC; |
| bta_csip_send_lock_req_act(cset_cb); |
| |
| // check if already locked (skip sending write request) |
| } else if (srvc->lock == LOCK_VALUE) { |
| LOG(INFO) << __func__ << ": Set Member (" << cset_cb->cur_dev_cb->addr |
| << ") is already locked. Skip this Set member"; |
| cset_cb->cur_lock_res.value = LOCK_VALUE; |
| // add element in the list |
| cset_cb->cur_lock_res.addr.push_back(bd_addr); |
| // add appid in the list if locked by different app |
| if (!bta_csip_is_member_locked_by_app(cset_cb->cur_lock_req.app_id, srvc)) { |
| srvc->lock_applist.push_back(cset_cb->cur_lock_req.app_id); |
| } |
| cset_cb->cur_lock_req.cur_idx++; |
| // process next set member |
| bta_csip_send_lock_req_act(cset_cb); |
| |
| // send the lock request |
| } else { |
| // Lock value in vector format (one uint8_t size element with ) |
| LOG(INFO) << __func__ << " Sending Lock Request to "<< cset_cb->cur_dev_cb->addr |
| << " Conn Id: " << +cset_cb->cur_dev_cb->conn_id; |
| std::vector<uint8_t> lock_value = {2}; |
| BtaGattQueue::WriteCharacteristic(cset_cb->cur_dev_cb->conn_id, |
| srvc->lock_handle, lock_value, GATT_WRITE, bta_csip_lock_req_cb, cset_cb); |
| |
| // Start set member request timeout alarm |
| cset_cb->unresp_timer = alarm_new("csip_unresp_sm_timer"); |
| alarm_set_on_mloop(cset_cb->unresp_timer, cset_cb->set_member_tout, |
| bta_csip_set_member_lock_timeout, cset_cb); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_unlock_req_cb |
| * |
| * Description This callback function is called when set member is unlocked |
| * by remote device after UNLOCK Request |
| * |
| * Parameters: GATT operation callback params. |
| * |
| ******************************************************************************/ |
| void bta_csip_unlock_req_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| void* data) { |
| LOG(INFO) << __func__ << " status = " << +status; |
| tBTA_CSET_CB* cset_cb = (tBTA_CSET_CB *)data; |
| tBTA_CSIP_DEV_CB* dev_cb = bta_csip_get_dev_cb_by_cid(conn_id); |
| tBTA_CSIS_SRVC_INFO* srvc = |
| bta_csip_get_csis_instance(dev_cb, cset_cb->cur_lock_req.set_id); |
| |
| /* Device control block or corresponding CSIS service instance not found */ |
| if (!dev_cb || !srvc) { |
| APPL_TRACE_ERROR("%s: Device CB not found for conn_id = %d", __func__, conn_id); |
| cset_cb->cur_lock_req.cur_idx++; |
| alarm_cancel(cset_cb->unresp_timer); |
| bta_csip_send_unlock_req_act(cset_cb); |
| return; |
| } |
| |
| /*check if this response is received from unresponsive set member */ |
| if (dev_cb->unresponsive) { |
| LOG(INFO) << __func__ << " unresponsive remote: " << dev_cb->addr; |
| srvc->lock = UNLOCK_VALUE; |
| srvc->unrsp_applist.clear(); |
| dev_cb->unresponsive = false; |
| return; |
| } |
| |
| // cancel alarm (used for unresponsive set member) |
| alarm_cancel(cset_cb->unresp_timer); |
| |
| /* PTS Test Case: read any characteristic */ |
| if (status == CSIP_LOCK_RELEASE_NOT_ALLOWED || |
| status == CSIP_INVALID_LOCK_VALUE) { |
| /* for PTS to ensure set coordinator is working fine */ |
| BtaGattQueue::ReadCharacteristic(cset_cb->cur_dev_cb->conn_id, |
| srvc->lock_handle, NULL, NULL); |
| |
| /* Stop unlocking remaining set members and inform requesting app */ |
| cset_cb->cur_lock_res.status = status; |
| bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res); |
| /* Process next lock request from pending queue */ |
| bta_csip_get_next_lock_request(cset_cb); |
| } else { |
| if (status == GATT_SUCCESS) { |
| srvc->lock = UNLOCK_VALUE; |
| // remove app id from applist |
| srvc->lock_applist.erase(std::remove(srvc->lock_applist.begin(), |
| srvc->lock_applist.end(), cset_cb->cur_lock_req.app_id), |
| srvc->lock_applist.end()); |
| cset_cb->cur_lock_res.addr.push_back(dev_cb->addr); |
| } |
| //proceed with next set member |
| cset_cb->cur_lock_req.cur_idx++; |
| bta_csip_send_unlock_req_act(cset_cb); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_send_unlock_req_act |
| * |
| * Description This function is is used to send UNLOCK request to set member. |
| * It validates if it is required to send unlock request based on |
| * connection state and current lock value. |
| * |
| * Parameters: cset_cb: current set control block. |
| * |
| ******************************************************************************/ |
| void bta_csip_send_unlock_req_act(tBTA_CSET_CB* cset_cb) { |
| RawAddress bd_addr; |
| uint8_t cur_index = cset_cb->cur_lock_req.cur_idx; |
| |
| if (cur_index == (uint8_t)cset_cb->cur_lock_req.members_addr.size()) { |
| cset_cb->request_in_progress = false; |
| bta_csip_send_lock_req_cmpl_cb(cset_cb->cur_lock_res); |
| bta_csip_get_next_lock_request(cset_cb); |
| LOG(INFO) << __func__ << " Request completed for all set members"; |
| return; |
| } |
| |
| bd_addr = cset_cb->cur_lock_req.members_addr[cur_index]; |
| |
| LOG(INFO) << __func__ << ": Set Member address: " << bd_addr.ToString(); |
| |
| // get device control block and corresponding csis service details |
| cset_cb->cur_dev_cb = bta_csip_find_dev_cb_by_bda(bd_addr); |
| tBTA_CSIS_SRVC_INFO* srvc = |
| bta_csip_get_csis_instance(cset_cb->cur_dev_cb, cset_cb->cur_lock_req.set_id); |
| |
| /* Device control block or corresponding CSIS service instance not found */ |
| if (!cset_cb->cur_dev_cb || !srvc) { |
| APPL_TRACE_ERROR("%s: Device CB not found for %s", __func__, |
| bd_addr.ToString().c_str()); |
| cset_cb->cur_lock_req.cur_idx++; |
| bta_csip_send_unlock_req_act(cset_cb); |
| return; |
| } |
| |
| /* Set member is not locked by requesting app */ |
| if (!bta_csip_is_member_locked_by_app(cset_cb->cur_lock_req.app_id, srvc)) { |
| LOG(INFO) << __func__ << " App "<< +cset_cb->cur_lock_req.app_id |
| << "has not locked this set member (" << srvc->bd_addr |
| << "). Skip this set member"; |
| cset_cb->cur_lock_req.cur_idx++; |
| bta_csip_send_unlock_req_act(cset_cb); |
| return; |
| } |
| |
| // Skip device if it is not in connected state |
| if (cset_cb->cur_dev_cb->state != BTA_CSIP_CONN_ST) { |
| LOG(INFO) << __func__ << ": Set Member (" << bd_addr.ToString() |
| << ") is not connected. Skip this Set member"; |
| |
| cset_cb->cur_lock_req.cur_idx++; |
| // remove app id from applist |
| srvc->lock_applist.erase(std::remove(srvc->lock_applist.begin(), srvc->lock_applist.end(), |
| cset_cb->cur_lock_req.app_id), srvc->lock_applist.end()); |
| bta_csip_send_unlock_req_act(cset_cb); |
| |
| // check if already unlocked or locked by multiple apps (skip sending write request) |
| } else if (srvc->lock == UNLOCK_VALUE || |
| bta_csip_is_locked_by_other_apps(srvc, cset_cb->cur_lock_req.app_id)) { |
| LOG(INFO) << __func__ << ": Set Member (" << bd_addr.ToString() |
| << ") is already unlocked or locked by other app. Skip this Set member"; |
| // remove app id from applist |
| srvc->lock_applist.erase(std::remove(srvc->lock_applist.begin(), srvc->lock_applist.end(), |
| cset_cb->cur_lock_req.app_id), srvc->lock_applist.end()); |
| cset_cb->cur_lock_req.cur_idx++; |
| cset_cb->cur_lock_res.addr.push_back(cset_cb->cur_dev_cb->addr); |
| // process next set member |
| bta_csip_send_unlock_req_act(cset_cb); |
| |
| // send the unlock request |
| } else { |
| // Unlock value in vector format (one uint8_t size element with unlock value) |
| std::vector<uint8_t> lock_value(1, UNLOCK_VALUE); |
| BtaGattQueue::WriteCharacteristic(cset_cb->cur_dev_cb->conn_id, |
| srvc->lock_handle, lock_value, GATT_WRITE, bta_csip_unlock_req_cb, cset_cb); |
| |
| // Start set member request timeout alarm |
| cset_cb->unresp_timer = alarm_new("csip_unresp_sm_timer"); |
| alarm_set_on_mloop(cset_cb->unresp_timer, cset_cb->set_member_tout, |
| bta_csip_set_member_lock_timeout, cset_cb); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_set_member_lock_timeout |
| * |
| * Description This API is called when Set Member has not responded within |
| * required set member lock timeout. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_set_member_lock_timeout(void* p_data) { |
| tBTA_CSET_CB* cset_cb = (tBTA_CSET_CB *)p_data; |
| tBTA_CSIP_DEV_CB* dev_cb = cset_cb->cur_dev_cb; |
| |
| APPL_TRACE_DEBUG("%s", __func__); |
| // Device not found or disconnected |
| if (!dev_cb || dev_cb->state != BTA_CSIP_CONN_ST) { |
| LOG(ERROR) << __func__ << " device disconnected."; |
| cset_cb->cur_lock_res.status = SOME_LOCKS_ACQUIRED_REASON_DISC; |
| cset_cb->cur_lock_req.cur_idx++; |
| bta_csip_send_lock_req_act(cset_cb); |
| return; |
| } |
| |
| tBTA_CSIS_SRVC_INFO* srvc = |
| bta_csip_get_csis_instance(cset_cb->cur_dev_cb, cset_cb->cur_lock_req.set_id); |
| |
| if (!srvc) { |
| APPL_TRACE_ERROR("%s: CSIS instance not found.", __func__); |
| return; |
| } |
| |
| dev_cb->unresponsive = true; |
| // add app_id in unresponsive set members app list |
| srvc->unrsp_applist.push_back(cset_cb->cur_lock_res.app_id); |
| |
| if (cset_cb->cur_lock_req.value == LOCK_VALUE) { |
| cset_cb->cur_lock_res.status = SOME_LOCKS_ACQUIRED_REASON_TIMEOUT; |
| APPL_TRACE_DEBUG("%s: Process next device in the lock request", __func__); |
| cset_cb->cur_lock_req.cur_idx++; |
| bta_csip_send_lock_req_act(cset_cb); |
| } else if (cset_cb->cur_lock_req.value == UNLOCK_VALUE) { |
| cset_cb->cur_lock_res.addr.push_back( |
| cset_cb->cur_lock_req.members_addr[cset_cb->cur_lock_req.cur_idx]); |
| cset_cb->cur_lock_req.cur_idx++; |
| bta_csip_send_unlock_req_act(cset_cb); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_le_encrypt_cback |
| * |
| * Description link encryption complete callback. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_le_encrypt_cback(const RawAddress* bd_addr, |
| UNUSED_ATTR tGATT_TRANSPORT transport, |
| UNUSED_ATTR void* p_ref_data, tBTM_STATUS result) { |
| APPL_TRACE_ERROR("%s: status = %d", __func__, result); |
| tBTA_CSIP_DEV_CB* p_cb = bta_csip_find_dev_cb_by_bda(*bd_addr); |
| |
| if (!p_cb) { |
| APPL_TRACE_ERROR("unexpected encryption callback, ignore"); |
| return; |
| } |
| |
| /* If encryption fails, disconnect the connection */ |
| if (result != BTM_SUCCESS) { |
| bta_csip_close_csip_conn(p_cb); |
| return; |
| } |
| |
| if (p_cb->state == BTA_CSIP_W4_SEC) { |
| bta_csip_sm_execute(p_cb, BTA_CSIP_ENC_CMPL_EVT, NULL); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_open_act |
| * |
| * Description API Call to open CSIP Gatt Connection |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_api_open_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) { |
| APPL_TRACE_DEBUG("%s: Open GATT connection for CSIP", __func__); |
| |
| tBTA_CSIP_API_CONN* p_conn_req = (tBTA_CSIP_API_CONN *)&p_data->conn_param; |
| |
| if (!bta_csip_is_app_reg(p_conn_req->app_id)) { |
| LOG(ERROR) << __func__ << ": Request from Invalid/Unregistered App: " |
| << +p_conn_req->app_id; |
| if (p_cb && (uint8_t)p_cb->conn_applist.size() == 0) { |
| p_cb->state = BTA_CSIP_IDLE_ST; |
| } |
| // No need to send callback to invalid/unregistered app |
| return; |
| } |
| |
| if (btm_sec_is_a_bonded_dev(p_cb->addr) && !bta_csip_is_csis_supported(p_cb)) { |
| APPL_TRACE_DEBUG("%s: Remote (%s) doesnt contain any coordinated set", __func__, |
| p_cb->addr.ToString().c_str()); |
| bta_csip_send_conn_state_changed_cb(p_cb, p_conn_req->app_id, |
| BTA_CSIP_DISCONNECTED, BTA_CSIP_COORDINATED_SET_NOT_SUPPORTED); |
| return; |
| } |
| |
| if (!p_cb) { |
| LOG(ERROR) << __func__ << ": Insufficient resources. Max" |
| " supported Set members have reached "; |
| tBTA_CSIP_DEV_CB invalid_cb = { |
| .addr = p_data->conn_param.bd_addr |
| }; |
| bta_csip_send_conn_state_changed_cb(&invalid_cb, p_conn_req->app_id, |
| BTA_CSIP_DISCONNECTED, BTA_CSIP_CONN_ESTABLISHMENT_FAILED); |
| return; |
| } |
| |
| // check if connection state is already connected |
| if (p_cb->state == BTA_CSIP_CONN_ST) { |
| if (!bta_csip_is_app_from_applist(p_cb, p_conn_req->app_id)) { |
| bta_csip_add_app_to_applist(p_cb, p_conn_req->app_id); |
| } |
| bta_csip_send_conn_state_changed_cb(p_cb, p_conn_req->app_id, |
| BTA_CSIP_CONNECTED, BTA_CSIP_CONN_ESTABLISHED); |
| return; |
| |
| // other app has already started connection procedure |
| } else if (!bta_csip_is_app_from_applist(p_cb, p_conn_req->app_id) |
| && (uint8_t)p_cb->conn_applist.size() > 0 |
| && p_cb->state != BTA_CSIP_IDLE_ST) { |
| LOG(INFO) << __func__ << ": Other app is establishing CSIP Connection." |
| << " Current connection state = " << +p_cb->state; |
| bta_csip_add_app_to_applist(p_cb, p_conn_req->app_id); |
| /* Note: Callback will given to all apps once connection procedure is completed */ |
| return; |
| } |
| |
| p_cb->addr = p_data->conn_param.bd_addr; |
| bta_csip_add_app_to_applist(p_cb, p_conn_req->app_id); |
| |
| BTA_GATTC_Open(bta_csip_cb.gatt_if, p_cb->addr, true, GATT_TRANSPORT_LE, |
| false); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_api_close_act |
| * |
| * Description API Call to close CSIP Gatt Connection |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_api_close_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) { |
| if (!p_cb) { |
| LOG(ERROR) << __func__ << " Already Closed"; |
| return; |
| } |
| |
| tBTA_CSIP_API_CONN* p_req = (tBTA_CSIP_API_CONN *)&p_data->conn_param; |
| |
| LOG(INFO) << __func__ << " Disconnect Request from App: " << +p_req->app_id; |
| |
| if (!bta_csip_is_app_reg(p_req->app_id)) { |
| LOG(ERROR) << __func__ << ": Request from Invalid/Unregistered App: " |
| << +p_req->app_id; |
| // No need to send callback to invalid/unregistered app |
| return; |
| } else if (!bta_csip_is_app_from_applist(p_cb, p_req->app_id)) { |
| LOG(ERROR) << __func__ << " App (ID:"<< +p_req->app_id <<") has not connected"; |
| bta_csip_send_conn_state_changed_cb(p_cb, p_req->app_id, |
| BTA_CSIP_DISCONNECTED, BTA_CSIP_DISCONNECT_WITHOUT_CONNECT); |
| return; |
| } |
| |
| // Check if its last disconnecting app |
| if ((uint8_t)p_cb->conn_applist.size() > 1) { |
| bta_csip_remove_app_from_conn_list(p_cb, p_req->app_id); |
| bta_csip_send_conn_state_changed_cb(p_cb, p_req->app_id, |
| BTA_CSIP_DISCONNECTED, BTA_CSIP_APP_DISCONNECTED); |
| return; |
| } |
| |
| bta_csip_close_csip_conn(p_cb); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_gatt_open_act |
| * |
| * Description Callback function when GATT Connection is created. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_gatt_open_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) { |
| tBTA_GATTC_OPEN* open_param = &p_data->gatt_open_param; |
| LOG(INFO) << __func__ << " Remote = " << open_param->remote_bda |
| << " Status = " << open_param->status |
| << " conn_id = " << open_param->conn_id; |
| |
| if (open_param->status == GATT_SUCCESS) { |
| p_cb->in_use = true; |
| p_cb->conn_id = open_param->conn_id; |
| BtaGattQueue::Clean(p_cb->conn_id); |
| bta_csip_sm_execute(p_cb, BTA_CSIP_START_ENC_EVT, NULL); |
| } else { |
| /* open failure */ |
| bta_csip_sm_execute(p_cb, BTA_CSIP_OPEN_FAIL_EVT, p_data); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_gatt_close_act |
| * |
| * Description Callback function when GATT Connection is closed. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_gatt_close_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) { |
| tBTA_GATTC_OPEN* open_param = &p_data->gatt_open_param; |
| |
| // Give callback to all apps from connection applist |
| bta_csip_send_conn_state_changed_cb(p_cb, BTA_CSIP_DISCONNECTED, open_param->status); |
| |
| // Clear applist |
| p_cb->conn_applist.clear(); |
| |
| for (int i = 0; i < MAX_SUPPORTED_SETS_PER_DEVICE; i++) { |
| tBTA_CSIS_SRVC_INFO* srvc = &p_cb->csis_srvc[i]; |
| if (srvc->in_use) { |
| srvc->lock = UNLOCK_VALUE; |
| } |
| } |
| |
| p_cb->conn_id = 0; |
| p_cb->in_use = false; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_gatt_open_fail_act |
| * |
| * Description Callback function when GATT Connection fails to be created. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_gatt_open_fail_act (tBTA_CSIP_DEV_CB* p_cb, |
| tBTA_CSIP_REQ_DATA* p_data) { |
| LOG(ERROR) << __func__ << " Failed to open GATT Connection"; |
| |
| tBTA_GATTC_OPEN* open_param = &p_data->gatt_open_param; |
| |
| // Give callback to all apps from connection applist waiting for connection |
| bta_csip_send_conn_state_changed_cb(p_cb, BTA_CSIP_DISCONNECTED, open_param->status); |
| |
| // Clear applist |
| p_cb->conn_applist.clear(); |
| p_cb->in_use = false; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_open_cmpl_act |
| * |
| * Description Tasks needed to be done when connection is established. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_open_cmpl_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) { |
| APPL_TRACE_DEBUG("%s", __func__); |
| |
| if (!p_cb) { |
| LOG(ERROR) << __func__ << " Invalid device contrl block"; |
| return; |
| } |
| |
| // Give callback to all apps from connection applist waiting for connection |
| bta_csip_send_conn_state_changed_cb(p_cb, BTA_CSIP_CONNECTED, |
| BTA_CSIP_CONN_ESTABLISHED); |
| |
| /* Register for notification of required CSIS characteristic*/ |
| int i = 0; |
| for (i = 0; i < MAX_SUPPORTED_SETS_PER_DEVICE; i++) { |
| tBTA_CSIS_SRVC_INFO* srvc = &p_cb->csis_srvc[i]; |
| if (srvc->in_use) { |
| bta_csip_write_cccd(p_cb, srvc->lock_handle, srvc->lock_ccd_handle); |
| bta_csip_write_cccd(p_cb, srvc->size_handle, srvc->size_ccd_handle); |
| bta_csip_write_cccd(p_cb, srvc->sirk_handle, srvc->sirk_ccd_handle); |
| } |
| } |
| |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_start_sec_act |
| * |
| * Description Tasks needed to be done to check or establish CSIP required |
| * security. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_start_sec_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) { |
| APPL_TRACE_DEBUG("%s", __func__); |
| |
| uint8_t sec_flag = 0; |
| |
| // Get security flags for the device |
| BTM_GetSecurityFlagsByTransport(p_cb->addr, &sec_flag, BT_TRANSPORT_LE); |
| |
| // link is already encrypted, send encryption complete callback to csip |
| if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) { |
| LOG(INFO) << __func__ << " Already Encrypted"; |
| bta_csip_sm_execute(p_cb, BTA_CSIP_ENC_CMPL_EVT, NULL); |
| } |
| // device is bonded but link is not encrypted. Start encryption |
| else if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) { |
| sec_flag = BTM_BLE_SEC_ENCRYPT; |
| BTM_SetEncryption(p_cb->addr, BTA_TRANSPORT_LE, bta_csip_le_encrypt_cback, |
| NULL, sec_flag); |
| } |
| // unbonded device. Set MITM Encryption |
| else if (p_cb->sec_mask != BTA_SEC_NONE) { |
| sec_flag = BTM_BLE_SEC_ENCRYPT_MITM; |
| BTM_SetEncryption(p_cb->addr, BTA_TRANSPORT_LE, bta_csip_le_encrypt_cback, |
| NULL, sec_flag); |
| } |
| // link is already encrypted |
| else { |
| bta_csip_sm_execute(p_cb, BTA_CSIP_ENC_CMPL_EVT, NULL); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_start_sec_act |
| * |
| * Description Tasks needed to be done to check or establish CSIP required |
| * security. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_sec_cmpl_act (tBTA_CSIP_DEV_CB* p_cb, tBTA_CSIP_REQ_DATA* p_data) { |
| APPL_TRACE_DEBUG("%s p_cb->csis_srvc[0].in_use = %d, p_cb->csis_srvc[0].sirk_handle = %d", |
| __func__, p_cb->csis_srvc[0].in_use, p_cb->csis_srvc[0].sirk_handle); |
| |
| if (!p_cb->csis_srvc[0].in_use || !p_cb->csis_srvc[0].sirk_handle) { |
| /* Service discovery is triggered from this path when csip connection is opened |
| * from 3rd party application */ |
| LOG(INFO) << __func__ << "Service discovery is pending"; |
| Uuid pri_srvc = Uuid::From16Bit(UUID_SERVCLASS_CSIS); |
| BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc); |
| } else { |
| LOG(INFO) << __func__ << "Service discovery is already completed"; |
| bta_csip_sm_execute(p_cb, BTA_CSIP_OPEN_CMPL_EVT, NULL); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_close_csip_conn |
| * |
| * Description API to close CSIP Connection and remove device from background |
| * list. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_close_csip_conn (tBTA_CSIP_DEV_CB* p_cb) { |
| LOG(INFO) << __func__; |
| if (p_cb->conn_id != GATT_INVALID_CONN_ID) { |
| // clear pending GATT Requests |
| BtaGattQueue::Clean(p_cb->conn_id); |
| p_cb->state = BTA_CSIP_DISCONNECTING_ST; |
| // Send Close to GATT Layer |
| if (p_cb->state == BTA_CSIP_CONN_ST || p_cb->conn_id) { |
| BTA_GATTC_Close(p_cb->conn_id); |
| } else { |
| BTA_GATTC_CancelOpen(bta_csip_cb.gatt_if, p_cb->addr, true); |
| tBTA_GATTC_OPEN open = {.status = GATT_SUCCESS}; |
| bta_csip_gatt_close_act(p_cb,(tBTA_CSIP_REQ_DATA *)&open); |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_handle_notification |
| * |
| * Description This function is called when notification is received on one |
| * of the characteristic registered for notification. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_handle_notification(tBTA_GATTC_NOTIFY* ntf) { |
| if (!ntf->is_notify) return; |
| LOG(INFO) << __func__<< " Set Member: " << ntf->bda << ", handle: " << ntf->handle; |
| |
| tBTA_CSIP_DEV_CB* p_cb = bta_csip_find_dev_cb_by_bda(ntf->bda); |
| if (!p_cb) { |
| LOG(ERROR) << __func__ << " No CSIP GATT Connection for this device"; |
| return; |
| } |
| |
| const gatt::Characteristic* p_char = |
| BTA_GATTC_GetCharacteristic(p_cb->conn_id, ntf->handle); |
| if (p_char == NULL) { |
| APPL_TRACE_ERROR( |
| "%s: notification received for Unknown Characteristic, conn_id: " |
| "0x%04x, handle: 0x%04x", |
| __func__, p_cb->conn_id, ntf->handle); |
| return; |
| } |
| |
| if (p_char->uuid == CSIS_SERVICE_LOCK_UUID) { |
| bta_csip_handle_lock_value_notif(p_cb, ntf->handle, ntf->value[0]); |
| } else if (p_char->uuid == CSIS_SERVICE_SIRK_UUID) { |
| //bta_csip_handle_sirk_change(); |
| } else if (p_char->uuid == CSIS_SERVICE_SIZE_UUID) { |
| //bta_csip_handle_size_change(); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_handle_lock_value_notif |
| * |
| * Description This function is called when notification is received for |
| * change in lock value on set member. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_handle_lock_value_notif(tBTA_CSIP_DEV_CB* p_cb, |
| uint16_t handle, uint8_t value) { |
| tBTA_CSIS_SRVC_INFO* srvc = bta_csip_find_csis_srvc_by_lock_handle(p_cb, handle); |
| if (!srvc) { |
| LOG(ERROR) << __func__ << " CSIS Service instance not found for this handle"; |
| return; |
| } |
| |
| /* LOCK has been released by Set member (by lock timeout) */ |
| if (value == UNLOCK_VALUE && srvc->lock == LOCK_VALUE) { |
| srvc->lock = UNLOCK_VALUE; |
| /* Give lock status changed notification to all apps holding |
| * lock for this set member */ |
| LOG(INFO) << __func__ << " Lock released by timeout"; |
| for (auto i: srvc->lock_applist) { |
| tBTA_CSIP_RCB* rcb = bta_csip_get_rcb(i); |
| if (rcb && rcb->p_cback) { |
| std::vector<RawAddress> sm(1, p_cb->addr); |
| tBTA_LOCK_STATUS_CHANGED p_data = {i, srvc->set_id, value, |
| LOCK_RELEASED_TIMEOUT, sm}; |
| (*rcb->p_cback) (BTA_CSIP_LOCK_STATUS_CHANGED_EVT, (tBTA_CSIP_DATA *)&p_data); |
| } |
| } |
| srvc->lock_applist.clear(); |
| } |
| /* LOCK held by other set coordinator is released */ |
| else if (value == UNLOCK_VALUE && srvc->lock == UNLOCK_VALUE) { |
| // check if lock was denied for any previous request |
| for (auto i: srvc->denied_applist) { |
| tBTA_CSIP_RCB* rcb = bta_csip_get_rcb(i); |
| if (rcb && rcb->p_cback) { |
| tBTA_LOCK_AVAILABLE p_data = {i, srvc->set_id, p_cb->addr}; |
| (*rcb->p_cback) (BTA_CSIP_LOCK_AVAILABLE_EVT, (tBTA_CSIP_DATA *)&p_data); |
| } |
| } |
| srvc->denied_applist.clear(); |
| } |
| /* Other Set Coordinator acquired the lock */ |
| else if (value == LOCK_VALUE && srvc->lock == UNLOCK_VALUE) { |
| // No action is required to be taken |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_csis_disc_complete_ind |
| * |
| * Description This function informas CSIS service discovery has been |
| * completed to DM layer. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_csis_disc_complete_ind (RawAddress& addr) { |
| tBTA_CSIP_DEV_CB* p_cb = bta_csip_find_dev_cb_by_bda(addr); |
| if (p_cb) { |
| p_cb->total_instance_disc++; |
| LOG(INFO) << __func__ << " discovered = " << +p_cb->total_instance_disc |
| << " Total = " << +p_cb->csis_instance_count; |
| if (p_cb->total_instance_disc == p_cb->csis_instance_count |
| && !p_cb->is_disc_external) { |
| bta_dm_csis_disc_complete(addr, true); |
| bta_dm_lea_disc_complete(addr); |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_give_new_set_found_cb |
| * |
| * Description Give new coordinate set found callback to upper layer. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_give_new_set_found_cb (tBTA_CSIS_SRVC_INFO *srvc) { |
| /* Check if this remote csis instance is included in another service */ |
| const std::vector<gatt::Service>* services = |
| BTA_GATTC_GetServices(srvc->conn_id); |
| |
| if (services) { |
| for (const gatt::Service& service : *services) { |
| if (service.is_primary) { |
| for (const gatt::IncludedService &included_srvc : service.included_services) { |
| if (included_srvc.uuid == CSIS_SERVICE_UUID |
| && service.handle == srvc->service_handle) { |
| APPL_TRACE_DEBUG("%s: service Uuid of service including CSIS service : %s", |
| __func__, service.uuid.ToString().c_str()); |
| srvc->including_srvc_uuid = service.uuid; |
| } |
| } |
| } |
| } |
| } |
| |
| // Given New Set found callback to upper layer |
| tBTA_CSIP_NEW_SET_FOUND new_set_params; |
| new_set_params.set_id = srvc->set_id; |
| memcpy(new_set_params.sirk, srvc->sirk, SIRK_SIZE); |
| new_set_params.size = srvc->size; |
| new_set_params.including_srvc_uuid = srvc->including_srvc_uuid; |
| new_set_params.addr = srvc->bd_addr; |
| new_set_params.lock_support = (srvc->lock_handle != 0)? true : false; |
| |
| (*bta_csip_cb.p_cback)(BTA_CSIP_NEW_SET_FOUND_EVT, (tBTA_CSIP_DATA *)&new_set_params); |
| } |
| |
| bool bta_csip_decrypt_sirk(tBTA_CSIS_SRVC_INFO *srvc, uint8_t *enc_sirk) { |
| // Get K from LTK or Link Key based on transport |
| Octet16 K = {}; |
| uint8_t gatt_if, transport = BT_TRANSPORT_LE; |
| RawAddress bdaddr; |
| GATT_GetConnectionInfor(srvc->conn_id, &gatt_if, bdaddr, &transport); |
| |
| char sample_data_prop[6]; |
| osi_property_get("vendor.bt.pts.sample_csis_data", sample_data_prop, "false"); |
| |
| if (!strncmp("true", sample_data_prop, 4)) { // comparing prop with "true" |
| K = {0x67, 0x6e, 0x1b, 0x9b, 0xd4, 0x48, 0x69, 0x6f, |
| 0x06, 0x1e, 0xc6, 0x22, 0x3c, 0xe5, 0xce, 0xd9}; |
| } else if (transport == BT_TRANSPORT_BR_EDR) { |
| K = BTM_SecGetDeviceLinkKey(srvc->bd_addr); |
| } else if (transport == BT_TRANSPORT_LE) { |
| RawAddress pseudo_addr; |
| pseudo_addr = bta_get_pseudo_addr_with_id_addr(srvc->bd_addr); |
| Octet16 rev_K = BTM_BleGetLTK(pseudo_addr); |
| std::reverse_copy(rev_K.begin(), rev_K.end(), K.begin()); |
| } |
| |
| if(is_key_empty(K)) { |
| APPL_TRACE_DEBUG("%s Invalid Key received", __func__); |
| srvc->discovery_status = BTA_CSIP_INVALID_KEY; |
| return false; |
| } |
| |
| /* compute SALT */ |
| Octet16 salt = bta_csip_get_salt(); |
| |
| // Compute T |
| Octet16 T = bta_csip_compute_T(salt, K); |
| |
| // Compute final result k1 |
| Octet16 k1 = bta_csip_compute_k1(T); |
| |
| // Get decrypted SIRK |
| Octet16 r_k1; |
| std::reverse_copy(k1.begin(), k1.end(), r_k1.begin()); |
| bta_csip_get_decrypted_sirk(r_k1, enc_sirk, srvc->sirk); |
| return true; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_sirk_read_cb |
| * |
| * Description Callback received when remote device Coordinated Sets SIRK |
| * is read. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_sirk_read_cb(uint16_t conn_id, tGATT_STATUS status, |
| uint16_t handle, uint16_t len, |
| uint8_t* value, void* data) { |
| APPL_TRACE_DEBUG("%s ", __func__); |
| |
| if (status != GATT_SUCCESS) { |
| APPL_TRACE_ERROR("%s: SIRK Read failed. conn_id = %d status = %04x", |
| __func__, conn_id, status); |
| return; |
| } |
| |
| tBTA_CSIS_SRVC_INFO *srvc = (tBTA_CSIS_SRVC_INFO *)data; |
| |
| uint8_t type = 0xFF; |
| LOG(INFO) << __func__ << " SIRK len = " << +len; |
| |
| if (len != (SIRK_SIZE + 1)) { |
| APPL_TRACE_ERROR("%s : Invalid SIRK length", __func__); |
| srvc->discovery_status = BTA_CSIP_INVALID_SIRK_FORMAT; |
| bta_csip_csis_disc_complete_ind(srvc->bd_addr); |
| return; |
| } |
| |
| STREAM_TO_UINT8(type, value); |
| APPL_TRACE_DEBUG("%s Type Field with SIRK = %d", __func__, type); |
| if (type != ENCRYPTED_SIRK && type != PLAINTEXT_SIRK) { |
| APPL_TRACE_ERROR("%s : Invalid SIRK Type", __func__); |
| srvc->discovery_status = BTA_CSIP_INVALID_KEY_TYPE; |
| bta_csip_csis_disc_complete_ind(srvc->bd_addr); |
| return; |
| } |
| |
| if (type == ENCRYPTED_SIRK) { |
| uint8_t enc_sirk[SIRK_SIZE] = {}; |
| STREAM_TO_ARRAY(enc_sirk, value, SIRK_SIZE); |
| if (!bta_csip_decrypt_sirk(srvc, enc_sirk)) { |
| APPL_TRACE_ERROR("%s : Invalid Empty Key", __func__); |
| srvc->discovery_status = BTA_CSIP_INVALID_KEY; |
| bta_csip_csis_disc_complete_ind(srvc->bd_addr); |
| return; |
| } |
| } else { |
| STREAM_TO_ARRAY(srvc->sirk, value, SIRK_SIZE); |
| } |
| // check if this set was found earlier |
| uint8_t set_id = bta_csip_find_set_id_by_sirk (srvc->sirk); |
| tBTA_CSET_CB *cset_cb = NULL; |
| |
| /* New Coordinated Set */ |
| if (set_id == INVALID_SET_ID) { |
| cset_cb = bta_csip_get_cset_cb(); |
| if (!cset_cb) { |
| LOG(ERROR) << __func__ << " Insufficient set control blocks available."; |
| srvc->discovery_status = BTA_CSIP_RSRC_EXHAUSTED; |
| bta_csip_csis_disc_complete_ind(srvc->bd_addr); |
| return; |
| } |
| memcpy(cset_cb->sirk, srvc->sirk, SIRK_SIZE); |
| |
| // Create new coordinated set and update in database |
| tBTA_CSIP_CSET cset = {}; |
| cset.set_id = cset_cb->set_id; |
| cset.set_members.push_back(srvc->bd_addr); |
| cset.total_discovered++; |
| cset.lock_support = (srvc->lock_handle != 0 ? true : false); |
| LOG(INFO) << __func__ << "New Set. Adding device " << srvc->bd_addr.ToString() |
| << " Set ID: " << +cset.set_id; |
| bta_csip_cb.csets.push_back(cset); |
| |
| // assign set id in respective control blocks |
| srvc->set_id = cset_cb->set_id; |
| |
| /* Existing coordinated Set */ |
| } else { |
| LOG(INFO) << __func__ << " Device from existing set (set_id: " << +set_id << " )"; |
| //bta_csip_csis_disc_complete_ind(srvc->bd_addr); |
| srvc->set_id = set_id; |
| if (!bta_csip_update_set_member(set_id, srvc->bd_addr)) { |
| srvc->discovery_status = BTA_CSIP_ALL_MEMBERS_DISCOVERED; |
| bta_csip_csis_disc_complete_ind(srvc->bd_addr); |
| return; |
| } |
| |
| // Give set member found callback |
| tBTA_SET_MEMBER_FOUND set_member_params = |
| { .set_id = set_id, |
| .addr = srvc->bd_addr, |
| }; |
| |
| bta_csip_cb.p_cback (BTA_CSIP_SET_MEMBER_FOUND_EVT, (tBTA_CSIP_DATA *)&set_member_params); |
| return; |
| } |
| |
| /* If size is optional, give callback to upper layer */ |
| if (!srvc->size_handle) { |
| bta_csip_give_new_set_found_cb(srvc); |
| } |
| |
| if (!srvc->size_handle && !srvc->rank_handle) { |
| bta_csip_preserve_cset(srvc); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_size_read_cb |
| * |
| * Description Callback received when remote device Coordinated Sets SIZE |
| * characteristic is read. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_size_read_cb(uint16_t conn_id, tGATT_STATUS status, |
| uint16_t handle, uint16_t len, |
| uint8_t* value, void* data) { |
| |
| if (status != GATT_SUCCESS) { |
| APPL_TRACE_ERROR("%s: SIZE Read failed. conn_id = %d status = %04x", |
| __func__, conn_id, status); |
| return; |
| } |
| |
| tBTA_CSIS_SRVC_INFO *srvc = (tBTA_CSIS_SRVC_INFO *)data; |
| |
| if (srvc->discovery_status != BTA_CSIP_DISC_SUCCESS) { |
| APPL_TRACE_ERROR("%s: Ignore response (Reason: %d)", __func__, srvc->discovery_status); |
| return; |
| } |
| |
| srvc->size = *value; |
| APPL_TRACE_DEBUG("%s size = %d", __func__, srvc->size); |
| |
| tBTA_CSIP_CSET* cset = bta_csip_get_or_create_cset(srvc->set_id, true); |
| if (cset) cset->size = srvc->size; |
| // Give callback only when its a first set member |
| uint8_t totalDiscovered = bta_csip_get_coordinated_set(srvc->set_id).set_members.size(); |
| if (totalDiscovered == 1) { |
| bta_csip_give_new_set_found_cb(srvc); |
| } |
| |
| if (!srvc->rank_handle) { |
| bta_csip_preserve_cset(srvc); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_lock_read_cb |
| * |
| * Description Callback received when remote device Coordinated Sets LOCK |
| * characteristic is read. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_lock_read_cb(uint16_t conn_id, tGATT_STATUS status, |
| uint16_t handle, uint16_t len, |
| uint8_t* value, void* data) { |
| if (status != GATT_SUCCESS) { |
| APPL_TRACE_ERROR("%s: LOCK Read failed. conn_id = %d status = %04x", |
| __func__, conn_id, status); |
| return; |
| } |
| |
| APPL_TRACE_DEBUG("%s lock value = %d", __func__, *value); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_rank_read_cb |
| * |
| * Description Callback received when remote device Coordinated Sets RANK |
| * characteristic is read. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_rank_read_cb(uint16_t conn_id, tGATT_STATUS status, |
| uint16_t handle, uint16_t len, |
| uint8_t* value, void* data) { |
| if (status != GATT_SUCCESS) { |
| APPL_TRACE_ERROR("%s: Rank Read failed. conn_id = %d status = %04x", |
| __func__, conn_id, status); |
| return; |
| } |
| |
| tBTA_CSIS_SRVC_INFO *srvc = (tBTA_CSIS_SRVC_INFO *)data; |
| if (srvc->discovery_status != BTA_CSIP_DISC_SUCCESS) { |
| APPL_TRACE_ERROR("%s: Ignore response (Reason: %d)", __func__, srvc->discovery_status); |
| return; |
| } |
| |
| srvc->rank = *value; |
| APPL_TRACE_DEBUG("%s device: %s Rank = %d set_id: %d", __func__, |
| srvc->bd_addr.ToString().c_str(), srvc->rank, srvc->set_id); |
| |
| // get coordinated set control block from set_id |
| tBTA_CSET_CB *cset_cb = bta_csip_get_cset_cb_by_id(srvc->set_id); |
| if (cset_cb) { |
| cset_cb->ordered_members.insert({srvc->rank, srvc->bd_addr}); |
| } |
| |
| bta_csip_preserve_cset(srvc); |
| bta_csip_csis_disc_complete_ind(srvc->bd_addr); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_csip_gatt_disc_cmpl_act |
| * |
| * Description This APIS is used to serach presence of csis service on |
| * remote device and initialize CSIS handles in csis service |
| * control block. |
| * |
| * Returns None |
| * |
| ******************************************************************************/ |
| void bta_csip_gatt_disc_cmpl_act(tBTA_CSIP_DISC_SET *disc_params) { |
| uint16_t conn_id = disc_params->conn_id; |
| uint8_t status = disc_params->status; |
| RawAddress addr = disc_params->addr; |
| |
| APPL_TRACE_DEBUG("%s conn_id = %d, status = %d addr: %s", __func__, conn_id, |
| status, addr.ToString().c_str()); |
| |
| if (status) return; |
| |
| // Fetch remote device gatt services from database |
| const std::vector<gatt::Service>* services = |
| BTA_GATTC_GetServices(conn_id); |
| |
| if (!services) { |
| LOG(ERROR) << __func__ << " No Services discovered."; |
| bta_csip_csis_disc_complete_ind(addr); |
| return; |
| } |
| |
| tBTA_CSIP_DEV_CB* dev_cb = bta_csip_find_dev_cb_by_bda(addr); |
| |
| if (!dev_cb) { |
| dev_cb = bta_csip_create_dev_cb_for_bda(addr); |
| } |
| |
| dev_cb->csis_instance_count = 0; |
| |
| // Search for CSIS service in the database |
| for (const gatt::Service& service : *services) { |
| if (service.uuid == CSIS_SERVICE_UUID) { |
| dev_cb->csis_instance_count++; |
| // Get service control block from service handle (subsequent connection) |
| tBTA_CSIS_SRVC_INFO *srvc = bta_csip_get_csis_service_by_handle(dev_cb, service.handle); |
| if (!srvc) { |
| // create new service cb (if its a first time connection) |
| srvc = bta_csip_get_csis_service_cb(dev_cb); |
| if (!srvc) { |
| APPL_TRACE_ERROR("%s Resources not available for storing CSIS Service.", __func__); |
| return; |
| } |
| } |
| srvc->bd_addr = addr; |
| srvc->service_handle = service.handle; |
| srvc->conn_id = conn_id; |
| APPL_TRACE_DEBUG("%s: CSIS service found Uuid: %s service_handle = %d", __func__, |
| service.uuid.ToString().c_str(), srvc->service_handle); |
| |
| // Get Characteristic and CCCD handle |
| for (const gatt::Characteristic& charac : service.characteristics) { |
| Uuid uuid1 = charac.uuid; |
| if (uuid1 == CSIS_SERVICE_SIRK_UUID) { |
| srvc->sirk_handle = charac.value_handle; |
| srvc->sirk_ccd_handle = bta_csip_get_cccd_handle(conn_id, charac.value_handle); |
| } else if (uuid1 == CSIS_SERVICE_SIZE_UUID) { |
| srvc->size_handle = charac.value_handle; |
| srvc->size_ccd_handle = bta_csip_get_cccd_handle(conn_id, charac.value_handle); |
| } else if (uuid1 == CSIS_SERVICE_LOCK_UUID) { |
| srvc->lock_handle = charac.value_handle; |
| srvc->lock_ccd_handle = bta_csip_get_cccd_handle(conn_id, charac.value_handle); |
| } else if (uuid1 == CSIS_SERVICE_RANK_UUID) { |
| srvc->rank_handle = charac.value_handle; |
| } |
| } |
| |
| /* Skip reading characteristics and Set Discovery procedure if it was done earlier */ |
| if (srvc->set_id >= 0 && srvc->set_id < BTA_MAX_SUPPORTED_SETS) { |
| LOG(INFO) << __func__ << " Coordinated set discovery procedure already completed."; |
| continue; |
| } |
| |
| if (srvc->sirk_handle) { |
| BtaGattQueue::ReadCharacteristic( |
| conn_id, srvc->sirk_handle, bta_sirk_read_cb, srvc); |
| } |
| |
| if (srvc->size_handle) { |
| BtaGattQueue::ReadCharacteristic( |
| conn_id, srvc->size_handle, bta_size_read_cb, srvc); |
| } |
| |
| if (srvc->lock_handle) { |
| BtaGattQueue::ReadCharacteristic( |
| conn_id, srvc->lock_handle, bta_lock_read_cb, srvc); |
| } |
| |
| if (srvc->rank_handle) { |
| BtaGattQueue::ReadCharacteristic( |
| conn_id, srvc->rank_handle, bta_rank_read_cb, srvc); |
| } |
| } |
| |
| } |
| } |