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

Side by Side Diff: src/service_impl.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_impl.h ('k') | src/service_manager.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "src/service_impl.h"
6
7 #include <glog/logging.h>
8
9 #include "src/data_plan_provider.h"
10 #include "src/device.h"
11 #include "src/policy.h"
12 #include "src/service_manager.h"
13
14 namespace cashew {
15
16 // Flimflam Service D-Bus identifiers
17 static const char *kFlimflamServiceName = "org.chromium.flimflam";
18
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 ServiceImpl::ServiceImpl(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 ServiceImpl::~ServiceImpl() {
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& ServiceImpl::GetPath() const {
113 return path_;
114 }
115
116 Service::State ServiceImpl::GetState() const {
117 return state_;
118 }
119
120 Service::Type ServiceImpl::GetType() const {
121 return type_;
122 }
123
124 // static
125 Service::Type ServiceImpl::TypeFromString(const std::string& type) {
126 if (type == kFlimflamServiceTypeEthernet) {
127 return kTypeEthernet;
128 }
129 if (type == kFlimflamServiceTypeWifi) {
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* ServiceImpl::GetDevice() const {
145 return device_;
146 }
147
148 DBusDataPlanList ServiceImpl::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 ServiceImpl::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 ServiceImpl::GetConnectivityState() const {
178 return connectivity_state_;
179 }
180
181 // Flimflam Service D-Bus Proxy methods
182
183 void ServiceImpl::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 ServiceImpl::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 ServiceImpl::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 ServiceImpl::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 ServiceImpl::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 ServiceImpl::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 ServiceImpl::OnFlimflamOnline() {
442 DLOG(INFO) << path_ << ": OnFlimflamOnline";
443 ReconsiderSendingUsageRequests();
444 }
445
446 void ServiceImpl::OnFlimflamOffline() {
447 DLOG(INFO) << path_ << ": OnFlimflamOffline";
448 ReconsiderSendingUsageRequests();
449 }
450
451 // Private methods
452
453 // static
454 Service::ConnectivityState ServiceImpl::ConnectivityStateFromString(
455 const std::string& connectivity_state) {
456 if (connectivity_state == kFlimflamServiceConnectivityStateUnknown) {
457 return kConnectivityStateUnknown;
458 }
459 if (connectivity_state == kFlimflamServiceConnectivityStateRestricted) {
460 return kConnectivityStateRestricted;
461 }
462 if (connectivity_state == kFlimflamServiceConnectivityStateUnrestricted) {
463 return kConnectivityStateUnrestricted;
464 }
465 if (connectivity_state == kFlimflamServiceConnectivityStateNone) {
466 return kConnectivityStateNone;
467 }
468 return kConnectivityStateUnknown;
469 }
470
471 // static
472 Service::State ServiceImpl::StateFromString(const std::string& state) {
473 if (state == kFlimflamServiceStateIdle) {
474 return kStateIdle;
475 }
476 if (state == kFlimflamServiceStateCarrier) {
477 return kStateCarrier;
478 }
479 if (state == kFlimflamServiceStateAssociation) {
480 return kStateAssociation;
481 }
482 if (state == kFlimflamServiceStateConfiguration) {
483 return kStateConfiguration;
484 }
485 if (state == kFlimflamServiceStateReady) {
486 return kStateReady;
487 }
488 if (state == kFlimflamServiceStateDisconnect) {
489 return kStateDisconnect;
490 }
491 if (state == kFlimflamServiceStateFailure) {
492 return kStateFailure;
493 }
494 if (state == kFlimflamServiceStateActivationFailure) {
495 return kStateActivationFailure;
496 }
497 return kStateUnknown;
498 }
499
500 void ServiceImpl::OnDeviceUpdate(const DBus::Path& device_path) {
501 DLOG(INFO) << path_ << ": OnDeviceUpdate: device_path = " << device_path;
502
503 // if there's an existing device with a non-matching path: destroy it and
504 // fall through to make a new one below.
505 if (device_ != NULL && device_->GetPath() != device_path) {
506 LOG(WARNING) << path_ << ": OnDeviceUpdate: device path changed from "
507 << device_->GetPath() << " to " << device_path;
508 delete device_;
509 device_ = NULL;
510 }
511
512 // if there's an existing device with matching path: all is well
513 if (device_ != NULL) {
514 return;
515 }
516
517 // if there's no existing device: make one
518 device_ = Device::NewDevice(this, connection_, device_path);
519 if (device_ == NULL) {
520 LOG(ERROR) << path_ << ": OnDeviceUpdate: couldn't create device for "
521 << device_path;
522 return;
523 }
524 DLOG(INFO) << path_ << ": OnDeviceUpdate: created device for "
525 << device_path;
526 }
527
528 void ServiceImpl::OnConnectivityStateUpdate(
529 const std::string& connectivity_state) {
530 DLOG(INFO) << path_ << ": OnConnectivityStateUpdate: connectivity_state = "
531 << connectivity_state;
532 Service::ConnectivityState new_connectivity_state =
533 ConnectivityStateFromString(connectivity_state);
534 if (connectivity_state_ == new_connectivity_state) {
535 return;
536 }
537 connectivity_state_ = new_connectivity_state;
538 ReconsiderSendingUsageRequests();
539 }
540
541 void ServiceImpl::OnStateUpdate(const std::string& state) {
542 DLOG(INFO) << path_ << ": OnStateUpdate: state = " << state;
543 Service::State old_state = state_;
544 state_ = StateFromString(state);
545 if (old_state != kStateReady && IsConnected()) {
546 OnConnected();
547 } else if (old_state == kStateReady && !IsConnected()) {
548 OnDisconnected();
549 }
550 }
551
552 void ServiceImpl::OnTypeUpdate(const std::string& type) {
553 DLOG(INFO) << path_ << ": OnTypeUpdate: type = " << type;
554 type_ = TypeFromString(type);
555 if (type_ != kTypeCellular) {
556 LOG(WARNING) << path_ << ": OnTypeUpdate: type is not cellular!";
557 }
558 }
559
560 void ServiceImpl::OnUsageUrlUpdate(const std::string& usage_url) {
561 DLOG(INFO) << path_ << ": OnUsageUrlUpdate: url = " << usage_url;
562 if (usage_url == usage_url_) {
563 return;
564 }
565 usage_url_ = usage_url;
566 if (provider_ != NULL) {
567 DCHECK(usage_url_ != provider_->GetUsageUrl());
568 StopSendingUsageRequests();
569 provider_->SetUsageUrl(usage_url_);
570 ReconsiderSendingUsageRequests();
571 }
572 }
573
574 // static
575 gboolean ServiceImpl::StaticGetServicePropertiesCallback(gpointer data) {
576 ServiceImpl *service = reinterpret_cast<ServiceImpl*>(data);
577 CHECK_NOTNULL(service);
578 DCHECK_NE(service->get_properties_source_id_, 0);
579 if (!service->GetServiceProperties()) {
580 // call failed, so try again later
581 DLOG(WARNING) << service->GetPath()
582 << ": StaticGetServicePropertiesCallback: "
583 << "GetServiceProperties failed, will retry in "
584 << kGetServicePropertiesIntervalSeconds << " secs";
585 // if we've arrived here from our first g_idle_add call, set up a timer
586 if (!service->retrying_get_properties_) {
587 guint source_id =
588 g_timeout_add_seconds(kGetServicePropertiesIntervalSeconds,
589 StaticGetServicePropertiesCallback, service);
590 service->get_properties_source_id_ = source_id;
591 if (source_id == 0) {
592 LOG(ERROR) << service->GetPath()
593 << ": StaticGetServicePropertiesCallback: "
594 << "g_timeout_add_seconds failed";
595 return FALSE; // get rid of old glib source and give up
596 }
597 service->retrying_get_properties_ = true;
598 // get rid of our old glib source, but our new timer will call us
599 return FALSE;
600 }
601 // otherwise, our timer is already set up
602 // return TRUE to indicate that we want to be called again later
603 return TRUE;
604 }
605 service->get_properties_source_id_ = 0;
606 service->retrying_get_properties_ = false;
607 return FALSE; // we succeeded, so don't repeat
608 }
609
610 bool ServiceImpl::GetServiceProperties() {
611 DLOG(INFO) << path_ << ": GetServiceProperties";
612 PropertyMap properties;
613 // dbus-c++ throws exceptions
614 // invoke the "Existing Non-conformant Code" clause of the style guide and
615 // isolate the rest of the system from this
616 try {
617 // TODO(vlaviano): make this call asynchronous
618 properties = GetProperties();
619 } catch (DBus::Error& error) { // NOLINT
620 LOG(WARNING) << path_
621 << ": GetServiceProperties: GetProperties() -> Exception: "
622 << error.name() << ": " << error.message();
623 return false;
624 } catch (...) { // NOLINT
625 LOG(WARNING) << path_
626 << ": GetServiceProperties: GetProperties() -> Exception";
627 return false;
628 }
629 DLOG(INFO) << path_ << ": GetServiceProperties: Received "
630 << properties.size() << " properties";
631
632 // grab the properties in which we're interested
633 PropertyMap::const_iterator it;
634 it = properties.find(kFlimflamServiceDeviceProperty);
635 if (it != properties.end()) {
636 const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
637 OnDeviceUpdate(value.reader().get_path());
638 } else {
639 DLOG(WARNING) << path_ << ": GetServiceProperties: no Device property";
640 }
641 it = properties.find(kFlimflamServiceStateProperty);
642 if (it != properties.end()) {
643 const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
644 OnStateUpdate(value.reader().get_string());
645 } else {
646 DLOG(WARNING) << path_ << ": GetServiceProperties: no State property";
647 }
648 it = properties.find(kFlimflamServiceTypeProperty);
649 if (it != properties.end()) {
650 const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
651 OnTypeUpdate(value.reader().get_string());
652 } else {
653 DLOG(WARNING) << path_ << ": GetServiceProperties: no Type property";
654 }
655 // don't expect to find Cellular.* properties if we're not a cellular service
656 if (type_ != kTypeCellular) {
657 return true;
658 }
659 it = properties.find(kFlimflamServiceUsageUrlProperty);
660 if (it != properties.end()) {
661 const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
662 OnUsageUrlUpdate(value.reader().get_string());
663 } else {
664 DLOG(WARNING) << path_
665 << ": GetServiceProperties: no Cellular.UsageUrl property";
666 }
667 // flimflam docs re: ConnectivityState: "This state is currently only
668 // computed for services of type Cellular"
669 it = properties.find(kFlimflamServiceConnectivityStateProperty);
670 if (it != properties.end()) {
671 const DBus::Variant& value = static_cast<DBus::Variant>(it->second);
672 OnConnectivityStateUpdate(value.reader().get_string());
673 } else {
674 DLOG(WARNING) << path_
675 << ": GetServiceProperties: no ConnectivityState property";
676 }
677 return true;
678 }
679
680 void ServiceImpl::DeleteDataPlans(DataPlanList *data_plans) {
681 DCHECK(data_plans != NULL);
682 while (!data_plans->empty()) {
683 DataPlan *plan = *data_plans->begin();
684 DCHECK(plan != NULL);
685 DLOG(INFO) << path_ << ": DeleteDataPlans: deleting plan: "
686 << plan->GetName();
687 delete plan;
688 data_plans->erase(data_plans->begin());
689 }
690 }
691
692 void ServiceImpl::AddHardcodedDataPlan() {
693 const std::string name = "Chromium OS Test Plan";
694 DataPlan::Type type = DataPlan::kTypeMeteredPaid;
695 base::Time update_time = base::Time::Now();
696 base::TimeDelta twelve_hours = base::TimeDelta::FromHours(12);
697 base::Time start_time = base::Time::Now() - twelve_hours;
698 base::Time end_time = base::Time::Now() + twelve_hours;
699 Bytes data_bytes_max = 100*1024*1024; // 100 MB
700 Bytes data_bytes_used = 30*1024*1024; // 30 MB
701
702 DataPlan *plan = new(std::nothrow) DataPlan(name, type, update_time,
703 start_time, end_time,
704 data_bytes_max, data_bytes_used);
705 if (plan == NULL) {
706 LOG(ERROR) << path_ << ": AddHardcodedDataPlan: could not create plan";
707 return;
708 }
709
710 data_plans_.push_back(plan);
711 }
712
713 void ServiceImpl::RequestUsageUpdate() {
714 DCHECK(ShouldSendUsageRequests());
715 if (request_in_progress_) {
716 DLOG(WARNING) << path_
717 << ": RequestUsageUpdate: request already in progress";
718 return;
719 }
720 request_in_progress_ = true;
721 if (!provider_->RequestUsageUpdate()) {
722 DLOG(WARNING) << path_ << ": RequestUsageUpdate: request failed";
723 request_in_progress_ = false;
724 } else {
725 DLOG(INFO) << path_ << ": RequestUsageUpdate: request succeeded";
726 }
727 }
728
729 void ServiceImpl::CancelPendingRequests() {
730 if (provider_ != NULL && request_in_progress_) {
731 provider_->CancelPendingRequests();
732 request_in_progress_ = false;
733 }
734 }
735
736 gboolean ServiceImpl::UpdateTimeoutCallback() {
737 DLOG(INFO) << path_ << ": UpdateTimeoutCallback";
738 RequestUsageUpdate();
739 return TRUE;
740 }
741
742 // static
743 gboolean ServiceImpl::StaticUpdateTimeoutCallback(gpointer data) {
744 ServiceImpl *service = reinterpret_cast<ServiceImpl*>(data);
745 CHECK_NOTNULL(service);
746 return service->UpdateTimeoutCallback();
747 }
748
749 bool ServiceImpl::CreateUpdateTimer() {
750 DCHECK(provider_ != NULL);
751 DCHECK(policy_ != NULL);
752 guint idle_seconds = policy_->GetUpdateTimerIdleSecs(data_plans_);
753 DLOG(INFO) << path_ << ": CreateUpdateTimer: idle_seconds = " << idle_seconds;
754 if (update_timeout_source_ != NULL) {
755 DLOG(WARNING) << path_ << ": CreateUpdateTimer: timer already running";
756 return false;
757 }
758 update_timeout_source_ = g_timeout_source_new_seconds(idle_seconds);
759 if (update_timeout_source_ == NULL) {
760 DLOG(WARNING) << path_
761 << ": CreateUpdateTimer: could not create timeout source";
762 return false;
763 }
764 g_source_set_callback(update_timeout_source_, StaticUpdateTimeoutCallback,
765 this, NULL);
766 g_source_attach(update_timeout_source_, NULL);
767 return true;
768 }
769
770 void ServiceImpl::DestroyUpdateTimer() {
771 DLOG(INFO) << path_ << ": DestroyUpdateTimer";
772 if (update_timeout_source_ != NULL) {
773 g_source_destroy(update_timeout_source_);
774 update_timeout_source_ = NULL;
775 }
776 }
777
778 void ServiceImpl::DeleteCarrierState() {
779 DLOG(INFO) << path_ << ": DeleteCarrierState";
780 if (policy_ != NULL) {
781 DLOG(INFO) << path_ << ": DeleteCarrierState: deleting policy";
782 delete policy_;
783 policy_ = NULL;
784 }
785 StopSendingUsageRequests();
786 if (provider_ != NULL) {
787 DLOG(INFO) << path_ << ": DeleteCarrierState: deleting data plan provider";
788 delete provider_;
789 provider_ = NULL;
790 }
791 }
792
793 bool ServiceImpl::IsValidCrosUsageStatus(const std::string& status) const {
794 if (status == kCrosUsageStatusOk ||
795 status == kCrosUsageStatusError ||
796 status == kCrosUsageStatusMalformedRequest ||
797 status == kCrosUsageStatusInternalError ||
798 status == kCrosUsageStatusServiceUnavailable ||
799 status == kCrosUsageStatusRequestRefused ||
800 status == kCrosUsageStatusUnknownDevice) {
801 return true;
802 }
803 return false;
804 }
805
806 void ServiceImpl::OnCrosUsageErrorResult(const std::string& status) {
807 DLOG(WARNING) << path_ << ": OnCrosUsageErrorResult: " << status;
808 // TODO(vlaviano): hook for future use
809 }
810
811 bool ServiceImpl::IsConnected() const {
812 if (state_ != kStateReady) {
813 return false;
814 }
815 // cross-check with parent Service Manager's idea of Flimflam global
816 // connectivity state
817 if (ServiceManager::IsOfflineConnectivityState(
818 parent_->GetConnectivityState())) {
819 DLOG(WARNING) << path_
820 << ": IsConnected: service manager thinks we're offline";
821 return false;
822 }
823 return true;
824 }
825
826 void ServiceImpl::OnConnected() {
827 DLOG(INFO) << path_ << ": OnConnected";
828 DCHECK(IsConnected());
829 ReconsiderSendingUsageRequests();
830 }
831
832 void ServiceImpl::OnDisconnected() {
833 DLOG(INFO) << path_ << ": OnDisconnected";
834 DCHECK(!IsConnected());
835 // stop byte counter
836 // we'll issue a new usage API request when we reconnect
837 if (device_ != NULL) {
838 device_->StopByteCounter();
839 }
840 ReconsiderSendingUsageRequests();
841 }
842
843 bool ServiceImpl::IsSendingUsageRequests() const {
844 return request_in_progress_ || update_timeout_source_ != NULL;
845 }
846
847 // NOTE: we centralize our decisionmaking here to avoid insanity and just call
848 // ReconsiderSendingUsageRequests when anything changes
849 bool ServiceImpl::ShouldSendUsageRequests() const {
850 if (provider_ == NULL) {
851 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: no: no provider";
852 DCHECK(policy_ == NULL);
853 return false;
854 }
855 DCHECK(device_ != NULL);
856 DCHECK(!device_->GetCarrier().empty());
857 DCHECK(policy_ != NULL);
858 if (usage_url_.empty()) {
859 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: no: no usage url";
860 return false;
861 }
862 if (ServiceManager::IsOfflineConnectivityState(
863 parent_->GetConnectivityState())) {
864 DLOG(INFO) << path_
865 << ": ShouldSendUsageRequests: no: flimflam is offline";
866 return false;
867 }
868 if (!IsConnected()) {
869 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: no: not connected";
870 return false;
871 }
872 if (!IsDefaultService()) {
873 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: no: not default service";
874 return false;
875 }
876 // TODO(vlaviano): this is a blunt way to ensure that we don't count local
877 // traffic if we're already in the restricted pool when cashew starts
878 if (GetConnectivityState() == kConnectivityStateRestricted) {
879 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: no: in restricted pool";
880 return false;
881 }
882 if (device_->ByteCounterRunning()) {
883 DLOG(INFO) << path_
884 << ": ShouldSendUsageRequests: no: local byte counter is running";
885 return false;
886 }
887 DLOG(INFO) << path_ << ": ShouldSendUsageRequests: yes";
888 return true;
889 }
890
891 void ServiceImpl::StopSendingUsageRequests() {
892 DLOG(INFO) << path_ << ": StopSendingUsageRequests";
893 DestroyUpdateTimer();
894 CancelPendingRequests();
895 }
896
897 void ServiceImpl::StartSendingUsageRequests() {
898 DLOG(INFO) << path_ << ": StartSendingUsageRequests";
899 DCHECK(!IsSendingUsageRequests());
900 RequestUsageUpdate();
901 CreateUpdateTimer();
902 }
903
904 void ServiceImpl::ReconsiderSendingUsageRequests() {
905 DLOG(INFO) << path_ << ": ReconsiderSendingUsageRequests";
906 if (!IsSendingUsageRequests() && ShouldSendUsageRequests()) {
907 StartSendingUsageRequests();
908 } else if (IsSendingUsageRequests() && !ShouldSendUsageRequests()) {
909 StopSendingUsageRequests();
910 }
911 }
912
913 void ServiceImpl::MaybeEmitDataPlansUpdate() {
914 DCHECK(policy_ != NULL);
915 if (policy_->ShouldEmitDataPlansUpdate(data_plans_)) {
916 DLOG(INFO) << path_ << ": MaybeEmitDataPlansUpdate: sending update";
917 parent_->EmitDataPlansUpdate(*this);
918 } else {
919 DLOG(INFO) << path_ << ": MaybeEmitDataPlansUpdate: not sending update";
920 }
921 }
922
923 } // namespace cashew
OLDNEW
« no previous file with comments | « src/service_impl.h ('k') | src/service_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698