I'm working on a project connecting my Android phone to an Arduino bluetooth transceiver. It works fine until the phone goes into suspend (sleep?) mode; when that happens, the bluetooth connection I establish seems to still exist, but no data is transferred. I'm using Android Sdk 30.
Here's what I do: I have a UI list with BT devices for the user to choose; after one is selected, this is called to connect:
// in my BluetoothService class
private BluetoothDevice bluetoothDevice;
private BluetoothSocket bluetoothSocket;
public void connect(BluetoothDevice device) {
try {
bluetoothDevice = device;
String uuid = getString(R.string.bluetooth_uuid);
bluetoothSocket = bluetoothDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(uuid));
bluetoothSocket.connect();
if (bluetoothSocket.isConnected()) {
Log.i(TAG, "connected to " + device + " with socket " + bluetoothSocket);
} else {
Log.w(TAG, "still not connected to " + device + " with socket " + bluetoothSocket);
}
} catch (IOException e) {
Log.e(TAG, "failed to connect to device", e);
}
}
So again, this works fine; the connection is established and data is sent/received.
Now, I send data every 30 seconds, and on the receiver side light an led until 60 seconds have passed with no data received.
private ScheduledFuture<?> sendScheduler; // canceled in onDestroy()
public void onCreate() {
sendScheduler = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
sendData(lastLevelSent); // i can post sendData, but it works in normal state
}, 30, 30, TimeUnit.SECONDS);
}
Now when the phone goes into sleep (ie I press the power button once), the led turns off after 60 seconds, ie no data is being received. However, the connection itself seems to still be established (I can tell by how the receiver led blinks).
When I wake the device back up, it does not resume sending data. The disconnect button in my app doesn't work (bluetoothSocket.close()), and trying to connect to the device again doesn't either. I have to kill the app, then I can disconnect.
The check I'm using whether the connection is still valid is bluetoothSocket != null && bluetoothSocket.isConnected().
I did add my app to the "don't put into standby" in the Android settings.
So how can I either convince the app to continue sending data while in sleep mode, or at least reliably check the connection status?
Related
I've been needing a way to check if the user has Internet. I used this approach:
public class InternetCheck extends AsyncTask<Void, Void, Boolean> {
private Consumer mConsumer;
public interface Consumer {
void accept(Boolean internet);
}
public InternetCheck(Consumer consumer) {
mConsumer = consumer;
execute();
}
#Override
protected Boolean doInBackground(Void... voids) {
try {
Socket sock = new Socket();
sock.connect(new InetSocketAddress("8.8.8.8", 53), 1500);
sock.close();
Log.w("INTERNET CHECK", "has Internet");
return true;
} catch (IOException e) {
Log.w("INTERNET CHECK", "NO Internet");
return false;
}
}
#Override
protected void onPostExecute(Boolean internet) {
mConsumer.accept(internet);
}
}
... the following way:
new InternetCheck(hasInternet -> { /* do something with boolean response */ });
However, it seems like it isn't as robust as one would think: sometimes (not so often) my phone is connected to WiFi and yet this method returns false.
What are the possible scenarios/diagnostics as of why this behaviour might happen?
My personal experience is that it seems to happen when my phone has my application open and is connected to a WiFi. Then, the phone goes to sleep and I move places and open it back up to the application on a new WiFi connection. The check returns false despite my phone displaying that it clearly has established the new WiFi connection (since it was a saved network).
However, this is not the only way this method seems to have failed. Another developer had it happen while he didn't change his WiFi connection.
Wifi connections are disconnected when the phone goes to sleep. It takes time for the connection to be re-established when the phone wakes up again.
Your 1.5 second connection timeout is too short to accommodate that extra delay.
Also, the DNS server, or the intermediate network, may simply be busy. Again, the connection timeout is too short.
Before inventing your own way to check internet connectivity, you should read these:
Android Developer Guide: Determine and monitor the connectivity status
StackOverflow: Android check internet connection
I have a problem trying to connect to a peripheral. Sometimes the callback onConnectionStateChange(...) is not called after BluetoothDevice#connectGatt(...). What I'm trying to achieve is fast and short connections triggered by user action.
This situation occurs about 1 every 10 times without specific prior action. It lasts about 20 to 30 seconds or until the application is killed and reopened. The normal sequence of steps I follow is:
Scan devices to find the peripheral.
Call BluetoothDevice#connectGatt(...). If it takes longer than 1 second to connect, it means that the connection is "stuck" and therefore it won't connect, so BluetoothDevice#connectGatt(...) is called again. This is done with a limit of 5 attempts.
onConnectionStateChange(...) is called with newState CONNECTED and begins the services discovery.
The rest of the operations are performed without problems.
After disconnection BluetoothGatt#close() is called.
The problem occurs at point 3. Sometimes onConnectionStateChange(...)is not called. I have noticed that most of the times the problem starts with a specific behavior. After calling BluetoothDevice#connectGatt(...), onConnectionStateChange(...) is called with newState CONNECTED, but almost immediately afterwards (~40 milliseconds) is called again with newStatus DISCONNECTED. Due to the short time of the status change, I can deduce that the device does not even tried to make the connection and changed the state to DISCONNECTED.
The problem ends when:
20-30 seconds have passed. During this time onConnectionStateChange(...) is never called. When the problem ends, onConnectionStateChange(...) is called the number of times that the app tried to connect. For example, if BluetoothDevice#connectGatt(...) is called 15 times, onConnectionStateChange(...) is called 15 times with newState equal to DISCONNECTED. This is curious because never in any of those connection attempts the status changed to CONNECTED.
The app is killed and started again.
This error occurs in SDK18 and SDK 21.
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
String deviceName = device.getName();
if (deviceName == null) return;
Log.d("BLUETOOTH CONNECTION", "Device found: " + device.getName());
if (mMode == SCAN_MODE) {
mListener.deviceFound(device, rssi, scanRecord);
}
else {
mDevices.put(device.hashCode(), device);
stopScan();
// Samsung devices with SDK 18 or 19 requires that connectGatt is called in main thread.
mHandler.post(new Runnable() {
#Override
public void run() {
Log.d("BLUETOOTH CONNECTION", "Executing first device.connectGatt()");
BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback);
retryIfNecessary(device, gatt);
mTryingToConnect = true;
}
});
}
}
private void retryIfNecessary(final BluetoothDevice device, final BluetoothGatt gatt) {
if (isRetryLimitReached()) {
Log.d("BLUETOOTH CONNECTION", "Try count limit reached");
finishConnection(gatt);
mRetryCount = 0;
mListener.error(TIMEOUT);
return;
}
mRetryCount++;
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Log.d("BLUETOOTH CONNECTION", "Check if it is frozen.");
if (isWorking()) {
Log.d("BLUETOOTH CONNECTION", "Frozen, create new connection.");
BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback);
retryIfNecessary(device, gatt);
}
}
}, RETRY_INTERVAL_MS);
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) {
Log.d("BLUETOOTH CONNECTION", "On connection state changed. Device: "+ gatt.getDevice().getAddress());
if (!mConnected && BluetoothGatt.STATE_CONNECTED == newState) {
Log.d("BLUETOOTH CONNECTION", "Connected");
mTryingToConnect = false;
mTryingToDiscoverServices = true;
mConnected = true;
gatt.discoverServices();
}
else if(BluetoothGatt.STATE_DISCONNECTED == newState) {
Log.d("BLUETOOTH CONNECTION", "Disconnected and closing gatt.");
mConnected = false;
gatt.close();
if (!mConnectionFinished && mRetryCount == 0) {
finishConnection(gatt);
}
}
}
I think that the peripheral is not relevant, because the iOS app can always connect without this problem.
Any ideas? Thanks in advance.
Edit!
This answer say that:
Direct connection has interval of 60ms and window of 30ms so
connections complete much faster. Additionally there can only be one
direct connection request pending at a time and it times out after 30
seconds. onConnectionStateChange() gets called with state=2,
status=133 to indicate this timeout.
So in this 30 seconds interval there is a pending connection request and times out at the second 30. It's unlikely but, is there anything I can do to make this time shorter? Or maybe there is an explanation for the connection failure that I am not seeing. Thanks.
EDIT 02/03/2016
A new information that may help. When the problem starts (when onConnectionStateChange(...) is called with newState=DISCONNECTED after ~40ms of being called with newState=CONNECTED), the status is 62 = 0x03E. Looking here that status code means GATT_CONN_FAIL_ESTABLISH. When I detect this status I'm closing the gatt connection, but the problem persists. I also tried disconnecting and closing. Ideas? Thanks.
If someone is having a similar issue, the problem was finally solved by changing the BLE chip used by the peripheral (arduino). Before that change, a workaround I found was turning off and on the BLE after each connection. The solution was not perfect, but improved the connection rate a lot.
Android Bluetooth needs to be recycled occasionally, have you tried restarting the BLE on the device when you encounter this timeount?
Here's a snippet I've used to restart the BLE when strange things start happening.
static Handler mHandler = new Handler();
public static void restartBle() {
final BluetoothManager mgr = (BluetoothManager) ApplicationBase.getAppContext().getSystemService(Context.BLUETOOTH_SERVICE);
final BluetoothAdapter adp = mgr.getAdapter();
if (null != adp) {
if (adp.isEnabled()) {
adp.disable();
// TODO: display some kind of UI about restarting BLE
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (!adp.isEnabled()) {
adp.enable();
} else {
mHandler.postDelayed(this, 2500);
}
}
}, 2500);
}
}
}
I am not sure if you're still looking for an answer for this question. Personally, I would not advise making "fast and short connections triggered by user action" for low energy devices. Instead you could set the autoConnect option to "true" in your connectGatt method.
device.connectGatt(mContext, true, mGattCallback); [instead of false]
Hope it helps!
I am trying to connect android device with laptop or desktop which contains Bluetooth via Bluetooth socket connection.
I have created one android application (Client) which tries to connect laptop Bluetooth device where java application (Server) is running.
My concern is that, Is it possible to connect both the device insecurely (without pin authentication) using Bluetooth socket connection?
If possible, Please suggest me solution.
If not, Is there any way to programmatically auto pair both the devices?
Thanks in advance !!!
By referring java api for bluetooth, I got the solution for Insecure connection between two Android and laptop Bluetooth devices.
I have used SPP client server mechanism.
My server is in java.
In java add certain parameters to URL.
Make authentication= false; authorize=false;encrypt=false;
open this URL for connection acceptance.
//Create a UUID for SPP
UUID uuid=new UUID("0f2b61c18be240e6ab90e735818da0a7", false);
System.out.println("\n"+uuid.toString());
//Create the servicve url
String url="btspp://localhost:"+uuid.toString()+";"+"name=remoteNotifier;authenticate=false;authorize=false;encrypt=false";
//open server url
StreamConnectionNotifier streamConnNotifier = (StreamConnectionNotifier)Connector.open(url);
//Create a UUID for SPP
UUID uuid=new UUID("0f2b61c18be240e6ab90e735818da0a7", false);
System.out.println("\n"+uuid.toString());
//Create the servicve url
String url="btspp://localhost:"+uuid.toString()+";"+"name=remoteNotifier;authenticate=false;authorize=false;encrypt=false";
//open server url
StreamConnectionNotifier streamConnNotifier = (StreamConnectionNotifier)Connector.open(url);
Now on client side:
Android API 10 above contains insecure connection method. "createInsecureRfcommSocketToServiceRecord(UUID)"
So use this method for connection. It will not pop up for pairing request adn try to connect with remote Bluetooth device where Java server is already running.
Code:
// Set up a pointer to the remote node using it's address.
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
// Two things are needed to make a connection:
// A MAC address, which we got above.
// A Service ID or UUID. In this case we are using the
// UUID for SPP.
try {
// btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
btSocket = device.createInsecureRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
AlertBox("Fatal Error", "In onResume() and socket create failed: " + e.getMessage() + ".");
}
// Discovery is resource intensive. Make sure it isn't going on
// when you attempt to connect and pass your message.
mBluetoothAdapter.cancelDiscovery();
// Establish the connection. This will block until it connects.
try {
btSocket.connect();
out.append("\n...Connection established and data link opened...");
} catch (IOException e) {
try {
btSocket.close();
e.printStackTrace();
} catch (IOException e2) {
e2.printStackTrace();
AlertBox("Fatal Error", "In onResume() and unable to close socket during connection failure" + e2.getMessage() + ".");
}
}
// Create a data stream so we can talk to server.
out.append("\n...Sending message to server...");
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
AlertBox("Fatal Error", "In onResume() and output stream creation failed:" + e.getMessage() + ".");
}
// Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.android_logo);
// byte[] msgBuffer = getBytesFromBitmap(bitmap);
String message = "Hello from Android.\n";
byte[] msgBuffer = message.getBytes();
try {
outStream.write(msgBuffer);
} catch (IOException e) {
String msg = "In onResume() and an exception occurred during write: " + e.getMessage();
if (address.equals("00:00:00:00:00:00")) {
msg = msg + ".\n\nUpdate your server address from 00:00:00:00:00:00 to the correct address on line 37 in the java code";
msg = msg + ".\n\nCheck that the SPP UUID: " + MY_UUID.toString() + " exists on server.\n\n";
}
// AlertBox("Fatal Error", msg);
}
I have provided only required code.
For connection UUID should be same for both the devices.
Provide server Bluetooth MAC address at "address" field at client side.
We are able to communicate with remote Bluetooth device insecurely (Without pairing).
But this code is device dependent...
certain device are able to communicate very efficiently.
Like Lenovo laptop, external bluetooth device for PC for Java server
AND
Android devices DELL venue 7, Sony, LG mobiles for client.
Tested and working properly.
But in Dell laptop, Micromaxx, xolo mobile it doesn't work.
I don't know why it is not working, If anyone knows please give the solution.
For Bluetooth 2.1 and above devices, security is mandatory.
If you are just trying to avoid the passkey entry/display, you can set the security requirements on the laptop and android device to "MITM protection not required".
This way the devices will pair automatically, but the link would be susceptible to man in the middle attacks.
My aplication is a multiplayer game in android, with a server running on google app engine and using GCM to conect the server with the player devices. i have registered the device in GCM and then i sent the registerId to the server to connect with the device. When i run the game sometimes it works fine but sometimes the device doesn¡t receive anything from GCM, the server still receive from the device. I have no idea what is happening :s
Here is where i register my device in the onCreate of the main class :
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
if (GCMRegistrar.isRegistered(this)) {
Log.d("info", GCMRegistrar.getRegistrationId(this));
}
regId = GCMRegistrar.getRegistrationId(this);
deviceId = getDeviceId();
if (regId.equals("")) {
GCMRegistrar.register(this, SENDER_ID);
Log.d("info", GCMRegistrar.getRegistrationId(this));
}
Then the first time i run the application on a phone i use this code to send the registationId to the server:
sendMessage("code=" + REGISTRATION_CODE +
"&deviceId=" + deviceId +
"®Id=" + regId +
"&phoneNumber=" + phoneNumber);
And here is the code in the server to send messages back to the device:
public void sendMessage(String regId, String text) {
Sender sender = new Sender(APIKey);
Message message = new Message.Builder().collapseKey("1").timeToLive(3).delayWhileIdle(true).addData("message", text).build();
try {
sender.send(message,regId,1);
} catch (IOException e) {
e.printStackTrace();//Manejar la excepcion
}
}
I can't understand why stop working, i'm in the middle of a game and stops working and then works again ...
I just solved it, the problem was the collapseKey here
Message message = new Message.Builder().collapseKey("1").timeToLive(3).delayWhileIdle(true).addData("message", text).build();
If you use the same collapseKey always sometimes you don't receive messages randomly so i try this and works finally works!!
Message message = new Message.Builder().collapseKey(""+((int) (Math.random () * (10000)))).timeToLive(3).delayWhileIdle(true).addData("message", text).build();
I have been going through several threads on here and didn't come across an answer to the issue I am running into.
My setup:
I have a Mac pc that I am using as a virtual serial port to communicate with my android Nexus S phone. Running the bluetooth chat app on the phone and using it as a client to talk to the virt comm I set up.
Initially I tried the bluetooth chat app with 2 android phones to confirm it works, which it does. I can send texts back and forth.
My Use case:
I have a device that reads RFid tags and sends the data to an android phone to collect the info.
I am using my PC to represent my device for now.
++++++++++++++++++
Ok to the problem,
I try to connect to the pc from my phone and initially I get a "connecting...." status bar update and after 15secs or so I get a toast message saying "I am connected to the pc" but immediately after I get "device lost connection" toast. Then the status bar goes to "not connected"
When I step through with the debugger, it seems to fail at the following portion of the bluetooth chat app. Specifically this line (bytes = mmInStream.read(buffer);)
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
When I look in logcat, the i/o exception is "software caused connection abort"
for the read() on inputstream.
Questions:
Does this have to do with my virtual port not setup right? I have the terminal up and waiting to receive input on /dev/tty.Nexus....
using the screen command # 9600 baud
Otherwise, I thought maybe the socket which the inputstream connects to is unavailable somehow. I printed that to log and it seems like it was not NULL. Every time I step through though it dies at the ConnectThread not in the ConnectedThread.
The following portion of code: specifically this line (mmSocket.connect();)
public void run() {
Log.i(TAG, "BEGIN mConnectThread");
setName("ConnectThread");
// Always cancel discovery because it will slow down a connection
mAdapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket.connect();
} catch (IOException e) {
connectionFailed();
// Close the socket
try {
mmSocket.close();
} catch (IOException e2) {
Log.e(TAG, "unable to close() socket during connection failure", e2);
}
// Start the service over to restart listening mode
BluetoothChatService.this.start();
return;
}
// Reset the ConnectThread because we're done
synchronized (BluetoothChatService.this) {
mConnectThread = null;
}
// Start the connected thread
connected(mmSocket, mmDevice);
}
I wonder if the socket variable is losing scope due to multi-threading and the socket is being passed around?
Thanks
How you set up the virtual serial post on your Mac PC. Since you have tried to run the app on 2 phones and it's working, I think the problem is on the PC.
I have posted an entry about Android and Java Bluetooth here. Hope it will help.