Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / local_discovery / privet_notifications.cc
1 // Copyright 2013 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/local_discovery/privet_notifications.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/rand_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/local_discovery/privet_device_lister_impl.h"
16 #include "chrome/browser/local_discovery/privet_http_asynchronous_factory.h"
17 #include "chrome/browser/local_discovery/service_discovery_shared_client.h"
18 #include "chrome/browser/notifications/notification.h"
19 #include "chrome/browser/notifications/notification_ui_manager.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/signin/signin_manager_factory.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/browser_finder.h"
24 #include "chrome/browser/ui/browser_window.h"
25 #include "chrome/browser/ui/host_desktop.h"
26 #include "chrome/browser/ui/tabs/tab_strip_model.h"
27 #include "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "chrome/common/pref_names.h"
30 #include "components/signin/core/browser/signin_manager_base.h"
31 #include "content/public/browser/browser_context.h"
32 #include "content/public/browser/navigation_controller.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/common/page_transition_types.h"
35 #include "grit/generated_resources.h"
36 #include "grit/theme_resources.h"
37 #include "ui/base/l10n/l10n_util.h"
38 #include "ui/base/resource/resource_bundle.h"
39 #include "ui/message_center/notifier_settings.h"
40
41 #if defined(ENABLE_MDNS)
42 #include "chrome/browser/local_discovery/privet_traffic_detector.h"
43 #endif
44
45 namespace local_discovery {
46
47 namespace {
48
49 const int kTenMinutesInSeconds = 600;
50 const char kPrivetInfoKeyUptime[] = "uptime";
51 const char kPrivetNotificationID[] = "privet_notification";
52 const char kPrivetNotificationOriginUrl[] = "chrome://devices";
53 const int kStartDelaySeconds = 5;
54
55 enum PrivetNotificationsEvent {
56   PRIVET_SERVICE_STARTED,
57   PRIVET_LISTER_STARTED,
58   PRIVET_DEVICE_CHANGED,
59   PRIVET_INFO_DONE,
60   PRIVET_NOTIFICATION_SHOWN,
61   PRIVET_NOTIFICATION_CANCELED,
62   PRIVET_NOTIFICATION_CLICKED,
63   PRIVET_DISABLE_NOTIFICATIONS_CLICKED,
64   PRIVET_EVENT_MAX,
65 };
66
67 void ReportPrivetUmaEvent(PrivetNotificationsEvent privet_event) {
68   UMA_HISTOGRAM_ENUMERATION("LocalDiscovery.PrivetNotificationsEvent",
69                             privet_event, PRIVET_EVENT_MAX);
70 }
71
72 }  // namespace
73
74 PrivetNotificationsListener::PrivetNotificationsListener(
75     scoped_ptr<PrivetHTTPAsynchronousFactory> privet_http_factory,
76     Delegate* delegate) : delegate_(delegate), devices_active_(0) {
77   privet_http_factory_.swap(privet_http_factory);
78 }
79
80 PrivetNotificationsListener::~PrivetNotificationsListener() {
81 }
82
83 void PrivetNotificationsListener::DeviceChanged(
84     bool added,
85     const std::string& name,
86     const DeviceDescription& description) {
87   ReportPrivetUmaEvent(PRIVET_DEVICE_CHANGED);
88   DeviceContextMap::iterator found = devices_seen_.find(name);
89   if (found != devices_seen_.end()) {
90     if (!description.id.empty() &&  // Device is registered
91         found->second->notification_may_be_active) {
92       found->second->notification_may_be_active = false;
93       NotifyDeviceRemoved();
94     }
95     return;  // Already saw this device.
96   }
97
98   linked_ptr<DeviceContext> device_context(new DeviceContext);
99
100   device_context->notification_may_be_active = false;
101   device_context->registered = !description.id.empty();
102
103   devices_seen_.insert(make_pair(name, device_context));
104
105   if (!device_context->registered) {
106     device_context->privet_http_resolution =
107         privet_http_factory_->CreatePrivetHTTP(
108             name,
109             description.address,
110             base::Bind(&PrivetNotificationsListener::CreateInfoOperation,
111                        base::Unretained(this)));
112
113     device_context->privet_http_resolution->Start();
114   }
115 }
116
117 void PrivetNotificationsListener::CreateInfoOperation(
118     scoped_ptr<PrivetHTTPClient> http_client) {
119   std::string name = http_client->GetName();
120   DeviceContextMap::iterator device_iter = devices_seen_.find(name);
121   DCHECK(device_iter != devices_seen_.end());
122   DeviceContext* device = device_iter->second.get();
123   device->privet_http.swap(http_client);
124   device->info_operation = device->privet_http->CreateInfoOperation(
125       base::Bind(&PrivetNotificationsListener::OnPrivetInfoDone,
126                  base::Unretained(this),
127                  device));
128   device->info_operation->Start();
129 }
130
131 void PrivetNotificationsListener::OnPrivetInfoDone(
132     DeviceContext* device,
133     const base::DictionaryValue* json_value) {
134   int uptime;
135
136   if (!json_value ||
137       !json_value->GetInteger(kPrivetInfoKeyUptime, &uptime) ||
138       uptime > kTenMinutesInSeconds) {
139     return;
140   }
141
142   DCHECK(!device->notification_may_be_active);
143   device->notification_may_be_active = true;
144   devices_active_++;
145   delegate_->PrivetNotify(devices_active_ > 1, true);
146 }
147
148 void PrivetNotificationsListener::DeviceRemoved(const std::string& name) {
149   DCHECK_EQ(1u, devices_seen_.count(name));
150   DeviceContextMap::iterator device_iter = devices_seen_.find(name);
151   DCHECK(device_iter != devices_seen_.end());
152   DeviceContext* device = device_iter->second.get();
153
154   device->info_operation.reset();
155   device->privet_http_resolution.reset();
156   device->notification_may_be_active = false;
157   NotifyDeviceRemoved();
158 }
159
160 void PrivetNotificationsListener::DeviceCacheFlushed() {
161   for (DeviceContextMap::iterator i = devices_seen_.begin();
162        i != devices_seen_.end(); ++i) {
163     DeviceContext* device = i->second.get();
164
165     device->info_operation.reset();
166     device->privet_http_resolution.reset();
167     if (device->notification_may_be_active) {
168       device->notification_may_be_active = false;
169     }
170   }
171
172   devices_active_ = 0;
173   delegate_->PrivetRemoveNotification();
174 }
175
176 void PrivetNotificationsListener::NotifyDeviceRemoved() {
177   devices_active_--;
178   if (devices_active_ == 0) {
179     delegate_->PrivetRemoveNotification();
180   } else {
181     delegate_->PrivetNotify(devices_active_ > 1, false);
182   }
183 }
184
185 PrivetNotificationsListener::DeviceContext::DeviceContext() {
186 }
187
188 PrivetNotificationsListener::DeviceContext::~DeviceContext() {
189 }
190
191 PrivetNotificationService::PrivetNotificationService(
192     content::BrowserContext* profile)
193     : profile_(profile) {
194   base::MessageLoop::current()->PostDelayedTask(
195       FROM_HERE,
196       base::Bind(&PrivetNotificationService::Start, AsWeakPtr()),
197       base::TimeDelta::FromSeconds(kStartDelaySeconds +
198                                    base::RandInt(0, kStartDelaySeconds/4)));
199 }
200
201 PrivetNotificationService::~PrivetNotificationService() {
202 }
203
204 void PrivetNotificationService::DeviceChanged(
205     bool added,
206     const std::string& name,
207     const DeviceDescription& description) {
208   privet_notifications_listener_->DeviceChanged(added, name, description);
209 }
210
211 void PrivetNotificationService::DeviceRemoved(const std::string& name) {
212   privet_notifications_listener_->DeviceRemoved(name);
213 }
214
215 void PrivetNotificationService::DeviceCacheFlushed() {
216   privet_notifications_listener_->DeviceCacheFlushed();
217 }
218
219 // static
220 bool PrivetNotificationService::IsEnabled() {
221   CommandLine* command_line = CommandLine::ForCurrentProcess();
222   return !command_line->HasSwitch(switches::kDisableDeviceDiscovery) &&
223       !command_line->HasSwitch(switches::kDisableDeviceDiscoveryNotifications);
224 }
225
226 // static
227 bool PrivetNotificationService::IsForced() {
228   CommandLine* command_line = CommandLine::ForCurrentProcess();
229   return command_line->HasSwitch(switches::kEnableDeviceDiscoveryNotifications);
230 }
231
232 void PrivetNotificationService::PrivetNotify(bool has_multiple,
233                                              bool added) {
234   base::string16 product_name = l10n_util::GetStringUTF16(
235       IDS_LOCAL_DISOCVERY_SERVICE_NAME_PRINTER);
236
237   int title_resource = has_multiple ?
238       IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER_MULTIPLE :
239       IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER;
240
241   int body_resource = has_multiple ?
242       IDS_LOCAL_DISOCVERY_NOTIFICATION_CONTENTS_PRINTER_MULTIPLE :
243       IDS_LOCAL_DISOCVERY_NOTIFICATION_CONTENTS_PRINTER;
244
245   base::string16 title = l10n_util::GetStringUTF16(title_resource);
246   base::string16 body = l10n_util::GetStringUTF16(body_resource);
247
248   Profile* profile_object = Profile::FromBrowserContext(profile_);
249   message_center::RichNotificationData rich_notification_data;
250
251   rich_notification_data.buttons.push_back(
252       message_center::ButtonInfo(l10n_util::GetStringUTF16(
253           IDS_LOCAL_DISOCVERY_NOTIFICATION_BUTTON_PRINTER)));
254
255   rich_notification_data.buttons.push_back(
256       message_center::ButtonInfo(l10n_util::GetStringUTF16(
257           IDS_LOCAL_DISCOVERY_NOTIFICATIONS_DISABLE_BUTTON_LABEL)));
258
259   Notification notification(
260       message_center::NOTIFICATION_TYPE_SIMPLE,
261       GURL(kPrivetNotificationOriginUrl),
262       title,
263       body,
264       ui::ResourceBundle::GetSharedInstance().GetImageNamed(
265           IDR_LOCAL_DISCOVERY_CLOUDPRINT_ICON),
266       blink::WebTextDirectionDefault,
267       message_center::NotifierId(GURL(kPrivetNotificationOriginUrl)),
268       product_name,
269       base::UTF8ToUTF16(kPrivetNotificationID),
270       rich_notification_data,
271       new PrivetNotificationDelegate(profile_));
272
273   bool updated = g_browser_process->notification_ui_manager()->Update(
274       notification, profile_object);
275   if (!updated && added && !LocalDiscoveryUIHandler::GetHasVisible()) {
276     ReportPrivetUmaEvent(PRIVET_NOTIFICATION_SHOWN);
277     g_browser_process->notification_ui_manager()->Add(notification,
278                                                       profile_object);
279   }
280 }
281
282 void PrivetNotificationService::PrivetRemoveNotification() {
283   ReportPrivetUmaEvent(PRIVET_NOTIFICATION_CANCELED);
284   g_browser_process->notification_ui_manager()->CancelById(
285       kPrivetNotificationID);
286 }
287
288 void PrivetNotificationService::Start() {
289 #if defined(CHROMEOS)
290   SigninManagerBase* signin_manager =
291       SigninManagerFactory::GetForProfileIfExists(
292           Profile::FromBrowserContext(profile_));
293
294   if (!signin_manager || signin_manager->GetAuthenticatedUsername().empty())
295     return;
296 #endif
297
298   enable_privet_notification_member_.Init(
299       prefs::kLocalDiscoveryNotificationsEnabled,
300       Profile::FromBrowserContext(profile_)->GetPrefs(),
301       base::Bind(&PrivetNotificationService::OnNotificationsEnabledChanged,
302                  base::Unretained(this)));
303   OnNotificationsEnabledChanged();
304 }
305
306 void PrivetNotificationService::OnNotificationsEnabledChanged() {
307 #if defined(ENABLE_MDNS)
308   if (IsForced()) {
309     StartLister();
310   } else if (*enable_privet_notification_member_) {
311     ReportPrivetUmaEvent(PRIVET_SERVICE_STARTED);
312     traffic_detector_ =
313         new PrivetTrafficDetector(
314             net::ADDRESS_FAMILY_IPV4,
315             base::Bind(&PrivetNotificationService::StartLister, AsWeakPtr()));
316     traffic_detector_->Start();
317   } else {
318     traffic_detector_ = NULL;
319     device_lister_.reset();
320     service_discovery_client_ = NULL;
321     privet_notifications_listener_.reset();
322   }
323 #else
324   if (IsForced() || *enable_privet_notification_member_) {
325     StartLister();
326   } else {
327     device_lister_.reset();
328     service_discovery_client_ = NULL;
329     privet_notifications_listener_.reset();
330   }
331 #endif
332 }
333
334 void PrivetNotificationService::StartLister() {
335   ReportPrivetUmaEvent(PRIVET_LISTER_STARTED);
336 #if defined(ENABLE_MDNS)
337   traffic_detector_ = NULL;
338 #endif  // ENABLE_MDNS
339   service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance();
340   device_lister_.reset(new PrivetDeviceListerImpl(service_discovery_client_,
341                                                   this));
342   device_lister_->Start();
343   device_lister_->DiscoverNewDevices(false);
344
345   scoped_ptr<PrivetHTTPAsynchronousFactory> http_factory(
346       PrivetHTTPAsynchronousFactory::CreateInstance(
347           service_discovery_client_.get(), profile_->GetRequestContext()));
348
349   privet_notifications_listener_.reset(new PrivetNotificationsListener(
350       http_factory.Pass(), this));
351 }
352
353 PrivetNotificationDelegate::PrivetNotificationDelegate(
354     content::BrowserContext* profile)
355     :  profile_(profile) {
356 }
357
358 PrivetNotificationDelegate::~PrivetNotificationDelegate() {
359 }
360
361 std::string PrivetNotificationDelegate::id() const {
362   return kPrivetNotificationID;
363 }
364
365 content::WebContents* PrivetNotificationDelegate::GetWebContents() const {
366   return NULL;
367 }
368
369 void PrivetNotificationDelegate::Display() {
370 }
371
372 void PrivetNotificationDelegate::Error() {
373   LOG(ERROR) << "Error displaying privet notification";
374 }
375
376 void PrivetNotificationDelegate::Close(bool by_user) {
377 }
378
379 void PrivetNotificationDelegate::Click() {
380 }
381
382 void PrivetNotificationDelegate::ButtonClick(int button_index) {
383   if (button_index == 0) {
384     ReportPrivetUmaEvent(PRIVET_NOTIFICATION_CLICKED);
385     OpenTab(GURL(kPrivetNotificationOriginUrl));
386   } else if (button_index == 1) {
387     ReportPrivetUmaEvent(PRIVET_DISABLE_NOTIFICATIONS_CLICKED);
388     DisableNotifications();
389   }
390 }
391
392 void PrivetNotificationDelegate::OpenTab(const GURL& url) {
393   Profile* profile_obj = Profile::FromBrowserContext(profile_);
394
395   chrome::NavigateParams params(profile_obj,
396                               url,
397                               content::PAGE_TRANSITION_AUTO_TOPLEVEL);
398   params.disposition = NEW_FOREGROUND_TAB;
399   chrome::Navigate(&params);
400 }
401
402 void PrivetNotificationDelegate::DisableNotifications() {
403   Profile* profile_obj = Profile::FromBrowserContext(profile_);
404
405   profile_obj->GetPrefs()->SetBoolean(
406       prefs::kLocalDiscoveryNotificationsEnabled,
407       false);
408 }
409
410 }  // namespace local_discovery