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"
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
;
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
{
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
);
70 return l10n_util::GetStringFUTF16(IDS_PRINTER_DETECTED_NOTIFICATION_TITLE
,
71 base::UTF8ToUTF16(vendor_name
));
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
)) {
98 const extensions::UsbPrinterManifestData
* manifest_data
=
99 extensions::UsbPrinterManifestData::Get(extension
.get());
100 if (manifest_data
&& manifest_data
->SupportsDevice(device
)) {
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
, ¶m
)) {
115 // Delegate for notification shown when a printer provider app for the plugged
116 // in printer is found.
117 class PrinterProviderExistsNotificationDelegate
: public NotificationDelegate
{
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_
);
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
{
144 SearchPrinterAppNotificationDelegate(content::BrowserContext
* browser_context
,
146 const std::string
& vendor_id_str
,
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
,
182 ~SearchPrinterAppNotificationDelegate() override
= default;
184 content::BrowserContext
* browser_context_
;
186 std::string vendor_id_str_
;
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(
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
,
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(),
246 notification
->SetSystemPriority();
247 notification_ui_manager
->Add(*notification
, profile
);
254 PrinterDetector::PrinterDetector(Profile
* profile
)
256 notification_ui_manager_(nullptr),
258 weak_ptr_factory_(this) {
259 extensions::ExtensionSystem::Get(profile
)->ready().Post(
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();
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()) {
286 device::UsbDeviceFilter printer_filter
;
287 printer_filter
.SetInterfaceClass(kPrinterInterfaceClass
);
288 if (!printer_filter
.Matches(device
)) {
292 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent", PRINTER_ADDED
,
293 PRINTER_SERVICE_EVENT_MAX
);
295 ShowPrinterPluggedNotification(
297 notification_ui_manager_
? notification_ui_manager_
298 : g_browser_process
->notification_ui_manager(),
302 void PrinterDetector::SetNotificationUIManagerForTesting(
303 NotificationUIManager
* manager
) {
304 notification_ui_manager_
= manager
;
307 } // namespace chromeos