Update To 11.40.268.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 "chrome/grit/generated_resources.h"
31 #include "components/signin/core/browser/signin_manager_base.h"
32 #include "content/public/browser/browser_context.h"
33 #include "content/public/browser/navigation_controller.h"
34 #include "content/public/browser/web_contents.h"
35 #include "grit/theme_resources.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/page_transition_types.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)
77     : delegate_(delegate), devices_active_(0) {
78   privet_http_factory_.swap(privet_http_factory);
79 }
80
81 PrivetNotificationsListener::~PrivetNotificationsListener() {
82 }
83
84 void PrivetNotificationsListener::DeviceChanged(
85     bool added,
86     const std::string& name,
87     const DeviceDescription& description) {
88   ReportPrivetUmaEvent(PRIVET_DEVICE_CHANGED);
89   DeviceContextMap::iterator found = devices_seen_.find(name);
90   if (found != devices_seen_.end()) {
91     if (!description.id.empty() &&  // Device is registered
92         found->second->notification_may_be_active) {
93       found->second->notification_may_be_active = false;
94       NotifyDeviceRemoved();
95     }
96     return;  // Already saw this device.
97   }
98
99   linked_ptr<DeviceContext> device_context(new DeviceContext);
100
101   device_context->notification_may_be_active = false;
102   device_context->registered = !description.id.empty();
103
104   devices_seen_.insert(make_pair(name, device_context));
105
106   if (!device_context->registered) {
107     device_context->privet_http_resolution =
108         privet_http_factory_->CreatePrivetHTTP(
109             name,
110             description.address,
111             base::Bind(&PrivetNotificationsListener::CreateInfoOperation,
112                        base::Unretained(this)));
113
114     device_context->privet_http_resolution->Start();
115   }
116 }
117
118 void PrivetNotificationsListener::CreateInfoOperation(
119     scoped_ptr<PrivetHTTPClient> http_client) {
120   if (!http_client) {
121     // Do nothing if resolution fails.
122     return;
123   }
124
125   std::string name = http_client->GetName();
126   DeviceContextMap::iterator device_iter = devices_seen_.find(name);
127   DCHECK(device_iter != devices_seen_.end());
128   DeviceContext* device = device_iter->second.get();
129   device->privet_http.swap(http_client);
130   device->info_operation = device->privet_http->CreateInfoOperation(
131       base::Bind(&PrivetNotificationsListener::OnPrivetInfoDone,
132                  base::Unretained(this),
133                  device));
134   device->info_operation->Start();
135 }
136
137 void PrivetNotificationsListener::OnPrivetInfoDone(
138     DeviceContext* device,
139     const base::DictionaryValue* json_value) {
140   int uptime;
141
142   if (!json_value ||
143       !json_value->GetInteger(kPrivetInfoKeyUptime, &uptime) ||
144       uptime > kTenMinutesInSeconds) {
145     return;
146   }
147
148   DCHECK(!device->notification_may_be_active);
149   device->notification_may_be_active = true;
150   devices_active_++;
151   delegate_->PrivetNotify(devices_active_ > 1, true);
152 }
153
154 void PrivetNotificationsListener::DeviceRemoved(const std::string& name) {
155   DCHECK_EQ(1u, devices_seen_.count(name));
156   DeviceContextMap::iterator device_iter = devices_seen_.find(name);
157   DCHECK(device_iter != devices_seen_.end());
158   DeviceContext* device = device_iter->second.get();
159
160   device->info_operation.reset();
161   device->privet_http_resolution.reset();
162   device->notification_may_be_active = false;
163   NotifyDeviceRemoved();
164 }
165
166 void PrivetNotificationsListener::DeviceCacheFlushed() {
167   for (DeviceContextMap::iterator i = devices_seen_.begin();
168        i != devices_seen_.end(); ++i) {
169     DeviceContext* device = i->second.get();
170
171     device->info_operation.reset();
172     device->privet_http_resolution.reset();
173     if (device->notification_may_be_active) {
174       device->notification_may_be_active = false;
175     }
176   }
177
178   devices_active_ = 0;
179   delegate_->PrivetRemoveNotification();
180 }
181
182 void PrivetNotificationsListener::NotifyDeviceRemoved() {
183   devices_active_--;
184   if (devices_active_ == 0) {
185     delegate_->PrivetRemoveNotification();
186   } else {
187     delegate_->PrivetNotify(devices_active_ > 1, false);
188   }
189 }
190
191 PrivetNotificationsListener::DeviceContext::DeviceContext() {
192 }
193
194 PrivetNotificationsListener::DeviceContext::~DeviceContext() {
195 }
196
197 PrivetNotificationService::PrivetNotificationService(
198     content::BrowserContext* profile)
199     : profile_(profile) {
200   base::MessageLoop::current()->PostDelayedTask(
201       FROM_HERE,
202       base::Bind(&PrivetNotificationService::Start, AsWeakPtr()),
203       base::TimeDelta::FromSeconds(kStartDelaySeconds +
204                                    base::RandInt(0, kStartDelaySeconds/4)));
205 }
206
207 PrivetNotificationService::~PrivetNotificationService() {
208 }
209
210 void PrivetNotificationService::DeviceChanged(
211     bool added,
212     const std::string& name,
213     const DeviceDescription& description) {
214   privet_notifications_listener_->DeviceChanged(added, name, description);
215 }
216
217 void PrivetNotificationService::DeviceRemoved(const std::string& name) {
218   privet_notifications_listener_->DeviceRemoved(name);
219 }
220
221 void PrivetNotificationService::DeviceCacheFlushed() {
222   privet_notifications_listener_->DeviceCacheFlushed();
223 }
224
225 // static
226 bool PrivetNotificationService::IsEnabled() {
227   CommandLine* command_line = CommandLine::ForCurrentProcess();
228   return !command_line->HasSwitch(
229       switches::kDisableDeviceDiscoveryNotifications);
230 }
231
232 // static
233 bool PrivetNotificationService::IsForced() {
234   CommandLine* command_line = CommandLine::ForCurrentProcess();
235   return command_line->HasSwitch(switches::kEnableDeviceDiscoveryNotifications);
236 }
237
238 void PrivetNotificationService::PrivetNotify(bool has_multiple,
239                                              bool added) {
240   base::string16 product_name = l10n_util::GetStringUTF16(
241       IDS_LOCAL_DISOCVERY_SERVICE_NAME_PRINTER);
242
243   int title_resource = has_multiple ?
244       IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER_MULTIPLE :
245       IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER;
246
247   int body_resource = has_multiple ?
248       IDS_LOCAL_DISOCVERY_NOTIFICATION_CONTENTS_PRINTER_MULTIPLE :
249       IDS_LOCAL_DISOCVERY_NOTIFICATION_CONTENTS_PRINTER;
250
251   base::string16 title = l10n_util::GetStringUTF16(title_resource);
252   base::string16 body = l10n_util::GetStringUTF16(body_resource);
253
254   Profile* profile_object = Profile::FromBrowserContext(profile_);
255   message_center::RichNotificationData rich_notification_data;
256
257   rich_notification_data.buttons.push_back(
258       message_center::ButtonInfo(l10n_util::GetStringUTF16(
259           IDS_LOCAL_DISOCVERY_NOTIFICATION_BUTTON_PRINTER)));
260
261   rich_notification_data.buttons.push_back(
262       message_center::ButtonInfo(l10n_util::GetStringUTF16(
263           IDS_LOCAL_DISCOVERY_NOTIFICATIONS_DISABLE_BUTTON_LABEL)));
264
265   Notification notification(
266       message_center::NOTIFICATION_TYPE_SIMPLE,
267       GURL(kPrivetNotificationOriginUrl),
268       title,
269       body,
270       ui::ResourceBundle::GetSharedInstance().GetImageNamed(
271           IDR_LOCAL_DISCOVERY_CLOUDPRINT_ICON),
272       blink::WebTextDirectionDefault,
273       message_center::NotifierId(GURL(kPrivetNotificationOriginUrl)),
274       product_name,
275       base::UTF8ToUTF16(kPrivetNotificationID),
276       rich_notification_data,
277       new PrivetNotificationDelegate(profile_));
278
279   bool updated = g_browser_process->notification_ui_manager()->Update(
280       notification, profile_object);
281   if (!updated && added && !LocalDiscoveryUIHandler::GetHasVisible()) {
282     ReportPrivetUmaEvent(PRIVET_NOTIFICATION_SHOWN);
283     g_browser_process->notification_ui_manager()->Add(notification,
284                                                       profile_object);
285   }
286 }
287
288 void PrivetNotificationService::PrivetRemoveNotification() {
289   ReportPrivetUmaEvent(PRIVET_NOTIFICATION_CANCELED);
290   Profile* profile_object = Profile::FromBrowserContext(profile_);
291   g_browser_process->notification_ui_manager()->CancelById(
292       kPrivetNotificationID,
293       NotificationUIManager::GetProfileID(profile_object));
294 }
295
296 void PrivetNotificationService::Start() {
297 #if defined(CHROMEOS)
298   SigninManagerBase* signin_manager =
299       SigninManagerFactory::GetForProfileIfExists(
300           Profile::FromBrowserContext(profile_));
301
302   if (!signin_manager || !signin_manager->IsAuthenticated())
303     return;
304 #endif
305
306   enable_privet_notification_member_.Init(
307       prefs::kLocalDiscoveryNotificationsEnabled,
308       Profile::FromBrowserContext(profile_)->GetPrefs(),
309       base::Bind(&PrivetNotificationService::OnNotificationsEnabledChanged,
310                  base::Unretained(this)));
311   OnNotificationsEnabledChanged();
312 }
313
314 void PrivetNotificationService::OnNotificationsEnabledChanged() {
315 #if defined(ENABLE_MDNS)
316   if (IsForced()) {
317     StartLister();
318   } else if (*enable_privet_notification_member_) {
319     ReportPrivetUmaEvent(PRIVET_SERVICE_STARTED);
320     traffic_detector_ =
321         new PrivetTrafficDetector(
322             net::ADDRESS_FAMILY_IPV4,
323             base::Bind(&PrivetNotificationService::StartLister, AsWeakPtr()));
324     traffic_detector_->Start();
325   } else {
326     traffic_detector_ = NULL;
327     device_lister_.reset();
328     service_discovery_client_ = NULL;
329     privet_notifications_listener_.reset();
330   }
331 #else
332   if (IsForced() || *enable_privet_notification_member_) {
333     StartLister();
334   } else {
335     device_lister_.reset();
336     service_discovery_client_ = NULL;
337     privet_notifications_listener_.reset();
338   }
339 #endif
340 }
341
342 void PrivetNotificationService::StartLister() {
343   ReportPrivetUmaEvent(PRIVET_LISTER_STARTED);
344 #if defined(ENABLE_MDNS)
345   traffic_detector_ = NULL;
346 #endif  // ENABLE_MDNS
347   service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance();
348   device_lister_.reset(
349       new PrivetDeviceListerImpl(service_discovery_client_.get(), this));
350   device_lister_->Start();
351   device_lister_->DiscoverNewDevices(false);
352
353   scoped_ptr<PrivetHTTPAsynchronousFactory> http_factory(
354       PrivetHTTPAsynchronousFactory::CreateInstance(
355           service_discovery_client_.get(), profile_->GetRequestContext()));
356
357   privet_notifications_listener_.reset(new PrivetNotificationsListener(
358       http_factory.Pass(), this));
359 }
360
361 PrivetNotificationDelegate::PrivetNotificationDelegate(
362     content::BrowserContext* profile)
363     :  profile_(profile) {
364 }
365
366 PrivetNotificationDelegate::~PrivetNotificationDelegate() {
367 }
368
369 std::string PrivetNotificationDelegate::id() const {
370   return kPrivetNotificationID;
371 }
372
373 void PrivetNotificationDelegate::ButtonClick(int button_index) {
374   if (button_index == 0) {
375     ReportPrivetUmaEvent(PRIVET_NOTIFICATION_CLICKED);
376     OpenTab(GURL(kPrivetNotificationOriginUrl));
377   } else if (button_index == 1) {
378     ReportPrivetUmaEvent(PRIVET_DISABLE_NOTIFICATIONS_CLICKED);
379     DisableNotifications();
380   }
381 }
382
383 void PrivetNotificationDelegate::OpenTab(const GURL& url) {
384   Profile* profile_obj = Profile::FromBrowserContext(profile_);
385
386   chrome::NavigateParams params(profile_obj,
387                               url,
388                               ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
389   params.disposition = NEW_FOREGROUND_TAB;
390   chrome::Navigate(&params);
391 }
392
393 void PrivetNotificationDelegate::DisableNotifications() {
394   Profile* profile_obj = Profile::FromBrowserContext(profile_);
395
396   profile_obj->GetPrefs()->SetBoolean(
397       prefs::kLocalDiscoveryNotificationsEnabled,
398       false);
399 }
400
401 }  // namespace local_discovery