Ejemplo n.º 1
0
    def read_until(self, expected_commands, timeout):
        """Read AdbMessages from this transport until we get an expected command.

    The ADB protocol specifies that before a successful CNXN handshake, any
    other packets must be ignored, so this method provides the ability to
    ignore unwanted commands.  It's primarily used during the initial
    connection to the device.  See Read() for more details, including more
    exceptions that may be raised.

    Args:
      expected_commands: Iterable of expected command responses, like ('CNXN',
        'AUTH').
      timeout: timeouts.PolledTimeout object to use for timeout.

    Returns:
      The ADB message received that matched one of expected_commands.

    Raises:
      AdbProtocolError: If timeout expires between reads, this can happen
        if we are getting spammed with unexpected commands.
    """
        msg = timeouts.loop_until_timeout_or_valid(
            timeout, lambda: self.read_message(timeout),
            lambda m: m.command in expected_commands, 0)
        if msg.command not in expected_commands:
            raise usb_exceptions.AdbTimeoutError(
                'Timed out establishing connection, waiting for: %s' %
                expected_commands)
        return msg
Ejemplo n.º 2
0
    def read_for_stream(self, stream_transport, timeout_ms=None):
        """Attempt to read a packet for the given stream transport.

    Will read packets from self.transport until one intended for the given
    AdbStream is found.  If another thread is already reading packets, this will
    block until that thread reads a packet for this stream, or timeout expires.
    Note that this method always returns None, but if a packet was read for the
    given stream, it will have been added to that stream's message queue.

    This is somewhat tricky to do - first we check if there's a message already
    in our queue.  If not, then we try to use our AdbConnection to read a
    message for this stream.

    If some other thread is already doing reads, then read_for_stream() will sit
    in a tight loop, with a short delay, checking our message queue for a
    message from the other thread.

    Note that we must pass the queue in from the AdbStream, rather than looking
    it up in the AdbConnection's map, because the AdbConnection may have
    removed the queue from its map (while it still had messages in it).  The
    AdbStream itself maintains a reference to the queue to avoid dropping those
    messages.

    The AdbMessage read is guaranteed to be one of 'OKAY', 'WRTE', or 'CLSE'.
    If it was a WRTE message, then it will have been automatically ACK'd with an
    OKAY message, if it was a CLSE message it will have been ACK'd with a
    corresponding CLSE message, and this AdbStream will be marked as closed.

    Args:
      stream_transport: The AdbStreamTransport for the stream that is reading
        an AdbMessage from this AdbConnection.
      timeout_ms: If provided, timeout, in milliseconds, to use.  Note this
        timeout applies to this entire call, not for each individual Read, since
        there may be multiple reads if messages for other streams are read.
        This argument may be a timeouts.PolledTimeout.

    Returns:
      AdbMessage that was read, guaranteed to be one of 'OKAY', 'CLSE', or
    'WRTE' command.

    Raises:
      AdbTimeoutError: If we don't get a packet for this stream before
        timeout expires.
      AdbStreamClosedError: If the given stream has been closed.
    """
        timeout = timeouts.PolledTimeout.from_millis(timeout_ms)
        # Bail when the timeout expires, or when we no longer have the given stream
        # in our map (it may have been closed, we don't want to leave a thread
        # hanging in this loop when that happens).
        while (not timeout.has_expired()
               and stream_transport.local_id in self._stream_transport_map):
            try:
                # Block for up to 10ms to rate-limit how fast we spin.
                return stream_transport.message_queue.get(True, .01)
            except Queue.Empty:
                pass

            # If someone else has the Lock, just keep checking our queue.
            if not self._reader_lock.acquire(False):
                continue

            try:
                # Now that we've acquired the Lock, we have to check the queue again,
                # just in case someone had the Lock but hadn't yet added our message
                # to the queue when we checked the first time.  Now that we have the
                # Lock ourselves, we're sure there are no potentially in-flight reads.
                try:
                    return stream_transport.message_queue.get_nowait()
                except Queue.Empty:
                    pass

                while not timeout.has_expired():
                    msg = self._handle_message_for_stream(
                        stream_transport, self.transport.read_message(timeout),
                        timeout)
                    if msg:
                        return msg
            finally:
                self._reader_lock.release()

        if timeout.has_expired():
            raise usb_exceptions.AdbTimeoutError('Read timed out for %s',
                                                 stream_transport)

        # The stream is no longer in the map, so it's closed, but check for any
        # queued messages.
        try:
            return stream_transport.message_queue.get_nowait()
        except Queue.Empty:
            raise usb_exceptions.AdbStreamClosedError(
                'Attempt to read from closed or unknown %s', stream_transport)
Ejemplo n.º 3
0
    def _read_messages_until_true(self, predicate, timeout):
        """Read a message from this stream and handle it.

    This method tries to read a message from this stream, blocking until a
    message is read.  Once read, it will handle it accordingly by calling
    self._handle_message().

    This is repeated as long as predicate() returns False.  There is some
    locking used internally here so that we don't end up with multiple threads
    blocked on a call to read_for_stream when another thread has read the
    message that caused predicate() to become True.

    Args:
      predicate: Callable, keep reading messages until it returns true.  Note
        that predicate() should not block, as doing so may cause this method to
        hang beyond its timeout.
      timeout: Timeout to use for this call.

    Raises:
      AdbStreamClosedError: If this stream is already closed.
    """
        while not predicate():
            # Hold the message_received Lock while we try to acquire the reader_lock
            # and waiting on the message_received condition, to prevent another reader
            # thread from notifying the condition between us failing to acquire the
            # reader_lock and waiting on the condition.
            self._message_received.acquire()
            if self._reader_lock.acquire(False):
                try:
                    # Release the message_received Lock while we do the read so other
                    # threads can wait() on the condition without having to block on
                    # acquiring the message_received Lock (we may have a longer timeout
                    # than them, so that would be bad).
                    self._message_received.release()

                    # We are now the thread responsible for reading a message.  Check
                    # predicate() to make sure nobody else read a message between our last
                    # check and acquiring the reader Lock.
                    if predicate():
                        return

                    # Read and handle a message, using our timeout.
                    self._handle_message(
                        self.adb_connection.read_for_stream(self, timeout))

                    # Notify anyone interested that we handled a message, causing them to
                    # check their predicate again.
                    with self._message_received:
                        self._message_received.notify_all()
                finally:
                    self._reader_lock.release()
            else:
                # There is some other thread reading a message.  Since we are already
                # holding the message_received Lock, we can immediately do the wait.
                try:
                    self._message_received.wait(timeout.remaining)
                    if timeout.has_expired():
                        raise usb_exceptions.AdbTimeoutError(
                            '%s timed out reading messages.', self)
                finally:
                    # Make sure we release this even if an exception occurred.
                    self._message_received.release()