Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / printer_detector / printer_detector.cc
blobe37f48eeec85eb498b81164a6474855ce540f127
1 // Copyright 2015 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.
5 #include "chrome/browser/chromeos/printer_detector/printer_detector.h"
7 #include <stdint.h>
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chromeos/profiles/profile_helper.h"
17 #include "chrome/browser/notifications/notification.h"
18 #include "chrome/browser/notifications/notification_delegate.h"
19 #include "chrome/browser/notifications/notification_ui_manager.h"
20 #include "chrome/common/extensions/api/webstore_widget_private.h"
21 #include "chrome/common/extensions/extension_constants.h"
22 #include "chrome/grit/generated_resources.h"
23 #include "components/user_manager/user.h"
24 #include "components/user_manager/user_manager.h"
25 #include "device/core/device_client.h"
26 #include "device/usb/usb_device.h"
27 #include "device/usb/usb_device_filter.h"
28 #include "device/usb/usb_ids.h"
29 #include "extensions/browser/event_router.h"
30 #include "extensions/browser/extension_registry.h"
31 #include "extensions/browser/extension_system.h"
32 #include "extensions/common/api/printer_provider/usb_printer_manifest_data.h"
33 #include "extensions/common/extension.h"
34 #include "extensions/common/extension_set.h"
35 #include "extensions/common/one_shot_event.h"
36 #include "extensions/common/permissions/api_permission.h"
37 #include "extensions/common/permissions/permissions_data.h"
38 #include "extensions/common/permissions/usb_device_permission.h"
39 #include "grit/theme_resources.h"
40 #include "ui/base/l10n/l10n_util.h"
41 #include "ui/base/resource/resource_bundle.h"
43 namespace webstore_widget_private_api =
44 extensions::api::webstore_widget_private;
46 namespace {
48 const char kPrinterProviderFoundNotificationID[] =
49 "chrome://settings/printer/printer_app_found";
51 const char kNoPrinterProviderNotificationID[] =
52 "chrome://settings/printer/no_printer_app";
54 // Base class used for printer USB interfaces
55 // (https://www.usb.org/developers/defined_class).
56 const uint8 kPrinterInterfaceClass = 7;
58 enum PrinterServiceEvent {
59 PRINTER_ADDED,
60 DEPRECATED_PAGE_DISPLAYED,
61 NOTIFICATION_SHOWN_PRINTER_SUPPORTED,
62 NOTIFICATION_SHOWN_PRINTER_NOT_SUPPORTED,
63 WEBSTORE_WIDGET_APP_LAUNCHED,
64 PRINTER_SERVICE_EVENT_MAX,
67 base::string16 GetNotificationTitle(uint16 vendor_id, uint16 product_id) {
68 const char* vendor_name = device::UsbIds::GetVendorName(vendor_id);
69 if (vendor_name) {
70 return l10n_util::GetStringFUTF16(IDS_PRINTER_DETECTED_NOTIFICATION_TITLE,
71 base::UTF8ToUTF16(vendor_name));
72 } else {
73 return l10n_util::GetStringUTF16(
74 IDS_PRINTER_DETECTED_NOTIFICATION_TITLE_UNKNOWN_VENDOR);
78 std::string GetNotificationTag(const std::string& vendor_id,
79 const std::string& product_id) {
80 return vendor_id + ":" + product_id;
83 // Checks if there is an enabled extension with printerProvider permission and
84 // usbDevices persmission for the USB (vendor_id, product_id) pair.
85 bool HasAppThatSupportsPrinter(Profile* profile,
86 const scoped_refptr<device::UsbDevice>& device) {
87 const extensions::ExtensionSet& enabled_extensions =
88 extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
89 for (const auto& extension : enabled_extensions) {
90 if (!extension->permissions_data() ||
91 !extension->permissions_data()->HasAPIPermission(
92 extensions::APIPermission::kPrinterProvider) ||
93 !extension->permissions_data()->HasAPIPermission(
94 extensions::APIPermission::kUsb)) {
95 continue;
98 const extensions::UsbPrinterManifestData* manifest_data =
99 extensions::UsbPrinterManifestData::Get(extension.get());
100 if (manifest_data && manifest_data->SupportsDevice(device)) {
101 return true;
104 extensions::UsbDevicePermission::CheckParam param(
105 device->vendor_id(), device->product_id(),
106 extensions::UsbDevicePermissionData::UNSPECIFIED_INTERFACE);
107 if (extension->permissions_data()->CheckAPIPermissionWithParam(
108 extensions::APIPermission::kUsbDevice, &param)) {
109 return true;
112 return false;
115 // Delegate for notification shown when a printer provider app for the plugged
116 // in printer is found.
117 class PrinterProviderExistsNotificationDelegate : public NotificationDelegate {
118 public:
119 PrinterProviderExistsNotificationDelegate(const std::string& vendor_id,
120 const std::string& product_id)
121 : vendor_id_(vendor_id), product_id_(product_id) {}
123 std::string id() const override {
124 return "system.printer.printer_provider_exists/" +
125 GetNotificationTag(vendor_id_, product_id_);
128 private:
129 ~PrinterProviderExistsNotificationDelegate() override = default;
131 const std::string vendor_id_;
132 const std::string product_id_;
134 DISALLOW_COPY_AND_ASSIGN(PrinterProviderExistsNotificationDelegate);
137 // Delegate for notification shown when there are no printer provider apps that
138 // support the plugged in printer found.
139 // The notification is clickable, and clicking it is supposed to launch
140 // Chrome Web Store widget listing apps that can support the plugged in printer.
141 // (not implemented yet).
142 class SearchPrinterAppNotificationDelegate : public NotificationDelegate {
143 public:
144 SearchPrinterAppNotificationDelegate(content::BrowserContext* browser_context,
145 uint16 vendor_id,
146 const std::string& vendor_id_str,
147 uint16 product_id,
148 const std::string& product_id_str)
149 : browser_context_(browser_context),
150 vendor_id_(vendor_id),
151 vendor_id_str_(vendor_id_str),
152 product_id_(product_id),
153 product_id_str_(product_id_str) {}
155 std::string id() const override {
156 return "system.printer.no_printer_provider_found/" +
157 GetNotificationTag(vendor_id_str_, product_id_str_);
159 bool HasClickedListener() override { return true; }
161 void Click() override {
162 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent",
163 WEBSTORE_WIDGET_APP_LAUNCHED,
164 PRINTER_SERVICE_EVENT_MAX);
165 webstore_widget_private_api::Options options;
166 options.type = webstore_widget_private_api::TYPE_PRINTER_PROVIDER;
167 options.usb_id.reset(new webstore_widget_private_api::UsbId());
168 options.usb_id->vendor_id = vendor_id_;
169 options.usb_id->product_id = product_id_;
171 extensions::EventRouter* event_router =
172 extensions::EventRouter::Get(browser_context_);
173 scoped_ptr<extensions::Event> event(new extensions::Event(
174 extensions::events::WEBSTORE_WIDGET_PRIVATE_ON_SHOW_WIDGET,
175 webstore_widget_private_api::OnShowWidget::kEventName,
176 webstore_widget_private_api::OnShowWidget::Create(options)));
177 event_router->DispatchEventToExtension(extension_misc::kWebstoreWidgetAppId,
178 event.Pass());
181 private:
182 ~SearchPrinterAppNotificationDelegate() override = default;
184 content::BrowserContext* browser_context_;
185 uint16 vendor_id_;
186 std::string vendor_id_str_;
187 uint16 product_id_;
188 std::string product_id_str_;
190 DISALLOW_COPY_AND_ASSIGN(SearchPrinterAppNotificationDelegate);
193 // Shows a notification for a plugged in printer.
194 // If there is a printerProvider app that handles the printer's USB (vendor_id,
195 // product_id) pair, the notification informs the user that the printer is ready
196 // to be used, otherwise it offers the user to search the Chrome Web Store for
197 // an app that can handle the printer.
198 void ShowPrinterPluggedNotification(
199 Profile* profile,
200 NotificationUIManager* notification_ui_manager,
201 const scoped_refptr<device::UsbDevice>& device) {
202 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
203 scoped_ptr<Notification> notification;
205 const std::string kVendorIdStr = base::IntToString(device->vendor_id());
206 const std::string kProductIdStr = base::IntToString(device->product_id());
208 if (HasAppThatSupportsPrinter(profile, device)) {
209 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent",
210 NOTIFICATION_SHOWN_PRINTER_SUPPORTED,
211 PRINTER_SERVICE_EVENT_MAX);
212 notification.reset(new Notification(
213 message_center::NOTIFICATION_TYPE_SIMPLE,
214 GetNotificationTitle(device->vendor_id(), device->product_id()),
215 l10n_util::GetStringUTF16(
216 IDS_PRINTER_DETECTED_NOTIFICATION_PRINT_APP_FOUND_BODY),
217 bundle.GetImageNamed(IDR_PRINTER_NOTIFICATION),
218 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
219 kPrinterProviderFoundNotificationID),
220 base::string16(), GURL(kPrinterProviderFoundNotificationID),
221 GetNotificationTag(kVendorIdStr, kProductIdStr),
222 message_center::RichNotificationData(),
223 new PrinterProviderExistsNotificationDelegate(kVendorIdStr,
224 kProductIdStr)));
225 } else {
226 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent",
227 NOTIFICATION_SHOWN_PRINTER_NOT_SUPPORTED,
228 PRINTER_SERVICE_EVENT_MAX);
229 message_center::RichNotificationData options;
230 options.clickable = true;
231 notification.reset(new Notification(
232 message_center::NOTIFICATION_TYPE_SIMPLE,
233 GetNotificationTitle(device->vendor_id(), device->product_id()),
234 l10n_util::GetStringUTF16(
235 IDS_PRINTER_DETECTED_NOTIFICATION_NO_PRINT_APP_BODY),
236 bundle.GetImageNamed(IDR_PRINTER_NOTIFICATION),
237 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
238 kNoPrinterProviderNotificationID),
239 base::string16(), GURL(kNoPrinterProviderNotificationID),
240 GetNotificationTag(kVendorIdStr, kProductIdStr), options,
241 new SearchPrinterAppNotificationDelegate(
242 profile, device->vendor_id(), kVendorIdStr, device->product_id(),
243 kProductIdStr)));
246 notification->SetSystemPriority();
247 notification_ui_manager->Add(*notification, profile);
250 } // namespace
252 namespace chromeos {
254 PrinterDetector::PrinterDetector(Profile* profile)
255 : profile_(profile),
256 notification_ui_manager_(nullptr),
257 observer_(this),
258 weak_ptr_factory_(this) {
259 extensions::ExtensionSystem::Get(profile)->ready().Post(
260 FROM_HERE,
261 base::Bind(&PrinterDetector::Initialize, weak_ptr_factory_.GetWeakPtr()));
264 PrinterDetector::~PrinterDetector() {
267 void PrinterDetector::Shutdown() {
270 void PrinterDetector::Initialize() {
271 device::UsbService* usb_service =
272 device::DeviceClient::Get()->GetUsbService();
273 if (!usb_service)
274 return;
275 observer_.Add(usb_service);
278 void PrinterDetector::OnDeviceAdded(scoped_refptr<device::UsbDevice> device) {
279 const user_manager::User* user =
280 ProfileHelper::Get()->GetUserByProfile(profile_);
281 if (!user || !user->HasGaiaAccount() || !user_manager::UserManager::Get() ||
282 user != user_manager::UserManager::Get()->GetActiveUser()) {
283 return;
286 device::UsbDeviceFilter printer_filter;
287 printer_filter.SetInterfaceClass(kPrinterInterfaceClass);
288 if (!printer_filter.Matches(device)) {
289 return;
292 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent", PRINTER_ADDED,
293 PRINTER_SERVICE_EVENT_MAX);
295 ShowPrinterPluggedNotification(
296 profile_,
297 notification_ui_manager_ ? notification_ui_manager_
298 : g_browser_process->notification_ui_manager(),
299 device);
302 void PrinterDetector::SetNotificationUIManagerForTesting(
303 NotificationUIManager* manager) {
304 notification_ui_manager_ = manager;
307 } // namespace chromeos