android communicating two apps in separate devices
Connecting over networks that accept incoming socket connections
The usual way to do this between Android devices (or between any peer devices) is to use sockets.
You set up one or both devices to 'listen' for connections on a socket and then accept a connection from the other when they want to communicate (or you can have a dedicated client and server and the client always initiates the connections).
Once the connection is established you can send messages back and forth.
There are many examples of Android client server socket applications, but one I found useful was:
- Android Server/Client example - client side using Socket (and its companion server side blog article - link included in the client blog)
Note that you may have to add your own 'protocol' on top of this - for example if you are sending a file of unknown length without any special 'end' character, you may want to add a byte (or several byte to represent an int, long etc) at the start to indicate the length of the transmission so the receiving side knows when it has received everything (or that it has not received everything in case of an error).
Connecting over networks which do not allow incoming connections (e.g. most 3G/4G)
In these scenarios, while there is nothing theoretically stopping sockets working, in practice many mobile operators will not allow incoming socket connections. In addition you would need to find the public IP address of the Mobile, which is possible but is extra complexity. If your solution will only ever run on a single operators network you can experiment and see if it works, but if not you may find it better and easier to use a server in the 'middle':
- Device A connectes to server
- Device B connectes to server
- Device A asks server for addresses of connected devices and 'discovers' device B
- Device A send a message for device B. It actually sends the messages to the server with an indication that it is to be sent to device B
- The server notifies device B that a message is available for it (using some sort of message notification like Google Cloud Messaging for example, or simply by the devices polling regularly to see if they have any messages).
- Device B retrieves the messages from the server
The above will work on pretty much any network that allows connectivity to the internet. It does have the disadvantage of requiring a server but it is likely a necessary approach over most mobile networks.
If you want the two instances of your Android app on two different devices located on the different parts of the world to communicate with each other directly without the server, then the best way to do it is to use Tor Hidden Services. Tor Hidden Services allow the apps to bypass the firewall or NAT (if Tor is not blocked, of course), and the devices can easily communicate with each other without the need for a central server. Here, I will try to give some code examples that you can try. The best library suitable to this stuff is this.
Step 1: Add dependencies to your gradle.build in app module:
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
dependencies {
compile 'com.github.jehy:Tor-Onion-Proxy-Library:0.0.7'
compile 'org.slf4j:slf4j-api:1.7.7'
compile 'org.slf4j:slf4j-android:1.7.7'
}
Step 2: Add permissions (Internet permissions or whatever) to your manifest file.
Step 3(i): Now we will just write the classic Client-Server programs in Java but with added Android and Tor flavor. To test this properly, try creating two different apps. One app will be the server and the other app will be a client. Preferably, you can even install the two apps on different phones. In this example, we will try to send "Hello from Tor client" string from client app to server app.
For the server side: You can try this function inside any Activity and AsyncTask.
void server(Context context){
//For comments and documentation, visit the original repo
//https://github.com/thaliproject/Tor_Onion_Proxy_Library
String fileStorageLocation = "hiddenservicemanager";;
com.msopentech.thali.toronionproxy.OnionProxyManager onionProxyManager =
new com.msopentech.thali.android.toronionproxy.AndroidOnionProxyManager(context, fileStorageLocation);
int totalSecondsPerTorStartup = 4 * 60;
int totalTriesPerTorStartup = 5;
try {
boolean ok = onionProxyManager.startWithRepeat(totalSecondsPerTorStartup, totalTriesPerTorStartup);
if (!ok)
System.out.println("Couldn't start tor");
while (!onionProxyManager.isRunning())
Thread.sleep(90);
System.out.println("Tor initialized on port " + onionProxyManager.getIPv4LocalHostSocksPort());
int hiddenServicePort = 8080;
int localPort = 9343;
String onionAddress = onionProxyManager.publishHiddenService(hiddenServicePort, localPort);
System.out.println("Tor onion address of the server is: "+onionAddress);
ServerSocket serverSocket = new ServerSocket(localPort);
while(true) {
System.out.println("Waiting for client request");
Socket receivedSocket = serverSocket.accept();
ObjectInputStream ois = new ObjectInputStream(receivedSocket.getInputStream());
String message = (String) ois.readObject();
//Here we will print the message received from the client to the console.
/*You may want to modify this function to display the received
string in your View.*/
System.out.println("Message Received: " + message);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
Step 3(ii): For the client side try this function
//Inputs:
//'String onionAddress' should be the one obtained in server() function.
//It will be printed in the console and it will possibly remain the same
//even if the app restarts, because all the data/cache will be stored locally.
//Also, when you run the code for the first time, Tor will take about 1 or 2 mins
//to bootstrap. In the subsequent runs, Tor will start relatively faster as the
//data will be cached. 'int hiddenServicePort' is the port at which the hidden
//service has started on the server. In our example code, it is 8080. So, pass that here
void client(Context context, String onionAddress, int hiddenServicePort){
String fileStorageLocation = "clientmanager";
com.msopentech.thali.toronionproxy.OnionProxyManager onionProxyManager =
new com.msopentech.thali.android.toronionproxy.AndroidOnionProxyManager(context, fileStorageLocation);
int totalSecondsPerTorStartup = 4 * 60;
int totalTriesPerTorStartup = 5;
try {
boolean ok = onionProxyManager.startWithRepeat(totalSecondsPerTorStartup, totalTriesPerTorStartup);
int socksPort=onionProxyManager.getIPv4LocalHostSocksPort();
if (!ok)
System.out.println("Couldn't start tor in client");
while (!onionProxyManager.isRunning())
Thread.sleep(90);
System.out.println("Client Tor initialized on port " + socksPort);
System.out.println("Client is waiting for the server to get ready");
Thread.sleep(2000);
Socket clientSocket =
Utilities.socks4aSocketConnection(onionAddress, hiddenServicePort, "127.0.0.1", socksPort);
ObjectOutputStream oos = new ObjectOutputStream(clientSocket.getOutputStream());
oos.writeObject("Hello from Tor client\n");
System.out.println("Client has sent the message");
oos.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
It's done. Run your apps and test it. If you get stuck, try consulting here.
So, now your apps can communicate without any central server. Tor Hidden Services are so awesome in these use cases.