diff --git a/services/device/serial/serial_io_handler_posix.cc b/services/device/serial/serial_io_handler_posix.cc index 452d81538fd34..7f2a2c421b3f5 100644 --- a/services/device/serial/serial_io_handler_posix.cc +++ b/services/device/serial/serial_io_handler_posix.cc @@ -126,7 +126,11 @@ scoped_refptr SerialIoHandler::Create( void SerialIoHandlerPosix::ReadImpl() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(pending_read_buffer()); - DCHECK(file().IsValid()); + + if (!file().IsValid()) { + QueueReadCompleted(0, mojom::SerialReceiveError::DISCONNECTED); + return; + } // Try to read immediately. This is needed because on some platforms // (e.g., OSX) there may not be a notification from the message loop @@ -138,7 +142,11 @@ void SerialIoHandlerPosix::ReadImpl() { void SerialIoHandlerPosix::WriteImpl() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(pending_write_buffer()); - DCHECK(file().IsValid()); + + if (!file().IsValid()) { + QueueWriteCompleted(0, mojom::SerialSendError::DISCONNECTED); + return; + } EnsureWatchingWrites(); } diff --git a/services/device/serial/serial_io_handler_win.cc b/services/device/serial/serial_io_handler_win.cc index 29a69f2685df1..12d91e68ea9c5 100644 --- a/services/device/serial/serial_io_handler_win.cc +++ b/services/device/serial/serial_io_handler_win.cc @@ -266,7 +266,11 @@ bool SerialIoHandlerWin::PostOpen() { void SerialIoHandlerWin::ReadImpl() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(pending_read_buffer()); - DCHECK(file().IsValid()); + + if (!file().IsValid()) { + QueueReadCompleted(0, mojom::SerialReceiveError::DISCONNECTED); + return; + } if (!SetCommMask(file().GetPlatformFile(), EV_RXCHAR)) { VPLOG(1) << "Failed to set serial event flags"; @@ -285,7 +289,11 @@ void SerialIoHandlerWin::ReadImpl() { void SerialIoHandlerWin::WriteImpl() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(pending_write_buffer()); - DCHECK(file().IsValid()); + + if (!file().IsValid()) { + QueueWriteCompleted(0, mojom::SerialSendError::DISCONNECTED); + return; + } BOOL ok = ::WriteFile(file().GetPlatformFile(), pending_write_buffer(), pending_write_buffer_len(), NULL, diff --git a/services/device/serial/serial_port_impl_unittest.cc b/services/device/serial/serial_port_impl_unittest.cc index 3618b88e916ec..93c7f0aa31714 100644 --- a/services/device/serial/serial_port_impl_unittest.cc +++ b/services/device/serial/serial_port_impl_unittest.cc @@ -21,9 +21,65 @@ class SerialPortImplTest : public DeviceServiceTestBase { ~SerialPortImplTest() override = default; protected: + void CreateDataPipe(mojo::ScopedDataPipeProducerHandle* producer, + mojo::ScopedDataPipeConsumerHandle* consumer) { + MojoCreateDataPipeOptions options; + options.struct_size = sizeof(MojoCreateDataPipeOptions); + options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE; + options.element_num_bytes = 1; + options.capacity_num_bytes = 64; + + MojoResult result = mojo::CreateDataPipe(&options, producer, consumer); + DCHECK_EQ(result, MOJO_RESULT_OK); + } + + mojo::ScopedDataPipeConsumerHandle StartReading( + mojom::SerialPort* serial_port) { + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + CreateDataPipe(&producer, &consumer); + serial_port->StartReading(std::move(producer)); + return consumer; + } + + mojo::ScopedDataPipeProducerHandle StartWriting( + mojom::SerialPort* serial_port) { + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + CreateDataPipe(&producer, &consumer); + serial_port->StartWriting(std::move(consumer)); + return producer; + } + DISALLOW_COPY_AND_ASSIGN(SerialPortImplTest); }; +TEST_F(SerialPortImplTest, StartIoBeforeOpen) { + mojo::Remote serial_port; + mojo::PendingRemote watcher_remote; + mojo::SelfOwnedReceiverRef watcher = + mojo::MakeSelfOwnedReceiver( + std::make_unique(), + watcher_remote.InitWithNewPipeAndPassReceiver()); + SerialPortImpl::Create( + base::FilePath(FILE_PATH_LITERAL("/dev/fakeserialmojo")), + serial_port.BindNewPipeAndPassReceiver(), std::move(watcher_remote), + task_environment_.GetMainThreadTaskRunner()); + + mojo::ScopedDataPipeConsumerHandle consumer = StartReading(serial_port.get()); + mojo::ScopedDataPipeProducerHandle producer = StartWriting(serial_port.get()); + + // Write some data so that StartWriting() will cause a call to Write(). + static const char kBuffer[] = "test"; + uint32_t bytes_written = base::size(kBuffer); + MojoResult result = + producer->WriteData(&kBuffer, &bytes_written, MOJO_WRITE_DATA_FLAG_NONE); + DCHECK_EQ(result, MOJO_RESULT_OK); + DCHECK_EQ(bytes_written, base::size(kBuffer)); + + base::RunLoop().RunUntilIdle(); +} + TEST_F(SerialPortImplTest, WatcherClosedWhenPortClosed) { mojo::Remote serial_port; mojo::PendingRemote watcher;