diff --git a/sns_client_test/LICENSE b/sns_client_test/LICENSE new file mode 100644 index 0000000..753f441 --- /dev/null +++ b/sns_client_test/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2020 Qualcomm Innovation Center, Inc. All Rights Reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. +* Neither the name of the copyright holder nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written + permission. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS +LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Where there is uncertainty as to how, or +where, to apply marks, open an OSR to escalate to OSG for review. + +SPDX-License-Identifier: BSD-3-Clause-Clear diff --git a/sns_client_test/README.md b/sns_client_test/README.md new file mode 100644 index 0000000..b43a5ff --- /dev/null +++ b/sns_client_test/README.md @@ -0,0 +1,43 @@ +# sns_client_test +``` +A sample app based on sns_clent_example +The purpose is helping users to learn how to implement sensor client to connect to see +``` + +# Get Start (On build machine) + +## Connect build machine to board through USB-C +``` +$ adb shell +sh-4.4# cd /data +sh-4.4# git clone https://github.com/quic/sample-apps-for-Qualcomm-Robotics-RB5-platform.git + +``` +## Compile app on baord +``` +sh-4.4# cd sns_client_test/src +sh-4.4# make + +``` +## Test (on board) +On main board, make sure dip switch (DIP_SW_0) settings as below: + +DIP switch number + 1 2 3 4 5 6 + -------------------------------- +State | ON | ON | OFF | OFF | OFF | ON | + -------------------------------- + +The sensor device on board: +ICM-4x6xx, the SUID : 12370169555311111083ull + +sh-4.4# chmod +x sns_client_test + + Get help informatioon about sns_client_test +sh-4.4# ./sns_client_test -h + + Run accel test for 10 senconds +sh-4.4# ./sns_client_test -s0 -t10 + +## License +This is licensed under the BSD 3-Clause-Clear “New” or “Revised” License. Check out the [LICENSE](LICENSE) for more details. diff --git a/sns_client_test/inc/sensor_client.h b/sns_client_test/inc/sensor_client.h new file mode 100644 index 0000000..dedb9e1 --- /dev/null +++ b/sns_client_test/inc/sensor_client.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause-Clear +*/ + +#pragma once +#include +#include "sensor_connection.h" + +struct sensor_uid +{ + sensor_uid() : low(0), high(0) {} + sensor_uid(uint64_t low, uint64_t high): low(low), high(high) {} + bool operator==(const sensor_uid& rhs) const + { + return (low == rhs.low && high == rhs.high); + } + uint64_t low, high; +}; + +using suid_event_function = + std::function& suids)>; + +class sns_client +{ +public: + + sns_client(suid_event_function cb); + + void request_suid(std::string datatype, bool default_only = false); + +private: + suid_event_function _cb; + void handle_ssc_event(const uint8_t *data, size_t size); + std::unique_ptr _ssc_connect; + + ssc_event_cb_ts get_ssc_event_cb() + { + return [this](const uint8_t *data, size_t size, uint64_t ts) + { + handle_ssc_event(data, size); + }; + } +}; + diff --git a/sns_client_test/inc/sensor_connection.h b/sns_client_test/inc/sensor_connection.h new file mode 100644 index 0000000..dfb458b --- /dev/null +++ b/sns_client_test/inc/sensor_connection.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2020 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause-Clear +*/ +#pragma once +#include +#include +#include +#include +#include +#include "google/protobuf/io/zero_copy_stream_impl_lite.h" +#include "qmi_client.h" +#include "sns_client_api_v01.h" +#include "utils/SystemClock.h" + +using namespace std; +using namespace google::protobuf::io; + +#define android_loge ALOGE +#define android_logi ALOGI +#define android_logd ALOGD +#define android_logv ALOGV + +enum ssc_error_type +{ + SSC_CONNECTION_RESET +}; + +using ssc_event_cb_ts = std::function; + +using ssc_error_cb = std::function; + +using ssc_resp_cb = std::function; + +class see_connection +{ +public: + see_connection(ssc_event_cb_ts event_cb_ts): + _event_cb_ts(event_cb_ts), + _connection_closed(false) { + see_connect(); + } + + ~see_connection(){ + _connection_closed = true; + see_disconnect(); + } + + void send_request(const string& pb_req_message_encoded, bool use_qmi_sync); + void register_error_cb(ssc_error_cb cb) { _error_cb = cb; } + void register_resp_cb(ssc_resp_cb cb) { _resp_cb = cb; } + +private: + ssc_event_cb_ts _event_cb_ts; + qmi_client_type _qmi_handle = nullptr; + bool _service_ready; + std::mutex _mutex; + std::condition_variable _cv; + qmi_cci_os_signal_type _os_params; + + bool _connection_closed; + ssc_error_cb _error_cb; + ssc_resp_cb _resp_cb; + static const uint32_t QMI_RESPONSE_TIMEOUT_MS = 2000; + sns_client_resp_msg_v01 resp_async = {}; + void see_connect(); + void see_disconnect(); + void wait_for_see_service(); + static void see_notify_cb(qmi_client_type user_handle, + qmi_idl_service_object_type service_obj, + qmi_client_notify_event_type service_event, + void *notify_cb_data) + { + see_connection *conn = (see_connection *) notify_cb_data; + unique_lock lk(conn->_mutex); + conn->_service_ready = true; + conn->_cv.notify_one(); + } + + static void see_indication_cb(qmi_client_type user_handle, + unsigned int msg_id, void* ind_buf, + unsigned int ind_buf_len, + void* ind_cb_data) + { + struct timespec ts; + if (clock_gettime(CLOCK_BOOTTIME, &ts) == -1) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } + + uint64_t sample_received_ts = 1000000000*(ts.tv_sec) + ts.tv_nsec; + see_connection *conn = (see_connection*)ind_cb_data; + conn->handle_indication(msg_id, ind_buf, ind_buf_len, sample_received_ts); + } + + static void qmi_response_cb(qmi_client_type user_handle, + unsigned int msg_id, + void* resp_cb, + unsigned int resp_cb_len, + void* resp_cb_data, + qmi_client_error_type qmi_err) + { + see_connection *conn = (see_connection*)resp_cb_data; + sns_client_resp_msg_v01 resp = *((sns_client_resp_msg_v01 *)resp_cb); + if (nullptr != conn){ + if(conn->_resp_cb && resp.result_valid) { + conn->_resp_cb(resp.result); + } + } + } + + static void qmi_error_cb(qmi_client_type user_handle, + qmi_client_error_type error, + void* err_cb_data) + { + see_connection* conn = (see_connection*)err_cb_data; + android_loge("error=%d", error); + if (error != QMI_NO_ERR) { + if (conn->_error_cb) + conn->_error_cb(SSC_CONNECTION_RESET); + } + } + + void handle_indication(unsigned int msg_id, + void *ind_buf, + unsigned int ind_buf_len, + uint64_t ts); +}; + +class sensor_connection +{ +public: + + sensor_connection(ssc_event_cb_ts event_cb); + + ~sensor_connection(); + + void send_request(const std::string& pb_req_message_encoded, bool use_qmi_sync = true); + + void register_error_cb(ssc_error_cb cb); + + void register_resp_cb(ssc_resp_cb cb); + +private: + see_connection* _see_conn; + +}; diff --git a/sns_client_test/src/Makefile b/sns_client_test/src/Makefile new file mode 100644 index 0000000..245bec8 --- /dev/null +++ b/sns_client_test/src/Makefile @@ -0,0 +1,71 @@ +# Copyright (c) 2020 Qualcomm Innovation Center, Inc. All Rights Reserved. +# +# SPDX-License-Identifier: BSD-3-Clause-Clear + +IDIR =../inc + +BIN_PROG = sns_client_test + +CC=g++ + +AM_CPPFLAGS = -Werror \ + -Wall \ + -Wno-unused-parameter \ + -Wno-unused-variable \ + -fexceptions \ + -I/usr/include/qrb5165 \ + -I/usr/include/qrb5165/cutils \ + -I/usr/include/qrb5165/qmi-framework + +CFLAGS = $(AM_CPPFLAGS) -I$(IDIR) + +LDIR =/usr/lib + +_SEE_LIBS = libsnsapi.so \ + libsensorslog.so + +SEE_LIBS = $(patsubst %,$(LDIR)/%,$(_SEE_LIBS)) + +_QMIFRAMEWORK_LIBS = libqmi_common_so.so.1 \ + libqmi_encdec.so.1 \ + libqmi_cci.so.1 \ + libqmi_csi.so.1 \ + libqmi_sap.so.1 +QMIFRAMEWORK_LIBS = $(patsubst %,$(LDIR)/%,$(_QMIFRAMEWORK_LIBS)) + +_ANDROID_LIBS = liblog.so.0 \ + libcutils.so.0 \ + libtime_genoff.so.1 + +ANDROID_LIBS = $(patsubst %,$(LDIR)/%,$(_ANDROID_LIBS)) + +PROTOBUF_LIBS = $(LDIR)/libprotobuf.so.13 \ + -pthread \ + $(LDIR)/aarch64-linux-gnu/libpthread.so + +requiredlibs = $(SEE_LIBS) \ + $(ANDROID_LIBS) \ + $(QMIFRAMEWORK_LIBS) \ + $(PROTOBUF_LIBS) + +LIBS = -lm $(requiredlibs) + + +_DEPS = sensor_client.h sensor_connection.h +DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS)) + +OBJ = sensor_client_test.o \ + sensor_connection.o \ + sensor_client.o + +%.o: %.cpp $(DEPS) + $(CC) -c -o $@ $< $(CFLAGS) + +$(BIN_PROG): $(OBJ) + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +.PHONY: clean + +clean: + rm -f *.o $(BIN_PROG) + diff --git a/sns_client_test/src/sensor_client.cpp b/sns_client_test/src/sensor_client.cpp new file mode 100644 index 0000000..344fd8d --- /dev/null +++ b/sns_client_test/src/sensor_client.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause-Clear +*/ +#include +#include +#include +#include +#include +#include +#include "sns_suid.pb.h" +#include "sns_client.pb.h" +#include "sensor_client.h" + +using namespace std; +using namespace google::protobuf::io; +using namespace std::chrono; + +sns_client::sns_client(suid_event_function cb) : _cb(cb) +{ + _ssc_connect = make_unique(get_ssc_event_cb()); +} + +void sns_client::request_suid(std::string datatype, bool default_only) +{ + sns_client_request_msg req_msg; + sns_suid_req suid_req; + string suid_req_encoded; + const sensor_uid LOOKUP_SUID = { + 12370169555311111083ull, + 12370169555311111083ull + }; + + android_logv("requesting suid for %s, ts = %fs", datatype.c_str(), + duration_cast>(high_resolution_clock::now(). + time_since_epoch()).count()); + + /* populate SUID request */ + suid_req.set_data_type(datatype); + suid_req.set_register_updates(true); + suid_req.set_default_only(default_only); + suid_req.SerializeToString(&suid_req_encoded); + + /* populate the client request message */ + req_msg.set_msg_id(SNS_SUID_MSGID_SNS_SUID_REQ); + req_msg.mutable_request()->set_payload(suid_req_encoded); + req_msg.mutable_suid()->set_suid_high(LOOKUP_SUID.high); + req_msg.mutable_suid()->set_suid_low(LOOKUP_SUID.low); + req_msg.mutable_susp_config()->set_delivery_type(SNS_CLIENT_DELIVERY_WAKEUP); + req_msg.mutable_susp_config()->set_client_proc_type(SNS_STD_CLIENT_PROCESSOR_APSS); + string req_msg_encoded; + req_msg.SerializeToString(&req_msg_encoded); + _ssc_connect->send_request(req_msg_encoded); +} + +void sns_client::handle_ssc_event(const uint8_t *data, size_t size) +{ + /* parse the pb encoded event */ + sns_client_event_msg event_msg; + event_msg.ParseFromArray(data, size); + /* iterate over all events in the message */ + for (int i = 0; i < event_msg.events_size(); i++) { + auto& event = event_msg.events(i); + if (event.msg_id() != SNS_SUID_MSGID_SNS_SUID_EVENT) { + android_loge("invalid event msg_id=%d", event.msg_id()); + continue; + } + sns_suid_event suid_event; + suid_event.ParseFromString(event.payload()); + const string& datatype = suid_event.data_type(); + + android_logv("suid_event for %s, num_suids=%d, ts=%fs", datatype.c_str(), + suid_event.suid_size(), + duration_cast>(high_resolution_clock::now(). + time_since_epoch()).count()); + + /* create a list of all suids found for this datatype */ + vector suids(suid_event.suid_size()); + for (int j=0; j < suid_event.suid_size(); j++) { + suids[j] = sensor_uid(suid_event.suid(j).suid_low(), + suid_event.suid(j).suid_high()); + } + /* send callback for this datatype */ + _cb(datatype, suids); + } +} diff --git a/sns_client_test/src/sensor_client_test.cpp b/sns_client_test/src/sensor_client_test.cpp new file mode 100644 index 0000000..d662cf7 --- /dev/null +++ b/sns_client_test/src/sensor_client_test.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2020 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause-Clear +*/ + +#include +#include +#include +#include "sensor_client.h" +#include +#include +#include +#include "sns_std_sensor.pb.h" +#include "sns_std_type.pb.h" +#include "sns_client.pb.h" + +using namespace std; + +#define TEST_TIME 20 // 10 Seconds +#define TEST_SAMPLE_RATE 25 // 10 Hz +#define TEST_BATCH_PERIOD 0 // 0 ms + +static int total_samples_rxved = 0; +static sensor_connection *connection; +static const vector sensor_names = {"accel", "gyro"}; +static string sensor_name; + +/** + * Event callback function, as registered with sensor_connection. + */ +static void sensor_event_cb(const uint8_t *data, size_t size, uint64_t ts) +{ + sns_client_event_msg pb_event_msg; + + android_loge("Received QMI indication with length %zu", size); + + pb_event_msg.ParseFromArray(data, size); + for(int i = 0; i < pb_event_msg.events_size(); i++) + { + const sns_client_event_msg_sns_client_event &pb_event= pb_event_msg.events(i); + android_loge("Event[%i] msg_id=%i, ts=%llu", i, pb_event.msg_id(), + (unsigned long long)pb_event.timestamp()); + + if(SNS_STD_MSGID_SNS_STD_ERROR_EVENT == pb_event.msg_id()) + { + sns_std_error_event error; + error.ParseFromString(pb_event.payload()); + + android_loge("Received error event %i", error.error()); + } + else if(SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_PHYSICAL_CONFIG_EVENT == pb_event.msg_id()) + { + sns_std_sensor_physical_config_event config; + config.ParseFromString(pb_event.payload()); + + android_loge("Received config event with sample rate %f", config.sample_rate()); + } + else if(SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_EVENT == pb_event.msg_id()) + { + sns_std_sensor_event event; + event.ParseFromString(pb_event.payload()); + android_loge("Received sample <%f, %f, %f>", + event.data(0), event.data(1), event.data(2)); + total_samples_rxved++; + } + else + { + android_loge("Received unknown message ID %i", pb_event.msg_id()); + } + } +} + +/** + * Send a SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_CONFIG to SUID + */ +static void send_config_req(sensor_connection *conn, sensor_uid const *suid) +{ + string req_msg_encoded; + string config_encoded; + sns_client_request_msg req_msg; + sns_std_sensor_config config; + + android_logi("Send config request with sample rate %i and batch period %d", + TEST_SAMPLE_RATE, TEST_BATCH_PERIOD); + + config.set_sample_rate(TEST_SAMPLE_RATE); + config.SerializeToString(&config_encoded); + + req_msg.set_msg_id(SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_CONFIG); + req_msg.mutable_request()->set_payload(config_encoded); + req_msg.mutable_suid()->set_suid_high(suid->high); + req_msg.mutable_suid()->set_suid_low(suid->low); + req_msg.mutable_susp_config()->set_delivery_type(SNS_CLIENT_DELIVERY_WAKEUP); + req_msg.mutable_susp_config()->set_client_proc_type(SNS_STD_CLIENT_PROCESSOR_APSS); + req_msg.mutable_request()->mutable_batching()->set_batch_period(TEST_BATCH_PERIOD); + req_msg.SerializeToString(&req_msg_encoded); + conn->send_request(req_msg_encoded); +} + +/** + * see callback as registered with sns_client. + */ +static void see_cb(const std::string& datatype, const std::vector& suids) +{ + android_logv("Received SUID event with length %zu", suids.size()); + if(suids.size() > 0) + { + sensor_uid suid = suids.at(0); + connection = new sensor_connection(sensor_event_cb); + + android_logi("Received SUID %" PRIx64 "%" PRIx64 " for '%s'", + suid.high, suid.low, datatype.c_str()); + + printf("Received SUID %" PRIx64 "%" PRIx64 " for '%s' \n", + suid.high, suid.low, datatype.c_str()); + send_config_req(connection, &suid); + } else { + android_logi("%s sensor is not available", sensor_name.c_str()); + cout << "sensor " << sensor_name << " is not available" << endl; + exit(-1); + } +} + +int main(int argc, char *argv[]) +{ + int test_time = TEST_TIME; + uint32_t sensor_index = 0; + int opt; + + /* parse command line options */ + while ((opt = getopt(argc, argv, "s:t:h")) != -1) { + switch (opt) { + case 's': + sensor_index = atoi(optarg); + if (sensor_index < 0 || sensor_index >= sensor_names.size()) { + cout << "Invalid snesor index, sensor index option : [0, " << sensor_names.size() - 1 << "]" << endl; + return -1; + } + break; + case 't': + test_time = atoi(optarg); + break; + case 'h': + cout << argv[0] << " option : " << endl; + cout << "-s [sensor] sensor = 0 ~ 1 [accel, gyro], default 0" << endl; + cout << "-t [test_time] senosr test time, default 20 sencods" << endl; + cout << "-h help" << endl; + return 0; + default: + cout << "Unknown options, default optiins for test" << endl; + break; + } + } + + cout << "<========================= Start sensor client test =========================>\n"; + + sensor_name = sensor_names[sensor_index]; + + cout << "streaming started for " << sensor_name << "set SR/RR" << TEST_SAMPLE_RATE << "/" << TEST_BATCH_PERIOD << + " and duration " << test_time << " seconds\n"; + + android_logi("streaming started for '%s' set SR/RR '(%d/%d)Hz' and duration '%dSec' ", sensor_name.c_str(), + TEST_SAMPLE_RATE, TEST_BATCH_PERIOD, test_time); + sns_client ssc_client(see_cb); + ssc_client.request_suid(sensor_name); + + cout << "Wait " << test_time << " seconds to receive sample data in callback" << endl; + + sleep(test_time); + delete connection; + android_logi("Received %d samples for '%s' sensor, set SR/RR '(%d/%d)Hz' and duration '%dSec'", + total_samples_rxved, sensor_name.c_str(), TEST_SAMPLE_RATE, TEST_BATCH_PERIOD, test_time); + + cout << "Received " << total_samples_rxved << " samples for " << sensor_name << "sensor, set SR/RR (" << + TEST_SAMPLE_RATE << "/" << TEST_BATCH_PERIOD << ")Hz and duration " << test_time << " seconds \n"; + + cout << "<========================= Sensor client test done ==========================>\n"; + + return 0; +} + + diff --git a/sns_client_test/src/sensor_connection.cpp b/sns_client_test/src/sensor_connection.cpp new file mode 100644 index 0000000..78a9204 --- /dev/null +++ b/sns_client_test/src/sensor_connection.cpp @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2020 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: BSD-3-Clause-Clear +*/ + +#include "sensor_connection.h" +#include "sensor_client.h" + +using namespace std; +using namespace google::protobuf::io; +#define MAX_SVC_INFO_ARRAY_SIZE 5 + +/* number of times to wait for sensors service */ +#define SENSORS_SERVICE_DISCOVERY_TRIES 4 +/* timeout for each try */ +#define SENSORS_SERVICE_DISCOVERY_TIMEOUT 1 + +/* exception type defining errors related to qmi API calls */ +struct qmi_error : public runtime_error +{ + qmi_error(int error_code, const std::string& what = "") : + runtime_error(what + ": " + error_code_to_string(error_code)) { } + + static string error_code_to_string(int code) + { + static const map error_map = { + { QMI_NO_ERR, "qmi no error" }, + { QMI_INTERNAL_ERR, "qmi internal error" }, + { QMI_TIMEOUT_ERR, "qmi timeout" }, + { QMI_XPORT_BUSY_ERR, "qmi transport busy" }, + { QMI_SERVICE_ERR, "qmi service error" }, + }; + string msg; + try { + msg = error_map.at(code); + } catch (out_of_range& e) { + msg = "qmi error"; + } + return msg + " (" + to_string(code) + ")"; + } +}; + +void see_connection::wait_for_see_service() +{ + qmi_client_type notifier_handle; + qmi_client_error_type qmi_err; + qmi_cci_os_signal_type os_params; + int num_tries = SENSORS_SERVICE_DISCOVERY_TRIES; + qmi_err = qmi_client_notifier_init(SNS_CLIENT_SVC_get_service_object_v01(), + &os_params, ¬ifier_handle); + if (QMI_NO_ERR != qmi_err) { + throw qmi_error(qmi_err, "qmi_client_notifier_init() failed"); + } + /* register a callback and wait until service becomes available */ + _service_ready = false; + qmi_err = qmi_client_register_notify_cb(notifier_handle, see_notify_cb, + this); + if (qmi_err != QMI_NO_ERR) { + qmi_client_release(notifier_handle); + throw qmi_error(qmi_err, "qmi_client_register_notify_cb() failed: %d"); + } + num_tries = SENSORS_SERVICE_DISCOVERY_TRIES; + std::unique_lock lk(_mutex); + bool timeout = false; + while (num_tries > 0 && (_service_ready != true)) { + num_tries--; + timeout = !_cv.wait_for(lk, std::chrono::seconds(SENSORS_SERVICE_DISCOVERY_TIMEOUT), + [this]{ return _service_ready; }); + if (timeout) { + if (num_tries == 0) { + lk.unlock(); + qmi_client_release(notifier_handle); + throw runtime_error( + "FATAL: could not find sensors QMI service"); + } + android_loge("timeout while waiting for sensors QMI service: " + "will try %d more time(s)", num_tries); + } + } + lk.unlock(); + qmi_client_release(notifier_handle); +} + +void see_connection::see_connect() +{ + qmi_idl_service_object_type svc_obj = + SNS_CLIENT_SVC_get_service_object_v01(); + + qmi_client_error_type qmi_err; + qmi_service_info svc_info_array[MAX_SVC_INFO_ARRAY_SIZE]; + uint32_t num_services, num_entries = MAX_SVC_INFO_ARRAY_SIZE; + + /*svc_info_array[5] - Initialized to 0 to avoid static analysis errors*/ + for(uint32_t i = 0 ; i < num_entries ; i++) + memset(&svc_info_array[i], 0, sizeof(svc_info_array[i])); + + android_logv("waiting for sensors qmi service"); + wait_for_see_service(); + android_logv("connecting to qmi service"); + qmi_err = qmi_client_get_service_list(svc_obj, svc_info_array, + &num_entries, &num_services); + if (QMI_NO_ERR != qmi_err) { + throw qmi_error(qmi_err, "qmi_client_get_service_list() failed"); + } + + if (num_entries == 0) { + throw runtime_error("sensors service has no available instances"); + } + + if (_connection_closed) { + android_logi("connection got closed do not open qmi_channel"); + return ; + } + + /* As only one qmi service is expected for sensors, use the 1st instance */ + qmi_service_info svc_info = svc_info_array[0]; + + std::unique_lock lk(_mutex); + qmi_err = qmi_client_init(&svc_info, svc_obj, see_indication_cb, + (void*)this, &_os_params, &_qmi_handle); + if (qmi_err != QMI_IDL_LIB_NO_ERR) { + lk.unlock(); + throw qmi_error(qmi_err, "qmi_client_init() failed"); + } + + qmi_err = qmi_client_register_error_cb(_qmi_handle, qmi_error_cb, this); + if (QMI_NO_ERR != qmi_err) { + lk.unlock(); + qmi_client_release(_qmi_handle); + throw qmi_error(qmi_err, "qmi_client_register_error_cb() failed"); + } + lk.unlock(); + android_logv("connected to ssc for %p", (void *)this); +} + +void see_connection::see_disconnect() +{ + std::unique_lock lk(_mutex); + if (_qmi_handle != nullptr) { + qmi_client_release(_qmi_handle); + _qmi_handle = nullptr; + /*in ssr call back and sensor disabled , so notify see_connect to comeout*/ + if (_connection_closed) + _cv.notify_one(); + } + /*explicit unlock not required , just added not to miss the logic*/ + lk.unlock(); + android_logv("disconnected from ssc for %p", (void *)this); +} + +void see_connection::handle_indication(unsigned int msg_id, void *ind_buf, + unsigned int ind_buf_len, uint64_t ts) +{ + int32_t qmi_err; + size_t ind_size; + size_t buffer_len; + uint8_t *buffer = NULL; + android_loge("msg_id %d " , msg_id); + + if(SNS_CLIENT_REPORT_IND_V01 == msg_id){ + sns_client_report_ind_msg_v01 *ind = NULL; + ind = (sns_client_report_ind_msg_v01 *)calloc(1, sizeof(sns_client_report_ind_msg_v01)); + if(ind == NULL){ + android_loge("Error while creating memory for ind"); + return; + } + ind_size = sizeof(sns_client_report_ind_msg_v01); + qmi_err = qmi_idl_message_decode(SNS_CLIENT_SVC_get_service_object_v01(), + QMI_IDL_INDICATION, msg_id, ind_buf, + ind_buf_len, (void*)ind, + ind_size); + if (QMI_IDL_LIB_NO_ERR != qmi_err) { + android_loge("qmi_idl_message_decode() failed. qmi_err=%d SNS_CLIENT_REPORT_IND_V01", qmi_err); + free(ind); + ind = NULL; + return; + } + android_loge("indication, payload_len=%u", ind->payload_len); + + buffer_len = ind->payload_len; + buffer = (uint8_t*)calloc(1, buffer_len); + if(buffer == NULL){ + android_loge("buffer failed to creat "); + free(ind); + ind = NULL; + return; + } + memcpy(buffer, ind->payload, buffer_len); + free(ind); + this->_event_cb_ts(buffer, buffer_len, ts); + free(buffer); + } + else if(SNS_CLIENT_JUMBO_REPORT_IND_V01 == msg_id){ + sns_client_jumbo_report_ind_msg_v01 *ind_jumbo = NULL; + ind_jumbo = (sns_client_jumbo_report_ind_msg_v01 *)calloc(1, sizeof(sns_client_jumbo_report_ind_msg_v01)); + if(ind_jumbo == NULL){ + android_loge("Error while creating memory for ind_jumbo"); + return; + } + ind_size = sizeof(sns_client_jumbo_report_ind_msg_v01); + qmi_err = qmi_idl_message_decode(SNS_CLIENT_SVC_get_service_object_v01(), + QMI_IDL_INDICATION, msg_id, ind_buf, + ind_buf_len, (void*)ind_jumbo, + ind_size); + if (QMI_IDL_LIB_NO_ERR != qmi_err) { + android_loge("qmi_idl_message_decode() failed. qmi_err=%d SNS_CLIENT_JUMBO_REPORT_IND_V01", qmi_err); + free(ind_jumbo); + ind_jumbo = NULL; + return; + } + android_loge("indication, payload_len=%u", ind_jumbo->payload_len); + buffer_len = ind_jumbo->payload_len; + buffer = (uint8_t*)calloc(1, buffer_len); + if(buffer == NULL){ + android_loge("buffer failed to creat ind_jumbo "); + free(ind_jumbo); + ind_jumbo = NULL; + return; + } + memcpy(buffer, ind_jumbo->payload, buffer_len); + free(ind_jumbo); + this->_event_cb_ts(buffer, buffer_len, ts); + free(buffer); + } else{ + android_loge("Unknown indication message ID %i", msg_id); + return; + } +} + +void see_connection::send_request(const string& pb_req_msg_encoded , bool use_qmi_sync) +{ + sns_client_req_msg_v01 req_msg; + if (pb_req_msg_encoded.size() > SNS_CLIENT_REQ_LEN_MAX_V01) { + throw runtime_error("error: payload size too large"); + } + + memcpy(req_msg.payload, pb_req_msg_encoded.c_str(), + pb_req_msg_encoded.size()); + + req_msg.use_jumbo_report_valid = true; + req_msg.use_jumbo_report = true; + req_msg.payload_len = pb_req_msg_encoded.size(); + qmi_client_error_type qmi_err; + sns_client_resp_msg_v01 resp = {}; + + if(use_qmi_sync == true) { + /* send a sync message to ssc */ + qmi_err = qmi_client_send_msg_sync(_qmi_handle, SNS_CLIENT_REQ_V01, + (void*)&req_msg, sizeof(req_msg), + &resp, + sizeof(sns_client_resp_msg_v01), + QMI_RESPONSE_TIMEOUT_MS); + /*in case of sync - send the response immediately to its clients */ + if(_resp_cb && resp.result_valid) + _resp_cb(resp.result); + } else { + qmi_txn_handle qmi_txn_handle; + qmi_err = qmi_client_send_msg_async(_qmi_handle, SNS_CLIENT_REQ_V01, + (void*)&req_msg, sizeof(req_msg), + &resp_async, + sizeof(sns_client_resp_msg_v01), + qmi_response_cb, + (void*)this, + &qmi_txn_handle); + } + if (qmi_err != QMI_NO_ERR){ + throw qmi_error(qmi_err, + "qmi_client_send_msg() failed, (client_id=)" + + to_string(resp.client_id) + ", result=" + + to_string(resp.result)); + } +} + +/* creates new connection to ssc */ +sensor_connection::sensor_connection(ssc_event_cb_ts event_cb) +{ + _see_conn = new see_connection(event_cb); + android_logv("ssc connected"); +} + +sensor_connection::~sensor_connection() +{ + delete _see_conn; + android_logv("ssc disconnected"); +} + +/* send encoded client request message to ssc */ +void sensor_connection::send_request(const std::string& pb_req_message_encoded , bool use_qmi_sync) +{ + if(_see_conn) + _see_conn->send_request(pb_req_message_encoded, use_qmi_sync); + else + android_loge("_see_conn is NULL"); +} + +void sensor_connection::register_error_cb(ssc_error_cb cb) +{ + _see_conn->register_error_cb(cb); +} + +void sensor_connection::register_resp_cb(ssc_resp_cb cb) +{ + _see_conn->register_resp_cb(cb); +}