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.
5 #include "chrome/browser/chromeos/dbus/printer_service_provider.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/metrics/histogram.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chromeos/profiles/profile_helper.h"
22 #include "chrome/browser/notifications/notification.h"
23 #include "chrome/browser/notifications/notification_delegate.h"
24 #include "chrome/browser/notifications/notification_ui_manager.h"
25 #include "chrome/common/extensions/api/webstore_widget_private.h"
26 #include "chrome/common/extensions/extension_constants.h"
27 #include "chrome/grit/generated_resources.h"
28 #include "chromeos/chromeos_switches.h"
29 #include "components/user_manager/user.h"
30 #include "components/user_manager/user_manager.h"
32 #include "dbus/exported_object.h"
33 #include "dbus/message.h"
34 #include "device/usb/usb_ids.h"
35 #include "extensions/browser/event_router.h"
36 #include "extensions/browser/extension_registry.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/common/extension_set.h"
39 #include "extensions/common/permissions/api_permission.h"
40 #include "extensions/common/permissions/permissions_data.h"
41 #include "extensions/common/permissions/usb_device_permission.h"
42 #include "grit/theme_resources.h"
43 #include "third_party/cros_system_api/dbus/service_constants.h"
44 #include "ui/base/l10n/l10n_util.h"
45 #include "ui/base/resource/resource_bundle.h"
47 namespace webstore_widget_private_api
=
48 extensions::api::webstore_widget_private
;
52 const char kPrinterAdded
[] = "PrinterAdded";
54 const char kPrinterProviderFoundNotificationID
[] =
55 "chrome://settings/printer/printer_app_found";
57 const char kNoPrinterProviderNotificationID
[] =
58 "chrome://settings/printer/no_printer_app";
60 enum PrinterServiceEvent
{
62 DEPRECATED_PAGE_DISPLAYED
,
63 NOTIFICATION_SHOWN_PRINTER_SUPPORTED
,
64 NOTIFICATION_SHOWN_PRINTER_NOT_SUPPORTED
,
65 WEBSTORE_WIDGET_APP_LAUNCHED
,
66 PRINTER_SERVICE_EVENT_MAX
,
69 bool HexStringToUInt16(const std::string
& input
, uint16
* output
) {
70 uint32 output_uint
= 0;
71 if (!base::HexStringToUInt(input
, &output_uint
) ||
72 output_uint
> std::numeric_limits
<uint16
>::max())
74 *output
= static_cast<uint16
>(output_uint
);
78 base::string16
GetNotificationTitle(uint16 vendor_id
, uint16 product_id
) {
79 const char* vendor_name
= device::UsbIds::GetVendorName(vendor_id
);
81 return l10n_util::GetStringFUTF16(IDS_PRINTER_DETECTED_NOTIFICATION_TITLE
,
82 base::UTF8ToUTF16(vendor_name
));
84 return l10n_util::GetStringUTF16(
85 IDS_PRINTER_DETECTED_NOTIFICATION_TITLE_UNKNOWN_VENDOR
);
89 std::string
GetNotificationTag(const std::string
& vendor_id
,
90 const std::string
& product_id
) {
91 return vendor_id
+ ":" + product_id
;
94 // Checks if there is an enabled extension with printerProvider permission and
95 // usbDevices persmission for the USB (vendor_id, product_id) pair.
96 bool HasAppThatSupportsPrinter(Profile
* profile
,
99 const extensions::ExtensionSet
& enabled_extensions
=
100 extensions::ExtensionRegistry::Get(profile
)->enabled_extensions();
101 for (const auto& extension
: enabled_extensions
) {
102 if (!extension
->permissions_data() ||
103 !extension
->permissions_data()->HasAPIPermission(
104 extensions::APIPermission::kPrinterProvider
) ||
105 !extension
->permissions_data()->HasAPIPermission(
106 extensions::APIPermission::kUsb
)) {
110 extensions::UsbDevicePermission::CheckParam
param(
111 vendor_id
, product_id
,
112 extensions::UsbDevicePermissionData::UNSPECIFIED_INTERFACE
);
113 if (extension
->permissions_data()->CheckAPIPermissionWithParam(
114 extensions::APIPermission::kUsbDevice
, ¶m
)) {
121 // Delegate for notification shown when a printer provider app for the plugged
122 // in printer is found.
123 class PrinterProviderExistsNotificationDelegate
: public NotificationDelegate
{
125 PrinterProviderExistsNotificationDelegate(const std::string
& vendor_id
,
126 const std::string
& product_id
)
127 : vendor_id_(vendor_id
), product_id_(product_id
) {}
129 std::string
id() const override
{
130 return "system.printer.printer_provider_exists/" +
131 GetNotificationTag(vendor_id_
, product_id_
);
135 ~PrinterProviderExistsNotificationDelegate() override
= default;
137 const std::string vendor_id_
;
138 const std::string product_id_
;
140 DISALLOW_COPY_AND_ASSIGN(PrinterProviderExistsNotificationDelegate
);
143 // Delegate for notification shown when there are no printer provider apps that
144 // support the plugged in printer found.
145 // The notification is clickable, and clicking it is supposed to launch
146 // Chrome Web Store widget listing apps that can support the plugged in printer.
147 // (not implemented yet).
148 class SearchPrinterAppNotificationDelegate
: public NotificationDelegate
{
150 SearchPrinterAppNotificationDelegate(content::BrowserContext
* browser_context
,
152 const std::string
& vendor_id_str
,
154 const std::string
& product_id_str
)
155 : browser_context_(browser_context
),
156 vendor_id_(vendor_id
),
157 vendor_id_str_(vendor_id_str
),
158 product_id_(product_id
),
159 product_id_str_(product_id_str
) {}
161 std::string
id() const override
{
162 return "system.printer.no_printer_provider_found/" +
163 GetNotificationTag(vendor_id_str_
, product_id_str_
);
165 bool HasClickedListener() override
{ return true; }
167 void Click() override
{
168 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent",
169 WEBSTORE_WIDGET_APP_LAUNCHED
,
170 PRINTER_SERVICE_EVENT_MAX
);
171 webstore_widget_private_api::Options options
;
172 options
.type
= webstore_widget_private_api::TYPE_PRINTER_PROVIDER
;
173 options
.usb_id
.reset(new webstore_widget_private_api::UsbId());
174 options
.usb_id
->vendor_id
= vendor_id_
;
175 options
.usb_id
->product_id
= product_id_
;
177 extensions::EventRouter
* event_router
=
178 extensions::EventRouter::Get(browser_context_
);
179 scoped_ptr
<extensions::Event
> event(new extensions::Event(
180 webstore_widget_private_api::OnShowWidget::kEventName
,
181 webstore_widget_private_api::OnShowWidget::Create(options
)));
182 event_router
->DispatchEventToExtension(extension_misc::kWebstoreWidgetAppId
,
187 ~SearchPrinterAppNotificationDelegate() override
= default;
189 content::BrowserContext
* browser_context_
;
191 std::string vendor_id_str_
;
193 std::string product_id_str_
;
195 DISALLOW_COPY_AND_ASSIGN(SearchPrinterAppNotificationDelegate
);
198 // Shows a notification for a plugged in printer.
199 // If there is a printerProvider app that handles the printer's USB (vendor_id,
200 // product_id) pair, the notification informs the user that the printer is ready
201 // to be used, otherwise it offers the user to search the Chrome Web Store for
202 // an app that can handle the printer.
203 void ShowPrinterPluggedNotification(
204 NotificationUIManager
* notification_ui_manager
,
205 const std::string
& vendor_id_str
,
206 const std::string
& product_id_str
) {
207 uint16 vendor_id
= 0;
208 uint16 product_id
= 0;
209 if (!HexStringToUInt16(vendor_id_str
, &vendor_id
) ||
210 !HexStringToUInt16(product_id_str
, &product_id
)) {
211 LOG(WARNING
) << "Invalid USB ID " << vendor_id_str
<< ":" << product_id_str
;
215 const user_manager::User
* user
=
216 user_manager::UserManager::Get()
217 ? user_manager::UserManager::Get()->GetActiveUser()
219 if (!user
|| !user
->HasGaiaAccount())
222 Profile
* profile
= chromeos::ProfileHelper::Get()->GetProfileByUser(user
);
226 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
227 scoped_ptr
<Notification
> notification
;
229 if (HasAppThatSupportsPrinter(profile
, vendor_id
, product_id
)) {
230 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent",
231 NOTIFICATION_SHOWN_PRINTER_SUPPORTED
,
232 PRINTER_SERVICE_EVENT_MAX
);
233 notification
.reset(new Notification(
234 message_center::NOTIFICATION_TYPE_SIMPLE
,
235 GURL(kPrinterProviderFoundNotificationID
),
236 GetNotificationTitle(vendor_id
, product_id
),
237 l10n_util::GetStringUTF16(
238 IDS_PRINTER_DETECTED_NOTIFICATION_PRINT_APP_FOUND_BODY
),
239 bundle
.GetImageNamed(IDR_PRINTER_NOTIFICATION
),
240 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT
,
241 kPrinterProviderFoundNotificationID
),
242 base::string16(), GetNotificationTag(vendor_id_str
, product_id_str
),
243 message_center::RichNotificationData(),
244 new PrinterProviderExistsNotificationDelegate(vendor_id_str
,
247 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent",
248 NOTIFICATION_SHOWN_PRINTER_NOT_SUPPORTED
,
249 PRINTER_SERVICE_EVENT_MAX
);
250 message_center::RichNotificationData options
;
251 options
.clickable
= true;
252 notification
.reset(new Notification(
253 message_center::NOTIFICATION_TYPE_SIMPLE
,
254 GURL(kNoPrinterProviderNotificationID
),
255 GetNotificationTitle(vendor_id
, product_id
),
256 l10n_util::GetStringUTF16(
257 IDS_PRINTER_DETECTED_NOTIFICATION_NO_PRINT_APP_BODY
),
258 bundle
.GetImageNamed(IDR_PRINTER_NOTIFICATION
),
259 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT
,
260 kNoPrinterProviderNotificationID
),
261 base::string16(), GetNotificationTag(vendor_id_str
, product_id_str
),
263 new SearchPrinterAppNotificationDelegate(
264 profile
, vendor_id
, vendor_id_str
, product_id
, product_id_str
)));
267 notification
->SetSystemPriority();
268 notification_ui_manager
->Add(*notification
, profile
);
275 PrinterServiceProvider::PrinterServiceProvider()
276 : notification_ui_manager_(nullptr), weak_ptr_factory_(this) {
279 PrinterServiceProvider::~PrinterServiceProvider() {
282 void PrinterServiceProvider::Start(
283 scoped_refptr
<dbus::ExportedObject
> exported_object
) {
284 exported_object_
= exported_object
;
286 DVLOG(1) << "PrinterServiceProvider started";
287 exported_object_
->ExportMethod(
288 kLibCrosServiceInterface
,
290 base::Bind(&PrinterServiceProvider::PrinterAdded
,
291 weak_ptr_factory_
.GetWeakPtr()),
292 base::Bind(&PrinterServiceProvider::OnExported
,
293 weak_ptr_factory_
.GetWeakPtr()));
296 void PrinterServiceProvider::SetNotificationUIManagerForTesting(
297 NotificationUIManager
* manager
) {
298 notification_ui_manager_
= manager
;
301 void PrinterServiceProvider::OnExported(
302 const std::string
& interface_name
,
303 const std::string
& method_name
,
306 LOG(ERROR
) << "Failed to export " << interface_name
<< "."
309 DVLOG(1) << "Method exported: " << interface_name
<< "." << method_name
;
312 void PrinterServiceProvider::PrinterAdded(
313 dbus::MethodCall
* method_call
,
314 dbus::ExportedObject::ResponseSender response_sender
) {
315 DVLOG(1) << "PrinterAdded " << method_call
->ToString();
316 dbus::MessageReader
reader(method_call
);
318 std::string vendor_id
;
319 reader
.PopString(&vendor_id
);
320 base::StringToUpperASCII(&vendor_id
);
322 std::string product_id
;
323 reader
.PopString(&product_id
);
324 base::StringToUpperASCII(&product_id
);
326 // Send an empty response.
327 response_sender
.Run(dbus::Response::FromMethodCall(method_call
));
329 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent", PRINTER_ADDED
,
330 PRINTER_SERVICE_EVENT_MAX
);
332 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
333 switches::kEnablePrinterAppSearch
)) {
337 ShowPrinterPluggedNotification(
338 notification_ui_manager_
? notification_ui_manager_
339 : g_browser_process
->notification_ui_manager(),
340 vendor_id
, product_id
);
343 } // namespace chromeos