Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(319)

Side by Side Diff: src/service.cc

Issue 6395007: cashew: gmock integration (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/cashew.git@master
Patch Set: Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/service.h ('k') | src/service_impl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 1 // Copyright (c) 2010, 2011 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/service.h" 5 #include "src/service.h"
6 6
7 #include <glog/logging.h> 7 #include <glog/logging.h>
8 8
9 #include "src/data_plan_provider.h" 9 #include "src/service_impl.h"
10 #include "src/device.h"
11 #include "src/policy.h"
12 #include "src/service_manager.h"
13 10
14 namespace cashew { 11 namespace cashew {
15 12
16 // Flimflam Service D-Bus identifiers 13 // static
17 static const char *kFlimflamServiceName = "org.chromium.flimflam"; 14 Service::Type Service::TypeFromString(const std::string& type) {
18 15 return ServiceImpl::TypeFromString(type);
19 // Flimflam Service property names
20 static const char *kFlimflamServiceConnectivityStateProperty =
21 "ConnectivityState";
22 static const char *kFlimflamServiceDeviceProperty = "Device";
23 static const char *kFlimflamServiceStateProperty = "State";
24 static const char *kFlimflamServiceTypeProperty = "Type";
25 static const char *kFlimflamServiceUsageUrlProperty = "Cellular.UsageUrl";
26
27 // Flimflam Service on-the-wire ConnectivityState values
28 static const char *kFlimflamServiceConnectivityStateUnknown = "unknown";
29 static const char *kFlimflamServiceConnectivityStateRestricted = "restricted";
30 static const char *kFlimflamServiceConnectivityStateUnrestricted =
31 "unrestricted";
32 static const char *kFlimflamServiceConnectivityStateNone = "none";
33
34 // Flimflam Service on-the-wire State values
35 static const char *kFlimflamServiceStateIdle = "idle";
36 static const char *kFlimflamServiceStateCarrier = "carrier";
37 static const char *kFlimflamServiceStateAssociation = "association";
38 static const char *kFlimflamServiceStateConfiguration = "configuration";
39 static const char *kFlimflamServiceStateReady = "ready";
40 static const char *kFlimflamServiceStateDisconnect = "disconnect";
41 static const char *kFlimflamServiceStateFailure = "failure";
42 static const char *kFlimflamServiceStateActivationFailure =
43 "activation-failure";
44
45 // Flimflam Service on-the-wire Type values
46 static const char *kFlimflamServiceTypeEthernet = "ethernet";
47 static const char *kFlimflamServiceTypeWifi = "wifi";
48 static const char *kFlimflamServiceTypeWimax = "wimax";
49 static const char *kFlimflamServiceTypeBluetooth = "bluetooth";
50 static const char *kFlimflamServiceTypeCellular = "cellular";
51
52 // Chromium OS Usage API property names
53 static const char *kCrosUsageVersionProperty = "version";
54 static const char *kCrosUsageStatusProperty = "status";
55 static const char *kCrosUsageRestrictedProperty = "restricted";
56 static const char *kCrosUsagePlansProperty = "plans";
57
58 // Chromium OS Usage API version values
59 static const int kCrosUsageVersionMinSupported = 1;
60 static const int kCrosUsageVersionMaxSupported = 1;
61
62 // Chromium OS Usage API status values
63 // NOTE: add new values to IsValidCrosUsageStatus below also
64 static const char *kCrosUsageStatusOk = "OK";
65 static const char *kCrosUsageStatusError = "ERROR";
66 static const char *kCrosUsageStatusMalformedRequest = "MALFORMED REQUEST";
67 static const char *kCrosUsageStatusInternalError = "INTERNAL ERROR";
68 static const char *kCrosUsageStatusServiceUnavailable = "SERVICE UNAVAILABLE";
69 static const char *kCrosUsageStatusRequestRefused = "REQUEST REFUSED";
70 static const char *kCrosUsageStatusUnknownDevice = "UNKNOWN DEVICE";
71
72 // GetServiceProperties retry interval
73 static const guint kSecondsPerMinute = 60;
74 static const guint kGetServicePropertiesIntervalSeconds = 1 * kSecondsPerMinute;
75
76 Service::Service(ServiceManager * const parent,
77 DBus::Connection& connection, // NOLINT
78 const DBus::Path& path)
79 : DBus::ObjectProxy(connection, path, kFlimflamServiceName),
80 parent_(CHECK_NOTNULL(parent)), connection_(connection), path_(path),
81 state_(kStateUnknown), type_(kTypeUnknown), device_(NULL),
82 provider_(NULL), request_in_progress_(false),
83 update_timeout_source_(NULL), policy_(NULL),
84 is_default_service_(false), get_properties_source_id_(0),
85 retrying_get_properties_(false),
86 connectivity_state_(kConnectivityStateUnknown) {
87 // schedule a GetProperties() call to our Flimflam service path to init state
88 // we'll keep trying periodically until we succeed
89 // we'll subsequently update this state by monitoring PropertyChanged signals
90 get_properties_source_id_ =
91 g_idle_add(StaticGetServicePropertiesCallback, this);
92 if (get_properties_source_id_ == 0) {
93 LOG(ERROR) << path_ << ": ctor: g_idle_add failed";
94 }
95 property_changed_handler_.delegate(this);
96 }
97
98 Service::~Service() {
99 DeleteCarrierState();
100 DeleteDataPlans(&data_plans_);
101 if (device_ != NULL) {
102 DLOG(INFO) << path_ << ": deleting device " << device_->GetPath();
103 delete device_;
104 device_ = NULL;
105 }
106 if (get_properties_source_id_ != 0 &&
107 !g_source_remove(get_properties_source_id_)) {
108 DLOG(WARNING) << path_ << ": dtor: g_source_remove failed";
109 }
110 }
111
112 const DBus::Path& Service::GetPath() const {
113 return path_;
114 }
115
116 Service::State Service::GetState() const {
117 return state_;
118 }
119
120 Service::Type Service::GetType() const {
121 return type_;
122 } 16 }
123 17
124 // static 18 // static
125 Service::Type Service::TypeFromString(const std::string& type) { 19 Service* Service::NewService(ServiceManager * const parent,
126 if (type == kFlimflamServiceTypeEthernet) { 20 DBus::Connection& connection, // NOLINT
127 return kTypeEthernet; 21 const DBus::Path& path) {
128 } 22 DCHECK(parent != NULL);
129 if (type == kFlimflamServiceTypeWifi) { 23 return new(std::nothrow) ServiceImpl(parent, connection, path);
130 return kTypeWifi;
131 }
132 if (type == kFlimflamServiceTypeWimax) {
133 return kTypeWimax;
134 }
135 if (type == kFlimflamServiceTypeBluetooth) {
136 return kTypeBluetooth;
137 }
138 if (type == kFlimflamServiceTypeCellular) {
139 return kTypeCellular;
140 }
141 return kTypeUnknown;
142 }
143
144 Device* Service::GetDevice() const {
145 return device_;
146 }
147
148 DBusDataPlanList Service::GetDBusDataPlans() const {
149 DBusDataPlanList dbus_data_plans;
150 DataPlanList::const_iterator it;
151 for (it = data_plans_.begin(); it != data_plans_.end(); ++it) {
152 DataPlan *plan = *it;
153 DCHECK(plan != NULL);
154 // filter out inactive plans
155 if (plan->IsActive()) {
156 dbus_data_plans.push_back(plan->ToDBusFormat());
157 } else {
158 DLOG(INFO) << path_ << ": GetBusDataPlans: skipping inactive plan: \""
159 << plan->GetName() << "\"";
160 }
161 }
162 return dbus_data_plans;
163 }
164
165 bool Service::IsDefaultService() const {
166 if (is_default_service_) {
167 // cross-check with parent's idea of default technology
168 if (parent_->GetDefaultTechnology() != kTypeCellular) {
169 DLOG(WARNING) << path_ << ": IsDefaultService: "
170 << "service manager doesn't think default technology is cellular";
171 }
172 return true;
173 }
174 return false;
175 }
176
177 Service::ConnectivityState Service::GetConnectivityState() const {
178 return connectivity_state_;
179 }
180
181 // Flimflam Service D-Bus Proxy methods
182
183 void Service::PropertyChanged(const std::string& property_name,
184 const DBus::Variant& new_value) {
185 DLOG(INFO) << path_ << ": PropertyChanged: property_name = " << property_name;
186 // Queue a tuple representing this signal for later processing from the glib
187 // main loop. We do this to avoid libdbus-c++ deadlocks that can occur when
188 // sending a dbus message from within a dbus callback like this one.
189 PropertyChangedSignal signal(property_name, new_value);
190 property_changed_handler_.EnqueueSignal(signal);
191 }
192
193 // PropertyChangedDelegate methods
194
195 void Service::OnPropertyChanged(const PropertyChangedHandler *handler,
196 const std::string& property_name,
197 const DBus::Variant& new_value) {
198 DCHECK(handler == &property_changed_handler_);
199 DLOG(INFO) << path_ << ": OnPropertyChanged: property_name = "
200 << property_name;
201 if (property_name == kFlimflamServiceDeviceProperty) {
202 OnDeviceUpdate(new_value.reader().get_path());
203 } else if (property_name == kFlimflamServiceStateProperty) {
204 OnStateUpdate(new_value.reader().get_string());
205 } else if (property_name == kFlimflamServiceTypeProperty) {
206 OnTypeUpdate(new_value.reader().get_string());
207 } else if (property_name == kFlimflamServiceUsageUrlProperty) {
208 OnUsageUrlUpdate(new_value.reader().get_string());
209 } else if (property_name == kFlimflamServiceConnectivityStateProperty) {
210 OnConnectivityStateUpdate(new_value.reader().get_string());
211 } else {
212 // we don't care about this property
213 }
214 }
215
216 // Device methods
217
218 void Service::OnCarrierUpdate(const std::string& carrier) {
219 DLOG(INFO) << path_ << ": OnCarrierUpdate: carrier = " << carrier;
220 DCHECK(device_ != NULL);
221 DCHECK(carrier == device_->GetCarrier());
222
223 // get rid of state associated with old carrier
224 if (provider_ != NULL) {
225 DCHECK(carrier != provider_->GetCarrier());
226 }
227 DeleteCarrierState();
228
229 // if we don't have new carrier info, we can't do anything now
230 if (carrier.empty()) {
231 return;
232 }
233
234 // create state for new carrier
235 DLOG(INFO) << path_ << ": OnCarrierUpdate: creating data plan provider";
236 provider_ = new(std::nothrow) DataPlanProvider(carrier);
237 if (provider_ == NULL) {
238 LOG(ERROR) << path_
239 << ": OnCarrierUpdate: could not create data plan provider";
240 return;
241 }
242 provider_->SetDelegate(this);
243 DLOG(INFO) << path_ << ": OnCarrierUpdate: getting policy for " << carrier;
244 policy_ = Policy::GetPolicy(carrier);
245 if (policy_ == NULL) {
246 LOG(ERROR) << path_ << ": OnCarrierUpdate: could not get policy for "
247 << carrier;
248 DeleteCarrierState();
249 return;
250 }
251
252 // if we already have usage url, set new provider in motion
253 if (!usage_url_.empty()) {
254 provider_->SetUsageUrl(usage_url_);
255 ReconsiderSendingUsageRequests();
256 } else {
257 // we'll do this later in OnUsageUrlUpdate
258 }
259 }
260
261 void Service::OnByteCounterUpdate(uint64 rx_bytes, uint64 tx_bytes) {
262 DCHECK(device_ != NULL);
263 DCHECK(device_->ByteCounterRunning());
264 DLOG(INFO) << path_ << ": OnByteCounterUpdate: rx_bytes = " << rx_bytes
265 << ", tx_bytes = " << tx_bytes;
266 DataPlan *active_plan = DataPlan::GetActivePlan(data_plans_);
267 if (active_plan == NULL) {
268 DLOG(WARNING) << path_ << ": OnByteCounterUpdate: no active plan";
269 return;
270 }
271 Bytes local_bytes_used = rx_bytes + tx_bytes;
272 DLOG(INFO) << path_ << ": OnByteCounterUpdate: local_bytes_used = "
273 << local_bytes_used;
274 // try to detect two error conditions:
275 // (1) overflow of the unsigned addition above prior to the assignment
276 // (2) no overflow, but result has high order bit set and so is interpreted as
277 // a negative number when assigned to |local_bytes_used|
278 if (local_bytes_used < 0 ||
279 static_cast<uint64>(local_bytes_used) < rx_bytes ||
280 static_cast<uint64>(local_bytes_used) < tx_bytes) {
281 LOG(WARNING) << path_ << ": OnByteCounterUpdate: overflow detected";
282 return;
283 }
284 active_plan->SetLocalBytesUsed(local_bytes_used);
285 DLOG(INFO) << path_
286 << ": OnByteCounterUpdate: updated plan state: data bytes used = "
287 << active_plan->GetDataBytesUsed() << ", local bytes used = "
288 << active_plan->GetLocalBytesUsed() << ", total bytes used = "
289 << active_plan->GetTotalBytesUsed();
290 MaybeEmitDataPlansUpdate();
291 }
292
293 // DataPlanProviderDelegate methods
294
295 void Service::OnRequestComplete(const DataPlanProvider *provider,
296 bool successful,
297 const Value *parsed_usage_update) {
298 DCHECK(provider != NULL);
299 DCHECK(!successful || parsed_usage_update != NULL);
300 if (provider != provider_) {
301 DLOG(WARNING) << path_ << ": OnRequestComplete: wrong provider";
302 return;
303 }
304 DCHECK(request_in_progress_);
305 DCHECK(policy_ != NULL);
306 DLOG(INFO) << path_ << ": OnRequestComplete: result = " << successful;
307 request_in_progress_ = false;
308 if (!successful) {
309 LOG(WARNING) << path_ << ": OnRequestComplete: request failed";
310 return;
311 }
312
313 // interpret and validate parsed usage update
314 if (!parsed_usage_update->IsType(Value::TYPE_DICTIONARY)) {
315 LOG(WARNING) << path_
316 << ": OnRequestComplete: root value is not a dictionary";
317 return;
318 }
319 const DictionaryValue *root =
320 static_cast<const DictionaryValue*>(parsed_usage_update);
321
322 int version;
323 if (!root->GetInteger(kCrosUsageVersionProperty, &version)) {
324 LOG(WARNING) << path_ << ": OnRequestComplete: no version property";
325 return;
326 }
327 DLOG(INFO) << path_ << ": OnRequestComplete: version = " << version;
328 if (version < kCrosUsageVersionMinSupported ||
329 version > kCrosUsageVersionMaxSupported) {
330 LOG(WARNING) << path_ << ": OnRequestComplete: version " << version
331 << " not in supported range of [" << kCrosUsageVersionMinSupported
332 << ", " << kCrosUsageVersionMaxSupported << "]";
333 return;
334 }
335
336 std::string status;
337 if (!root->GetString(kCrosUsageStatusProperty, &status)) {
338 LOG(WARNING) << path_ << ": OnRequestComplete: no status property";
339 return;
340 }
341 DLOG(INFO) << path_ << ": OnRequestComplete: status = " << status;
342 if (!IsValidCrosUsageStatus(status)) {
343 LOG(WARNING) << path_ << ": OnRequestComplete: invalid status: " << status;
344 return;
345 }
346 if (status != kCrosUsageStatusOk) {
347 LOG(WARNING) << path_ << ": OnRequestComplete: status: " << status;
348 OnCrosUsageErrorResult(status);
349 return;
350 }
351
352 bool restricted = false;
353 if (root->GetBoolean(kCrosUsageRestrictedProperty, &restricted)) {
354 DLOG(INFO) << path_ << ": OnRequestComplete: restricted = " << restricted;
355 // TODO(vlaviano): this should probably be combined with flimflam's
356 // ConnectivityState property to arrive at a conclusion.
357
358 } else {
359 DLOG(INFO) << path_ << ": OnRequestComplete: no restricted property";
360 // restricted property is optional
361 }
362
363 ListValue *plans = NULL; // list to which this points is owned by dictionary
364 if (!root->GetList(kCrosUsagePlansProperty, &plans)) {
365 LOG(WARNING) << path_ << ": OnRequestComplete: no plans property";
366 return;
367 }
368 DCHECK(plans != NULL);
369 size_t plans_size = plans->GetSize();
370 DLOG(INFO) << path_ << ": OnRequestComplete: plans list has size "
371 << plans_size;
372
373 // walk plans list and convert each into a DataPlan object
374 DataPlanList new_plans;
375 for (size_t i = 0; i < plans_size; ++i) {
376 DictionaryValue *plan_dict = NULL;
377 if (!plans->GetDictionary(i, &plan_dict)) {
378 LOG(WARNING) << path_
379 << ": OnRequestComplete: could not get plan[" << i << "]";
380 continue;
381 }
382 DCHECK(plan_dict != NULL);
383 DataPlan *plan = DataPlan::FromDictionaryValue(plan_dict, policy_);
384 if (plan == NULL) {
385 LOG(WARNING) << path_
386 << ": OnRequestComplete: could not convert plan[" << i << "]";
387 continue;
388 }
389 DLOG(INFO) << path_ << ": OnRequestComplete: converted plan[" << i << "]";
390 new_plans.push_back(plan);
391 }
392
393 // store new info, causing it to be used for subsequent updates to clients
394 DataPlanList old_plans = data_plans_;
395 data_plans_ = new_plans;
396 LOG(INFO) << path_ << ": OnRequestComplete: updated data plans";
397
398 MaybeEmitDataPlansUpdate();
399
400 DLOG(INFO) << path_ << ": OnRequestComplete: deleting old data plans";
401 DeleteDataPlans(&old_plans);
402
403 // start local byte counter so that we can stop hitting usage API
404 // we'll stop the counter when we disconnect
405 // we don't bother if carrier told us that there are no active data plans
406 DataPlan *active_plan = DataPlan::GetActivePlan(data_plans_);
407 if (active_plan == NULL) {
408 DLOG(INFO) << path_
409 << ": OnRequestComplete: no active plans, not starting byte counter";
410 return;
411 }
412 DCHECK(device_ != NULL);
413 DCHECK(!device_->ByteCounterRunning());
414 if (!device_->StartByteCounter()) {
415 LOG(WARNING) << path_
416 << ": OnRequestComplete: could not start byte counter";
417 // we'll keep the update timer running and try again later
418 }
419 DLOG(INFO) << path_ << ": OnRequestComplete: started byte counter";
420 ReconsiderSendingUsageRequests();
421 }
422
423 // Service Manager methods
424
425 void Service::OnDefaultServiceUpdate(bool is_default_service) {
426 DCHECK(is_default_service_ == !is_default_service);
427 DLOG(INFO) << path_ << ": OnDefaultServiceUpdate: is_default_service = "
428 << is_default_service;
429 is_default_service_ = is_default_service;
430 if (is_default_service_) {
431 // we just became the default service
432 // cross-check with parent's idea of default technology
433 if (parent_->GetDefaultTechnology() != kTypeCellular) {
434 DLOG(WARNING) << path_ << ": OnDefaultServiceUpdate: "
435 << "service manager doesn't think default technology is cellular";
436 }
437 }
438 ReconsiderSendingUsageRequests();
439 }
440
441 void Service::OnFlimflamOnline() {
442 DLOG(INFO) << path_ << ": OnFlimflamOnline";
443 ReconsiderSendingUsageRequests();
444 }
445
446 void Service::OnFlimflamOffline() {
447 DLOG(INFO) << path_ << ": OnFlimflamOffline";
448 ReconsiderSendingUsageRequests();
449 }
450
451 // glib integration interface
452
453 guint Service::GetGetPropertiesSourceId() const {
454 return get_properties_source_id_;
455 }
456
457 void Service::SetGetPropertiesSourceId(guint source_id) {
458 get_properties_source_id_ = source_id;
459 }
460
461 bool Service::RetryingGetProperties() const {
462 return retrying_get_properties_;
463 }
464
465 void Service::OnRetryingGetProperties(bool retrying) {
466 retrying_get_properties_ = retrying;
467 }
468
469 // Private methods
470
471 // static
472 Service::ConnectivityState Service::ConnectivityStateFromString(
473 const std::string& connectivity_state) {
474 if (connectivity_state == kFlimflamServiceConnectivityStateUnknown) {
475 return kConnectivityStateUnknown;
476 }
477 if (connectivity_state == kFlimflamServiceConnectivityStateRestricted) {
478 return kConnectivityStateRestricted;
479 }
480 if (connectivity_state == kFlimflamServiceConnectivityStateUnrestricted) {
481 return kConnectivityStateUnrestricted;
482 }
483 if (connectivity_state == kFlimflamServiceConnectivityStateNone) {
484 return kConnectivityStateNone;
485 }
486 return kConnectivityStateUnknown;
487 }
488
489 // static
490 Service::State Service::StateFromString(const std::string& state) {
491 if (state == kFlimflamServiceStateIdle) {
492 return kStateIdle;
493 }
494 if (state == kFlimflamServiceStateCarrier) {
495 return kStateCarrier;
496 }
497 if (state == kFlimflamServiceStateAssociation) {
498 return kStateAssociation;
499 }
500 if (state == kFlimflamServiceStateConfiguration) {
501 return kStateConfiguration;
502 }
503 if (state == kFlimflamServiceStateReady) {
504 return kStateReady;
505 }
506 if (state == kFlimflamServiceStateDisconnect) {
507 return kStateDisconnect;
508 }
509 if (state == kFlimflamServiceStateFailure) {
510 return kStateFailure;
511 }
512 if (state == kFlimflamServiceStateActivationFailure) {
513 return kStateActivationFailure;
514 }
515 return kStateUnknown;
516 }
517
518 void Service::OnDeviceUpdate(const DBus::Path& device_path) {
519 DLOG(INFO) << path_ << ": OnDeviceUpdate: device_path = " << device_path;
520
521 // if there's an existing device with a non-matching path: destroy it and
522 // fall through to make a new one below.
523 if (device_ != NULL && device_->GetPath() != device_path) {
524 LOG(WARNING) << path_ << ": OnDeviceUpdate: device path changed from "
525 << device_->GetPath() << " to " << device_path;
526 delete device_;
527 device_ = NULL;
528 }
529
530 // if there's an existing device with matching path: all is well
531 if (device_ != NULL) {
532 return;
533 }
534
535 // if there's no existing device: make one
536 device_ = new(std::nothrow) Device(this, connection_, device_path);
537 if (device_ == NULL) {
538 LOG(ERROR) << path_ << ": OnDeviceUpdate: couldn't create device for "
539 << device_path;
540 return;
541 }
542 DLOG(INFO) << path_ << ": OnDeviceUpdate: created device for "
543 << device_path;
544 }
545
546 void Service::OnConnectivityStateUpdate(const std::string& connectivity_state) {
547 DLOG(INFO) << path_ << ": OnConnectivityStateUpdate: connectivity_state = "
548 << connectivity_state;
549 Service::ConnectivityState new_connectivity_state =
550 ConnectivityStateFromString(connectivity_state);
551 if (connectivity_state_ == new_connectivity_state) {
552 return;
553 }
554 connectivity_state_ = new_connectivity_state;
555 ReconsiderSendingUsageRequests();
556 }
557
558 void Service::OnStateUpdate(const std::string& state) {
559 DLOG(INFO) << path_ << ": OnStateUpdate: state = " << state;
560 Service::State old_state = state_;
561 state_ = StateFromString(state);
562 if (old_state != kStateReady && IsConnected()) {
563 OnConnected();
564 } else if (old_state == kStateReady && !IsConnected()) {
565 OnDisconnected();
566 }
567 }
568
569 void Service::OnTypeUpdate(const std::string& type) {
570 DLOG(INFO) << path_ << ": OnTypeUpdate: type = " << type;
571 type_ = TypeFromString(type);
572 if (type_ != kTypeCellular) {
573 LOG(WARNING) << path_ << ": OnTypeUpdate: type is not cellular!";
574 }
575 }
576
577 void Service::OnUsageUrlUpdate(const std::string& usage_url) {
578 DLOG(INFO) << path_ << ": OnUsageUrlUpdate: url = " << usage_url;
579 if (usage_url == usage_url_) {
580 return;
581 }
582 usage_url_ = usage_url;
583 if (provider_ != NULL) {
584 DCHECK(usage_url_ != provider_->GetUsageUrl());
585 StopSendingUsageRequests();
586 provider_->SetUsageUrl(usage_url_);
587 ReconsiderSendingUsageRequests();
588 }
589 }
590
591 // static
592 gboolean Service::StaticGetServicePropertiesCallback(gpointer data) {
593 Service *service = reinterpret_cast<Service*>(data);
594 CHECK_NOTNULL(service);
595 DCHECK_NE(service->GetGetPropertiesSourceId(), 0);
596 if (!service->GetServiceProperties()) {
597 // call failed, so try again later
598 DLOG(WARNING) << service->GetPath()
599 << ": StaticGetServicePropertiesCallback: "
600 << "GetServiceProperties failed, will retry in "
601 << kGetServicePropertiesIntervalSeconds << " secs";
602 // if we've arrived here from our first g_idle_add call, set up a timer
603 if (!service->RetryingGetProperties()) {
604 guint source_id =
605 g_timeout_add_seconds(kGetServicePropertiesIntervalSeconds,
606 StaticGetServicePropertiesCallback, service);
607 service->SetGetPropertiesSourceId(source_id);
608 if (source_id == 0) {
609 LOG(ERROR) << service->GetPath()
610 << ": StaticGetServicePropertiesCallback: "
611 << "g_timeout_add_seconds failed";
612 return FALSE; // get rid of old glib source and give up
613 }
614 service->OnRetryingGetProperties(true);
615 // get rid of our old glib source, but our new timer will call us
616 return FALSE;
617 }
618 // otherwise, our timer is already set up
619 // return TRUE to indicate that we want to be called again later
620 return TRUE;
621 }
622 service->SetGetPropertiesSourceId(0);
623 service->OnRetryingGetProperties(false);
624 return FALSE; // we succeeded, so don't repeat
625 }
626
627 bool Service::GetServiceProperties() {
628 DLOG(INFO) << path_ << ": GetServiceProperties";
629 PropertyMap properties;
630 // dbus-c++ throws exceptions
631 // invoke the "Existing Non-conformant Code" clause of the style guide and
632 // isolate the rest of the system from this
633 try {
634 // TODO(vlaviano): make this call asynchronous
635 properties = GetProperties();
636 } catch (DBus::Error& error) { // NOLINT
637 LOG(WARNING) << path_
638 << ": GetServiceProperties: GetProperties() -> Exception: "
639 << error.name() << ": " << error.message();
640 return false;
641 } catch (...) { // NOLINT
642 LOG(WARNING) << path_
643 << ": GetServiceProperties: GetProperties() -> Exception";
644 return false;
645 }
646 DLOG(INFO) << path_ << ": GetServiceProperties: Received "
647 << properties.size() << " properties";
648
649 // grab the properties in which we're interested
650 PropertyMap::const_iterator it;
651 it = properties.find(kFlimflamServiceDeviceProperty);
652 if (it != properties.end()) {
653 const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
654 OnDeviceUpdate(value.reader().get_path());
655 } else {
656 DLOG(WARNING) << path_ << ": GetServiceProperties: no Device property";
657 }
658 it = properties.find(kFlimflamServiceStateProperty);
659 if (it != properties.end()) {
660 const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
661 OnStateUpdate(value.reader().get_string());
662 } else {
663 DLOG(WARNING) << path_ << ": GetServiceProperties: no State property";
664 }
665 it = properties.find(kFlimflamServiceTypeProperty);
666 if (it != properties.end()) {
667 const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
668 OnTypeUpdate(value.reader().get_string());
669 } else {
670 DLOG(WARNING) << path_ << ": GetServiceProperties: no Type property";
671 }
672 // don't expect to find Cellular.* properties if we're not a cellular service
673 if (type_ != kTypeCellular) {
674 return true;
675 }
676 it = properties.find(kFlimflamServiceUsageUrlProperty);
677 if (it != properties.end()) {
678 const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
679 OnUsageUrlUpdate(value.reader().get_string());
680 } else {
681 DLOG(WARNING) << path_
682 << ": GetServiceProperties: no Cellular.UsageUrl property";
683 }
684 // flimflam docs re: ConnectivityState: "This state is currently only
685 // computed for services of type Cellular"
686 it = properties.find(kFlimflamServiceConnectivityStateProperty);
687 if (it != properties.end()) {
688 const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
689 OnConnectivityStateUpdate(value.reader().get_string());
690 } else {
691 DLOG(WARNING) << path_
692 << ": GetServiceProperties: no ConnectivityState property";
693 }
694 return true;
695 }
696
697 void Service::DeleteDataPlans(DataPlanList *data_plans) {
698 DCHECK(data_plans != NULL);
699 while (!data_plans->empty()) {
700 DataPlan *plan = *data_plans->begin();
701 DCHECK(plan != NULL);
702 DLOG(INFO) << path_ << ": DeleteDataPlans: deleting plan: "
703 << plan->GetName();
704 delete plan;
705 data_plans->erase(data_plans->begin());
706 }
707 }
708
709 void Service::AddHardcodedDataPlan() {
710 const std::string name = "Chromium OS Test Plan";
711 DataPlan::Type type = DataPlan::kTypeMeteredPaid;
712 base::Time update_time = base::Time::Now();
713 base::TimeDelta twelve_hours = base::TimeDelta::FromHours(12);
714 base::Time start_time = base::Time::Now() - twelve_hours;
715 base::Time end_time = base::Time::Now() + twelve_hours;
716 Bytes data_bytes_max = 100*1024*1024; // 100 MB
717 Bytes data_bytes_used = 30*1024*1024; // 30 MB
718
719 DataPlan *plan = new(std::nothrow) DataPlan(name, type, update_time,
720 start_time, end_time,
721 data_bytes_max, data_bytes_used);
722 if (plan == NULL) {
723 LOG(ERROR) << path_ << ": AddHardcodedDataPlan: could not create plan";
724 return;
725 }
726
727 data_plans_.push_back(plan);
728 }
729
730 void Service::RequestUsageUpdate() {
731 DCHECK(ShouldSendUsageRequests());
732 if (request_in_progress_) {
733 DLOG(WARNING) << path_
734 << ": RequestUsageUpdate: request already in progress";
735 return;
736 }
737 request_in_progress_ = true;
738 if (!provider_->RequestUsageUpdate()) {
739 DLOG(WARNING) << path_ << ": RequestUsageUpdate: request failed";
740 request_in_progress_ = false;
741 } else {
742 DLOG(INFO) << path_ << ": RequestUsageUpdate: request succeeded";
743 }
744 }
745
746 void Service::CancelPendingRequests() {
747 if (provider_ != NULL && request_in_progress_) {
748 provider_->CancelPendingRequests();
749 request_in_progress_ = false;
750 }
751 }
752
753 gboolean Service::UpdateTimeoutCallback() {
754 DLOG(INFO) << path_ << ": UpdateTimeoutCallback";
755 RequestUsageUpdate();
756 return TRUE;
757 }
758
759 // static
760 gboolean Service::StaticUpdateTimeoutCallback(gpointer data) {
761 Service *service = reinterpret_cast<Service*>(data);
762 CHECK_NOTNULL(service);
763 return service->UpdateTimeoutCallback();
764 }
765
766 bool Service::CreateUpdateTimer() {
767 DCHECK(provider_ != NULL);
768 DCHECK(policy_ != NULL);
769 guint idle_seconds = policy_->GetUpdateTimerIdleSecs(data_plans_);
770 DLOG(INFO) << path_ << ": CreateUpdateTimer: idle_seconds = " << idle_seconds;
771 if (update_timeout_source_ != NULL) {
772 DLOG(WARNING) << path_ << ": CreateUpdateTimer: timer already running";
773 return false;
774 }
775 update_timeout_source_ = g_timeout_source_new_seconds(idle_seconds);
776 if (update_timeout_source_ == NULL) {
777 DLOG(WARNING) << path_
778 << ": CreateUpdateTimer: could not create timeout source";
779 return false;
780 }
781 g_source_set_callback(update_timeout_source_, StaticUpdateTimeoutCallback,
782 this, NULL);
783 g_source_attach(update_timeout_source_, NULL);
784 return true;
785 }
786
787 void Service::DestroyUpdateTimer() {
788 DLOG(INFO) << path_ << ": DestroyUpdateTimer";
789 if (update_timeout_source_ != NULL) {
790 g_source_destroy(update_timeout_source_);
791 update_timeout_source_ = NULL;
792 }
793 }
794
795 void Service::DeleteCarrierState() {
796 DLOG(INFO) << path_ << ": DeleteCarrierState";
797 if (policy_ != NULL) {
798 DLOG(INFO) << path_ << ": DeleteCarrierState: deleting policy";
799 delete policy_;
800 policy_ = NULL;
801 }
802 StopSendingUsageRequests();
803 if (provider_ != NULL) {
804 DLOG(INFO) << path_ << ": DeleteCarrierState: deleting data plan provider";
805 delete provider_;
806 provider_ = NULL;
807 }
808 }
809
810 bool Service::IsValidCrosUsageStatus(const std::string& status) const {
811 if (status == kCrosUsageStatusOk ||
812 status == kCrosUsageStatusError ||
813 status == kCrosUsageStatusMalformedRequest ||
814 status == kCrosUsageStatusInternalError ||
815 status == kCrosUsageStatusServiceUnavailable ||
816 status == kCrosUsageStatusRequestRefused ||
817 status == kCrosUsageStatusUnknownDevice) {
818 return true;
819 }
820 return false;
821 }
822
823 void Service::OnCrosUsageErrorResult(const std::string& status) {
824 DLOG(WARNING) << path_ << ": OnCrosUsageErrorResult: " << status;
825 // TODO(vlaviano): hook for future use
826 }
827
828 bool Service::IsConnected() const {
829 if (state_ != kStateReady) {
830 return false;
831 }
832 // cross-check with parent Service Manager's idea of Flimflam global
833 // connectivity state
834 if (ServiceManager::IsOfflineConnectivityState(
835 parent_->GetConnectivityState())) {
836 DLOG(WARNING) << path_
837 << ": IsConnected: service manager thinks we're offline";
838 return false;
839 }
840 return true;
841 }
842
843 void Service::OnConnected() {
844 DLOG(INFO) << path_ << ": OnConnected";
845 DCHECK(IsConnected());
846 ReconsiderSendingUsageRequests();
847 }
848
849 void Service::OnDisconnected() {
850 DLOG(INFO) << path_ << ": OnDisconnected";
851 DCHECK(!IsConnected());
852 // stop byte counter
853 // we'll issue a new usage API request when we reconnect
854 if (device_ != NULL) {
855 device_->StopByteCounter();
856 }
857 ReconsiderSendingUsageRequests();
858 }
859
860 bool Service::IsSendingUsageRequests() const {
861 return request_in_progress_ || update_timeout_source_ != NULL;
862 }
863
864 // NOTE: we centralize our decisionmaking here to avoid insanity and just call
865 // ReconsiderSendingUsageRequests when anything changes
866 bool Service::ShouldSendUsageRequests() const {
867 if (provider_ == NULL) {
868 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: no: no provider";
869 DCHECK(policy_ == NULL);
870 return false;
871 }
872 DCHECK(device_ != NULL);
873 DCHECK(!device_->GetCarrier().empty());
874 DCHECK(policy_ != NULL);
875 if (usage_url_.empty()) {
876 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: no: no usage url";
877 return false;
878 }
879 if (ServiceManager::IsOfflineConnectivityState(
880 parent_->GetConnectivityState())) {
881 DLOG(INFO) << path_
882 << ": ShouldSendUsageRequests: no: flimflam is offline";
883 return false;
884 }
885 if (!IsConnected()) {
886 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: no: not connected";
887 return false;
888 }
889 if (!IsDefaultService()) {
890 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: no: not default service";
891 return false;
892 }
893 // TODO(vlaviano): this is a blunt way to ensure that we don't count local
894 // traffic if we're already in the restricted pool when cashew starts
895 if (GetConnectivityState() == kConnectivityStateRestricted) {
896 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: no: in restricted pool";
897 return false;
898 }
899 if (device_->ByteCounterRunning()) {
900 DLOG(INFO) << path_
901 << ": ShouldSendUsageRequests: no: local byte counter is running";
902 return false;
903 }
904 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: yes";
905 return true;
906 }
907
908 void Service::StopSendingUsageRequests() {
909 DLOG(INFO) << path_ << ": StopSendingUsageRequests";
910 DestroyUpdateTimer();
911 CancelPendingRequests();
912 }
913
914 void Service::StartSendingUsageRequests() {
915 DLOG(INFO) << path_ << ": StartSendingUsageRequests";
916 DCHECK(!IsSendingUsageRequests());
917 RequestUsageUpdate();
918 CreateUpdateTimer();
919 }
920
921 void Service::ReconsiderSendingUsageRequests() {
922 DLOG(INFO) << path_ << ": ReconsiderSendingUsageRequests";
923 if (!IsSendingUsageRequests() && ShouldSendUsageRequests()) {
924 StartSendingUsageRequests();
925 } else if (IsSendingUsageRequests() && !ShouldSendUsageRequests()) {
926 StopSendingUsageRequests();
927 }
928 }
929
930 void Service::MaybeEmitDataPlansUpdate() {
931 DCHECK(policy_ != NULL);
932 if (policy_->ShouldEmitDataPlansUpdate(data_plans_)) {
933 DLOG(INFO) << path_ << ": MaybeEmitDataPlansUpdate: sending update";
934 parent_->EmitDataPlansUpdate(*this);
935 } else {
936 DLOG(INFO) << path_ << ": MaybeEmitDataPlansUpdate: not sending update";
937 }
938 } 24 }
939 25
940 } // namespace cashew 26 } // namespace cashew
OLDNEW
« no previous file with comments | « src/service.h ('k') | src/service_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698