1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "remoting/client/client_status_logger.h" 6 7 #include "base/macros.h" 8 #include "base/rand_util.h" 9 #include "remoting/client/chromoting_stats.h" 10 #include "remoting/client/server_log_entry_client.h" 11 12 using remoting::protocol::ConnectionToHost; 13 14 namespace { 15 16 const char kSessionIdAlphabet[] = 17 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 18 const int kSessionIdLength = 20; 19 20 const int kMaxSessionIdAgeDays = 1; 21 22 bool IsStartOfSession(ConnectionToHost::State state) { 23 return state == ConnectionToHost::INITIALIZING || 24 state == ConnectionToHost::CONNECTING || 25 state == ConnectionToHost::AUTHENTICATED || 26 state == ConnectionToHost::CONNECTED; 27 } 28 29 bool IsEndOfSession(ConnectionToHost::State state) { 30 return state == ConnectionToHost::FAILED || 31 state == ConnectionToHost::CLOSED; 32 } 33 34 bool ShouldAddDuration(ConnectionToHost::State state) { 35 // Duration is added to log entries at the end of the session, as well as at 36 // some intermediate states where it is relevant (e.g. to determine how long 37 // it took for a session to become CONNECTED). 38 return IsEndOfSession(state) || state == ConnectionToHost::CONNECTED; 39 } 40 41 } // namespace 42 43 namespace remoting { 44 45 ClientStatusLogger::ClientStatusLogger(ServerLogEntry::Mode mode, 46 SignalStrategy* signal_strategy, 47 const std::string& directory_bot_jid) 48 : log_to_server_(mode, signal_strategy, directory_bot_jid) { 49 } 50 51 ClientStatusLogger::~ClientStatusLogger() { 52 } 53 54 void ClientStatusLogger::LogSessionStateChange( 55 protocol::ConnectionToHost::State state, 56 protocol::ErrorCode error) { 57 DCHECK(CalledOnValidThread()); 58 59 scoped_ptr<ServerLogEntry> entry( 60 MakeLogEntryForSessionStateChange(state, error)); 61 AddClientFieldsToLogEntry(entry.get()); 62 entry->AddModeField(log_to_server_.mode()); 63 64 MaybeExpireSessionId(); 65 if (IsStartOfSession(state)) { 66 // Maybe set the session ID and start time. 67 if (session_id_.empty()) { 68 GenerateSessionId(); 69 } 70 if (session_start_time_.is_null()) { 71 session_start_time_ = base::TimeTicks::Now(); 72 } 73 } 74 75 if (!session_id_.empty()) { 76 AddSessionIdToLogEntry(entry.get(), session_id_); 77 } 78 79 // Maybe clear the session start time and log the session duration. 80 if (ShouldAddDuration(state) && !session_start_time_.is_null()) { 81 AddSessionDurationToLogEntry(entry.get(), 82 base::TimeTicks::Now() - session_start_time_); 83 } 84 85 if (IsEndOfSession(state)) { 86 session_start_time_ = base::TimeTicks(); 87 session_id_.clear(); 88 } 89 90 log_to_server_.Log(*entry.get()); 91 } 92 93 void ClientStatusLogger::LogStatistics(ChromotingStats* statistics) { 94 DCHECK(CalledOnValidThread()); 95 96 MaybeExpireSessionId(); 97 98 scoped_ptr<ServerLogEntry> entry(MakeLogEntryForStatistics(statistics)); 99 AddClientFieldsToLogEntry(entry.get()); 100 entry->AddModeField(log_to_server_.mode()); 101 AddSessionIdToLogEntry(entry.get(), session_id_); 102 log_to_server_.Log(*entry.get()); 103 } 104 105 void ClientStatusLogger::SetSignalingStateForTest(SignalStrategy::State state) { 106 log_to_server_.OnSignalStrategyStateChange(state); 107 } 108 109 void ClientStatusLogger::GenerateSessionId() { 110 session_id_.resize(kSessionIdLength); 111 for (int i = 0; i < kSessionIdLength; i++) { 112 const int alphabet_size = arraysize(kSessionIdAlphabet) - 1; 113 session_id_[i] = kSessionIdAlphabet[base::RandGenerator(alphabet_size)]; 114 } 115 session_id_generation_time_ = base::TimeTicks::Now(); 116 } 117 118 void ClientStatusLogger::MaybeExpireSessionId() { 119 if (session_id_.empty()) { 120 return; 121 } 122 123 base::TimeDelta max_age = base::TimeDelta::FromDays(kMaxSessionIdAgeDays); 124 if (base::TimeTicks::Now() - session_id_generation_time_ > max_age) { 125 // Log the old session ID. 126 scoped_ptr<ServerLogEntry> entry(MakeLogEntryForSessionIdOld(session_id_)); 127 entry->AddModeField(log_to_server_.mode()); 128 log_to_server_.Log(*entry.get()); 129 130 // Generate a new session ID. 131 GenerateSessionId(); 132 133 // Log the new session ID. 134 entry = MakeLogEntryForSessionIdNew(session_id_); 135 entry->AddModeField(log_to_server_.mode()); 136 log_to_server_.Log(*entry.get()); 137 } 138 } 139 140 } // namespace remoting 141