Skip to main content
1 of 3

C# await and interactive Android ADB shell

I'm writing an app to let me interact and debug my android over USB using the android SDK and the ADB command. I create a Process with redirected input and output streams and basically run "ADB shell", and I want to be able to wait for the prompt, enter commands and parse the results. Here's a sample running 'ps' and getting the results:

CreateProcess("shell");
string data = await ReadToPrompt(":/ $ ");
_Process.StandardInput.WriteLine("ps");
string echo = await ReadLine(); // my command echoed back
string dataAndPrompt = await ReadToPrompt(":/ $ ");

It works (although I'm getting extra CR in the byte stream, CR/LF is coming as CR/CF/LF), but I can't help but wonder if there's a better way than what I have:

Process _Process;
MemoryStream _Remaining; // bytes left after ReadToPrompt
byte[] _Buffer = new byte[0x10000];

public async Task<string> ReadLine()
{
    return await ReadToPrompt("\r\n");
}

public async Task<string> ReadToPrompt(string prompt)
{
    MemoryStream ms = _Remaining == null ? new MemoryStream() : _Remaining;
    int promptSize = prompt.Length;
    int pos = 0;

    while (true)
    {
        // read what we can from the process's standard output
        int count = await _Process.StandardOutput.BaseStream.ReadAsync(_Buffer, 0, _Buffer.Length);
        ms.Write(_Buffer, 0, count); // write new data to memory stream

        // convert all new bytes to check for prompt
        string testString = Encoding.ASCII.GetString(ms.GetBuffer(), pos, (int)(ms.Position - pos));
        int promptPos = testString.IndexOf(prompt);
        if (promptPos > 0)
        {
            // we found it, result is up to and including prompt
            int stripLength = pos + promptPos + promptSize;
            string result = Encoding.ASCII.GetString(ms.GetBuffer(), 0, stripLength);

            // store remaining data in a MemoryStream for the next call
            _Remaining = new MemoryStream();
            _Remaining.Write(ms.GetBuffer(), stripLength, (int)(ms.Position - stripLength));
            return result.Replace("\r\r", "\r");
        }

        // we didn't find it but we know it isn't in what we got, so move first position
        // to check to the position - promptSize
        pos = (int)(ms.Position - promptSize);
        pos = (pos < 0) ? 0 : pos;
    }
}

Is there something I can reliably do to keep from creating new Memory streams, like could I do an array copy on MemoryStream.GetBuffer() to itself and use MemoryStream.SetLength()? Is there an entirely better way to do it than using async and await?