File: cros_usb_detector.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (385 lines) | stat: -rw-r--r-- 15,209 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_USB_CROS_USB_DETECTOR_H_
#define CHROME_BROWSER_ASH_USB_CROS_USB_DETECTOR_H_

#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/memory/weak_ptr.h"
#include "base/scoped_observation_traits.h"
#include "chrome/browser/ash/guest_os/guest_id.h"
#include "chromeos/ash/components/dbus/cicerone/cicerone_client.h"
#include "chromeos/ash/components/dbus/concierge/concierge_client.h"
#include "chromeos/ash/components/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h"
#include "chromeos/ash/components/disks/disk_mount_manager.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/device/public/mojom/usb_enumeration_options.mojom-forward.h"
#include "services/device/public/mojom/usb_manager.mojom.h"
#include "services/device/public/mojom/usb_manager_client.mojom.h"

namespace ash {

inline constexpr uint8_t kInvalidUsbPortNumber = 0xff;

// List of class codes to handle / not handle.
// See https://www.usb.org/defined-class-codes for more information.
enum UsbClassCode : uint8_t {
  USB_CLASS_PER_INTERFACE = 0x00,
  USB_CLASS_AUDIO = 0x01,
  USB_CLASS_COMM = 0x02,
  USB_CLASS_HID = 0x03,
  USB_CLASS_PHYSICAL = 0x05,
  USB_CLASS_STILL_IMAGE = 0x06,
  USB_CLASS_PRINTER = 0x07,
  USB_CLASS_MASS_STORAGE = 0x08,
  USB_CLASS_HUB = 0x09,
  USB_CLASS_CDC_DATA = 0x0a,
  USB_CLASS_CSCID = 0x0b,
  USB_CLASS_CONTENT_SEC = 0x0d,
  USB_CLASS_VIDEO = 0x0e,
  USB_CLASS_PERSONAL_HEALTHCARE = 0x0f,
  USB_CLASS_BILLBOARD = 0x11,
  USB_CLASS_DIAGNOSTIC_DEVICE = 0xdc,
  USB_CLASS_WIRELESS_CONTROLLER = 0xe0,
  USB_CLASS_MISC = 0xef,
  USB_CLASS_APP_SPEC = 0xfe,
  USB_CLASS_VENDOR_SPEC = 0xff,
};

// List of subclass codes to handle / not handle.
// See https://www.usb.org/defined-class-codes for more information.
// Each class may have subclasses defined.
enum UsbSubclassCode : uint8_t {
  // Subclasses for USB_CLASS_COMM
  USB_COMM_SUBCLASS_DIRECT_LINE_CTL = 0x01,
  USB_COMM_SUBCLASS_ABSTRACT_CTL = 0x02,
  USB_COMM_SUBCLASS_TELEPHONE_CTL = 0x03,
  USB_COMM_SUBCLASS_MULTICHANNEL_CTL = 0x04,
  USB_COMM_SUBCLASS_CAPI_CTL = 0x05,
  USB_COMM_SUBCLASS_ETHERNET = 0x06,
  USB_COMM_SUBCLASS_ATM_NETWORKING_CTL = 0x07,
  USB_COMM_SUBCLASS_WIRELESS_HANDSET_CTL = 0x08,
  USB_COMM_SUBCLASS_DEVICE_MGMT = 0x09,
  USB_COMM_SUBCLASS_MOBILE_DIRECT_LINE = 0x0a,
  USB_COMM_SUBCLASS_OBEX = 0x0b,
  USB_COMM_SUBCLASS_ETHERNET_EMULATION = 0x0c,
  USB_COMM_SUBCLASS_NETWORK_CTL = 0x0d,
};

// Reasons the notification may be closed. These are used in histograms so do
// not remove/reorder entries. Only add at the end just before kMaxValue. Also
// remember to update the enum listing in
// tools/metrics/histograms/histograms.xml.
enum class CrosUsbNotificationClosed {
  // The notification was dismissed but not by the user (either automatically
  // or because the device was unplugged).
  kUnknown,
  // The user closed the notification via the close box.
  kByUser,
  // The user clicked on the Connect to Linux button of the notification.
  kConnectToLinux,
  // Maximum value for the enum.
  kMaxValue = kConnectToLinux
};

// Represents a USB device tracked by a CrosUsbDetector instance. The
// CrosUsbDetector only exposes devices which can be shared with Guest OSes.
struct CrosUsbDeviceInfo {
  CrosUsbDeviceInfo(std::string guid,
                    std::u16string label,
                    std::optional<guest_os::GuestId> shared_guest_id,
                    uint16_t vendor_id,
                    uint16_t product_id,
                    std::string serial_number,
                    bool prompt_before_sharing);
  CrosUsbDeviceInfo(const CrosUsbDeviceInfo&);
  ~CrosUsbDeviceInfo();

  std::string guid;
  std::u16string label;
  // Name of VM shared with. Unset if not shared. The device may be shared but
  // not yet attached.
  std::optional<guest_os::GuestId> shared_guest_id;
  uint16_t vendor_id;
  uint16_t product_id;
  std::string serial_number;
  // Devices shared with other devices or otherwise in use by the system
  // should have a confirmation prompt shown prior to sharing.
  bool prompt_before_sharing;
};

class CrosUsbDeviceObserver : public base::CheckedObserver {
 public:
  // Called when the available USB devices change.
  virtual void OnUsbDevicesChanged() = 0;
};

// Detects USB Devices for Chrome OS and manages UI for controlling their use
// with CrOS, Web or GuestOSs.
class CrosUsbDetector : public device::mojom::UsbDeviceManagerClient,
                        public CiceroneClient::Observer,
                        public ConciergeClient::VmObserver,
                        public VmPluginDispatcherClient::Observer,
                        public disks::DiskMountManager::Observer {
 public:
  // Used to namespace USB notifications to avoid clashes with WebUsbDetector.
  static std::string MakeNotificationId(const std::string& guid);

  // Can return nullptr.
  static CrosUsbDetector* Get();

  CrosUsbDetector();

  CrosUsbDetector(const CrosUsbDetector&) = delete;
  CrosUsbDetector& operator=(const CrosUsbDetector&) = delete;

  ~CrosUsbDetector() override;

  void SetDeviceManagerForTesting(
      mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager);

  // Connect to the device manager to be notified of connection/removal.
  // Used during browser startup, after connection errors and to setup a fake
  // device manager during testing.
  void ConnectToDeviceManager();

  // Called when a VM starts, to attach USB devices marked as shared to the VM.
  void ConnectSharedDevicesOnVmStartup(const std::string& vm_name);

  void DisconnectSharedDevicesOnVmShutdown(const std::string& vm_name);

  // device::mojom::UsbDeviceManagerClient
  void OnDeviceAdded(device::mojom::UsbDeviceInfoPtr device) override;
  void OnDeviceRemoved(device::mojom::UsbDeviceInfoPtr device) override;

  // Attaches the device identified by |guid| into the guest identified by
  // |guest_id|. Will unmount filesystems and detach any already shared devices.
  void AttachUsbDeviceToGuest(const guest_os::GuestId& guest_id,
                              const std::string& guid,
                              base::OnceCallback<void(bool success)> callback);

  // Detaches the device identified by |guid| from the VM identified by
  // |vm_name|.
  void DetachUsbDeviceFromVm(const std::string& vm_name,
                             const std::string& guid,
                             base::OnceCallback<void(bool success)> callback);

  void AddUsbDeviceObserver(CrosUsbDeviceObserver* observer);
  void RemoveUsbDeviceObserver(CrosUsbDeviceObserver* observer);
  void SignalUsbDeviceObservers();

  // Returns all the USB devices that are shareable with Guest OSes. This may
  // not include all connected devices.
  std::vector<CrosUsbDeviceInfo> GetShareableDevices() const;

 private:
  friend class CrosUsbDetectorTest;

  // Internal representation of a shareable USB device.
  struct UsbDevice {
    UsbDevice();
    UsbDevice(const UsbDevice&) = delete;
    UsbDevice(UsbDevice&&);
    ~UsbDevice();

    // Device information from the USB manager.
    device::mojom::UsbDeviceInfoPtr info;

    std::u16string label;

    // Name of the guest the device is shared with. Unset if not shared. The
    // device may be shared but not yet attached.
    std::optional<guest_os::GuestId> shared_guest_id;
    // Non-empty only when device is attached to a VM.
    std::optional<uint8_t> guest_port;
    // For a mass storage device, the mount points for active mounts.
    std::set<std::string> mount_points;
    // An internal flag to suppress observer events as mount_points empties.
    bool is_unmounting = false;
    // TODO(nverne): Add current state and errors etc.
  };

  // CiceroneClient::Observer:
  void OnContainerStarted(
      const vm_tools::cicerone::ContainerStartedSignal& signal) override;
  void OnLxdContainerDeleted(
      const vm_tools::cicerone::LxdContainerDeletedSignal& signal) override;

  // ConciergeClient::VmObserver:
  void OnVmStarted(const vm_tools::concierge::VmStartedSignal& signal) override;
  void OnVmStopped(const vm_tools::concierge::VmStoppedSignal& signal) override;

  // VmPluginDispatcherClient::Observer:
  void OnVmToolsStateChanged(
      const vm_tools::plugin_dispatcher::VmToolsStateChangedSignal& signal)
      override;
  void OnVmStateChanged(
      const vm_tools::plugin_dispatcher::VmStateChangedSignal& signal) override;

  // disks::DiskMountManager::Observer:
  void OnMountEvent(
      disks::DiskMountManager::MountEvent event,
      MountError error_code,
      const disks::DiskMountManager::MountPoint& mount_info) override;

  // Called after USB device access has been checked.
  void OnDeviceChecked(device::mojom::UsbDeviceInfoPtr device,
                       bool hide_notification,
                       bool allowed);

  // Allows the notification to be hidden (OnDeviceAdded without the flag calls
  // this).
  void OnDeviceAdded(device::mojom::UsbDeviceInfoPtr device,
                     bool hide_notification);
  void OnDeviceManagerConnectionError();

  // Callback listing devices attached to the machine.
  void OnListAttachedDevices(
      std::vector<device::mojom::UsbDeviceInfoPtr> devices);

  // Attaching a device goes through the flow:
  // AttachUsbDeviceToVm() -> UnmountFilesystems() -> OnUnmountFilesystems()
  //  -> AttachAfterDetach() -> OnAttachUsbDeviceOpened() -> DoVmAttach()
  //  -> OnUsbDeviceAttachFinished().
  // Unmounting filesystems and detaching devices is only needed in some cases,
  // usually we will skip these.

  // This prevents data corruption and suppresses the notification about
  // ejecting USB drives. A corresponding mount step when detaching from a VM is
  // not necessary as PermissionBroker reattaches the usb-storage drivers,
  // causing the drive to get mounted as usual.
  void UnmountFilesystems(const guest_os::GuestId& guest_id,
                          const std::string& guid,
                          base::OnceCallback<void(bool success)> callback);

  void OnUnmountFilesystems(const guest_os::GuestId& guest_id,
                            const std::string& guid,
                            base::OnceCallback<void(bool success)> callback,
                            bool unmount_success);

  // Devices will be auto-detached if they are attached to another VM.
  void AttachAfterDetach(const guest_os::GuestId& guest_id,
                         const std::string& guid,
                         base::OnceCallback<void(bool success)> callback,
                         bool detach_success);

  // Callback for AttachUsbDeviceToVm after opening a file handler.
  void OnAttachUsbDeviceOpened(const guest_os::GuestId& guest_id,
                               device::mojom::UsbDeviceInfoPtr device,
                               base::OnceCallback<void(bool success)> callback,
                               base::File file);

  void DoVmAttach(const guest_os::GuestId& guest_id,
                  device::mojom::UsbDeviceInfoPtr device_info,
                  base::ScopedFD fd,
                  base::OnceCallback<void(bool success)> callback);

  // Callbacks for when the USB device state has been updated.
  void OnUsbDeviceAttachFinished(
      const guest_os::GuestId& guest_id,
      device::mojom::UsbDeviceInfoPtr device_info,
      base::OnceCallback<void(bool success)> callback,
      std::optional<vm_tools::concierge::AttachUsbDeviceResponse> response);

  void AttachUsbDeviceToContainer(
      const guest_os::GuestId& guest_id,
      uint8_t guest_port,
      const std::string& guid,
      base::OnceCallback<void(bool success)> callback);

  void OnContainerAttachFinished(
      const guest_os::GuestId& guest_id,
      const std::string& guid,
      base::OnceCallback<void(bool success)> callback,
      std::optional<vm_tools::cicerone::AttachUsbToContainerResponse> response);

  void DetachUsbDeviceFromContainer(
      const std::string& vm_name,
      uint8_t guest_port,
      const std::string& guid,
      base::OnceCallback<void(bool success)> callback);

  void OnContainerDetachFinished(
      const std::string& vm_name,
      const std::string& guid,
      base::OnceCallback<void(bool success)> callback,
      std::optional<vm_tools::cicerone::DetachUsbFromContainerResponse>
          response);

  void ContainerAttachAfterDetach(
      const guest_os::GuestId& guest_id,
      uint8_t guest_port,
      const std::string& guid,
      base::OnceCallback<void(bool success)> callback,
      bool detach_success);

  void OnUsbDeviceDetachFinished(
      const std::string& vm_name,
      const std::string& guid,
      base::OnceCallback<void(bool success)> callback,
      std::optional<vm_tools::concierge::SuccessFailureResponse> response);

  // Returns true when a device should show a notification when attached.
  bool ShouldShowNotification(const UsbDevice& device);

  void RelinquishDeviceClaim(const std::string& guid);

  mojo::Remote<device::mojom::UsbDeviceManager> device_manager_;
  mojo::AssociatedReceiver<device::mojom::UsbDeviceManagerClient>
      client_receiver_{this};

  // USB filters, if *ALL* interfaces match no notification will be shown.
  std::vector<device::mojom::UsbDeviceFilterPtr> guest_os_usb_int_all_filter_;
  // USB filters, if *ANY* interfaces match no notification will be shown.
  std::vector<device::mojom::UsbDeviceFilterPtr> guest_os_usb_int_any_filter_;

  // GUID -> UsbDevice map for all connected USB devices.
  std::map<std::string, UsbDevice> usb_devices_;

  // Populated when we open the device path on the host. Acts as a claim on the
  // device even if the intended VM has not started yet. Removed when the device
  // is shared successfully with the VM. When an file is closed (here or by the
  // VM,  PermissionBroker will reattach the previous host drivers (if any).
  struct DeviceClaim {
    DeviceClaim();
    ~DeviceClaim();
    base::ScopedFD device_file;
    base::ScopedFD lifeline_file;
  };
  std::map<std::string, DeviceClaim> devices_claimed_;

  base::ObserverList<CrosUsbDeviceObserver> usb_device_observers_;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<CrosUsbDetector> weak_ptr_factory_{this};
};

}  // namespace ash

namespace base {

template <>
struct ScopedObservationTraits<ash::CrosUsbDetector,
                               ash::CrosUsbDeviceObserver> {
  static void AddObserver(ash::CrosUsbDetector* source,
                          ash::CrosUsbDeviceObserver* observer) {
    source->AddUsbDeviceObserver(observer);
  }
  static void RemoveObserver(ash::CrosUsbDetector* source,
                             ash::CrosUsbDeviceObserver* observer) {
    source->RemoveUsbDeviceObserver(observer);
  }
};

}  // namespace base

#endif  // CHROME_BROWSER_ASH_USB_CROS_USB_DETECTOR_H_