@@ -128,6 +128,20 @@ void Countly::setSha256(SHA256Function fun) {
128128 mutex->unlock ();
129129}
130130
131+ /* *
132+ * Enable manual session handling.
133+ */
134+ void Countly::enableManualSessionControl () {
135+ if (is_sdk_initialized) {
136+ log (LogLevel::WARNING, " [Countly][enableManualSessionControl] You can not enable manual session control after SDK initialization." );
137+ return ;
138+ }
139+
140+ mutex->lock ();
141+ configuration->manualSessionControl = true ;
142+ mutex->unlock ();
143+ }
144+
131145void Countly::setMetrics (const std::string &os, const std::string &os_version, const std::string &device, const std::string &resolution, const std::string &carrier, const std::string &app_version) {
132146 if (is_sdk_initialized) {
133147 log (LogLevel::WARNING, " [Countly][setMetrics] You can not set metrics after SDK initialization." );
@@ -332,17 +346,19 @@ void Countly::_changeDeviceIdWithoutMerge(const std::string &value) {
332346
333347 // send all event to server and end current session of old user
334348 flushEvents ();
335- if (began_session) {
349+ if (!configuration-> manualSessionControl ) {
336350 endSession ();
337- mutex->lock ();
338- session_params[" device_id" ] = value;
339- mutex->unlock ();
351+ }
352+
353+ mutex->lock ();
354+ session_params[" device_id" ] = value;
355+ mutex->unlock ();
356+
357+ // start a new session for new user
358+ if (!configuration->manualSessionControl ){
340359 beginSession ();
341- } else {
342- mutex->lock ();
343- session_params[" device_id" ] = value;
344- mutex->unlock ();
345360 }
361+
346362}
347363#pragma endregion Device Id
348364
@@ -387,7 +403,7 @@ void Countly::start(const std::string &app_key, const std::string &host, int por
387403 log (LogLevel::INFO, " [Countly][start] '_WIN32' is not defined" );
388404#endif
389405
390- enable_automatic_session = start_thread;
406+ enable_automatic_session = start_thread && !configuration-> manualSessionControl ;
391407 start_thread = true ;
392408
393409 if (port < 0 || port > 65535 ) {
@@ -422,9 +438,11 @@ void Countly::start(const std::string &app_key, const std::string &host, int por
422438
423439 if (!running) {
424440
425- mutex->unlock ();
426- beginSession ();
427- mutex->lock ();
441+ if (!configuration->manualSessionControl ){
442+ mutex->unlock ();
443+ beginSession ();
444+ mutex->lock ();
445+ }
428446
429447 if (start_thread) {
430448 stop_thread = false ;
@@ -451,7 +469,7 @@ void Countly::startOnCloud(const std::string &app_key) {
451469
452470void Countly::stop () {
453471 _deleteThread ();
454- if (began_session ) {
472+ if (!configuration-> manualSessionControl ) {
455473 endSession ();
456474 }
457475}
@@ -593,8 +611,16 @@ bool Countly::attemptSessionUpdateEQ() {
593611 return false ;
594612 }
595613#endif
596-
597- return !updateSession ();
614+ bool result;
615+ if (!configuration->manualSessionControl ){
616+ result = !updateSession ();
617+ } else {
618+ log (LogLevel::WARNING, " [Countly][attemptSessionUpdateEQ] SDK is in manual session control mode. Please start a session first." );
619+ result = false ;
620+ }
621+
622+ packEvents ();
623+ return result;
598624}
599625
600626void Countly::clearEQInternal () {
@@ -656,6 +682,7 @@ bool Countly::beginSession() {
656682 log (LogLevel::INFO, " [Countly][beginSession]" );
657683 if (began_session) {
658684 mutex->unlock ();
685+ log (LogLevel::DEBUG, " [Countly][beginSession] Session is already active." );
659686 return true ;
660687 }
661688
@@ -711,15 +738,42 @@ bool Countly::updateSession() {
711738 mutex->lock ();
712739 if (!began_session) {
713740 mutex->unlock ();
741+ if (configuration->manualSessionControl ){
742+ log (LogLevel::WARNING, " [Countly][updateSession] SDK is in manual session control mode and there is no active session. Please start a session first." );
743+ return false ;
744+ }
714745 if (!beginSession ()) {
715746 // if beginSession fails, we should not try to update session
716747 return false ;
717748 }
718749 mutex->lock ();
719750 began_session = true ;
720751 }
752+
753+ mutex->unlock ();
754+ auto duration = std::chrono::duration_cast<std::chrono::seconds>(getSessionDuration ());
755+ mutex->lock ();
756+
757+ // report session duration if it is greater than the configured session duration value
758+ if (duration.count () >= configuration->sessionDuration ) {
759+ log (LogLevel::DEBUG, " [Countly][updateSession] sending session update." );
760+ std::map<std::string, std::string> data = {{" app_key" , session_params[" app_key" ].get <std::string>()}, {" device_id" , session_params[" device_id" ].get <std::string>()}, {" session_duration" , std::to_string (duration.count ())}};
761+ requestModule->addRequestToQueue (data);
762+
763+ last_sent_session_request += duration;
764+ }
765+ } catch (const std::system_error &e) {
766+ std::ostringstream log_message;
767+ log_message << " update session, error: " << e.what ();
768+ log (LogLevel::FATAL, log_message.str ());
769+ }
770+ mutex->unlock ();
771+ return true ;
772+ }
721773
722- // events array
774+ void Countly::packEvents () {
775+ try {
776+ // events array
723777 nlohmann::json events = nlohmann::json::array ();
724778 std::string event_ids;
725779 mutex->unlock ();
@@ -740,20 +794,7 @@ bool Countly::updateSession() {
740794 } else {
741795 log (LogLevel::DEBUG, " [Countly][updateSession] EQ empty." );
742796 }
743- mutex->unlock ();
744- auto duration = std::chrono::duration_cast<std::chrono::seconds>(getSessionDuration ());
745- mutex->lock ();
746-
747- // report session duration if it is greater than the configured session duration value
748- if (duration.count () >= configuration->sessionDuration ) {
749- log (LogLevel::DEBUG, " [Countly][updateSession] sending session update." );
750- std::map<std::string, std::string> data = {{" app_key" , session_params[" app_key" ].get <std::string>()}, {" device_id" , session_params[" device_id" ].get <std::string>()}, {" session_duration" , std::to_string (duration.count ())}};
751- requestModule->addRequestToQueue (data);
752-
753- last_sent_session_request += duration;
754- }
755-
756- // report events if there are any to request queue
797+ // report events if there are any to request queue
757798 if (!no_events) {
758799 sendEventsToRQ (events);
759800 }
@@ -774,9 +815,9 @@ bool Countly::updateSession() {
774815 log (LogLevel::FATAL, log_message.str ());
775816 }
776817 mutex->unlock ();
777- return true ;
778818}
779819
820+
780821void Countly::sendEventsToRQ (const nlohmann::json &events) {
781822 log (LogLevel::DEBUG, " [Countly][sendEventsToRQ] Sending events to RQ." );
782823 std::map<std::string, std::string> data = {{" app_key" , session_params[" app_key" ].get <std::string>()}, {" device_id" , session_params[" device_id" ].get <std::string>()}, {" events" , events.dump ()}};
@@ -785,6 +826,10 @@ void Countly::sendEventsToRQ(const nlohmann::json &events) {
785826
786827bool Countly::endSession () {
787828 log (LogLevel::INFO, " [Countly][endSession]" );
829+ if (!began_session) {
830+ log (LogLevel::DEBUG, " [Countly][endSession] There is no active session to end." );
831+ return true ;
832+ }
788833 const std::chrono::system_clock::time_point now = Countly::getTimestamp ();
789834 const auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch ());
790835 const auto duration = std::chrono::duration_cast<std::chrono::seconds>(getSessionDuration (now));
@@ -1125,6 +1170,7 @@ void Countly::updateLoop() {
11251170 if (enable_automatic_session) {
11261171 updateSession ();
11271172 }
1173+ packEvents ();
11281174 requestModule->processQueue (mutex);
11291175 }
11301176 mutex->lock ();
0 commit comments