Overview: I'm currently working on an Android app which connects to a Linux system via WiFi Direct, with the final intent of streaming video over this established connection.
The raspi will be acting as a server in this scenario, with the Android being the client. I will be streaming from a camera attached to the pi, and receiving this video on the Android side to display in my app using ExoPlayer. The pi cannot start sending this video over a datagram socket without having the client's ip address. The Android device will also need to send out miscellaneous packets to the Pi which would be telling it what to do, thus the need for both devices having each others ip addresses.
The current Linux system I am working on is a Raspberry Pi running Raspian, with development taking place in C++. The Android runtime environment being a Samsung Galaxy Tab A.
Problem: I'm able to successfully establish a connection between the two devices, which I have setup so that the Raspberry Pi is always the group owner. With the Pi being group owner, it doesn't have immediate access to the Android's IP address; thus a temporary TCP socket connection must be made between the two so the Raspberry Pi can take note of the Android's IP address.
After a WiFi connection is established, this C++ code is ran on the Pi to open & accept a TCP socket connection from the Android:
#include<cstddef>
#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<unistd.h>
#include<netdb.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define BACKLOG 2 //Allowed connection count.
#define PORT 8988 //The port we will be listening on.
//The static IP of p2p-dev-wlan0
const std::string MY_IP = "192.168.4.1";
bool listen_for_ip() {
//Buffer for receiving messages.
char buffer[256];
//Create Server Socket:
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
return false;
else
std::cout << "Created Server" << std::endl;
struct sockaddr_in my_addr, peer_addr;
//Recieve messages from IPv4 addresses.
my_addr.sin_family = AF_INET;
//Set our IP address of the socket to the value in "MY_IP"
my_addr.sin_addr.s_addr = inet_addr(MY_IP.c_str());
//my_addr.sin_addr.s_addr = INADDR_ANY; // Also tested with this, no luck.
//Set our in port to the value in "PORT"
my_addr.sin_port = htons(PORT);
//Attempt to bind to IP and port.
if (bind(sock_fd, (struct sockaddr*) &my_addr, sizeof(my_addr)) == 0)
std::cout << "Binded successfully" << std::endl;
else
return false;
//Listen on the socket
if (listen(sock_fd, BACKLOG) < 0)
return false;
//Accept a connection.
socklen_t peer_addr_size = sizeof(peer_addr);
std::cout << "Accepting connection ..." << std::endl;
int new_fd = accept(sock_fd, (struct sockaddr*) &peer_addr, &peer_addr_size);
if (new_fd == -1) {
std::cout << "Error accepting connect" << std::endl;
close(sock_fd);
return false;
}
else
std::cout << "Connection accept completed status = " << new_fd << std::endl;
//
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(peer_addr.sin_addr), ip, INET_ADDRSTRLEN);
if (recv(new_fd, buffer, 256, 0) < 0) {
close(new_fd);
close(sock_fd);
return false;
}
std::cout << "Client says " << buffer << std::endl;
//Do other stuff with connection ...
close(new_fd);
close(sock_fd);
return true;
}
This code runs successfully, and as should blocks on the accept() call to wait for the Android connect to the socket.
On the Android side this code is ran to connect to the Pi (Group owner) over a basic socket:
public static final int PORT = 8988;
private static final int TIMEOUT_MS = 10000;
private final String myIp;
private WifiP2pInfo mGroupInfo;
private final WifiDirectService mWifiDirectService;
public P2pClientSocket(WifiDirectService wifiDirectService, WifiP2pInfo groupInfo, String ourDeviceIP) {
mGroupInfo = groupInfo;
myIp = ourDeviceIP;
mWifiDirectService = wifiDirectService;
exchangeIP();
}
/**
* Utilizes a basic socket & TCP to ensure that the IP address of this machine is sent to the group owner of established P2P group.
* If there is an error with sending or writing this message, or the message times out,
* {#link WifiDirectService} will be notified to immediately dispose of its current connection.
*/
private void exchangeIP() {
Socket initSocket = new Socket();
Runnable r = () -> {
try {
String host = mGroupInfo.groupOwnerAddress.getHostAddress();
Log.d(TAG, "Opening socket to : " + host + ", " + PORT);
initSocket.connect(new InetSocketAddress(host, PORT), TIMEOUT_MS);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(initSocket.getOutputStream(), StandardCharsets.UTF_8);
outputStreamWriter.write(myIp, 0, myIp.length());
outputStreamWriter.close();
Log.d(TAG, "Successfully wrote IP.");
} catch (SocketTimeoutException e) {
String message = "Connection Timeout - Disconnecting.";
Log.e(TAG, message, e);
mWifiDirectService.socketError(message);
} catch (IOException e) {
String message = "IP Exchange Error - Disconnecting.";
Log.e(TAG, message, e);
mWifiDirectService.socketError(message);
} finally {
try {
initSocket.close();
} catch (IOException e) {
Log.e(TAG,"Error Closing Init Socket.");
}
}
};
new Thread(r).start();
}
The value of
String host = mGroupInfo.groupOwnerAddress.getHostAddress()
will always be the IP address of the raspberry pi. I have configured it so it runs as a DHCP server with a static IP using:
cat > /etc/systemd/network/12-p2p-wlan0.network <<EOF
[Match]
Name=p2p-wlan0-*
[Network]
Address=192.168.4.1/24
DHCPServer=yes
EOF
However, on the .connect() call, the timeout threshold is always reached. I'm a bit of a networking noob so I'm not entirely sure on what I'm doing wrong. Included is the timeout stack trace:
java.net.SocketTimeoutException: failed to connect to /192.168.4.1 (port 8988) from /192.168.4.163 (port 33122) after 10000ms
at libcore.io.IoBridge.connectErrno(IoBridge.java:191)
at libcore.io.IoBridge.connect(IoBridge.java:135)
at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:142)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
at java.net.Socket.connect(Socket.java:621)
at com.nlos.networking.P2pClientSocket.lambda$exchangeIP$0$P2pClientSocket(P2pClientSocket.java:54)
at com.nlos.networking.-$$Lambda$P2pClientSocket$fxU2Z0Zg0gRF1Fyl1fNnqgO2m8I.run(Unknown Source:4)
at java.lang.Thread.run(Thread.java:919)
I've tried multiple different sockets, ensured that the pi is actually up and listening, and am honestly stumped at this point. Any help would be greatly appreciated!
Related
so i'm currently developing an android service which implements the HFP profile for later use with a gui , i was able to successfully and easily implement the RFCOMM part where the AT commands like ATA(accep call) are sent , but i am stuck with accepting the audio SCO Connection on the app . so basically im testing with an Iphone AG Role and an android tablet HF Role which runs my app , to open the SCO connection i have tried calling AudioMAnager.startBluetoothSco(); without any luck , and even made a bluetooth SCO socket server in C using the ndk , which listens for a connection. but the actual problem is that the Iphone doesnt seem to try to connect with the sco socket , so i dumped the trafic from the android tablet and saw that when the Iphone requests the SCO connection the android hci automatically responds with Reject of reason: Connection Rejected due to Limited Resources (0x0d) , no matter what i do, maybe im missing something? any ideas? thanks. forgot to mention that both devices are paired using the os settings app and the connection is established by connecting to the android tablet from the native settings app of ios.
AudioHandler.java
public class AudioHandler implements Runnable{
static
{
System.loadLibrary("libsco");
}
#Override
public void run()
{
AudioManager amanager = (AudioManager) Common.APPCONTEXT.getSystemService(Context.AUDIO_SERVICE);
amanager.setMode(AudioManager.MODE_IN_COMMUNICATION);
amanager.startBluetoothSco();
amanager.setBluetoothScoOn(true);
if(amanager.isBluetoothScoOn())
{
int status= this.SCOINIT();
Log.d("SCO","SCOFinished");
}
/*while(Common.isCallActive)
{
play(stream.readbytes());
}
this.SCOCLOSE();
amanager.stopBSCO();
*/
}
private native int SCOINIT();}
libsco.c
void init() {
struct sockaddr_sco addr = {0}, remoteadress = {0};
int SCOServer, SCOClient;
SCOServer = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
if (SCOServer < 0) {
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "SCO socket create failed.");
print("SCO socket create failed.");
}
addr.sco_family = AF_BLUETOOTH;
bacpy(&addr.sco_bdaddr, BDADDR_ANY);
if (bind(SCOServer, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
print("failed to bind sco.");
}
if (listen(SCOServer, 1)) {
print("Listening Failed!!");
} else {
print("Listening for SCO connection.");
}
socklen_t addrlength = sizeof(remoteadress);
SCOClient = accept(SCOServer, (struct sockaddr *) &remoteadress, &addrlength);
if (SCOClient < 0) {
print("Accept Failed!!");
close(SCOClient);
} else {
print("Conected.\n");
close(SCOClient);
}
close(SCOServer);
}
everything runs without errors and i can see the "LIstening for SCO Connection " line, but it never accepts because the android hci rejects the connection before anything can be done...
screenshot from wireshark
I'm trying to send UDP packets from an emulated device (Nexus S 4.0", 480 x 800: hdpi) to my host PC for development and testing. The sending side seems correct and doesn't encounter any errors, but Wireshark indicates they are not arriving at the host PC. I've researched this problem and all the fixes that worked for others are not working for me:
I added "uses-permission android:name="android.permission.INTERNET" to the maifest XML file. (I also have ACCESS_NETWORK_STATE but I don't think that's necessary for this.)
I am sending the packets to the host loopback address 10.0.2.2. The port is 5006, so it's not one that I should need special privileges for.
I am calling DatagramSocket.send() in a dedicated thread, not in the main thread. (I think this would throw NetworkOnMainThreadException anyway, and I'm not getting any exceptions.)
I have Telnet-ed into "localhost 5444" and issued the "redir add udp:5006:5006" command to setup UDP port forwarding on the emulator's virtual router. The command returns "OK" without error, and "redir list" returns "udp:5006 => 5006".
I've also setup UDP port forwarding (port 5006) on my host PC's router (between PC and open internet). But I don't think that should be necessary, this router is not between the emulator and the host PC.
I have disabled Windows firewall and anti-virus on the host PC.
Here is the relevant code in my MainActivity.java. The start() and stop() methods are called from button clicks (omitted because they are not part of the problem):
private static String TAG = "MainActivity";
private volatile boolean running = false;
private String ip = "10.0.2.2";
private int port = 5006;
public void start(View view) {
new Thread() {
public void run() {
byte[] bytes = "Hi from UDPSender!".getBytes();
try {
InetAddress inetAddr = InetAddress.getByName(ip);
running = true;
while (running == true) {
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, inetAddr, port);
DatagramSocket socket = new DatagramSocket();
socket.setBroadcast(false);
socket.send(packet);
socket.close();
Log.d(TAG, "Send packet to "+packet.getAddress().getHostAddress()+":"+packet.getPort());
Thread.sleep(1000);
}
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
}
}.start();
}
public void stop(View view) {
running = false;
}
The Goal:
To have the ardiuno check if it's connected to the android with bluetooth. Then to perform an act if it is connected or reconnect if it's not connected.
What I am using:
Bluesmirf silver with arduino uno and note 3
What I've done so far:
[ARDUINO CODE]
The Bluesmirf is in master mode auto connect. The arduino is supposed to check if the android app is sending an H character. If it is that means its connected. If not then it needs to keep re-connecting.
#include <SoftwareSerial.h>
#include <TextFinder.h>
int bluetoothTx = 2; // TX-O pin of bluetooth mate, Arduino D2
int bluetoothRx = 3; // RX-I pin of bluetooth mate, Arduino D3
boolean running = false;
SoftwareSerial bluetooth(bluetoothTx, bluetoothRx);
void setup()
{
Serial.begin(9600); // Begin the serial monitor at 9600bps
bluetooth.begin(115200); // The Bluetooth Mate defaults to 115200bps
bluetooth.print("$"); // Print three times individually
bluetooth.print("$");
bluetooth.print("$"); // Enter command mode
delay(100); // Short delay, wait for the Mate to send back CMD
bluetooth.println("U,9600,N"); // Temporarily Change the baudrate to 9600, no parity
delay(100);
bluetooth.begin(9600); // Start bluetooth serial at 9600
}
void loop()
{
//Check If Connected
if(bluetooth.available()) // If the bluetooth sent any characters
{
//Check if bluetooth recieved an H and store in a value
char val = bluetooth.read();
if(val == 'H')
{
running = true;
}
else if(val != 'H')
{
running = false;
}
}
else if(!bluetooth.available())
{
running = false;
}
//Actions to perform if arduino is connected or not connected
if(running == true)
{
//It's connected so wait 5 seconds
delay(5000);
}
else if(running == false)
{
//It's not connected: Attempt to reconnect
bluetooth.print("$"); // Print three times individually
bluetooth.print("$");
bluetooth.print("$"); // Enter command mode
delay(100); // Short delay, wait for the Mate to send back CMD
bluetooth.println("C,30196692D7C0");
delay(500);
bluetooth.println("---");
delay(3000);
}
}
[ANDROID CODE]
And this is the method of the android app that sends an H once the app is connected.
private void sendMessage(BluetoothSocket socket, String msg) {
OutputStream outStream;
try {
outStream = socket.getOutputStream();
byte[] byteString = (msg).getBytes();
outStream.write(byteString);
} catch (IOException e) {
Log.d("BLUETOOTH_COMMS", e.getMessage());
}
}
Side Note:
I've tried so many things to get this arduino to check if its connected or not. I only just started programming 3 weeks ago so this is becoming increasingly difficult. Any help would be appreciated.
[UPDATE #1]
I've managed to send an 'h' with the android app with this snippet here:
//call send method to send this character over bluetooth
sendMessage(socket,"h");
//Method used to send 'h' over bluetooth
private void sendMessage(BluetoothSocket socket, String msg) {
OutputStream outStream;
try {
outStream = socket.getOutputStream();
//byte[] byteString = (msg).getBytes();
byte[] byteString = stringToBytesUTFCustom(msg);
outStream.write(byteString);
} catch (IOException e) {
Log.d("BLUETOOTH_COMMS", e.getMessage());
}
}
//Method used to convert
public byte[] stringToBytesUTFCustom(String str) {
char[] buffer = str.toCharArray();
byte[] b = new byte[buffer.length << 1];
for (int i = 0; i < buffer.length; i++) {
int bpos = i << 1;
b[bpos] = (byte) ((buffer[i]&0xFF00)>>8);
b[bpos + 1] = (byte) (buffer[i]&0x00FF);
}
return b;
}
And with arduino I can properly read the 'h' using this snippet.
if (bluetooth.available() > 0) { // if the data came
char incomingByte = bluetooth.read(); // read byte
if(incomingByte == 'h') {
running = true;
}
}
New Problem
I am having trouble telling when the arduino has lost connection with the android app.
In my application I'd like to get all the IP addresses that are taken by computers in the LAN using the broadcast address. I used the following code to determine the broadcast address.
InetAddress getBroadcastAddress()
{
try
{
WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
DhcpInfo dhcp = wifi.getDhcpInfo();
// handle null somehow
int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
byte[] quads = new byte[4];
for (int k = 0; k < 4; k++)
quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
return InetAddress.getByAddress(quads);
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
Now that I have it, as far as I know, when one uses the broadcast address, every computer answers it, so if I simply send a "ping" message to that address, the computers of the LAN will answer it. How should I ping them in Android? What command would send me the taken addresses?
The following code simply returns the packet from the sending phone but I need the computers' addresses:
int PORT = 8080;
int DISCOVERY_PORT = 8080;
try
{
DatagramSocket socket = new DatagramSocket(PORT);
socket.setBroadcast(true);
String data="TEST";
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(),
getBroadcastAddress(), DISCOVERY_PORT);
socket.send(packet);
byte[] buf = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(buf, buf.length);
Log.w(Tags.DEBUG,"Receive start");
socket.receive(packet2);
Log.w(Tags.DEBUG,packet2.getAddress().toString());
}
catch (Exception e)
{
e.printStackTrace();
}
Is it even possible?
EDIT:
If I'm honest it works as it is written: my phone sends an UDP packet and my phone receives the incoming packages. As the only package is coming from my phone, it is obvious that the address is my phone's address. However, if the broadcast address is valid, each network interface should send the signal back. Am I correct?
You are partially correct.
When you send a UDP packet to a broadcast address, all the computers on the network will receive the packet, unless a router on the network restricts UDP packets being sent to the broadcast address. This is mostly the case on a corporate network.
But not all computers will reply to that packet, they need to know what to do with it.
Either you write a server application that understands your UDP packet and is configured to reply to that packet and deploy that server application to all of the computers on the network.
Or you implement an existing discovery protocol such as Bonjour (Mac) or SSDP (Windows)
I suggest you take a look at ZeroConf Service Discovery if you want to use existing protocols rather then deploying your own application.
http://en.wikipedia.org/wiki/Zero-configuration_networking#Service_discovery
I hope this explanation helps you with your problem.
I would like to know about the 'service discovery' mechanisms supported by android - particularly, Printer Discovery.
Does android provide such a discovery option? example : support for snmp broadcast?
I tried out an application "PrinterShare" link : http://www.printeranywhere.com/mobile.sdf where Printer Discovery is achieved through ipp.
Any help is appreciated.
Roy, I came across the same problem as you, and was even getting the same behavior when running that code snippet on an actual device (while running the code standalone, not in android, worked fine). I found this page and got it working, although only on the device, by using the following to figure out the Broadcast IP (instead of 239.255.255.250):
InetAddress getBroadcastAddress() throws IOException {
WifiManager wifi = mContext.getSystemService(Context.WIFI_SERVICE);
DhcpInfo dhcp = wifi.getDhcpInfo();
// handle null somehow
int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
byte[] quads = new byte[4];
for (int k = 0; k < 4; k++)
quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
return InetAddress.getByAddress(quads);
}
Hope that helps:)
Does android provide such a discovery option?
Not that I am aware of, sorry.
This code snippet works fine on J2SE. However, on the Android emulator, I get a 'Time Out Exception' with response = 'null'
`DatagramSocket clientSocket = new DatagramSocket(8888);
clientSocket.setSoTimeout(20000);
/**
* SSDP is a text-based protocol based on the Hypertext Transfer Protocol (RFC 2616).
* However, it uses the User Datagram Protocol (UDP) as underlying transport protocol.
* Services are announced by the hosting system with multicast addressing to a
* specifically designated IP multicast address at port number 1900. In IPv4,
* the multicast address is 239.255.255.250.
*/
//getByName(host) //host the hostName to be resolved to an address or null.
InetAddress group = InetAddress.getByName("239.255.255.250");
//host can be null which means that an address of the loopback interface is returned.
if(group == null){
Log.d("Discovery","getByName(): returns address of loopback interface.");
}
byte[] sendData;
byte[] receiveData = new byte[128];
String sentence = "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: 239.255.255.250:1900\r\n"
+ "MAN: \"ssdp:discover\"\r\n"
+ "MX: 10\r\n"
+ "ST: ssdp:all\r\n"
+ "\r\n";
sendData = sentence.getBytes();
//public DatagramPacket (byte[] data, int length, InetAddress host, int port)
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, group, 1900);
try {
clientSocket.send(sendPacket);
} catch (Exception e) {
e.getMessage();
e.printStackTrace();
}
Log.d("Discovery","sent packet...");
while( true)
{
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
try
{
boolean isc = clientSocket.isConnected();
clientSocket.receive(receivePacket);
}
catch ( Exception Ex)
{
Log.d("Discovery","Time out Exception");
}
if (receivePacket.getAddress() == null)
{
Log.d("Discovery","receivePacket.getAddress() == null");
break;
}
Log.d("Discovery","Senders Address : " + receivePacket.getAddress().getHostAddress());
String controllerResponse = new String(receivePacket.getData());
} //end of while()
clientSocket.close(); `
For evaluation of SSDP in .NET this library may be useful
https://yortw.github.io/RSSDP/