OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include <stdint.h> | |
6 | |
7 #include <memory> | |
8 #include <utility> | |
9 #include <vector> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/bind_helpers.h" | |
13 #include "base/macros.h" | |
14 #include "base/metrics/histogram_macros.h" | |
15 #include "base/scoped_observer.h" | |
16 #include "base/strings/string_number_conversions.h" | |
17 #include "base/strings/utf_string_conversions.h" | |
18 #include "chrome/browser/browser_process.h" | |
19 #include "chrome/browser/chromeos/printer_detector/printer_detector.h" | |
20 #include "chrome/browser/chromeos/printing/usb_printer_util.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/browser/ui/browser_navigator.h" | |
26 #include "chrome/common/extensions/api/webstore_widget_private.h" | |
27 #include "chrome/common/extensions/extension_constants.h" | |
28 #include "chrome/grit/generated_resources.h" | |
29 #include "chrome/grit/theme_resources.h" | |
30 #include "components/user_manager/user.h" | |
31 #include "components/user_manager/user_manager.h" | |
32 #include "device/base/device_client.h" | |
33 #include "device/usb/usb_device.h" | |
34 #include "device/usb/usb_device_filter.h" | |
35 #include "device/usb/usb_ids.h" | |
36 #include "device/usb/usb_service.h" | |
37 #include "extensions/browser/event_router.h" | |
38 #include "extensions/browser/extension_registry.h" | |
39 #include "extensions/browser/extension_system.h" | |
40 #include "extensions/common/api/printer_provider/usb_printer_manifest_data.h" | |
41 #include "extensions/common/extension.h" | |
42 #include "extensions/common/extension_set.h" | |
43 #include "extensions/common/one_shot_event.h" | |
44 #include "extensions/common/permissions/api_permission.h" | |
45 #include "extensions/common/permissions/permissions_data.h" | |
46 #include "extensions/common/permissions/usb_device_permission.h" | |
47 #include "ui/base/l10n/l10n_util.h" | |
48 #include "ui/base/resource/resource_bundle.h" | |
49 | |
50 namespace webstore_widget_private_api = | |
51 extensions::api::webstore_widget_private; | |
52 | |
53 namespace chromeos { | |
54 namespace { | |
55 | |
56 const char kPrinterProviderFoundNotificationID[] = | |
57 "chrome://settings/printer/printer_app_found"; | |
58 | |
59 const char kNoPrinterProviderNotificationID[] = | |
60 "chrome://settings/printer/no_printer_app"; | |
61 | |
62 enum PrinterServiceEvent { | |
63 PRINTER_ADDED, | |
64 DEPRECATED_PAGE_DISPLAYED, | |
65 NOTIFICATION_SHOWN_PRINTER_SUPPORTED, | |
66 NOTIFICATION_SHOWN_PRINTER_NOT_SUPPORTED, | |
67 WEBSTORE_WIDGET_APP_LAUNCHED, | |
68 PRINTER_SERVICE_EVENT_MAX, | |
69 }; | |
70 | |
71 base::string16 GetNotificationTitle(uint16_t vendor_id, uint16_t product_id) { | |
72 const char* vendor_name = device::UsbIds::GetVendorName(vendor_id); | |
73 if (vendor_name) { | |
74 return l10n_util::GetStringFUTF16(IDS_PRINTER_DETECTED_NOTIFICATION_TITLE, | |
75 base::UTF8ToUTF16(vendor_name)); | |
76 } else { | |
77 return l10n_util::GetStringUTF16( | |
78 IDS_PRINTER_DETECTED_NOTIFICATION_TITLE_UNKNOWN_VENDOR); | |
79 } | |
80 } | |
81 | |
82 std::string GetNotificationTag(const std::string& vendor_id, | |
83 const std::string& product_id) { | |
84 return vendor_id + ":" + product_id; | |
85 } | |
86 | |
87 // Checks if there is an enabled extension with printerProvider permission and | |
88 // usbDevices persmission for the USB (vendor_id, product_id) pair. | |
89 bool HasAppThatSupportsPrinter(Profile* profile, | |
90 const scoped_refptr<device::UsbDevice>& device) { | |
91 const extensions::ExtensionSet& enabled_extensions = | |
92 extensions::ExtensionRegistry::Get(profile)->enabled_extensions(); | |
93 for (const auto& extension : enabled_extensions) { | |
94 if (!extension->permissions_data() || | |
95 !extension->permissions_data()->HasAPIPermission( | |
96 extensions::APIPermission::kPrinterProvider) || | |
97 !extension->permissions_data()->HasAPIPermission( | |
98 extensions::APIPermission::kUsb)) { | |
99 continue; | |
100 } | |
101 | |
102 const extensions::UsbPrinterManifestData* manifest_data = | |
103 extensions::UsbPrinterManifestData::Get(extension.get()); | |
104 if (manifest_data && manifest_data->SupportsDevice(device)) { | |
105 return true; | |
106 } | |
107 | |
108 std::unique_ptr<extensions::UsbDevicePermission::CheckParam> param = | |
109 extensions::UsbDevicePermission::CheckParam::ForUsbDevice( | |
110 extension.get(), device.get()); | |
111 if (extension->permissions_data()->CheckAPIPermissionWithParam( | |
112 extensions::APIPermission::kUsbDevice, param.get())) { | |
113 return true; | |
114 } | |
115 } | |
116 return false; | |
117 } | |
118 | |
119 // Delegate for notification shown when a printer provider app for the plugged | |
120 // in printer is found. | |
121 class PrinterProviderExistsNotificationDelegate : public NotificationDelegate { | |
122 public: | |
123 PrinterProviderExistsNotificationDelegate(const std::string& vendor_id, | |
124 const std::string& product_id) | |
125 : vendor_id_(vendor_id), product_id_(product_id) {} | |
126 | |
127 std::string id() const override { | |
128 return "system.printer.printer_provider_exists/" + | |
129 GetNotificationTag(vendor_id_, product_id_); | |
130 } | |
131 | |
132 private: | |
133 ~PrinterProviderExistsNotificationDelegate() override = default; | |
134 | |
135 const std::string vendor_id_; | |
136 const std::string product_id_; | |
137 | |
138 DISALLOW_COPY_AND_ASSIGN(PrinterProviderExistsNotificationDelegate); | |
139 }; | |
140 | |
141 // Delegate for notification shown when there are no printer provider apps that | |
142 // support the plugged in printer found. | |
143 // The notification is clickable, and clicking it is supposed to launch | |
144 // Chrome Web Store widget listing apps that can support the plugged in printer. | |
145 // (not implemented yet). | |
146 class SearchPrinterAppNotificationDelegate : public NotificationDelegate { | |
147 public: | |
148 SearchPrinterAppNotificationDelegate(content::BrowserContext* browser_context, | |
149 uint16_t vendor_id, | |
150 const std::string& vendor_id_str, | |
151 uint16_t product_id, | |
152 const std::string& product_id_str) | |
153 : browser_context_(browser_context), | |
154 vendor_id_(vendor_id), | |
155 vendor_id_str_(vendor_id_str), | |
156 product_id_(product_id), | |
157 product_id_str_(product_id_str) {} | |
158 | |
159 std::string id() const override { | |
160 return "system.printer.no_printer_provider_found/" + | |
161 GetNotificationTag(vendor_id_str_, product_id_str_); | |
162 } | |
163 | |
164 bool HasClickedListener() override { return true; } | |
165 | |
166 void Click() override { | |
167 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent", | |
168 WEBSTORE_WIDGET_APP_LAUNCHED, | |
169 PRINTER_SERVICE_EVENT_MAX); | |
170 webstore_widget_private_api::Options options; | |
171 options.type = webstore_widget_private_api::TYPE_PRINTER_PROVIDER; | |
172 options.usb_id.reset(new webstore_widget_private_api::UsbId()); | |
173 options.usb_id->vendor_id = vendor_id_; | |
174 options.usb_id->product_id = product_id_; | |
175 | |
176 extensions::EventRouter* event_router = | |
177 extensions::EventRouter::Get(browser_context_); | |
178 std::unique_ptr<extensions::Event> event(new extensions::Event( | |
179 extensions::events::WEBSTORE_WIDGET_PRIVATE_ON_SHOW_WIDGET, | |
180 webstore_widget_private_api::OnShowWidget::kEventName, | |
181 webstore_widget_private_api::OnShowWidget::Create(options))); | |
182 event_router->DispatchEventToExtension(extension_misc::kWebstoreWidgetAppId, | |
183 std::move(event)); | |
184 } | |
185 | |
186 private: | |
187 ~SearchPrinterAppNotificationDelegate() override = default; | |
188 | |
189 content::BrowserContext* browser_context_; | |
190 uint16_t vendor_id_; | |
191 std::string vendor_id_str_; | |
192 uint16_t product_id_; | |
193 std::string product_id_str_; | |
194 | |
195 DISALLOW_COPY_AND_ASSIGN(SearchPrinterAppNotificationDelegate); | |
196 }; | |
197 | |
198 // The PrinterDetector that initiates extension-based USB printer setup: | |
199 // | |
200 // * if there is a printer provider extension for a detected USB printer | |
201 // installed, it shows a notification informing the user the printer is ready to | |
202 // be used. | |
203 // | |
204 // * otherwise, it shows a notification offering the user an option to install a | |
205 // printer provider extension for the printer from the Chrome Web Store. | |
206 // | |
207 // TODO(justincarlson) - Remove this implementation when CUPS support is enabled | |
208 // by default. | |
209 class LegacyPrinterDetectorImpl : public PrinterDetector, | |
210 public device::UsbService::Observer { | |
211 public: | |
212 explicit LegacyPrinterDetectorImpl(Profile* profile) | |
213 : profile_(profile), | |
214 notification_ui_manager_(nullptr), | |
215 observer_(this), | |
216 weak_ptr_factory_(this) { | |
217 extensions::ExtensionSystem::Get(profile)->ready().Post( | |
218 FROM_HERE, base::Bind(&LegacyPrinterDetectorImpl::Initialize, | |
219 weak_ptr_factory_.GetWeakPtr())); | |
220 } | |
221 ~LegacyPrinterDetectorImpl() override = default; | |
222 | |
223 private: | |
224 // UsbService::observer override: | |
225 void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override { | |
226 const user_manager::User* user = | |
227 ProfileHelper::Get()->GetUserByProfile(profile_); | |
228 if (!user || !user->HasGaiaAccount() || !user_manager::UserManager::Get() || | |
229 user != user_manager::UserManager::Get()->GetActiveUser()) { | |
230 return; | |
231 } | |
232 | |
233 if (!UsbDeviceIsPrinter(*device)) { | |
234 return; | |
235 } | |
236 | |
237 if (notification_ui_manager_ == nullptr) { | |
238 notification_ui_manager_ = g_browser_process->notification_ui_manager(); | |
239 } | |
240 | |
241 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent", | |
242 PRINTER_ADDED, PRINTER_SERVICE_EVENT_MAX); | |
243 ShowPrinterPluggedNotification(device); | |
244 } | |
245 | |
246 // Initializes the printer detector. | |
247 void Initialize() { | |
248 device::UsbService* usb_service = | |
249 device::DeviceClient::Get()->GetUsbService(); | |
250 if (!usb_service) | |
251 return; | |
252 observer_.Add(usb_service); | |
253 } | |
254 | |
255 void SetNotificationUIManagerForTesting( | |
256 NotificationUIManager* manager) override { | |
257 notification_ui_manager_ = manager; | |
258 } | |
259 | |
260 // Shows a notification for a plugged in printer. | |
261 // If there is a printerProvider app that handles the printer's USB | |
262 // (vendor_id, product_id) pair, the notification informs the user that the | |
263 // printer is ready to be used, otherwise it offers the user to search the | |
264 // Chrome Web Store for an app that can handle the printer. | |
265 void ShowPrinterPluggedNotification( | |
266 const scoped_refptr<device::UsbDevice>& device) { | |
267 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); | |
268 std::unique_ptr<Notification> notification; | |
269 | |
270 const std::string kVendorIdStr = base::IntToString(device->vendor_id()); | |
271 const std::string kProductIdStr = base::IntToString(device->product_id()); | |
272 | |
273 if (HasAppThatSupportsPrinter(profile_, device)) { | |
274 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent", | |
275 NOTIFICATION_SHOWN_PRINTER_SUPPORTED, | |
276 PRINTER_SERVICE_EVENT_MAX); | |
277 notification.reset(new Notification( | |
278 message_center::NOTIFICATION_TYPE_SIMPLE, | |
279 GetNotificationTitle(device->vendor_id(), device->product_id()), | |
280 l10n_util::GetStringUTF16( | |
281 IDS_PRINTER_DETECTED_NOTIFICATION_PRINT_APP_FOUND_BODY), | |
282 bundle.GetImageNamed(IDR_PRINTER_NOTIFICATION), | |
283 message_center::NotifierId( | |
284 message_center::NotifierId::SYSTEM_COMPONENT, | |
285 kPrinterProviderFoundNotificationID), | |
286 base::string16(), GURL(kPrinterProviderFoundNotificationID), | |
287 GetNotificationTag(kVendorIdStr, kProductIdStr), | |
288 message_center::RichNotificationData(), | |
289 new PrinterProviderExistsNotificationDelegate(kVendorIdStr, | |
290 kProductIdStr))); | |
291 } else { | |
292 UMA_HISTOGRAM_ENUMERATION("PrinterService.PrinterServiceEvent", | |
293 NOTIFICATION_SHOWN_PRINTER_NOT_SUPPORTED, | |
294 PRINTER_SERVICE_EVENT_MAX); | |
295 message_center::RichNotificationData options; | |
296 options.clickable = true; | |
297 notification.reset(new Notification( | |
298 message_center::NOTIFICATION_TYPE_SIMPLE, | |
299 GetNotificationTitle(device->vendor_id(), device->product_id()), | |
300 l10n_util::GetStringUTF16( | |
301 IDS_PRINTER_DETECTED_NOTIFICATION_NO_PRINT_APP_BODY), | |
302 bundle.GetImageNamed(IDR_PRINTER_NOTIFICATION), | |
303 message_center::NotifierId( | |
304 message_center::NotifierId::SYSTEM_COMPONENT, | |
305 kNoPrinterProviderNotificationID), | |
306 base::string16(), GURL(kNoPrinterProviderNotificationID), | |
307 GetNotificationTag(kVendorIdStr, kProductIdStr), options, | |
308 new SearchPrinterAppNotificationDelegate( | |
309 profile_, device->vendor_id(), kVendorIdStr, device->product_id(), | |
310 kProductIdStr))); | |
311 } | |
312 | |
313 notification->SetSystemPriority(); | |
314 notification_ui_manager_->Add(*notification, profile_); | |
315 } | |
316 | |
317 std::unique_ptr<Notification> notification_; | |
318 | |
319 Profile* profile_; | |
320 NotificationUIManager* notification_ui_manager_; | |
321 ScopedObserver<device::UsbService, device::UsbService::Observer> observer_; | |
322 base::WeakPtrFactory<LegacyPrinterDetectorImpl> weak_ptr_factory_; | |
323 }; | |
324 | |
325 } // namespace | |
326 | |
327 // static | |
328 std::unique_ptr<PrinterDetector> PrinterDetector::CreateLegacy( | |
329 Profile* profile) { | |
330 return base::MakeUnique<LegacyPrinterDetectorImpl>(profile); | |
331 } | |
332 | |
333 } // namespace chromeos | |
OLD | NEW |