private async Task HandleData(Encoding encoding, CancellationToken ct) { try { using (StreamReader reader = new StreamReader(_sock.GetShellStream(), encoding)) { // Previously, we would loop while reader.Peek() >= 0. Turns out that this would // break too soon in certain cases (about every 10 loops, so it appears to be a timing // issue). Checking for reader.ReadLine() to return null appears to be much more robust // -- one of the integration test fetches output 1000 times and found no truncations. while (!ct.IsCancellationRequested) { var line = await reader.ReadLineAsync().ConfigureAwait(false); if (line == null) { break; } _outputBlock.Post(encoding.GetBytes(line)); } } } catch (Exception e) { // If a cancellation was requested, this main loop is interrupted with an exception // because the socket is closed. In that case, we don't need to throw a ShellCommandUnresponsiveException. // In all other cases, something went wrong, and we want to report it to the user. if (!ct.IsCancellationRequested) { throw new ShellCommandUnresponsiveException(e); } } }
/// <inheritdoc/> public async Task RunLogServiceAsync(DeviceData device, Action <LogEntry> messageSink, CancellationToken cancellationToken, params LogId[] logNames) { if (messageSink == null) { throw new ArgumentException(nameof(messageSink)); } this.EnsureDevice(device); // The 'log' service has been deprecated, see // https://android.googlesource.com/platform/system/core/+/7aa39a7b199bb9803d3fd47246ee9530b4a96177 using (IAdbSocket socket = this.adbSocketFactory(this.EndPoint)) { this.SetDevice(socket, device); StringBuilder request = new StringBuilder(); request.Append("shell:logcat -B"); foreach (var logName in logNames) { request.Append($" -b {logName.ToString().ToLowerInvariant()}"); } socket.SendAdbRequest(request.ToString()); var response = socket.ReadAdbResponse(); using (Stream stream = socket.GetShellStream()) { LogReader reader = new LogReader(stream); while (!cancellationToken.IsCancellationRequested) { LogEntry entry = null; try { entry = await reader.ReadEntry(cancellationToken).ConfigureAwait(false); } catch (EndOfStreamException) { // This indicates the end of the stream; the entry will remain null. } if (entry != null) { messageSink(entry); } else { break; } } } } }
/// <inheritdoc/> public async Task ExecuteRemoteCommandAsync(string command, DeviceData device, IShellOutputReceiver receiver, CancellationToken cancellationToken, int maxTimeToOutputResponse) { this.EnsureDevice(device); using (IAdbSocket socket = this.adbSocketFactory(this.EndPoint)) { cancellationToken.Register(() => socket.Dispose()); this.SetDevice(socket, device); socket.SendAdbRequest($"shell:{command}"); var response = socket.ReadAdbResponse(); try { using (StreamReader reader = new StreamReader(socket.GetShellStream(), Encoding)) { // Previously, we would loop while reader.Peek() >= 0. Turns out that this would // break too soon in certain cases (about every 10 loops, so it appears to be a timing // issue). Checking for reader.ReadLine() to return null appears to be much more robust // -- one of the integration test fetches output 1000 times and found no truncations. while (!cancellationToken.IsCancellationRequested) { var line = await reader.ReadLineAsync().ConfigureAwait(false); if (line == null) { break; } if (receiver != null) { receiver.AddOutput(line); } } } } catch (Exception e) { // If a cancellation was requested, this main loop is interrupted with an exception // because the socket is closed. In that case, we don't need to throw a ShellCommandUnresponsiveException. // In all other cases, something went wrong, and we want to report it to the user. if (!cancellationToken.IsCancellationRequested) { throw new ShellCommandUnresponsiveException(e); } } finally { if (receiver != null) { receiver.Flush(); } } } }
public async Task <IPropagatorBlock <byte[], byte[]> > ExecuteRemoteCommandAsyncBlock(string cmd, DeviceData d, CancellationToken ct, int maxTimeToOutputResponse, Encoding encoding) { this.EnsureDevice(d); using (IAdbSocket sock = this.GetSocket()) { this.SetDevice(sock, d); uint streamId = 1; sock.SendAdbRequest($"shell:{cmd}"); var ss = sock.GetShellStream(); var stream = new AdbLocalStream(sock, streamId, encoding, ct); //OpenStreams.Add(streamId, stream); return(stream.Block); } }
/// <inheritdoc/> public IEnumerable <LogEntry> RunLogService(DeviceData device, params LogId[] logNames) { this.EnsureDevice(device); // The 'log' service has been deprecated, see // https://android.googlesource.com/platform/system/core/+/7aa39a7b199bb9803d3fd47246ee9530b4a96177 using (IAdbSocket socket = Factories.AdbSocketFactory(this.EndPoint)) { this.SetDevice(socket, device); StringBuilder request = new StringBuilder(); request.Append("shell:logcat -B"); foreach (var logName in logNames) { request.Append($" -b {logName.ToString().ToLower()}"); } socket.SendAdbRequest(request.ToString()); var response = socket.ReadAdbResponse(); using (Stream stream = socket.GetShellStream()) using (LogReader reader = new LogReader(stream)) { while (true) { LogEntry entry = null; try { entry = reader.ReadEntry(); } catch (EndOfStreamException) { // This indicates the end of the stream; the entry will remain null. } if (entry != null) { yield return(entry); } else { break; } } } } }
public AdbLocalStream(IAdbSocket sock, uint localId, Encoding encoding, CancellationToken ct) { _sock = sock; _oStream = sock.GetShellStream(); _outputBlock = new BufferBlock <byte[]>(); _inputBlock = new ActionBlock <byte[]>(bytes => { var buff = Command.CreateWriteCommand(LocalId, RemoteId, bytes); OnWriting?.Invoke(this, EventArgs.Empty); if (RemoteId == 0) { throw new Exception("Remote stream not established yet!"); } _sock.Send(buff, buff.Length); }); Block = DataflowBlock.Encapsulate(_inputBlock, _outputBlock); HandleData(encoding, ct).ConfigureAwait(false); }