I am making a game for Android, in which two devices connect and then trade a small amount of data, then disconnect and use that data. I started with the Bluetooth Chat example. At this point, I have it mostly working, but some (most) times, one of the devices never receives any data. This is the same app on both devices, so the send and receive code for both is the same.
I am connecting like this:
public BluetoothClientThread(BluetoothDevice get_device,boolean secure){
BluetoothSocket temp=null;
device=get_device;
socket_type=secure?"Secure":"Insecure";
try{
if(secure){
temp=device.createRfcommSocketToServiceRecord(UUID_SECURE);
}
else{
temp=device.createRfcommSocketToServiceRecord(UUID_INSECURE);
}
}
catch(IOException e){
StorageManager.error_log_add(context,TAG,"create() on "+socket_type+" client socket failed.",e);
}
socket=temp;
}
public void run(){
bluetooth_adapter.cancelDiscovery();
try{
socket.connect();
}
catch(IOException e){
StorageManager.error_log_add(context,TAG,"Connection failed.",e);
try{
socket.close();
}
catch(IOException ioe){
StorageManager.error_log_add(context,TAG,"close() on "+socket_type+" client socket failed during connection failure.",ioe);
}
connection_failed();
return;
}
synchronized(BluetoothService.this){
bluetooth_client_thread=null;
}
connected(socket,device,socket_type,false);
}
Once connected, I grab the socket's getInputStream() and getOutputStream().
I then use the following code to read input on the socket:
public void run(){
String packet="";
while(true){
try{
int get_byte=input.read();
char get_char=(char)get_byte;
if(get_char!='\u0003'){
StorageManager.error_log_add(context,"BluetoothService","Adding packet raw data: \""+get_char+"\"",null);
packet+=get_char;
}
else{
StorageManager.error_log_add(context,"BluetoothService","Packet received:\n"+packet,null);
handler.obtainMessage(Activity_Battle_Menu.HANDLER_READ,packet).sendToTarget();
packet="";
}
if(done && they_done){
handler.obtainMessage(Activity_Battle_Menu.HANDLER_READY).sendToTarget();
}
}
catch(IOException e){
StorageManager.error_log_add(context,TAG,"Connection lost.",e);
connection_lost();
break;
}
}
}
It reads data one byte at a time, appending the bytes to a packet String. When the ETX character is reached (which I stick on the end of everything when I send it), a complete packet has been received, and is passed to the activity via handler, where it is acted upon.
Here is my writing code:
public void write(byte[] buffer){
try{
StorageManager.error_log_add(context,"BluetoothService","Writing data:\n"+new String(buffer),null);
output.write(buffer);
}
catch(IOException e){
StorageManager.error_log_add(context,TAG,"write() on "+socket_type+" connected socket failed.",e);
}
}
As you can see, I write to a log when a data "packet" is sent, when a byte is received, and when a whole "packet" has been received.
I'm not sure if any of this is relevant, but:
I am testing this with a Nook Color with Cyanogenmod 7.1 (Android 2.3.7) and Android x86 running in VirtualBox (Android 2.3.5). My app's minSdkVersion is 8 (Android 2.2). For the Android running on my computer, I'm using a Rocketfish USB Bluetooth adapter.
There are supposed to be two packets of data sent by each instance.
First, a data packet holding all of the needed game data is sent.
When the other instance's game data is received, the app sets its own boolean done to true.
It also sends a "done" packet to the other instance.
Thus, once both instances are done, and know the other is done, we know all data has been sent and received, and they both disconnect/stop their Bluetooth threads and act on the data.
Since I have it logging on both devices when data is sent and received, the logs tell me a story of what is going on.
-First, I start both games and go to the Bluetooth activity (obviously).
-Next, I connect to the Nook instance from the emulated instance.
-The emulated instance sends its game data packet.
-The Nook instance receives the emulated instance's game data, and sends its own (sometimes these two are switched in order, just because they happen asynchronously).
-Since it received a game data packet, the Nook instance now sends its "done" packet.
-That is it. The emulated instance never receives either packet. It never even receives a single byte, and the input.read() just blocks, waiting for input that never arrives (like it should).
So that is my problem. It seems that one of two things is happening:
The data never gets sent by output.write() on the Nook instance, even though it does not throw any exception, and the log verifies that it gets called.
The data never gets received by input.read() on the emulated instance. Maybe it gets lost in transmission?
As far as I can tell, my code is rock solid. The problem seems to be Bluetooth-related. Maybe one or both Android instance has a screwy Bluetooth setup?
I also want to add that every rare once in a while (maybe 1/15 times), all of the packets are sent and received properly, and the game proceeds as it should. So whatever is wrong only USUALLY happens, but not always.
I got access to another Android device with Bluetooth. I've since tested my game, and it connects and the devices trade data correctly. I've found several other bugs with my program that occur after the Bluetooth stuff, but that is neither here nor there!
The Nook Color from before and an HTC Status are what worked together. I'm officially concluding, then, that I have a faulty Bluetooth adapter on my computer. The code posted above works nicely enough.
Related
I am working on an application that makes multiple BLE connections in a minimum amount of time. I developed my application in Java Android. I set up a statistics-based system so that, among a group of phones, each phone makes connections on half the group and receives connections from the other half. This gives a pretty even distribution of BLE servers and clients. If a connection was made and failed or could not be launched, the other device that should have been a server will become a client and make the connection for the other in order to waste a minimum of time.
My problem arises when the number of devices present to make connections is greater than 5. When there are only two or three connections left to make, it takes a lot of tries to succeed. A lot of 133 errors or timeouts are thrown before getting a connection. The only time it is possible to speed up this is by clearing the Bluetooth cache. This is only possible by reflection and on the current connection. This operation doesn't work all the time and I'm trying to find out how to clear my own Bluetooth cache without it, faster and more reliably. Does anyone have any advice or idea to help me?
Here is the reflexive method we use to clear another's cache through a Gatt connection:
/**
* Method to refresh cache device
*/
private boolean refreshDeviceCache(BluetoothGatt gatt){
try {
Method localMethod = gatt.getClass().getMethod("refresh");
return (boolean) (Boolean) localMethod.invoke(gatt, new Object[0]);
}
catch (Exception localException) {
Log.e(TAG, "An exception occurred while refreshing device");
}
return false;
}
This method not being recommended, we had no choice since there is no other solution, and for the moment we haven't found one yet.
In my Android app I have an implementation of HostApduService. Here is a snippet of it's implementation:
public final class MyHostApduService extends HostApduService {
private boolean disconnected = false;
#Override
public byte[] processCommandApdu(byte[] commandApdu, #Nullable Bundle extras) {
//process apdu in a background thread and call sendResponseApdu() when ready
Single.fromCallable(() -> processInternal(commandApdu))
.subscribeOn(nfcScheduler)
.subscribe(this::sendResponseApdu, t -> Log.e("could not create response", t));
return null;
}
...
#Override
public void onDeactivated(int reason) {
disconnected = true;
}
private void processInternal(byte[] apdu) {
//business logic
if(!disconnected) {
//last message was probably received by the terminal
}
}
}
So in my observation the onDeactivated() callback can come right in the middle of a processCommandApdu() and even then the OS seems to recognize a bit earlier that the NFC field is lost than onDeactivated() is called.
Here is an example of a lost field during the communication:
16:21:16.808 I/MyHostApduService : processApdu[request|13bytes] 0A4040007A000000004306000
16:21:16.811 D/MyHostApduService : do business logic
16:21:16.890 D/HostEmulationManager: notifyHostEmulationDeactivated
16:21:16.890 D/HostEmulationManager: Unbinding from service ComponentInfo{app.debug/internal.MyHostApduService}
16:21:16.890 I/MyHostApduService : onDeactivated LINK_LOSS
16:21:16.898 I/MyHostApduService : processApdu[response|2bytes|90ms] 6A82
The problem is that I need to confidently check if the last message was received or dropped, because some important finalization code has to be executed (but only if the terminal receives the message). Is there a better way to check if a message was received than to use onDeactivated() (which seems to be quite non-deterministic in its timing)?
You can't. Instead you will need to adapt your communication protocol if you really need to reliably detect that case.
The problem is not really Android but the underlying communication protocols (ISO/IEC 7816-4 over ISO/IEC 14443-4). These protocols were built for communication with regular smartcards. Smartcards being fully passive devices which can't continue processing (due to lack of energy) when pulled out of a reader or away from the NFC RF field.
The protocol stack is designed for interrogator driven communication (where the interrogator is the terminal). Communication is performed in command-response sequences. In principal, each command-response sequence consists of the following steps (with a few additional corner cases):
Terminal sends a command.
Smartcard receives and processes the command.
Smartcard sends response data and concludes with a status word.
Neither the application protocol (ISO/IEC 7816-4) nor the transmission protocol (ISO/IEC 14443-4 aka ISO-DEP) confirm the smartcard response by any form of acknowledgement. Once the smartcard sent its response it is deemed to have finished processing.
In effect this would not be an issue for a classic smartcard (contact or contactless). An interrupted communication would cause the card to be power-cycled (either because the link-loss also implies a power-loss or because the terminal performs an explicit reset). So the smartcard would not be able to rely on cleanup sequences at that point.
However, that does not mean that there are no ways to overcome that limitation. Classic smartcards maintain persistent state even across power-cycles. Critical operations are performed as atomic transactions. In case of power-loss, cleanup/rollback is typically performed upon reset (boot-up). However, that's not quite easy to map to Android since link-loss does not cause execution on the HCE side to be interrupted. Consequently, there's no way to detect that the HCE smartcard was pulled before a response was sent back to the reader. Nevertheless, there's also no atomic transactions that would be interrupted by the link-loss. Hence, reset (i.e. the reception of the SELECT (by DF name) command that selects your application) is still the right place to perform cleanup such as resetting application state.
With regard to your specific requirement, a typical approach would be to adapt the application-level protocol and add an acknowledgment command that confirms reception of the (then second-)last response. I.e. if you currently have something like:
T---> SELECT APPLICATION
<---C FCI | 9000
T---> PERFORM CRITICAL OPERATION
<---C CRITICAL OPERATION RESULT
You could adapt the protocol to include a final acknowledgement:
T---> SELECT APPLICATION
<---C FCI | 9000
T---> PERFORM CRITICAL OPERATION
<---C CRITICAL OPERATION RESULT
T---> CONFIRM RECEPTION OF RESULT
<---C 9000
Now you would not really care if the final response (9000) is lost on its way to the terminal.
In, short: it is not possible to check if a message was received by the terminal as the role of a card (in HCE). The T=CL (ISO7816) protocol does not provide an ack when chunking the data during NFC communication for the last chunk.
Furthermore, using NFC in Android is communicating with the com.android.nfc.NfcService through a Messenger. This is exactly what e.g. HostApduService or HostNfcFService does.
There are 3 basic signals:
MSG_COMMAND_APDU = 0
MSG_RESPONSE_APDU = 1
MSG_DEACTIVATED = 2
The application service first receives a signal 0 (MSG_COMMAND_APDU) and response with a 1 (MSG_RESPONSE_APDU) type signal. At any point in time a 2 (MSG_DEACTIVATED) can happen. Now the application can check if after a command request, a deactivate was received BEFORE the response was send. This works well most of the time, but it is not consistent. It is not uncommon that the deactivate signal is received 100+ms after the actual deactivating therefore it seems that the response was sent. And even with this check you can only know when you passed the message to the NfcService, the actual transmission also takes some time (about couple of µs per byte).
So in the end, you can only tell if a response was definitely NOT received by the terminal (when the deactivate is before the response) but otherwise you are at the mercy of the NfcService and it's HAL implementation or the driver.
For receiving UDP broadcast packets from the server to an android device, i used a service class and listen for packets in a thread. It receives the packet successfully. The problem is that if multiple packets are being sent from the server in the same time then packet loss will be the result.
I even tried with a queue and processing the received packets in separate thread then also i am not getting the packet. I am completely new to network programming any help would be widely appreciated
void startListenForUdpBroadcast() {
UDPBroadcastThread = new Thread(new Runnable() {
public void run() {
try {
InetAddress broadcastIP = InetAddress.getByName(UdpConstants.IP_ADDRESS);
Integer port = UdpConstants.RECEIVER_PORT;
while (shouldRestartSocketListen) {
listenAndWaitAndThrowIntent(broadcastIP, port);
}
} catch (Exception e) {
Log.i("UDP", "no longer listening for UDP broadcasts cause of error " + e.getMessage());
}
}
});
UDPBroadcastThread.setPriority(Thread.MAX_PRIORITY); //Setting The Listener thread to MAX PRIORITY to minimize packet loss.
UDPBroadcastThread.start();
}
This code listens for new packets and pushes to queue
private void listenAndWaitAndThrowIntent(InetAddress broadcastIP, Integer port) throws Exception {
byte[] recvBuf = new byte[64000];
if (socket == null || socket.isClosed()) {
socket = new DatagramSocket(port, broadcastIP);
socket.setBroadcast(true);
}
//socket.setSoTimeout(1000);
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
socket.receive(packet);
messQueue.add(packet);
}
This checks the queue for new messages and process it
/**
* #purpose Checking queue and If anything is added to the queue then broadcast it to UI
*/
private void checkQueue() {
queueThread = new Thread(new Runnable() {
public void run() {
try {
while (shouldRestartSocketListen) {
if (!messQueue.isEmpty()) {
broadcastIntent(messQueue.poll());
}
}
} catch (Exception e) {
}
}
});
queueThread.start();
}
The problem with UDP is that your sender (your server) does not know you (your android device) missed some. It's not lost because you can't read it fast enough, sometimes just over the air interference/congestion or a busy socket.
The receiver would only know if:
you get an error while processing data since you're missing data
OR your UDP packets are numbered sequentially in its header and you detect a missing number (eg. 1,2,4 - missing 3)
Once the packet is lost, it's lost. You got two options:
implement a resend request: upon detection of a missing packet, the receiver would need to notify the sender to resend that missing packet until it does get it, and your packet processing might be halted until it does
OR be able to ignore it, "hey, we can do it without him", and fill-in with blank data (eg. a bitmap would have some blank pixels, like a broken image)
throttle your sending speed down so the packets wont jam up and get lost
the smaller your packets, the more likely they'll live
(option 1: all this resend requesting is just pseudo-TCP, so you might just consider abandoning UDP and go TCP)
I think your problem is mainly that you use Udp Broadcast over wifi.
Their are two very well documented answers why this is a very slow way to operate and why there are much more packet losts:
answer number one.
answer number two.
The thing I did to solve the extremely slow bandwidth was some kind of multi-unicast protocol:
Manage the list of clients you have connected.
Send each packet you have in your server to all of your clients separately with send call.
This is the code in java:
DatagramPacket packet = new DatagramPacket(buffer, size);
packet.setPort(PORT);
for (byte[] clientAddress : ClientsAddressList) {
packet.setAddress(InetAddress.getByAddress(clientAddress));
transceiverSocket.send(packet);
}
If you receive multiple datagrams in a short burst, your receiver loop may have trouble keeping up, and the OS-level RCVBUF in the socket may overflow (causing the OS to drop a packet it indeed did receive).
You might get better handling of short bursts if you increase the RCVBUF size. Prior to doing this, get an idea of how big it is already via socket.getReceiveBufferSize. Also bear in mind that the number of bytes in the receive buffer must accommodate not just the payload but also the headers and the sk_buff structure that stores packets in the kernel (see, e.g. lxr.free-electrons.com/source/include/linux/…).
You can adjust the recieve buffer size via socket.setReceiveBufferSize - but bear in mind that this message is just a hint, and may be overridden by settings in the kernel (if you request a size bigger than the max size allowable by the current kernel network settings, then you'll only get the max size).
After requesting a bigger receive buffer, you should double check what the kernel has allowed by calling socket.getReceiveBufferSize.
If you have the right permissions, you should be able to tweak the max buffer size the kernel will allow - see e.g. https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Web_Platform/5/html/Administration_And_Configuration_Guide/jgroups-perf-udpbuffer.html
[ Note that, in general, this will accommodate for short bursts where datagrams arrive faster than your client loop can read them - but if the client loop is chronically slower than datagram delivery, you'll still get occasional drops due to overflow. In this case, you need to speed up your client loop, or slow down the sender.
Also, as otherwise noted in other answers, packets may actually be dropped by your network - and mobile networks especially may be prone to this - so if you absolutely need guaranteed delivery you should use TCP. However, if this were your primary problem, you would expect to see dropped packets even when your server sends them slowly, rather than in a burst.]
I suppose that you are capturing only a single packet by saying
socket.receive(packet);
This is a Blocking I/O call which will wait infinitely until it receives a packet so once first packet arrives it is done waiting and next command executes i.e
messQueue.add(packet);
However when multiple packets are been received you need to continue receiving packets. in your case you just stopped receiving packages after arrival of first package
Note: UDP being a un-reliable protocol doesn't guarantee packet delivery so there might be a case a packet is lost , However this can't be a problem on every run of your program , However a nice way to check whether the packet is hitting your machine and problem is within your application (application is not able to handle the packets recieved) use tcpdump (it's a command-line utility for linux-based OS or MAC) use the following command
sudo tcpdump -i <interface name(one that handles traffic) eth0, eth1 .. en0, en1 (for MAC)> host <ipaddress or hostname of UDP Broadcast server>
Example:
sudo tcpdump -i en1 host 187.156.13.5
(if tcpdump command not found then go forward and install it)
By using this command you will see packets pumping in from destination ump server on terminal if you see more then one packets arriving then you would be sure that packets are arriving at machine , However application falls short to address the packet
it might help
With reference to above explanation by me following changes you can make to make code behave according to requirement
I suppose you can make following changes to make your problem code work instead of creating socket into listenAndWaitAndThrowIntent(InetAddress broadcastIP, Integer port ) method create it in startListenForUdpBroadcast() as follows
socket = new DatagramSocket(port, broadcastIP);
socket.setBroadcast(true);
while (shouldRestartSocketListen) {
listenAndWaitAndThrowIntent(broadcastIP, port, socket);
}
Now you also need to change implementation of listenAndWaitAndThrowIntent method as follows
private void listenAndWaitAndThrowIntent(InetAddress broadcastIP,
Integer port, DatagramSocket socket) throws Exception {
byte[] recvBuf = new byte[64000];
//socket.setSoTimeout(1000);
//change value of condition in for loop to desired number of packets you would like to receive in below example it is 2 so it captures two packets
for (int i=0 ; i <= 2 ; i++ ){
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
socket.receive(packet);
messQueue.add(packet);
}
}
Try this it should work !!
it might help
I have two phones which are paired and connected via bluetooth. How to programmatically check at one phone's end whether the other phone is getting an incoming call? Should I use a particular profile for this, that is, PBAP or HFP? If yes, how I am to do this?
Once I detect this, if I want to receive the incoming call via the connected phone, how should I implement that?
Download Hands Free Profile pdf. It is available easily. It provides you all details about how HFP works and AT commands supported by HFP for communication. No APIs available for this.
As eliasj said, you need to implement HFP and when two phones get connected, you can communicate between them via AT commands.
Suppose you have 1st phone which is Android device and 2nd phone Android or any device and they both are connected over HFP.
I don't have complete code but I can suggest you some AT commands -
1. Using AT+CIND? command you can read indicator status of other phone.
2. To enable reporting for Indicator status change, you need to use AT+CMER=3,0,0,1 command.
3. Once you get valid response from 'AT+CMER' command, you can use AlarmManager that will start a service which continuously reads the input stream of Bluetooth Socket.
4. Because of step 2., if the 2nd phone is having any incoming call, the input stream of Bluetooth Socket will contains RING as an alert.
I have used service implementing a Runnable. Here is a sample code for step 4.-
public void run()
{
try
{
// Get input and output streams from Bluetooth socket.
m_oInputStream = m_oBluetoothSocket.getInputStream();
m_oOutputStream = m_oBluetoothSocket.getOutputStream();
// Read input stream for +CIEV response is given or not.
byte[] buffer = new byte[200];
int nNumberOfBytesRead = m_oInputStream.read(buffer);
String strResponse = new String(buffer).trim();
if(true == strResponse.contains("RING"))
{
// Contains RING Alert. Answer the call.
// Start Activity for handling Incoming Call.
Intent oIncomingCallActivityIntent = new Intent(getApplicationContext(), IncomingCallActivity.class);
oIncomingCallActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
getApplicationContext().startActivity(oIncomingCallActivityIntent);
// Stop service.
stopSelf();
}
}
catch(Exception e)
{
// Log the error.
}
}
You need to implement acivity that handles incoming call. It will accept or reject call. To accept incoming call AT+ATA command is used. You will receive "OK" as a response from 2nd phone.
I hope this will help you.
You should implement HFP (the hands-free side). When I looked at this problem over a year ago it was not possible to send the audio between to phones (Android) but it could have change now.
Look at the Q/A in How to send AT commands based on BT Hands-Free profile in android? (hit on how to connect) and in the HFP spec https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=238193 (for how the profile works (incoming call on page 36))
I am currently working on an Android app to be linked into an existing product for my employer. The system is a device running firmware that is controllable via serial, ethernet, or wi-fi using a .NET Windows program, direct serial communication, or a control webpage. The Android app is meant to auto-connect control webpage so that the device is controllable from a phone without anyone having to manually find IP addresses. In order to do that, I need to be able to find IP addresses and determine whether or not the address corresponds to one of these firmware devices.
Essentially what I want to do is run through all IP addresses that the Android sees and send each one to the method above. This thing should ping the address with a firmware command and see what is sent back to determine whether the address corresponds to a firmware device. I’ve been doing that at the moment by sending a command like "HOME" or "GETINFO" (correctly formatted for the firmware, of course) and comparing what, if anything is sent back with the expected response. Commands like home also have the benefit of causing a physical response, so we know without a microchip debugger if the command has at least been received.
As it stands I have the correct address hardcoded and sent in to this method. I know that the app can load the webpage controls successfully using the same address as a URL, so it must be correct and the connection must be good, but direct communication to the device is not working. No physical response is observed and no information is sent back—the Input stream just times out and returns -1. What’s stumping me so badly about this is that as far as I can tell, I’m sending information exactly the same way as the .NET Windows controls, and yet it isn’t working.
One further note: I’m aware that sending the IP Address string to the socket constructor as a hostname probably should not work, but since no UnknownHostException is thrown, I know that the socket can resolve it to an IP Address. Correct?
My code is as follows:
private class NetworkTask extends AsyncTask<String, Boolean, Boolean> {
protected Boolean doInBackground(String... addr){
try {
String message = "<FHGETHUBINFO>";
byte[] input = new byte[8];
//addr is an array of string parameters containing a single IP address string. E.g. addr[0]=”192.168.199.108”
Socket s = new Socket(addr[0],80);
//outgoing stream redirect to socket
OutputStream out = s.getOutputStream();
out.write(message.getBytes());
Log.v(TAG, "output sent. Waiting for input.");
InputStream in = s.getInputStream();
//Skip the exclamation mark and newline. Verified that nothing is received even without a skip.
in.skip(2);
int numBytes = in.read(input,0,8);
Log.v(TAG, "Input received: "+numBytes);
String st = input.toString();
//Close connection
s.close();
if(st != "HUB INFO"){
return true;
}
else{
return false;
}
}
catch (UnknownHostException e) {
Log.v(TAG,"UnknownHostException: "+e.getMessage());
e.printStackTrace();
}
catch (IOException e) {
Log.v(TAG,"IOException: "+e.getMessage());
e.printStackTrace();
}
return false;
Thanks for any help you can give, I really appreciate it!
Agreed that I should be calling isReachable on the socket just for verification purposes, so thanks for the tip! However, it turned out the problem was that the device is not communicating on port 80, so the fact that I have the wrong port is definitely the source of the problem. Thank you for the advice, regardless.