text: # -*- coding: utf-8 -*-
  # Copyright 2016 The Chromium OS Authors. All rights reserved.
  # Use of this source code is governed by a BSD-style license that can be
  # found in the LICENSE file.
  """The control interface of Bluetooth HID flow module driver."""
  
  import logging
  import subprocess
  
  from chameleond.devices import chameleon_device
  from chameleond.utils import common
  from chameleond.utils import serial_utils
  from chameleond.utils import system_tools
  from chameleond.utils.bluetooth_hid import BluetoothHIDMouse
  from chameleond.utils.bluetooth_peripheral_kit import PeripheralKit
  from chameleond.utils.bluetooth_rn42 import RN42
  from chameleond.utils.bluetooth_nrf52 import nRF52
  
  
  class BluetoothHIDFlow(chameleon_device.Flow):
    """The control interface of bluetooth HID flow module driver."""
  
    # Subclasses must override this DRIVER attribute
    DRIVER = None
  
    # TODO(crbug.com/763504): Can we lower detection time? Or maybe wait longer
    # only when enabling the driver the first time, since the first detect was
    # timing out.
    # NOTE: This timeout was increased because the first detection after
    # enabling the driver was taking too long. This may increase startup times
    # by ~10+ seconds on Chameleons without a Bluetooth kit.
    DETECT_TIMEOUT_SECS = 5  # the timeout in detection
    DETECT_INTERVAL_SECS = 1  # the time to wait before retrying in detection
  
    def __init__(self, port_id, connector_type, usb_ctrl, kit_vid_hex,
                 kit_pid_hex, known_device_set=None):
      """Initializes a BluetoothHIDFlow object.
  
      Args:
        port_id: the port id that represents the type of port used.
        connector_type: the string obtained by GetConnectorType().
        usb_ctrl: a USBController object that BluetoothHIDFlow references to.
        serial_driver: the serial driver name for the kit
        kit_vid_hex: The USB VID (Vendor ID) of the kit, as a hexadecimal string
        kit_pid_hex: The USB PID (Product ID) of the kit, as a hexadecimal string
        known_device_set: List of serial nums of child device type (RN42 or RN52)
      """
      self._port_id = port_id
      self._connector_type = connector_type
      self._usb_ctrl = usb_ctrl
      self._tty = None
      self._kit_vid_hex = kit_vid_hex
      self._kit_pid_hex = kit_pid_hex
      self._known_device_set = known_device_set
      super(BluetoothHIDFlow, self).__init__()
  
    def _FindAndSetTty(self):
      if self._known_device_set:
        devices = serial_utils.FindTtyListByUsbVidPid(self._kit_vid_hex,
                                                      self._kit_pid_hex)
        for device in devices:
          if device['serial'] in self._known_device_set:
            self._tty = device['port']
            break
      else:
        self._tty = serial_utils.FindTtyByUsbVidPid(self._kit_vid_hex,
                                                    self._kit_pid_hex,
                                                    driver_name=self.DRIVER)
      return self._tty
  
    def IsUSBHostMode(self):
      """Check if the platform is in USB host mode.
  
      Returns:
        True if the platform is in USB host mode; otherwise, False.
      """
      try:
        pci_info = system_tools.SystemTools.Output('lspci', '-v')
      except subprocess.CalledProcessError:
        logging.info('Failed to use lspci')
        return False
  
      for line in pci_info.splitlines():
        if 'xhci_hcd' in line:
          logging.info('USB host mode: %s', line)
          return True
  
      logging.info('Not in USB host mode')
      return False
  
    def IsDetected(self):
      """Returns if the device can be detected."""
  
      # Enables Bluetooth HID port controller.
      # If the platform is 'chromeos' which always acts in the USB host mode,
      # there is no need to enable the USB OTG driver.
      if not self.IsUSBHostMode():
        self._usb_ctrl.EnableUSBOTGDriver()
      self._usb_ctrl.EnableDriver()
      # Our Bluetooth HID flow differs substantially from other flows.
      # Everything needed for IsDetected does the job of InitDevice:
      # Initialize a TTY, and report detecting it instead of a driver.
      # (Other USB flows simulate Plug/Unplug by Enabling/Diabling the driver.)
      # Ultimately, this is reasonable given that we expect the kit to stay
      # plugged in to chameleon, but Plug/Unplug for resetting the USB device
      # might be useful.
      # TODO(josephsih): Investigate plug/unplug for the Bluetooth HID Flow.
      try:
        common.WaitForCondition(lambda: bool(self._FindAndSetTty()), True,
                                self.DETECT_INTERVAL_SECS,
                                self.DETECT_TIMEOUT_SECS)
        return True
      except common.TimeoutError:
        logging.warn("Did not detect bluetooth kit for %s before timing out.",
                     self.__class__.__name__)
        return False
  
    def InitDevice(self):
      """Init the tty of the kit attached to the chameleon board."""
      logging.debug("InitDevice in Bluetooth HID Flow #%d is a no-op.",
                    self._port_id)
  
    def Reset(self):
      """Reset chameleon device.
  
      Do nothing for BlueoothHIDFlow.
      """
      logging.debug('Bluetooth HID flow #%d: Reset() called.', self._port_id)
  
    def Select(self):
      """Selects the USB HID flow."""
      logging.debug('Selected Bluetooth HID flow #%d.', self._port_id)
  
    def GetConnectorType(self):
      """Returns the human readable string for the connector type."""
      return self._connector_type
  
    def IsPhysicalPlugged(self):
      """Returns if the physical cable is plugged.
  
      Always returns True.
      """
      logging.debug('Bluetooth HID flow #%d: IsPhysicalPlugged() called.',
                    self._port_id)
      return True
  
    def IsPlugged(self):
      """Returns a Boolean value about the status of bluetooth hid serial driver.
  
      Returns:
        True if Bluetooth hid serial driver is enabled and the tty is found.
        False otherwise.
      """
      return self._usb_ctrl.DriverIsEnabled() and bool(self._tty)
  
    def Plug(self):
      """Emulates plug by enabling the bluetooth hid serial driver."""
      logging.debug('Bluetooth HID flow #%d: Plug() called.', self._port_id)
  
    def Unplug(self):
      """Emulates unplug by disabling bluetooth hid serial driver.
  
      Do nothing for BlueoothHIDFlow.
      """
      logging.debug('Bluetooth HID flow #%d: Unplug() called.', self._port_id)
  
    def DoFSM(self):
      """fpga_tio calls DoFSM after a flow is selected.
  
      Do nothing for BlueoothHIDFlow.
      """
      logging.debug('Bluetooth HID flow #%d: DoFSM() called.', self._port_id)
  
  
  class BluetoothHIDMouseFlow(BluetoothHIDFlow, BluetoothHIDMouse):
    """A flow object that emulates a Bluetooth BR/EDR mouse device."""
  
    DRIVER = RN42.DRIVER
  
    def __init__(self, port_id, usb_ctrl):
      """Initializes a BluetoothHIDMouseFlow object.
  
      Args:
        port_id: the port id that represents the type of port used.
        usb_ctrl: a USBController object that BluetoothHIDFlow references to.
      """
      BluetoothHIDFlow.__init__(self, port_id, 'BluetoothBR/EDR', usb_ctrl,
                                RN42.USB_VID, RN42.USB_PID, RN42.KNOWN_DEVICE_SET)
      # TODO(josephsih): Ideally constants at this level of Bluetooth abstraction
      # should be in BluetoothHID*, but that doesn't currently work due to cyclic
      # imports. Remove this when constants are moved to BluetoothHID.
      BluetoothHIDMouse.__init__(self, PeripheralKit.PIN_CODE_MODE, RN42)
  
  
  
  class BleHIDMouseFlow(BluetoothHIDFlow, BluetoothHIDMouse):
    """A flow object that emulates a BLE mouse device."""
  
    DRIVER = nRF52.DRIVER
    DRIVER_MODULE = nRF52.DRIVER_MODULE # See explanation in bluetooth_nrf52.py
  
    def __init__(self, port_id, usb_ctrl):
      """Initializes a BleHIDMouseFlow object.
  
      Args:
        port_id: the port id that represents the type of port used.
        usb_ctrl: a USBController object that BluetoothHIDFlow references to.
      """
      BluetoothHIDFlow.__init__(self, port_id, 'BleMouse', usb_ctrl,
                                nRF52.USB_VID, nRF52.USB_PID)
      BluetoothHIDMouse.__init__(self, PeripheralKit.OPEN_MODE, nRF52)
  