Move StringToUpperASCII to base namespace
[chromium-blink-merge.git] / chrome / browser / chromeos / dbus / printer_service_provider.cc
blob4c4c4cc5abe0eeadc741667a23f2446cd0e5216e
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"
7 #include <stdint.h>
9 #include <limits>
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"
31 #include "dbus/bus.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;
50 namespace {
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 {
61 PRINTER_ADDED,
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())
73 return false;
74 *output = static_cast<uint16>(output_uint);
75 return true;
78 base::string16 GetNotificationTitle(uint16 vendor_id, uint16 product_id) {
79 const char* vendor_name = device::UsbIds::GetVendorName(vendor_id);
80 if (vendor_name) {
81 return l10n_util::GetStringFUTF16(IDS_PRINTER_DETECTED_NOTIFICATION_TITLE,
82 base::UTF8ToUTF16(vendor_name));
83 } else {
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,
97 uint16 vendor_id,
98 uint16 product_id) {
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)) {
107 continue;
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, &param)) {
115 return true;
118 return false;
121 // Delegate for notification shown when a printer provider app for the plugged
122 // in printer is found.
123 class PrinterProviderExistsNotificationDelegate : public NotificationDelegate {
124 public:
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_);
134 private:
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 {
149 public:
150 SearchPrinterAppNotificationDelegate(content::BrowserContext* browser_context,
151 uint16 vendor_id,
152 const std::string& vendor_id_str,
153 uint16 product_id,
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,
183 event.Pass());
186 private:
187 ~SearchPrinterAppNotificationDelegate() override = default;
189 content::BrowserContext* browser_context_;
190 uint16 vendor_id_;
191 std::string vendor_id_str_;
192 uint16 product_id_;
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;
212 return;
215 const user_manager::User* user =
216 user_manager::UserManager::Get()
217 ? user_manager::UserManager::Get()->GetActiveUser()
218 : nullptr;
219 if (!user || !user->HasGaiaAccount())
220 return;
222 Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(user);
223 if (!profile)
224 return;
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,
245 product_id_str)));
246 } else {
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),
262 options,
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);
271 } // namespace
273 namespace chromeos {
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,
289 kPrinterAdded,
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,
304 bool success) {
305 if (!success) {
306 LOG(ERROR) << "Failed to export " << interface_name << "."
307 << method_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)) {
334 return;
337 ShowPrinterPluggedNotification(
338 notification_ui_manager_ ? notification_ui_manager_
339 : g_browser_process->notification_ui_manager(),
340 vendor_id, product_id);
343 } // namespace chromeos