Connecting android to the pc over usb

*Update* There is some new documentation on the android dev guide. I suggest you start there.

http://developer.android.com/guide/topics/usb/index.html

This will be a two part mini tutorial on how to establish device to host computer communication through usb by using adb. A lot of this is based on Alex Florescu’s work on the subject. My thanks go out to him.

You can read his post here

In part 2, I’ll post a super simple java client to demonstrate the communication.

In short, the way it works is through port-forwarded tcp sockets. This technique sets up a socket server on the Device and a client socket on the host computer. Port forwarding to the device is achieved via adb forward. More on this later.

Ok so let’s get down to it.

After you’ve written both your server and your client apps these are the steps you need to take to get this to work:

  • Launch the server app on the device.
  • Make sure you forward the ports on adb (you only need to do this once)
  • Launch the client app on the host machine

3 steps! that’s it! it’s not that hard at all! Now let’s cover each step in detail.

Let’s start with the server app on the device.

The server socket needs to run on the phone. If you think about it, it makes perfect sense.

The device can’t really know that the host it’s connected to exists. ADB forwards tcp requests FROM the host computer to the device. ADB can’t do that the other way around. This means that only the host computer can initiate a connection. This makes knowing where the server goes pretty obvious.

Don’t worry though, once the connection is made communication can happen bi-directionally.

The first thing to do is to set up a socket connection:

The code below is based on Jason Wei’s awesome post over on think android. My thanks to him

http://thinkandroid.wordpress.com/2010/03/27/incorporating-socket-programming-into-your-applications/

In the code block above we create a new connection to ‘localhost’ and set up a tcp port of our choosing. Any port works, just stay away from the standard 80 or 21.

The connection is wrapped inside a try/catch to allow for a connection timeout.

With a timeout set the server is now open to accept a client connection.

client = server.accept();

Once the connection is established we can get a reference to our I/O. We store this in a global static for easy reference.

Globals.socketIn = new Scanner(client.getInputStream());
				Globals.socketOut = new PrintWriter(client.getOutputStream(),

After the connection is established we can now being to work with the input and output streams.

In the next code block we send some messaging to the ui thread and monitor any input coming from the client.

if (client != null) {
				Globals.connected = true;
				// print out success
				connectionStatus = "Connection was succesful!";
                               //handler out to the ui thread
				mHandler.post(showConnectionStatus);
                                //monitor input fro the client input
				while (Globals.socketIn.hasNext()) {
					socketData = Globals.socketIn.next();
                                        //update the ui thread
					mHandler.post(socketStatus);
				}

			}

And that’s it! now that the connection is set up and we have access to the I/O we can do some work with it. In this example we do some simple ui buttons to test the connection.

Below is a simple click handler for some buttons.

public void onClick(View v) {
		switch (v.getId()) {
		case R.id.connect_button:
			tv = (TextView) findViewById(R.id.connection_text);
			// initialize server socket in a new separate thread
			new Thread(initializeConnection).start();
			String msg = "Attempting to connect...";
			Toast.makeText(this, msg, msg.length()).show();
			break;
		case R.id.hdmi_on:
			Log.d(TAG, "disconnect" + Globals.socketOut);
			if (Globals.socketOut != null) {
				Globals.socketOut.println("hdmiOn");
				Globals.socketOut.flush();
			}
			break;
		case R.id.hdmi_off:
			Log.d(TAG, "disconnect" + Globals.socketOut);
			if (Globals.socketOut != null) {
				Globals.socketOut.println("hdmiOff!!");
				Globals.socketOut.flush();
			}
			break;
		}
	}

Here is what the code for the Server socket on the device looks like.

Keep in mind that this is a very simplistic example. But it should work.

As always, there are many (and better) ways to do things. This is the way that helped me understand what’s going on.

package com.qtcstation;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Scanner;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import android.widget.Toast;

public class SocketMain extends Activity implements OnClickListener {

	public static final String TAG = "Connection";
	public static final int TIMEOUT = 10;
	Intent i = null;
	TextView tv = null;
	private String connectionStatus = null;
	private String socketData = null;
	private Handler mHandler = null;
	ServerSocket server = null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		// Set up click listeners for the buttons
		View connectButton = findViewById(R.id.connect_button);
		connectButton.setOnClickListener(this);
		View onBtn = findViewById(R.id.hdmi_on);
		onBtn.setOnClickListener(this);
		View offBtn = findViewById(R.id.hdmi_off);
		offBtn.setOnClickListener(this);

		// i = new Intent(this, Connected.class);
		mHandler = new Handler();
	}

	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.connect_button:
			tv = (TextView) findViewById(R.id.connection_text);
			// initialize server socket in a new separate thread
			new Thread(initializeConnection).start();
			String msg = "Attempting to connect...";
			Toast.makeText(this, msg, msg.length()).show();
			break;
		case R.id.hdmi_on:
			Log.d(TAG, "disconnect" + Globals.socketOut);
			if (Globals.socketOut != null) {
				Globals.socketOut.println("hdmiOn");
				Globals.socketOut.flush();
			}
			break;
		case R.id.hdmi_off:
			Log.d(TAG, "disconnect" + Globals.socketOut);
			if (Globals.socketOut != null) {
				Globals.socketOut.println("hdmiOff!!");
				Globals.socketOut.flush();
			}
			break;
		}
	}

	private Runnable initializeConnection = new Thread() {
		public void run() {

			Socket client = null;
			// initialize server socket
			try {
				server = new ServerSocket(38300);
				server.setSoTimeout(TIMEOUT * 1000);

				// attempt to accept a connection
				client = server.accept();
				Globals.socketIn = new Scanner(client.getInputStream());
				Globals.socketOut = new PrintWriter(client.getOutputStream(),
						true);

				// Globals.socketIn.
			} catch (SocketTimeoutException e) {
				// print out TIMEOUT
				connectionStatus = "Connection has timed out! Please try again";
				mHandler.post(showConnectionStatus);
			} catch (IOException e) {
				Log.e(TAG, "" + e);
			} finally {
				// close the server socket
				try {
					if (server != null)
						server.close();
				} catch (IOException ec) {
					Log.e(TAG, "Cannot close server socket" + ec);
				}
			}

			if (client != null) {
				Globals.connected = true;
				// print out success
				connectionStatus = "Connection was succesful!";
				Log.d(TAG, "connected!");
				mHandler.post(showConnectionStatus);
				while (Globals.socketIn.hasNext()) {
					socketData = Globals.socketIn.next();
					mHandler.post(socketStatus);

				}
				// startActivity(i);
			}
		}
	};

	/**
	 * Pops up a "toast" to indicate the connection status
	 */
	private Runnable showConnectionStatus = new Runnable() {
		public void run() {
			Toast.makeText(getBaseContext(), connectionStatus,
					Toast.LENGTH_SHORT).show();
		}
	};

	private Runnable socketStatus = new Runnable() {

		public void run() {
			TextView tv = (TextView) findViewById(R.id.connection_text);
			tv.setText(socketData);
		}
	};

	public static class Globals {
		public static boolean connected;
		public static Scanner socketIn;
		public static PrintWriter socketOut;
	}
}

UPDATE!

Since this post i’ve updated the “server” code to be more robust now that I understand it better.

I separated the socket stuff into its own service that will run constantly (WARNING: this WILL kill the battery quickly, you’ll need to optimize for your needs, for my needs the device is always plugged in. so power is not a concern to me)

Here is the new service code:

package com.qtcstation;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;

import android.app.ActivityManager;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class ZeusSocketService extends Service {

	private static final String TAG = "ZeusSocket";
	private final IBinder mBinder = new ZeusBinder();

	private Socket client = null;
	private ServerSocket server;

	// socket stuff

	private HashMap<String, Boolean> hdmiApps;

	@Override
	public void onCreate() {
		super.onCreate();
		hdmiApps = parseFeaturedApps();
		createSocketServer();
	}

	public static class Globals {
		public static boolean connected;
		public static Scanner socketIn;
		public static PrintWriter socketOut;
	}

	private HashMap<String, Boolean> parseFeaturedApps() {
		// TODO parse xml.
		HashMap<String, Boolean> h = new HashMap<String, Boolean>();
		AppListHandler handler = new AppListHandler();
		handler.parseXML("apps.xml");
		if (handler.parseSuccess()) {
			Log.d(TAG, "parse success!");
			h = handler.getAppList();
		} else {
			// failsafe hardcoded
			h.put("com.samsung.android.example.Blocks", true);
			h.put("com.android.mms.ui.ConversationList", true);
		}
		return h;

	}

	// socket server needs to do the following things.
	// accept connections until one is established.
	// restart if a connection is lost.
	private void createSocketServer() {
		if (!Globals.connected) {
			Thread t = new Thread(new ServerThread());
			t.start();

			Log.d(TAG, "INITIALIZING SOCKET");
		} else {
			Log.d(TAG, "ALREADY CONNECTED");
		}
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

	public class ZeusBinder extends Binder {
		ZeusSocketService getService() {
			return ZeusSocketService.this;
		}
	}

	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
		// an alarm invokes this every second
		checkRunningApp();
	}

	private void checkRunningApp() {
		// check which activity is currently running.
		ActivityManager am = (ActivityManager) this
				.getSystemService(ACTIVITY_SERVICE);
		List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);

		ComponentName componentInfo = taskInfo.get(0).topActivity;

		String className = componentInfo.getClassName();

		int status = (hdmiApps.get(className) == null) ? 0 : 1;

		if (status != currentStatus) {
			String action = (status == 0) ? "hdmi_off" : "hdmi_on";
			trackEvent("status_change", action);
		}
		currentStatus = status;

		if (status == 1) {
			if (currentApp.contentEquals(className)
					|| className.equalsIgnoreCase("com.xx.xx.ScreenWaker")) {
			} else {
				trackPageView(hdmiApps.get(className));
				currentApp = className;
			}
		}
		currentApp = className;

		// Log.d(TAG, "running " + className + " \n status: " + status
		// + " \n current app" + currentApp);

		if (Globals.socketOut != null) {

			Globals.socketOut.print(status);
			Globals.socketOut.flush();
		}

		// TODO peridically check if the xml was updated.

	}

	public class ServerThread implements Runnable {

		public void run() {
			try {
				server = new ServerSocket(38300);
				Log.d(TAG, "waiting for connection");
				while (true) {
					// listen for incoming clients
					Socket client = server.accept();
					try {
						BufferedReader in = new BufferedReader(
								new InputStreamReader(client.getInputStream()));
						String line = null;
						Globals.socketOut = new PrintWriter(
								client.getOutputStream(), true);
						while ((line = in.readLine()) != null) {
							// read here
						}
						// break;
					} catch (Exception e) {
						Log.d(TAG, "lost client");
						e.printStackTrace();
					}
				}

			}
			// }
			catch (Exception e) {
				Log.d(TAG, "error, disconnected");
				e.printStackTrace();
			}
		}
	}

}

Update 2

The AppListHandler reference in the code parses an xml file stored in the sdcard. In there we maintain a list of app package names. When we see one of these apps running, then we do something with it. I decided to drop it into an external xml so it would be easy to update from outside the app. After parsing, I simply drop the list of apps into a hashtable. This list is compared in the checkRunningApp() method of the socket server (which is now updated to show that)

package com.rga.sony;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import android.os.Environment;
import android.util.Log;

public class AppListHandler extends DefaultHandler {
	private static final String TAG = "parser";
	private HashMap<String, Boolean> appList;
	private long fileSize;

	public void startElement(String uri, String name, String qName,
			Attributes atts) {
		if (name == "app") {
			appList.put(atts.getValue(0), true);
		}
	}

	public void endElement(String uri, String name, String qName)
			throws SAXException {
	}

	public void characters(char ch[], int start, int length) {
		String chars = (new String(ch).substring(start, start + length));
		Log.d(TAG, "chars :" + chars);
	}

	/**
	 * @param path
	 *            - path to the xml file on the SD card.
	 */
	public void parseXML(String path) {
		try {
			appList = new HashMap<String, Boolean>();
			File file = new File(Environment.getExternalStorageDirectory()
					+ "/download/" + path);

			fileSize = file.length();
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();
			XMLReader xr = sp.getXMLReader();
			xr.setContentHandler(this);
			xr.parse(new InputSource(new FileInputStream(file)));
		} catch (IOException e) {
			appList = null;
			Log.e(TAG, e.toString());
		} catch (SAXException e) {
			appList = null;
			Log.e(TAG, e.toString());
		} catch (ParserConfigurationException e) {
			appList = null;
			Log.e(TAG, e.toString());
		}

	}

	public boolean parseSuccess() {
		return appList != null;
	}

	public long getFileSize() {
		return fileSize;
	}

	public HashMap<String, Boolean> getAppList() {
		return appList;
	}

}

Finally here is what the xml looks like

<apps>
<app package="com.sony.android.psone.BootActivity"/>
        <app package="com.sonyericsson.zsystem.jni.ZActivity"/>
        <app package="com.gameloft.android.GAND.GloftAsp6.asphalt6.MyVideoView"/>
        <app package="com.gameloft.android.GAND.GloftAsp6.asphalt6.GLGame"/>
        <app package="com.gameloft.android.GAND.GloftStsq.ML.installer.GameInstaller"/>
       <app package="com.gameloft.android.GAND.GloftStsq.ML.MyVideoView"/>
       <app package="com.gameloft.android.GAND.GloftStsq.ML.GLGame"/>
      <app package="com.ea.tetris.Main"/>
      <app package="com.ea.thesims3.Main"/>
      <app package="com.eamobile.madden10_carrier_WF.Madden10"/>
</apps>

I hope this helps!

further reading:

PART 2 – a simple java socket client

Android USB connection to PC

communicating over the usb cable

Tetherbot, a project that uses the same technique. http://graha.ms/androidproxy/

22 thoughts on “Connecting android to the pc over usb

  1. Hi Nelson,

    I tried to put all codes into a PC Java application (Client) and Android apk application (Server)

    My understanding to establish a connection is
    1) run adp port forwarding on PC
    2) Start PC application (Client)
    3) Start Android application (server) and click “Connect”

    Upon performing mentioned steps, I get a continuous “echo:null” at from PC Java and “Attempting to connect” from Android side. I tried netstat to see established connection at port 38300 but no connection there

    The steps i took is correct? Thanks much

  2. Hi Simeh,
    Pretty close.
    You need to do the following:

    1. run adp port forwarding.
    2. run android app
    3. run pc client app

    I’ve posted updated code at the bottom. I moved the socket code into its own service because i needed it to run all the time.
    Please let me know if you have any more questions, I’m happy to help.

  3. hi i am trying to connect a set of LEDs to the micro USB , how do i connect and communicate with my custom device ?
    The problem is its not a client server thing .. i have to send raw data to and receive raw data from that socket .. and my device can only put lets say raw bits on the port …

    any help ?

    thanks in advance ….

  4. Hi Fahad.
    The same concept should apply. There has to be something on your custom device you’re writing code for correct?
    Is it a microcontroller of some sort?
    I imagine you have some sort of way to communicate with the USB com port.
    You have to break it up into 3 steps.
    1 establish the TCP connection via USB
    2 read the USB i/o that you’re getting
    3 interpret the USB io into hi/lo for the appropriate pins for your LEDs.

    That’s about as much as i can say without knowing anything about how your custom device works and how you’re programming for it.
    Good luck, I hope this helps!

  5. Hi,
    why is there an error “Syntax error on token “boolean”, Dimensions expected after this token” when I try to put this
    HashMap.?

  6. Hi Sherry,
    Yeah I’m sorry about that. It’s my syntax formatter doing that.
    The proper code should read private HashMap< String,Boolean>

    I’m not sure why my stupid formatter does that. It’s time to change it!

  7. Thanks. How about “AppListHandler handler = new AppListHandler();”. Did you create an AppListHandler class seperately? and what will it be in apps.xml file?

  8. Hi Sherry,
    I updated the post above to answer your question about the AppListHandler. Please let me know if you have any more questions. Thanks for posting!

  9. Hi,
    I am new on creating this server socket on android, so I want to know what does apps.xml do ?

  10. Thanks for this post.I am new on Android.I am trying to send the images of the android phone to my computer having operating system linux using wifi through my application.For this ,I have using the socket programming . I just make the android phone as a client and computer as a server. I just make a server in core java. that run on computer .They do not make the communication with each other.Please provide me proper suggestion so that I’ll proceed further.

  11. Hi Noslen,

    I followed the same steps which specified by you but i am getting following errors, can you please look at it
    which I run client app the following is the error..
    EchoClient.main()
    connected!!
    Exception in thread “main” java.net.SocketException: Software caused connection abort: recv failed
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(Unknown Source)
    at java.net.SocketInputStream.read(Unknown Source)
    at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
    at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
    at sun.nio.cs.StreamDecoder.read(Unknown Source)
    at java.io.InputStreamReader.read(Unknown Source)
    at java.io.BufferedReader.fill(Unknown Source)
    at java.io.BufferedReader.readLine(Unknown Source)
    at java.io.BufferedReader.readLine(Unknown Source)
    at com.rga.vzw.EchoClient.main(EchoClient.java:47)

    when I run sever apK on mobile I can see “force close error” can you please let me know reasons?

  12. Hi Mahi,
    It could be a number of reasons. Some devices have different bluetooth implementations so they behave a bit differently.
    Can you post more of your code and the full stack trace?

  13. Hi Nolsen, I have found your post very helpful. I need to know the following: I want to build an app that will run on Pc and will show the android sensors’ readings on the PC. Is it possible?

    Thanks

  14. Hi Nolsen,
    Great post and information.I am new in android.I didn’t get the idea of how this program works.I mean what is the basic explanation of this.Is the connection between emulator and android phone? For example when I enter string into my computer(emulator) I want it in my phone as the same.

    Please help me.

  15. Hi Ica,
    The communication between the PC and the phone is established via a tcp socket connection which is facilitated via adb.
    You need a separate host program running on the pc to establish the communication. It doesn’t really have anything to do with the emulator.

    If you wanted to say create an editor where you see the strings on your phone, you would do something like this:

    The editor program on your pc will establish the connection with the android device connected on usb via adb.
    To establish the connection, you need to have your pc program monitor adb for when a device becomes available (you can do it via adb devices command) look at the adb documentation.
    Once available, you can send a command to start up your app. Another way (which is what I recommend) is to have a service running on the Android device that is constantly listening for client connections.

    Once you have a two way communication via tcp, you can pretty much do anything you need.

    I hope this makes it clearer. Feel free to ask any more questions.

  16. Which is exactly the command I have to execute in the PC?

    Is there any way to automatize the process of running the command? (like making the java application execute it)

  17. Hi Neloson,

    Thanks for the useful post! I am using this technique to create a java application that will send commands to the Android app and app will perform some corresponding actions in response. The communication part is working now. Question is, what should I do in a real production environment? Should I bundle ADB with my Java (desktop) application? If so, should they install Android SDK as a prerequisite?

    Thanks,
    Hooman

  18. It would be possible to send a byte from PC app to android App by this way? and also show this bytes in a label or textbox in the android app as result?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>