Add SessionTrack for session status tracking
1. Stop the ongoing sessions on country code switching.
When the country code is switching from A to B,
Active Sessions restricted by B country should be stopped.
2. Delay per-country calibrations when it's ACTIVE
Device calibrations shouldn't be executed when device is ACTIVE state.
Execute per-country calibrations only when the device is in IDLE.
3. (Optional/Experimental) Issue URSK_DELETE_CMD for CCC session
closing. This is activated only when DELETE_URSK_FOR_CCC_SESSION=1
is set from config.
4. (Optional/Expermental) Call suspend to kernel driver on idle.
It calls ioctl/suspend when it's idle for a given duration
and ioctl/resume the device before sending any commands.
This is only activated when AUTO_SUSPEND_ENABLED=1 set from config.
Bug: 323275554
Test: Change country code, regression_2_test_suite, stress_1_test_suite
Change-Id: Iceac1478e9100adaf44b63c8a78257c178851b52
(cherry picked from commit f13904b23d47746816730e963e080005a068c143)
Signed-off-by: Ikjoon Jang <ikjn@google.com>
diff --git a/extns/inc/uci_defs.h b/extns/inc/uci_defs.h
index 837f4e0..009e5f6 100644
--- a/extns/inc/uci_defs.h
+++ b/extns/inc/uci_defs.h
@@ -50,15 +50,20 @@
#define UCI_PBF_SHIFT 4
#define UCI_PBF_ST_CONT 0x10 /* start or continuing fragment */
+/* Ocet 3 = Payload Length(L) */
+#define UCI_PAYLOAD_LENGTH_OFFSET 3
+
/* GID: Group Identifier (byte 0) */
-#define UCI_GID_MASK 0x0F
-#define UCI_GID_CORE 0x00 /* 0000b UCI Core group */
-#define UCI_GID_SESSION_MANAGE 0x01 /* 0001b Session Config commands */
-#define UCI_GID_ANDROID 0x0C /* 1100b Android vendor group */
-#define UCI_GID_PROPRIETARY_0X0A 0x0A /* 1010b Proprietary Group */
-#define UCI_GID_PROPRIETARY 0x0E /* 1110b Proprietary Group */
-#define UCI_GID_PROPRIETARY_0X0F 0x0F /* 1111b Proprietary Group */
-#define UCI_GID_INTERNAL 0x0B /* 1011b Internal Group */
+#define UCI_GID_MASK 0x0F
+#define UCI_GID_CORE 0x00 /* UCI Core group */
+#define UCI_GID_SESSION_MANAGE 0x01 /* Session Config Group */
+#define UCI_GID_SESSION_CONTROL 0x02 /* Session Control Group */
+#define UCI_GID_ANDROID 0x0C /* Android vendor group */
+#define UCI_GID_PROPRIETARY_0X0A 0x0A /* Proprietary Group */
+#define UCI_GID_PROPRIETARY 0x0E /* Proprietary Group */
+#define UCI_GID_PROPRIETARY_0X0F 0x0F /* Proprietary Group */
+#define UCI_GID_INTERNAL 0x0B /* Internal Group */
+
/* 0100b - 1100b RFU */
#define UCI_OID_GET_CAPS_INFO 0x03
@@ -116,13 +121,42 @@
#define UCI_MSG_CORE_GENERIC_ERROR_NTF 7
/*********************************************************
- * UCI session config Group-2: Opcodes and size of command
+ * UCI session config Group-1: Opcodes and size of command
********************************************************/
#define UCI_MSG_SESSION_STATUS_NTF 2
-#define UCI_MSG_SESSION_SET_APP_CONFIG 3
-#define UCI_MSG_SESSION_QUERY_DATA_SIZE 0x0B
+#define UCI_MSG_SESSION_STATUS_NTF_HANDLE_OFFSET 4
+#define UCI_MSG_SESSION_STATUS_NTF_STATE_OFFSET 8
+#define UCI_MSG_SESSION_STATUS_NTF_REASON_OFFSET 9
+#define UCI_MSG_SESSION_STATUS_NTF_LENGTH 10
+
+#define UCI_MSG_SESSION_STATE_INIT (0x00)
+#define UCI_MSG_SESSION_STATE_INIT_CMD_LEN (9)
+#define UCI_MSG_SESSION_STATE_INIT_CMD_ID_OFFSET (4)
+#define UCI_MSG_SESSION_STATE_INIT_CMD_TYPE_OFFSET (8)
+#define UCI_MSG_SESSION_STATE_INIT_RSP_LEN (9)
+#define UCI_MSG_SESSION_STATE_INIT_RSP_STATUS_OFFSET (4)
+#define UCI_MSG_SESSION_STATE_INIT_RSP_HANDLE_OFFSET (5)
+#define UCI_MSG_SESSION_STATE_DEINIT (0x01)
+#define UCI_MSG_SESSION_STATE_ACTIVE (0x02)
+#define UCI_MSG_SESSION_STATE_IDLE (0x03)
+#define UCI_MSG_SESSION_STATE_UNDEFINED (0xFF) // SW defined
+
+#define UCI_MSG_SESSION_SET_APP_CONFIG 3
+#define UCI_MSG_SESSION_SET_APP_CONFIG_HANDLE_OFFSET 4
+
+#define UCI_MSG_SESSION_QUERY_DATA_SIZE 0x0B
#define UCI_MSG_SESSION_QUERY_DATA_SIZE_STATUS_OFFSET 8
+// Session Type field in SESSION_INIT_CMD
+constexpr uint8_t kSessionType_Ranging = 0x00;
+constexpr uint8_t kSessionType_RangingAndData = 0x01;
+constexpr uint8_t kSessionType_CCCRanging = 0xA0;
+
+/*********************************************************
+ * UCI session config Group-2: Opcodes and size of command
+ ********************************************************/
+#define UCI_MSG_SESSION_STOP 0x01
+
/**********************************************************
* UCI Android Vendor Group-C: Opcodes and size of commands
**********************************************************/
@@ -146,6 +180,7 @@
/**********************************************
* UWB Prop Group Opcode-F Opcodes
**********************************************/
+#define UCI_MSG_URSK_DELETE 0x01
#define UCI_MSG_SET_DEVICE_CALIBRATION 0x21
#define UCI_MSG_GET_DEVICE_CALIBRATION 0x22
diff --git a/halimpl/hal/phNxpUciHal.cc b/halimpl/hal/phNxpUciHal.cc
index 92593c7..3c7131a 100644
--- a/halimpl/hal/phNxpUciHal.cc
+++ b/halimpl/hal/phNxpUciHal.cc
@@ -37,6 +37,7 @@
#include "hal_nxpuwb.h"
#include "phNxpConfig.h"
#include "phNxpUciHal_utils.h"
+#include "sessionTrack.h"
using namespace std;
using android::base::StringPrintf;
@@ -263,6 +264,8 @@
}
} else if ((gid == UCI_GID_SESSION_MANAGE) && (oid == UCI_MSG_SESSION_SET_APP_CONFIG)) {
return phNxpUciHal_handle_set_app_config(&nxpucihal_ctrl.cmd_len, nxpucihal_ctrl.p_cmd_data);
+ } else if ((gid == UCI_GID_SESSION_MANAGE) && (oid == UCI_MSG_SESSION_STATE_INIT)) {
+ SessionTrack_onSessionInit(nxpucihal_ctrl.cmd_len, nxpucihal_ctrl.p_cmd_data);
}
} else if (mt == UCI_MT_RSP) {
if ((gid == UCI_GID_CORE) && (oid == UCI_MSG_CORE_GET_CAPS_INFO)) {
@@ -449,6 +452,8 @@
}
uint16_t len = 0;
+ SessionTrack_keepAlive();
+
CONCURRENCY_LOCK();
phNxpUciHal_process_ext_cmd_rsp(data_len, p_data, &len);
CONCURRENCY_UNLOCK();
@@ -883,6 +888,8 @@
CONCURRENCY_LOCK();
+ SessionTrack_deinit();
+
nxpucihal_ctrl.halStatus = HAL_STATUS_CLOSE;
if (NULL != gpphTmlUwb_Context->pDevHandle) {
@@ -1485,6 +1492,8 @@
return status;
}
+ SessionTrack_init();
+
// report to upper-layer
phTmlUwb_DeferredCall(std::make_shared<phLibUwb_Message>(UCI_HAL_INIT_CPLT_MSG));
diff --git a/halimpl/hal/phNxpUciHal_ext.cc b/halimpl/hal/phNxpUciHal_ext.cc
index ebf62e9..d6bbfda 100644
--- a/halimpl/hal/phNxpUciHal_ext.cc
+++ b/halimpl/hal/phNxpUciHal_ext.cc
@@ -23,10 +23,12 @@
#include "phNxpConfig.h"
#include "phNxpLog.h"
-#include "phNxpUciHal_ext.h"
#include "phNxpUciHal.h"
+#include "phNxpUciHal_ext.h"
+#include "phNxpUciHal_utils.h"
#include "phTmlUwb.h"
#include "phUwbCommon.h"
+#include "sessionTrack.h"
/* Timeout value to wait for response from DEVICE_TYPE_SR1xx */
#define MAX_COMMAND_RETRY_COUNT 5
@@ -1219,8 +1221,6 @@
// TX_BASE_BAND_CONTROL
{
- NXPLOG_UCIHAL_D("Apply TX_BASE_BAND_CONTROL: ddfs_enable=%u, dc_suppress=%u", ddfs_enable, dc_suppress);
-
uint8_t flag = 0;
if (ddfs_enable)
flag |= 0x01;
@@ -1261,7 +1261,7 @@
extern bool isCountryCodeMapCreated;
-static void apply_per_country_calibrations(void)
+void apply_per_country_calibrations(void)
{
// TX-POWER can be provided by
// 1) COUNTRY_CODE_CAPS with offset values.
@@ -1290,10 +1290,6 @@
******************************************************************************/
void phNxpUciHal_handle_set_country_code(const char country_code[2])
{
- //
- // TODO: stop all active session which are affected by new country code
- // TODO: delay per-country calibrations when there's active sessions (or juststop them?)
- //
NXPLOG_UCIHAL_D("Apply country code %c%c", country_code[0], country_code[1]);
phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
@@ -1305,9 +1301,7 @@
}
if (NxpConfig_SetCountryCode(country_code)) {
- // Load extra calibration files first, so COUNTRY_CODE_CAPS can overrides the settings.
-
- // per-country extra calibrations are only triggered when 'COUNTRY_CODE_CAPS' is not provided
+ // Load ExtraCal restrictions
uint16_t mask= 0;
if (NxpConfig_GetNum("cal.restricted_channels", &mask, sizeof(mask))) {
NXPLOG_UCIHAL_D("Restriction flag, restricted channel mask=0x%x", mask);
@@ -1326,7 +1320,7 @@
country_code[0], country_code[1]);
}
- // Load 'COUNTRY_CODE_CAPS' and apply it to 'conf_map'
+ // Load 'COUNTRY_CODE_CAPS' restrictions (via 'conf_map')
uint8_t cc_caps[UCI_MAX_DATA_LEN];
long retlen = 0;
if (NxpConfig_GetByteArray(NAME_NXP_UWB_COUNTRY_CODE_CAPS, cc_caps, sizeof(cc_caps), &retlen) && retlen) {
@@ -1342,10 +1336,10 @@
NXPLOG_UCIHAL_D("Country code caps loaded");
}
}
+ // Apply per-country calibration, it's handled by SessionTrack
+ SessionTrack_onCountryCodeChanged();
}
- apply_per_country_calibrations();
-
// send country code response to upper layer
nxpucihal_ctrl.rx_data_len = 5;
static uint8_t rsp_data[5] = { 0x4c, 0x01, 0x00, 0x01 };
@@ -1390,6 +1384,9 @@
return false;
}
+ uint32_t session_handle = le_bytes_to_cpu<uint32_t>(&p_data[UCI_MSG_SESSION_SET_APP_CONFIG_HANDLE_OFFSET]);
+ uint8_t ch = 0;
+
// Create local copy of cmd_data for data manipulation
uint8_t uciCmd[UCI_MAX_DATA_LEN];
uint16_t packet_len = *data_len;
@@ -1418,7 +1415,7 @@
// check restricted channel
if (tlv_tag == UCI_PARAM_ID_CHANNEL_NUMBER && tlv_len == 1) {
- uint8_t ch = p_data[i + 2];
+ ch = p_data[i + 2];
if (((ch == CHANNEL_NUM_5) && (rt_set->restricted_channel_mask & (1 << 5))) ||
((ch == CHANNEL_NUM_9) && (rt_set->restricted_channel_mask & (1 << 9)))) {
@@ -1471,5 +1468,7 @@
*data_len = packet_len;
}
+ SessionTrack_onAppConfig(session_handle, ch);
+
return false;
}
diff --git a/halimpl/hal/phNxpUciHal_ext.h b/halimpl/hal/phNxpUciHal_ext.h
index e7ef9ae..d5a8926 100644
--- a/halimpl/hal/phNxpUciHal_ext.h
+++ b/halimpl/hal/phNxpUciHal_ext.h
@@ -71,4 +71,5 @@
void phNxpUciHal_process_response();
void phNxpUciHal_handle_set_country_code(const char country_code[2]);
bool phNxpUciHal_handle_set_app_config(uint16_t *data_len, uint8_t *p_data);
+void apply_per_country_calibrations(void);
#endif /* _PHNXPNICHAL_EXT_H_ */
diff --git a/halimpl/hal/sessionTrack.cc b/halimpl/hal/sessionTrack.cc
new file mode 100644
index 0000000..9b7170a
--- /dev/null
+++ b/halimpl/hal/sessionTrack.cc
@@ -0,0 +1,529 @@
+#include <bit>
+#include <mutex>
+#include <unordered_map>
+#include <thread>
+#include <vector>
+
+#include "phNxpConfig.h"
+#include "phNxpUciHal.h"
+#include "phNxpUciHal_ext.h"
+#include "phNxpUciHal_utils.h"
+
+extern phNxpUciHal_Control_t nxpucihal_ctrl;
+
+//
+// SessionTrack
+//
+// Keeps track of device/session state.
+//
+// 1. Per-country calibrations
+//
+// When the country code is switching from A to B,
+// a. Active Sessions restricted by B country should be stopped.
+// b. Per-country device calibrations should be delayed to where
+// device stays in IDLE.
+//
+// 2. Issue URSK_DELETE_CMD on SESSION_DEINIT_RSP (optional/experimental)
+//
+// Calls URSK_DELETE_CMD for every CCC session closing,
+// for the cases where CCC session ID was created but not started.
+// (This is only activated when DELETE_URSK_FOR_CCC_SESSION=1 is set
+// from config)
+//
+// 3. Call suspend to kernel driver on idle (optional/experimental)
+//
+// (This is only activated when AUTO_SUSPEND_ENABLED=1 is set from config)
+// Tracks the each session's status and automatically requests suspend
+// to kernel driver when it's idle for a given duration,
+// and resumes the device before sending any commands.
+// SessionTracks detects UWBS is in idle when there's no session created.
+//
+
+class SessionTrack {
+private:
+ // Session
+ struct SessionInfo {
+ uint32_t session_id_;
+ uint8_t session_type_;
+ uint8_t session_state_;
+ uint8_t channel_;
+ SessionInfo(uint32_t session_id, uint8_t session_type) :
+ session_id_(session_id),
+ session_type_(session_type),
+ session_state_(UCI_MSG_SESSION_STATE_UNDEFINED),
+ channel_(0) {
+ }
+ };
+ enum class SessionTrackWorkType {
+ IDLE = 0,
+ REFRESH_IDLE,
+ ACTIVATE,
+ IDLE_TIMER_FIRED,
+ DELETE_URSK,
+ STOP,
+ };
+ enum class PowerState {
+ SUSPEND = 0,
+ IDLE,
+ ACTIVE,
+ };
+ struct SessionTrackMsg {
+ SessionTrackWorkType type_;
+ std::shared_ptr<SessionInfo> session_info_;
+ bool sync_;
+ std::condition_variable cond_;
+
+ SessionTrackMsg(SessionTrackWorkType type, bool sync) : type_(type), sync_(sync) { }
+
+ // Per-session work item
+ SessionTrackMsg(SessionTrackWorkType type, std::shared_ptr<SessionInfo> session_info, bool sync) :
+ type_(type), session_info_(session_info), sync_(sync) { }
+ };
+ static constexpr unsigned long kAutoSuspendTimeoutDefaultMs_ = (30 * 1000);
+ static constexpr long kQueueTimeoutMs = 500;
+
+private:
+ std::shared_ptr<phNxpUciHal_RxHandler> rx_handler_session_status_ntf_;
+ std::unordered_map<uint32_t, std::shared_ptr<SessionInfo>> sessions_;
+ std::mutex sessions_lock_;
+
+ bool auto_suspend_enabled_;
+ bool delete_ursk_ccc_enabled_;
+ bool calibration_delayed_;
+ std::atomic<PowerState> power_state_;
+ bool idle_timer_started_;
+ unsigned long idle_timeout_ms_;
+
+ std::thread worker_thread_;
+ std::mutex sync_mutex_;
+ uint32_t idle_timer_;
+ std::unique_ptr<MessageQueue<SessionTrackMsg>> msgq_;
+
+public:
+ SessionTrack() :
+ auto_suspend_enabled_(false),
+ delete_ursk_ccc_enabled_(false),
+ calibration_delayed_(false),
+ power_state_(PowerState::IDLE),
+ idle_timer_started_(false),
+ idle_timeout_ms_(kAutoSuspendTimeoutDefaultMs_)
+ {
+ sessions_.clear();
+
+ msgq_ = std::make_unique<MessageQueue<SessionTrackMsg>>("SessionTrack");
+ worker_thread_ = std::thread(&SessionTrack::PowerManagerWorker, this);
+
+ unsigned long numval = 0;
+
+ if (NxpConfig_GetNum(NAME_DELETE_URSK_FOR_CCC_SESSION, &numval, sizeof(numval)) && numval) {
+ delete_ursk_ccc_enabled_ = true;
+ }
+
+ if (NxpConfig_GetNum(NAME_AUTO_SUSPEND_ENABLE, &numval, sizeof(numval)) && numval) {
+ auto_suspend_enabled_ = true;
+
+ NxpConfig_GetNum(NAME_AUTO_SUSPEND_TIMEOUT_MS, &idle_timeout_ms_, sizeof(idle_timeout_ms_));
+
+ // Idle timer is only activated when AUTO_SUSPEND_ENABLED=1
+ // device suspend won't be triggered when it's not activated.
+ idle_timer_ = phOsalUwb_Timer_Create();
+ RefreshIdle();
+ }
+
+ // register SESSION_STATUS_NTF rx handler
+ rx_handler_session_status_ntf_ = phNxpUciHal_rx_handler_add(
+ UCI_MT_NTF, UCI_GID_SESSION_MANAGE, UCI_MSG_SESSION_STATUS_NTF,
+ false, false,
+ std::bind(&SessionTrack::OnSessionStatusNtf, this, std::placeholders::_1, std::placeholders::_2));
+ }
+
+ virtual ~SessionTrack() {
+ phNxpUciHal_rx_handler_del(rx_handler_session_status_ntf_);
+
+ if (auto_suspend_enabled_) {
+ phOsalUwb_Timer_Delete(idle_timer_);
+ }
+ auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::STOP, true);
+ QueueSessionTrackWork(msg);
+ worker_thread_.join();
+ }
+
+ // Called upon upper-layer's SESSION_INIT_CMD
+ void OnSessionInit(size_t packet_len, const uint8_t *packet)
+ {
+ if (packet_len != UCI_MSG_SESSION_STATE_INIT_CMD_LEN)
+ return;
+
+ uint32_t session_id = le_bytes_to_cpu<uint32_t>(&packet[UCI_MSG_SESSION_STATE_INIT_CMD_ID_OFFSET]);
+ uint8_t session_type = packet[UCI_MSG_SESSION_STATE_INIT_CMD_TYPE_OFFSET];
+
+ // Check SESSION_INIT_RSP for SessionID - Handle matching
+ auto session_init_rsp_cb =
+ [this, session_id, session_type](size_t packet_len, const uint8_t *packet)
+ {
+ if (packet_len != UCI_MSG_SESSION_STATE_INIT_RSP_LEN )
+ return;
+
+ uint8_t status = packet[UCI_MSG_SESSION_STATE_INIT_RSP_STATUS_OFFSET];
+ uint32_t handle = le_bytes_to_cpu<uint32_t>(&packet[UCI_MSG_SESSION_STATE_INIT_RSP_HANDLE_OFFSET]);
+ if (status != UWBSTATUS_SUCCESS)
+ return;
+
+ bool was_idle;
+ {
+ std::lock_guard<std::mutex> lock(sessions_lock_);
+
+ was_idle = IsDeviceIdle();
+
+ sessions_.emplace(std::make_pair(handle,
+ std::make_shared<SessionInfo>(session_id, session_type)));
+ }
+ if (was_idle) {
+ NXPLOG_UCIHAL_D("Queue Active");
+ auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::ACTIVATE, false);
+ QueueSessionTrackWork(msg);
+ }
+ };
+
+ // XXX: This rx handler can be called multiple times on
+ // UCI_STATUS_COMMAND_RETRY(0xA) from SESSION_INIT_CMD
+ phNxpUciHal_rx_handler_add(UCI_MT_RSP, UCI_GID_SESSION_MANAGE,
+ UCI_MSG_SESSION_STATE_INIT, false, true, session_init_rsp_cb);
+ }
+
+ // Called by upper-layer's SetAppConfig command handler
+ void OnChannelConfig(uint32_t session_handle, uint8_t channel) {
+ // Update channel info
+ std::lock_guard<std::mutex> lock(sessions_lock_);
+ auto pSessionInfo = GetSessionInfo(session_handle);
+ if (!pSessionInfo)
+ return;
+ pSessionInfo->channel_ = channel;
+ }
+
+ // Called by upper-layer's SetCountryCode command handler,
+ // Check whether per-country calibration can be executed.
+ // phNxpUciHal_Runtime_Settings_t should've been updated.
+ void OnCountryCodeChanged() {
+ phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
+ NXPLOG_UCIHAL_D("SessionTrack: OnCountryCodeChanged");
+
+ calibration_delayed_ = false;
+ std::vector<uint32_t> blocked_session_handles;
+ {
+ std::lock_guard<std::mutex> lock(sessions_lock_);
+ for (const auto elem : sessions_) {
+ auto session_handle = elem.first;
+ auto pSessionInfo = elem.second;
+
+ if(pSessionInfo->session_state_ != UCI_MSG_SESSION_STATE_ACTIVE)
+ continue;
+ // there's active sessions existed, delay per-country calibrations
+ calibration_delayed_ = true;
+ if (!rt_set->uwb_enable || rt_set->restricted_channel_mask & (1 << pSessionInfo->channel_)) {
+ blocked_session_handles.push_back(session_handle);
+ }
+ }
+ }
+
+ if (rt_set->uwb_enable && !calibration_delayed_) {
+ NXPLOG_UCIHAL_D("SessionTrack: no active sessions, execute per-country cal now.")
+ apply_per_country_calibrations();
+ } else {
+ NXPLOG_UCIHAL_D("SessionTrack: device is in active state, delay per-country cal.")
+ // stop all sessions affected by new country code's restrictions
+ for (auto session_handle : blocked_session_handles) {
+ NXPLOG_UCIHAL_D("SessionTrack: stop session (handle=0x%08x) due to country code restrictions", session_handle);
+ // Can issue an UCI command. This function is only called from upper-layer thread.
+ StopRanging(session_handle);
+ }
+ }
+ }
+
+ void RefreshIdle() {
+ auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::REFRESH_IDLE, true);
+ QueueSessionTrackWork(msg);
+ }
+
+private:
+ // Send SESSION_STOP_CMD
+ void StopRanging(uint32_t session_handle) {
+ uint8_t session_handle_bytes[4];
+ cpu_to_le_bytes(session_handle_bytes, session_handle);
+
+ std::vector<uint8_t> packet{(UCI_MT_CMD << UCI_MT_SHIFT) | UCI_GID_SESSION_CONTROL, UCI_MSG_SESSION_STOP, 0, 0};
+ packet.insert(packet.end(), std::begin(session_handle_bytes), std::end(session_handle_bytes));
+ packet[UCI_PAYLOAD_LENGTH_OFFSET] = packet.size() - UCI_MSG_HDR_SIZE;
+
+ auto ret = phNxpUciHal_send_ext_cmd(packet.size(), packet.data());
+ if (ret != UWBSTATUS_SUCCESS) {
+ NXPLOG_UCIHAL_E("SessionTrack: Failed to stop session handle 0x%08x", session_handle);
+ }
+ }
+
+ // Send URSK_DELETE_CMD
+ void DeleteUrsk(std::shared_ptr<SessionInfo> session_info) {
+ if (!session_info)
+ return;
+
+ phNxpUciHal_rx_handler_add(UCI_MT_RSP, UCI_GID_PROPRIETARY_0X0F,
+ UCI_MSG_URSK_DELETE, true, true,
+ [](size_t packet_len, const uint8_t *packet) {
+ if (packet_len < 5)
+ return;
+ if (packet[4] != UWBSTATUS_SUCCESS) {
+ NXPLOG_UCIHAL_E("SessionTrack: URSR_DELETE failed, rsp status=0x%x", packet[4]);
+ }
+ }
+ );
+ phNxpUciHal_rx_handler_add(UCI_MT_NTF, UCI_GID_PROPRIETARY_0X0F,
+ UCI_MSG_URSK_DELETE, true, true,
+ [](size_t packet_len, const uint8_t *packet) {
+ if (packet_len < 6)
+ return;
+ uint8_t status = packet[4];
+ uint8_t nr = packet[5];
+ if (packet_len != (6 + 5 * nr)) {
+ NXPLOG_UCIHAL_E("SessionTrack: unrecognized packet type of URSK_DELETE_NTF");
+ } else {
+ for (auto i = 6; i < packet_len; i += 5) {
+ uint32_t session_id = le_bytes_to_cpu<uint32_t>(&packet[i]);
+ uint8_t del_status = packet[i + 4];
+ if (status != UWBSTATUS_SUCCESS) {
+ NXPLOG_UCIHAL_E("SessionTrack: URSK_DELETE failed, ntf status=0x%x", status);
+ } else {
+ NXPLOG_UCIHAL_D("SessionTrack: URSK_DELETE done");
+ }
+ }
+ }
+ }
+ );
+
+ NXPLOG_UCIHAL_D("SessionTrack: URSK_DELETE for session ID 0x%x", session_info->session_id_);
+ uint8_t session_id_bytes[4];
+ cpu_to_le_bytes(session_id_bytes, session_info->session_id_);
+
+ std::vector<uint8_t> packet{(UCI_MT_CMD << UCI_MT_SHIFT) | UCI_GID_PROPRIETARY_0X0F,
+ UCI_MSG_URSK_DELETE, 0, 0};
+
+ packet.push_back(1); // Num of Session IDs = 1
+ packet.insert(packet.end(), std::begin(session_id_bytes), std::end(session_id_bytes));
+ packet[UCI_PAYLOAD_LENGTH_OFFSET] = packet.size() - UCI_MSG_HDR_SIZE;
+
+ auto ret = phNxpUciHal_send_ext_cmd(packet.size(), packet.data());
+ if (ret != UWBSTATUS_SUCCESS) {
+ NXPLOG_UCIHAL_E("SessionTrack: Failed to delete URSK for session id 0x%08x",
+ session_info->session_id_);
+ }
+ }
+
+ // UCI_MSG_SESSION_STATUS_NTF rx handler
+ void OnSessionStatusNtf(size_t packet_len, const uint8_t* packet) {
+ if (packet_len != UCI_MSG_SESSION_STATUS_NTF_LENGTH) {
+ NXPLOG_UCIHAL_E("SessionTrack: SESSION_STATUS_NTF packet parse error");
+ return;
+ }
+
+ uint32_t session_handle = le_bytes_to_cpu<uint32_t>(&packet[UCI_MSG_SESSION_STATUS_NTF_HANDLE_OFFSET]);
+ uint8_t session_state = packet[UCI_MSG_SESSION_STATUS_NTF_STATE_OFFSET];
+
+ bool is_idle = false;
+ {
+ std::lock_guard<std::mutex> lock(sessions_lock_);
+
+ if (session_state == UCI_MSG_SESSION_STATE_DEINIT) {
+ NXPLOG_UCIHAL_D("SessionTrack: remove session handle 0x%08x", session_handle);
+ auto pSessionInfo = GetSessionInfo(session_handle);
+
+ if (delete_ursk_ccc_enabled_ && pSessionInfo &&
+ pSessionInfo->session_type_ == kSessionType_CCCRanging) {
+ // If this CCC ranging session, issue DELETE_URSK_CMD for this session.
+ auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::DELETE_URSK, pSessionInfo, false);
+ QueueSessionTrackWork(msg);
+ }
+ sessions_.erase(session_handle);
+ is_idle = IsDeviceIdle();
+ } else {
+ NXPLOG_UCIHAL_D("SessionTrack: update session handle 0x%08x state %u", session_handle, session_state);
+ auto pSessionInfo = GetSessionInfo(session_handle);
+ if (pSessionInfo) {
+ pSessionInfo->session_state_ = session_state;
+ }
+ }
+ }
+
+ if (is_idle) { // transition to IDLE
+ NXPLOG_UCIHAL_D("Queue Idle");
+ auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::IDLE, false);
+ QueueSessionTrackWork(msg);
+ }
+ }
+
+ static void IdleTimerCallback(uint32_t TimerId, void* pContext) {
+ SessionTrack *mgr = static_cast<SessionTrack*>(pContext);
+ auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::IDLE_TIMER_FIRED, false);
+ mgr->QueueSessionTrackWork(msg);
+ }
+
+ void PowerIdleTimerStop() {
+ if (!auto_suspend_enabled_)
+ return;
+
+ NXPLOG_UCIHAL_D("SessionTrack: stop idle timer");
+ if (idle_timer_started_) {
+ if (phOsalUwb_Timer_Stop(idle_timer_) != UWBSTATUS_SUCCESS) {
+ NXPLOG_UCIHAL_E("SessionTrack: idle timer stop failed");
+ }
+ idle_timer_started_ = false;
+ }
+ }
+ void PowerIdleTimerRefresh() {
+ if (!auto_suspend_enabled_)
+ return;
+
+ NXPLOG_UCIHAL_D("SessionTrack: refresh idle timer, %lums", idle_timeout_ms_);
+ if (idle_timer_started_) {
+ if (phOsalUwb_Timer_Stop(idle_timer_) != UWBSTATUS_SUCCESS) {
+ NXPLOG_UCIHAL_E("SessionTrack: idle timer stop failed");
+ }
+ }
+ if (phOsalUwb_Timer_Start(idle_timer_, idle_timeout_ms_, IdleTimerCallback, this) != UWBSTATUS_SUCCESS) {
+ NXPLOG_UCIHAL_E("SessionTrack: idle timer start failed");
+ }
+ idle_timer_started_ = true;
+ }
+
+ // Worker thread for auto suspend
+ void PowerManagerWorker() {
+ NXPLOG_UCIHAL_D("SessionTrack: worker thread started.")
+
+ bool stop_thread = false;
+ while (!stop_thread) {
+ auto msg = msgq_->recv();
+ if (!msg) {
+ NXPLOG_UCIHAL_E("Power State: CRITICAL: worker thread received a bad message!, stop the queue");
+ break;
+ }
+ NXPLOG_UCIHAL_D("SessionTrack: work %d state %d",
+ static_cast<int>(msg->type_), static_cast<int>(power_state_.load()));
+
+ switch (msg->type_) {
+ case SessionTrackWorkType::IDLE:
+ if (calibration_delayed_) {
+ NXPLOG_UCIHAL_D("SessionTrack: No active session, execute per-country calibrations");
+ CONCURRENCY_LOCK();
+ apply_per_country_calibrations();
+ CONCURRENCY_UNLOCK();
+ calibration_delayed_ = false;
+ }
+ power_state_ = PowerState::IDLE;
+ PowerIdleTimerRefresh();
+ break;
+ case SessionTrackWorkType::REFRESH_IDLE:
+ if (power_state_ == PowerState::SUSPEND) {
+ NXPLOG_UCIHAL_D("SessionTrack: resume");
+ phTmlUwb_Resume();
+ power_state_ = PowerState::IDLE;
+ }
+ if (power_state_ == PowerState::IDLE) {
+ PowerIdleTimerRefresh();
+ }
+ break;
+ case SessionTrackWorkType::ACTIVATE:
+ if (power_state_ == PowerState::SUSPEND) {
+ NXPLOG_UCIHAL_E("SessionTrack: activated while in suspend!");
+ phTmlUwb_Resume();
+ }
+ PowerIdleTimerStop();
+ power_state_ = PowerState::ACTIVE;
+ break;
+ case SessionTrackWorkType::IDLE_TIMER_FIRED:
+ if (power_state_ == PowerState::IDLE) {
+ NXPLOG_UCIHAL_D("SessionTrack: idle timer expired, go suspend");
+ power_state_ = PowerState::SUSPEND;
+ phTmlUwb_Suspend();
+ } else {
+ NXPLOG_UCIHAL_E("SessionTrack: idle timer expired while in %d",
+ static_cast<int>(power_state_.load()));
+ }
+ break;
+ case SessionTrackWorkType::DELETE_URSK:
+ DeleteUrsk(msg->session_info_);
+ break;
+ case SessionTrackWorkType::STOP:
+ stop_thread = true;
+ break;
+ default:
+ NXPLOG_UCIHAL_E("SessionTrack: worker thread received a bad message!");
+ break;
+ }
+ if (msg->sync_)
+ msg->cond_.notify_one();
+ }
+ if (idle_timer_started_) {
+ PowerIdleTimerStop();
+ }
+
+ NXPLOG_UCIHAL_D("SessionTrack: worker thread exit.");
+ }
+
+ void QueueSessionTrackWork(std::shared_ptr<SessionTrackMsg> msg) {
+ msgq_->send(msg);
+
+ if (msg->sync_) {
+ std::unique_lock<std::mutex> lock(sync_mutex_);
+ if (msg->cond_.wait_for(lock, std::chrono::milliseconds(kQueueTimeoutMs)) == std::cv_status::timeout) {
+ NXPLOG_UCIHAL_E("SessionTrack: timeout to process %d", static_cast<int>(msg->type_));
+ }
+ }
+ }
+
+ std::shared_ptr<SessionInfo> GetSessionInfo(uint32_t session_handle) {
+ auto it = sessions_.find(session_handle);
+ if (it == sessions_.end()) {
+ NXPLOG_UCIHAL_E("SessionTrack: Session 0x%08x not registered", session_handle);
+ return NULL;
+ }
+ return it->second;
+ }
+
+ bool IsDeviceIdle() {
+ return sessions_.size() == 0;
+ }
+};
+
+static std::unique_ptr<SessionTrack> gSessionTrack;
+
+void SessionTrack_init()
+{
+ gSessionTrack = std::make_unique<SessionTrack>();
+}
+
+void SessionTrack_deinit()
+{
+ gSessionTrack.reset();
+}
+
+void SessionTrack_onCountryCodeChanged()
+{
+ if (gSessionTrack)
+ gSessionTrack->OnCountryCodeChanged();
+}
+
+void SessionTrack_onAppConfig(uint32_t session_handle, uint8_t channel)
+{
+ if (gSessionTrack)
+ gSessionTrack->OnChannelConfig(session_handle, channel);
+}
+
+void SessionTrack_keepAlive()
+{
+ if (gSessionTrack)
+ gSessionTrack->RefreshIdle();
+}
+
+void SessionTrack_onSessionInit(size_t packet_len, const uint8_t *packet)
+{
+ if (gSessionTrack)
+ gSessionTrack->OnSessionInit(packet_len, packet);
+}
\ No newline at end of file
diff --git a/halimpl/hal/sessionTrack.h b/halimpl/hal/sessionTrack.h
new file mode 100644
index 0000000..142fad7
--- /dev/null
+++ b/halimpl/hal/sessionTrack.h
@@ -0,0 +1,12 @@
+#ifndef _SESSIONTRACK_H_
+#define _SESSIONTRACK_H_
+
+#include <cstdint>
+
+void SessionTrack_init();
+void SessionTrack_deinit();
+void SessionTrack_onCountryCodeChanged();
+void SessionTrack_onAppConfig(uint32_t session_handle, uint8_t channel);
+void SessionTrack_keepAlive();
+void SessionTrack_onSessionInit(size_t packet_len, const uint8_t *packet);
+#endif
\ No newline at end of file
diff --git a/halimpl/utils/phNxpConfig.h b/halimpl/utils/phNxpConfig.h
index 8a5ff89..5afd9eb 100644
--- a/halimpl/utils/phNxpConfig.h
+++ b/halimpl/utils/phNxpConfig.h
@@ -77,6 +77,11 @@
/* libuwb-countrycode.conf parameters */
#define NAME_NXP_COUNTRY_CODE_VERSION "VERSION"
+#define NAME_AUTO_SUSPEND_ENABLE "AUTO_SUSPEND_ENABLE"
+#define NAME_AUTO_SUSPEND_TIMEOUT_MS "AUTO_SUSPEND_TIMEOUT_MS"
+
+#define NAME_DELETE_URSK_FOR_CCC_SESSION "DELETE_URSK_FOR_CCC_SESSION"
+
/* default configuration */
#define default_storage_location "/data/vendor/uwb"