Search code examples
androidadb

Run adb on the device itself, i.e. as if it were the PC issuing the commands


My goal is to write an app which runs on a handset and lets the user choose from a list of APK's, then installs the one selected to an Android Things device on the same network.

We can actually forget Android Things because the same code would work between two handsets, it's just that there it would be pointless because the target can just receive the APK in many other ways such as an attachment, BT etc. AT devices only have ADB for this, apart from the recently announced Android Things Console which is overkill for regular local development. I'm therefore looking to replicate the sequence a PC would go through to install it, i.e. "adb connect, adb install ..." etc but from the handset itself. We can assume all devices involved are rooted.

It seems to me this means my app has to issue these commands as a process, but I'm having a hard time getting it fully working. When I issue "adb help" I get back the help message, and when I issue "adb reboot" the device reboots, so I think I'm on the right lines. It's when I try anything apart from those I get nothing back - the example of "adb shell ping -c 1 192.168.62.40" fails, but is OK from ADB on the PC. Also, it's very curious "adb version" fails which again is OK from the terminal.

At first I thought the handsets might only have an "adb lite" installed which has just enough to get them to work as an adb target, but using a shell from a PC showed that's not the case.

Here's the code I'm trying:

Process process = Runtime.getRuntime().exec("su adb help");
//Process process = Runtime.getRuntime().exec("su adb reboot");
//Process process = Runtime.getRuntime().exec("su adb version");   
//Process process = Runtime.getRuntime().exec("su adb shell ping -c 1 192.168.62.40");
process.waitFor();

Log.d("PROCESS", "Status: "+process.exitValue());
BufferedReader bufferedReader = new BufferedReader(
        new InputStreamReader(process.getInputStream()));

StringBuilder everything = new StringBuilder();
String line;
while( (line = bufferedReader.readLine()) != null) {
    everything.append(line);
}

Log.d("PROCESS", "Process output: "+everything.toString());

Toast.makeText(MainActivity.this, everything.toString(), Toast.LENGTH_SHORT).show();

Solution

  • The problem is that the adb client (which you use to communicate to the ADB server which communicates to and from a device on a development machine) is not packaged on an Android device. The adbd daemon, however, (used to communicate between the adb client and the device) can and will be found on an Android system.

    There are a few options to help use the adb client on an Android device:

    • Try and build the adb client for an Android device from the adb sources on GitHub.
    • Try and make a makeshift adb "client". Since the adb client and adbd daemon communicate via USB or TCP, you could try emulating the communication protocols to open read/write streams on the device. More on the client-daemon communication protocols here. This library that I am working on might help you: eviltak/adb-nmap

    The quick and dirty option is to build the adb client from sources and push to the device. That is, if you can get it to build on an Android device.

    The second option is probably the most time consuming, but can be the most "clean" choice. You will, however, have to emulate the adb authentication system, properly handle streams and so on, which can be cumbersome. The source will help you.

    In either case, the GitHub adb source directory should have everything you need for the long road ahead.