1// Copyright (c) 2012 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 "chrome/browser/extensions/api/dial/dial_api.h"
6
7#include <vector>
8
9#include "base/time/time.h"
10#include "chrome/browser/extensions/api/dial/dial_api_factory.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/common/extensions/api/dial.h"
13#include "content/public/browser/browser_thread.h"
14#include "extensions/browser/event_router.h"
15#include "extensions/browser/extension_system.h"
16
17using base::TimeDelta;
18using content::BrowserThread;
19
20namespace {
21
22// How often to poll for devices.
23const int kDialRefreshIntervalSecs = 120;
24
25// We prune a device if it does not respond after this time.
26const int kDialExpirationSecs = 240;
27
28// The maximum number of devices retained at once in the registry.
29const size_t kDialMaxDevices = 256;
30
31}  // namespace
32
33namespace extensions {
34
35namespace dial = api::dial;
36
37DialAPI::DialAPI(Profile* profile)
38    : RefcountedBrowserContextKeyedService(BrowserThread::IO),
39      profile_(profile) {
40  EventRouter::Get(profile)
41      ->RegisterObserver(this, dial::OnDeviceList::kEventName);
42}
43
44DialAPI::~DialAPI() {}
45
46DialRegistry* DialAPI::dial_registry() {
47  DCHECK_CURRENTLY_ON(BrowserThread::IO);
48  if (!dial_registry_.get()) {
49    dial_registry_.reset(new DialRegistry(this,
50        TimeDelta::FromSeconds(kDialRefreshIntervalSecs),
51        TimeDelta::FromSeconds(kDialExpirationSecs),
52        kDialMaxDevices));
53  }
54  return dial_registry_.get();
55}
56
57void DialAPI::OnListenerAdded(const EventListenerInfo& details) {
58  DCHECK_CURRENTLY_ON(BrowserThread::UI);
59  BrowserThread::PostTask(
60      BrowserThread::IO, FROM_HERE,
61      base::Bind(&DialAPI::NotifyListenerAddedOnIOThread, this));
62}
63
64void DialAPI::OnListenerRemoved(const EventListenerInfo& details) {
65  DCHECK_CURRENTLY_ON(BrowserThread::UI);
66  BrowserThread::PostTask(
67      BrowserThread::IO, FROM_HERE,
68      base::Bind(&DialAPI::NotifyListenerRemovedOnIOThread, this));
69}
70
71void DialAPI::NotifyListenerAddedOnIOThread() {
72  DCHECK_CURRENTLY_ON(BrowserThread::IO);
73  VLOG(2) << "DIAL device event listener added.";
74  dial_registry()->OnListenerAdded();
75}
76
77void DialAPI::NotifyListenerRemovedOnIOThread() {
78  DCHECK_CURRENTLY_ON(BrowserThread::IO);
79  VLOG(2) << "DIAL device event listener removed";
80  dial_registry()->OnListenerRemoved();
81}
82
83void DialAPI::OnDialDeviceEvent(const DialRegistry::DeviceList& devices) {
84  DCHECK_CURRENTLY_ON(BrowserThread::IO);
85  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
86      base::Bind(&DialAPI::SendEventOnUIThread, this, devices));
87}
88
89void DialAPI::OnDialError(const DialRegistry::DialErrorCode code) {
90  DCHECK_CURRENTLY_ON(BrowserThread::IO);
91  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
92      base::Bind(&DialAPI::SendErrorOnUIThread, this, code));
93}
94
95void DialAPI::SendEventOnUIThread(const DialRegistry::DeviceList& devices) {
96  DCHECK_CURRENTLY_ON(BrowserThread::UI);
97
98  std::vector<linked_ptr<api::dial::DialDevice> > args;
99  for (DialRegistry::DeviceList::const_iterator it = devices.begin();
100       it != devices.end(); ++it) {
101    linked_ptr<api::dial::DialDevice> api_device =
102        make_linked_ptr(new api::dial::DialDevice);
103    it->FillDialDevice(api_device.get());
104    args.push_back(api_device);
105  }
106  scoped_ptr<base::ListValue> results = api::dial::OnDeviceList::Create(args);
107  scoped_ptr<Event> event(
108      new Event(dial::OnDeviceList::kEventName, results.Pass()));
109  EventRouter::Get(profile_)->BroadcastEvent(event.Pass());
110}
111
112void DialAPI::SendErrorOnUIThread(const DialRegistry::DialErrorCode code) {
113  DCHECK_CURRENTLY_ON(BrowserThread::UI);
114
115  api::dial::DialError dial_error;
116  switch (code) {
117    case DialRegistry::DIAL_NO_LISTENERS:
118      dial_error.code = api::dial::DIAL_ERROR_CODE_NO_LISTENERS;
119      break;
120    case DialRegistry::DIAL_NO_INTERFACES:
121      dial_error.code = api::dial::DIAL_ERROR_CODE_NO_VALID_NETWORK_INTERFACES;
122      break;
123    case DialRegistry::DIAL_CELLULAR_NETWORK:
124      dial_error.code = api::dial::DIAL_ERROR_CODE_CELLULAR_NETWORK;
125      break;
126    case DialRegistry::DIAL_NETWORK_DISCONNECTED:
127      dial_error.code = api::dial::DIAL_ERROR_CODE_NETWORK_DISCONNECTED;
128      break;
129    case DialRegistry::DIAL_SOCKET_ERROR:
130      dial_error.code = api::dial::DIAL_ERROR_CODE_SOCKET_ERROR;
131      break;
132    default:
133      dial_error.code = api::dial::DIAL_ERROR_CODE_UNKNOWN;
134      break;
135  }
136
137  scoped_ptr<base::ListValue> results = api::dial::OnError::Create(dial_error);
138  scoped_ptr<Event> event(new Event(dial::OnError::kEventName, results.Pass()));
139  EventRouter::Get(profile_)->BroadcastEvent(event.Pass());
140}
141
142void DialAPI::ShutdownOnUIThread() {}
143
144namespace api {
145
146DialDiscoverNowFunction::DialDiscoverNowFunction()
147    : dial_(NULL), result_(false) {
148}
149
150bool DialDiscoverNowFunction::Prepare() {
151  DCHECK_CURRENTLY_ON(BrowserThread::UI);
152  DCHECK(browser_context());
153  dial_ = DialAPIFactory::GetForBrowserContext(browser_context()).get();
154  return true;
155}
156
157void DialDiscoverNowFunction::Work() {
158  DCHECK_CURRENTLY_ON(BrowserThread::IO);
159  result_ = dial_->dial_registry()->DiscoverNow();
160}
161
162bool DialDiscoverNowFunction::Respond() {
163  DCHECK_CURRENTLY_ON(BrowserThread::UI);
164  SetResult(new base::FundamentalValue(result_));
165  return true;
166}
167
168}  // namespace api
169
170}  // namespace extensions
171