blob: a924b8ab4a9791c5750bc4de688e35f8ce7782b1 [file] [log] [blame]
Darin Petkov65b01462010-04-14 13:32:20 -07001// Copyright (c) 2010 The Chromium OS 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 "metrics_daemon.h"
6#include "metrics_library.h"
7
8#include <glib-object.h>
9
10extern "C" {
11#include "marshal_void__string_boxed.h"
12}
13
14#include <base/logging.h>
15
16#define SAFE_MESSAGE(e) ((e && e->message) ? e->message : "unknown error")
17
18MetricsDaemon::NetworkState
19MetricsDaemon::network_states_[MetricsDaemon::kNumberNetworkStates] = {
20#define STATE(name, capname) { #name, "Connman" # capname },
21#include "network_states.h"
22};
23
24void MetricsDaemon::Run(bool run_as_daemon, bool testing) {
25 Init(testing);
26 if (!run_as_daemon || daemon(0, 0) == 0) {
27 Loop();
28 }
29}
30
31void MetricsDaemon::Init(bool testing) {
32 testing_ = testing;
33 network_state_id_ = kUnknownNetworkStateId;
34
35 ::g_thread_init(NULL);
36 ::g_type_init();
37 ::dbus_g_thread_init();
38
39 ::GError* error = NULL;
40 ::DBusGConnection* dbc = ::dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
41 // Note that LOG(FATAL) terminates the process; otherwise we'd have to worry
42 // about leaking |error|.
43 LOG_IF(FATAL, dbc == NULL) <<
44 "cannot connect to dbus: " << SAFE_MESSAGE(error);
45
46 ::DBusGProxy* net_proxy = ::dbus_g_proxy_new_for_name(
47 dbc, "org.moblin.connman", "/", "org.moblin.connman.Metrics");
48 LOG_IF(FATAL, net_proxy == NULL) << "no dbus proxy for network";
49
50#if 0
51 // Unclear how soon one can call dbus_g_type_get_map(). Doing it before the
52 // call to dbus_g_bus_get() results in a (non-fatal) assertion failure.
53 // GetProperties returns a hash table.
54 hashtable_gtype = ::dbus_g_type_get_map("GHashTable", G_TYPE_STRING,
55 G_TYPE_VALUE);
56#endif
57
58 dbus_g_object_register_marshaller(marshal_VOID__STRING_BOXED,
59 G_TYPE_NONE,
60 G_TYPE_STRING,
61 G_TYPE_VALUE,
62 G_TYPE_INVALID);
63 ::dbus_g_proxy_add_signal(net_proxy, "ConnectionStateChanged",
64 G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
65 ::dbus_g_proxy_connect_signal(net_proxy, "ConnectionStateChanged",
66 G_CALLBACK(&StaticNetSignalHandler),
67 this, NULL);
68}
69
70void MetricsDaemon::Loop() {
71 ::GMainLoop* loop = ::g_main_loop_new(NULL, false);
72 ::g_main_loop_run(loop);
73}
74
75void MetricsDaemon::StaticNetSignalHandler(::DBusGProxy* proxy,
76 const char* property,
77 const ::GValue* value,
78 void *data) {
79 (static_cast<MetricsDaemon*>(data))->NetSignalHandler(proxy, property, value);
80}
81
82void MetricsDaemon::NetSignalHandler(::DBusGProxy* proxy,
83 const char* property,
84 const ::GValue* value) {
85 if (strcmp("ConnectionState", property) != 0) {
86 return;
87 }
88
89 const char* newstate = static_cast<const char*>(g_value_get_string(value));
90 LogNetworkStateChange(newstate);
91}
92
93void MetricsDaemon::LogNetworkStateChange(const char* newstate) {
94 NetworkStateId new_id = GetNetworkStateId(newstate);
95 if (new_id == kUnknownNetworkStateId) {
96 LOG(WARNING) << "unknown network connection state " << newstate;
97 return;
98 }
99 NetworkStateId old_id = network_state_id_;
100 if (new_id == old_id) { // valid new state and no change
101 return;
102 }
103 struct timeval now;
104 if (gettimeofday(&now, NULL) != 0) {
105 PLOG(WARNING) << "gettimeofday";
106 }
107 if (old_id != kUnknownNetworkStateId) {
108 struct timeval diff;
109 timersub(&now, &network_state_start_, &diff);
110 int diff_ms = diff.tv_usec / 1000 + diff.tv_sec * 1000;
111 // Saturates rather than overflowing. We expect this to be statistically
112 // insignificant, since INT_MAX milliseconds is 24.8 days.
113 if (diff.tv_sec >= INT_MAX / 1000) {
114 diff_ms = INT_MAX;
115 }
116 char buffer[100];
117 snprintf(buffer, sizeof(buffer), "%d", diff_ms);
118 if (testing_) {
119 TestPublishMetric(network_states_[old_id].stat_name, buffer);
120 } else {
121 ChromePublishMetric(network_states_[old_id].stat_name, buffer);
122 }
123 }
124 network_state_id_ = new_id;
125 network_state_start_ = now;
126}
127
128MetricsDaemon::NetworkStateId
129MetricsDaemon::GetNetworkStateId(const char* state_name) {
130 for (int i = 0; i < kNumberNetworkStates; i++) {
131 if (strcmp(state_name, network_states_[i].name) == 0) {
132 return static_cast<NetworkStateId>(i);
133 }
134 }
135 return static_cast<NetworkStateId>(-1);
136}
137
138void MetricsDaemon::ChromePublishMetric(const char* name, const char* value) {
139 MetricsLibrary::SendToChrome(name, value);
140}
141
142void MetricsDaemon::TestPublishMetric(const char* name, const char* value) {
143 LOG(INFO) << "received metric: " << name << " " << value;
144}