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_
|