Detect bluetooth SPP disconnection without using packets - android

I have some Bluetooth devices which connect to an Android phone, however I'm having trouble detecting disconnections. The bluetooth devices don't send data packets unless they need to, so it's not an option to use a watchdog on packet reception to detect a disconnect. I've read that you can use the ACLDisconnected broadcast, but this event is never firing for me (I've waited minutes). What is a reliable way to detect a disconnection in Android 6?
Here is my AclDisconnect registering code:
_filter = new IntentFilter();
_filter.AddAction(BluetoothDevice.ActionFound);
_filter.AddAction(BluetoothDevice.ActionBondStateChanged);
_filter.AddAction(BluetoothAdapter.ActionDiscoveryStarted);
_filter.AddAction(BluetoothDevice.ActionAclDisconnected);
_filter.AddAction(BluetoothDevice.ActionAclDisconnectRequested);
context.RegisterReceiver(_bluetoothDeviceReceiver, _filter);
And the callback (which doesn't fire on disconnect)
public override void OnReceive(Context context, Intent intent)
{
string action = intent.Action;
if (action == BluetoothDevice.ActionAclDisconnected || action == BluetoothDevice.ActionAclDisconnectRequested)
{
Interlocked.CompareExchange(ref Disconnected, null, null)?.Invoke();
}
}

I don't know about Xamarin, but in plain Android / Java this is typically done by catching the IOException that is thrown by the InputStream when the stream is closed. For example:
// In your listening thread
InputStream inputStream = socket.getInputStream();
while (true) {
try {
int nextByte = inputStream.read();
// Do something with it
} catch (IOException e) {
// Here you know that you are disconnected
}
}

BluetoothDevice.ActionAclDisconnected is indeed unreliable and the only sure way to detect a disconnection is to catch an IOException, like the previous answer states.
So for this you would have to develop a "ping" mechanism where data is sent every X time.

Related

How can I stop a system service programmatically on a rooted android phone?

I am developing an androd bluetooth telnet(?) server which gets commands via bluetooth OPP. My plan is to monitor incoming Opp push, check if it is from certain user, then starting a worker service which actually performs given work.
So I researched information about receiving bluetooth incoming OPP, and I found that killing BluetoothOppService is a key point in this SO thread.
So I wrote the codes below to accept incoming OPP push.
private void KillOriginalService()
{
java.lang.Process suProcess=null;
int pid;
try
{
String[] command = {
"/system/bin/sh",
"-c",
"ps|grep com.android.bl"
};
suProcess = Runtime.getRuntime().exec(command);
DataOutputStream os = new DataOutputStream(suProcess.getOutputStream());
DataInputStream osRes = new DataInputStream(suProcess.getInputStream());
if (null != os && null != osRes)
{
String line;
while (osRes.available() > 0)
{
line = osRes.readLine();
if (line.contains("1002"))
{
String[] words=line.split(" ");
//pid=Integer.parseInt(words[0]);
final String p=words[0];
new ExecuteAsRootBase(){
#Override
protected ArrayList<String> getCommandsToExecute()
{
// TODO: Implement this method
ArrayList<String> list=new ArrayList<String>();
list.add("system/bin/kill -9 " + p);
return list;
}
}.execute();
Log.v(TAG,"Success kill");
return;
}
}
}
}
catch (IOException e)
{
Log.e(TAG, "error occured trying to kill opp service ",e);
}
}
And the following code to get ServerSocket.
private void getServerSocket()
{
boolean fail = true;
while (fail)
{
try
{
serversocket = null;
Log.v(TAG, "trying to get serverSocket");
serversocket = mBluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("OPP Listener", UUID.fromString("00001105-0000-1000-8000-00805f9b34fb"));
if(serversocket!=null)
fail = false;
}
catch (IOException e)
{
fail = true;
Log.e(TAG, "Failed to get serversocket " , e);
}
if (fail)
{
KillOriginalService();
}
}
//return serversocket;
}
And this code works but sometimes continually ignore the incomming connection until I restart my service manually, causing the original service to accept the connection, rejecting it (because the incoming file's mime type is null). Also the original service acquires full wakelock, consuming my device's battery significantly. Also even when mine accepts the connection, I have to fail about 2 times before mine accepts the connection instead of the original service.
I read the logcat outputs, and found out that the original BtOppService restarts after I kill it printing OnStartCommand logcat.
I solved the battery consuming problem by Administration screenoff api. But the first problem is not solved.
How can I make my service, not the original service, to receive every incoming connections?
(I am currently solving this problem by using watchdog thread that restarts my service automatically.)
P.S. I have a rooted device and the su works properly.
I finally found a way to stop the system services programmatically: Hook the system service!
As I had a rooted device, I could install XPosed Framework and modules. I can create a module that attatches to the target service, and then returning not START_STICKY can prevent it from being restarted.
However, it turned out to be a XY problem. I finally changed my implenentation to not Killing the system service, but Living with it.
I created an file observer to check if the system has received a file. When a file is received, it started a corresponding service.
It creates synergy effect with Auto-Accept, which makes a phone to accept every bluetooth OPP file transfer requests without having to ask the user(No prompts!).

zebra scanner integration in android

we are using symbol ls4278 bluetooth scanner to integrate in android application. It is connected as keyboard and types scanned barcode in any edittext field...
After scanner OnKeyUp event is called.
public override bool OnKeyUp(Keycode keyCode, KeyEvent e)
{
..
}
I was searching documentation and android sdk, but I can't found such one. But for LI4278 they have android sdk here : https://www.zebra.com/us/en/support-downloads/scanners/general-purpose-scanners/li4278.html
here is also documentation for sdk but LS4278 is not in supported device list.
Does anyone implemented LS4278 scanner in android devices?
The LS4278 product page is here: https://www.zebra.com/us/en/support-downloads/scanners/general-purpose-scanners/ls4278.html and lists support for the "Windows Scanner SDK" ONLY. The LS4278 was discontinued on September 24th 2012 so I am not surprised it does not have Android support. As you say, its successor, the LI4278 does have Android support. As the other answer states, if you want more control over how you receive data then I suggest trying SPP if the scanner supports it.
If it works as a bluetooth keyboard, then no support is needed. Just capture the key events, and react to the data when enter is pressed. Its just a mediocre experience and can mess with on screen keyboards and stop them from using an actual bluetooth keyboard. If the scanner supports SPP, you can pretty trivially parse the scan data out of it via bluetooth serial (I did this about 2 weeks ago).
BluetoothAdapter bta = BluetoothAdapter.getDefaultAdapter();
if(bta != null) {
Set<BluetoothDevice> devices = bta.getBondedDevices();
for (final BluetoothDevice device : devices) {
BluetoothClass btClass = device.getBluetoothClass();
if (btClass.getMajorDeviceClass() == 0x1f00) {
//Only look at devices which are considered uncategorized, so we don't screw up any bt headset, leyboard, mouse, etc
new DeviceThread(device).start();
}
}
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
if (state == BluetoothDevice.BOND_BONDED) {
new DeviceThread(device).start();
} else if (state == BluetoothDevice.BOND_NONE) {
DeviceThread thread = threadMap.get(device.getAddress());
if (thread != null) {
thread.interrupt();
}
}
}
}, filter);
}
private class DeviceThread extends Thread {
private BluetoothDevice device;
public DeviceThread(BluetoothDevice device) {
this.device = device;
threadMap.put(device.getAddress(), this);
}
#Override
public void run() {
try {
BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
socket.connect();
InputStream inputStream = socket.getInputStream();
while (!Thread.interrupted() && socket.isConnected()) {
inputStream.skip(5);
String data = "";
do {
int code = inputStream.read();
char character = (char) code;
data = data + character;
} while (inputStream.available() > 0);
data = data.substring(0, data.length() - 2);
if (scannerEventListener != null) {
scannerEventListener.onScan(data);
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
Log.d("GABE", "Exiting thread");
}
}
This code will register for bluetooth devices being paired, then check and see if they're unknown device types (scanners don't have a device class). If so, it will start a thread to listen for that device. When its unbonded, it will interrupt that thread. On the thread it opens up a SPP connection to the device and waits for input. When it gets it, it parses the input and sends the result to a listener.
For this to work, the scanner needs to be in SPP mode. Some scanners support it, some don't, and how to set it into that mode varies (the one on my desk has a control barcode I need to scan to set the mode). Generally I would code for it to accept either type of input- hardware keyboard mode or SPP.

How to send AT commands based on BT Hands-Free profile in android?

I am trying to establish Bluetooth connection between an Android device with other mobile phone over Handsfree profile. I am using following code -
private static final UUID MY_UUID = UUID.fromString("0000111F-0000-1000-8000-00805F9B34FB"); // UUID for Hands free profile
// Some code...
// Get Bluetooth Adapter.
m_oBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// Some code...
// For paired BT device, getting a connection established.
if(null != m_oBluetoothDevice)
{
if(BluetoothDevice.BOND_BONDED == m_oBluetoothDevice.getBondState())
{
try
{
m_oBluetoothSocket = m_oBluetoothDevice.createRfcommSocketToServiceRecord(MY_UUID);
m_oBluetoothSocket.connect();
Log.i(TAG, "Socket Connected");
}
catch(Exception e)
{
if(null != m_oBluetoothSocket)
{
Log.i(TAG, "Closing socket");
try
{
m_oBluetoothSocket.close();
}
catch (Exception e1)
{
Log.i(TAG, "Error while closing socket : " + e1.getMessage());
}
}
}
}
}
I can create RFCOMMSocket using this code.
Now I want to send AT commands based on Bluetooth Hands-Free profile. e.g. If other mobile phone receives a phone call, my Android device can reject this call by sending AT command- "+CHUP". I am not sure whether this is possible or not.
At this point, I am stuck. I have read Bluetooth APIs where I found -
BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT
Can we use this Intent for sending AT commands? Is this a proper way to send AT command based on Bluetooth Hands-Free profile? Please someone help me out and give me proper direction.
Any input from you all will be great help for me.
Thanks in advance.
You need to create InputStream and OutputStream so you can talk to the phone:
mmInStream = m_oBluetoothSocket.getInputStream();
mmOutStream = m_oBluetoothSocket.getOutputStream();
To setup the HFP connection you start to send:
mmOutStream.write("AT+BRSF=20\r".getBytes());
Where 20 is code for what you support of HFP.
And to read from the phone:
buffer = new byte[200];
mmInStream.read(buffer);
command = new String(buffer).trim();
So now you can talk beetwen the devices and you can read more about the Handsfree profile on https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=238193
Adding reference to AT commnads
http://forum.xda-developers.com/showthread.php?t=1471241
http://www.zeeman.de/wp-content/uploads/2007/09/ubinetics-at-command-set.pdf

Connecting with embedded device over bluetooth? [duplicate]

I'm currently working on an Android application that connects to an instrument via Bluetooth and need to write string commands and receive string responses back. Currently I have the connect/read/write working for TCP/IP over Wi-Fi and now trying to implement Bluetooth. But I am running into some roadblocks. I have been searching the web trying to find examples of something similar and haven't had any luck. I have been using the Android developer resource example: Bluetooth Chat as my main reference point.
My current code seems to work.. Then it throws a Service Discovery Failed exception at the point of the connection. I am using the DeviceListActivity class to do the discovery and selecting of the device I want to connect to. It returns anActivityResult and then my Bluetooth class waits for it to handle that and then does the connect to it. The code beneath is almost identical to the Bluetooth Chat App.
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(!m_BluetoothAdapter.isEnabled())
{
m_BluetoothAdapter.enable();
}
switch (requestCode) {
case REQUEST_CONNECT_DEVICE:
// When DeviceListActivity returns with a device to connect
if (resultCode == Activity.RESULT_OK) {
// Get the device MAC address
String address = data.getExtras()
.getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
// Get the BLuetoothDevice object
BluetoothDevice device = m_BluetoothAdapter.getRemoteDevice(address);
// Attempt to connect to the device
connect(device);
}
break;
case REQUEST_ENABLE_BT:
// When the request to enable Bluetooth returns
if (resultCode == Activity.RESULT_OK) {
// Bluetooth is now enabled, so set up a chat session
}
else {
// User did not enable Bluetooth or an error occured
Toast.makeText(this, "Bluetooth not enabled", Toast.LENGTH_SHORT).show();
finish();
}
}
}
This is my connect function:
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private void connect(BluetoothDevice device) {
m_Device = device;
BluetoothSocket tmp = null;
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
}
catch (IOException e) {
}
m_Socket = tmp;
m_BluetoothAdapter.cancelDiscovery();
try {
// This is a blocking call and will only return on a
// successful connection or an exception
m_Socket.connect();
}
catch (IOException e) {
try {
m_Socket.close();
}
catch (IOException e2) {
}
return;
}
}
Hopefully, whatever I am doing wrong is simple, but I'm afraid it's never that easy. This is my first time doing any Bluetooth development, and maybe I'm doing something blatantly wrong... But I'm not sure why I get the service discovery failed exception.
You can pair/find the device at all times manually on the phone... It does require a passcode, but I don't think that is the problem that I am having.
After three days I got it figured out thanks to some very helpful posts.
I had to replace:
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
with:
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
tmp = (BluetoothSocket) m.invoke(device, 1);
and voilĂ  it works!
As of API 15 you can use the following method:
Try replacing your UUID with the return value of getUuids() method of BluetoothDevice class.
What worked for me was something like this:
UUID uuid = bluetoothDevice.getUuids()[0].getUuid();
BluetoothSocket socket = bluetoothDevice.createRfcommSocketToServiceRecord(uuid);
The reason this works is that different devices support different UUIDs and by getting the UUIDs of the device using getUuids you are supporting all features and devices.
Another interesting new method (supported since API 14) is this: BluetoothHealth.getConnectionState. Haven't tried it but looks promising...
This was a suggested edit from an anonymous user attempting to reply to the accepted answer.
One big difference between your before and after code is the UUID you are passing. I found my answer here: http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createRfcommSocketToServiceRecord(java.util.UUID)
I had to replace:
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
with:
private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
tmp = device.createRfcommSocketToServiceRecord(SPP_UUID);
and voila it works!
The original code is for a peer to peer android app. It makes no sense to use the app UUID when connecting to a simple serial bluetooth device. Thats why discovery fails.
So as it mentioned above, the point is that you need to use the UUID that the server is waiting for.
If you are connecting to a bluetooth device, such as a headset or mouse, you need to check which UUIDs the device is listening for. You can see the UUIDs like this.
UUID[] uuids = bluetoothDevice.getUuids();
And if you want to know what these UUIDs mean, see this.
This is a realy old one question but i found that using the createInsecureRfcommSocketToServiceRecord() instead of createRfcommSocketToServiceRecord() along with the getUuids() previously mentioned do the trick for me
UUID uuid = bluetoothDevice.getUuids()[0].getUuid();
BluetoothSocket socket = bluetoothDevice.createInsecureRfcommSocketToServiceRecord(uuid);

Bluetooth on 2.0+

I'm doing bluetooth development for connecting with a PC. I've basicly used the BTChatExample and changed the UUID to the standard PC SPP-profile.
Trying to close a bluetooth application during a blocking read, by closing the BluetoothSocket will leave the Bluetooth stack in a unusable state. This can only be fixed by disabling and enabling bluetooth and restarting the application. Checking logcat, you can see that some of the internal methods are failing, leaving a open port. Any information on this?
First of all there seams to be differences on how bluetooth is implemented on N1 and HTC Legend/Desire both running 2.1, do you know anything about this?
Connecting isn't 100% reliable, sometimes I get a warning saying ~PortSystemContext init: FAILED. This leaves bluetooth unusable, and restarting is needed.
Am I right in assuming that SPP is the only profile supported for use with the APIs? That's what the docs on the BluetoothAdapter says.
I would love to discuss issues on bluetooth with a developer and iron out these bugs so that Android can have good proper Bluetooth support it deserves.
Closing a socket in one thread during a blocking read should definitely cause the read to return (by throwing IOException) and should not leave the stack in a 'bad state'. This is the behavior on Droid and Nexus.
I spoke directly to the original poster, and he had observed this problem on HTC Legend and HTC Desire. It appears like they are not implementing the API's correctly. I am raising the issue with them.
You are correct that SPP/RFCOMM is the only profile that is intended for use with the API's. SPP/RFCOMM gets you a streaming socket which is good enough for a lot of use cases.
For now I recommend BT development on Nexus One / Motorola Droid, which are considered 'reference' implementations of the Bluetooth API's.
May I suggest that you do not perform blocking read() calls unless you have first checked that there is data ready to be read by using inputstream.available() which returns an integer indicating how many bytes are waiting in the input buffer.
long timeouttime = gettimeinseconds() + 2;
String response = "";
while (gettimeinseconds() < timeouttime) {
if (inputstream.available() > 0)
response = response + inputstream.read();
} else {
Thread.sleep(100); // sleep to slow down the while() loop.
}
}
return response;
That's just pseudo code, and its oversimplified. The bottom line is that we're not performing any blocking calls (read()) unless we're sure they will return immediately without delay.
Also, I highly recommend using BufferedInputStream instead of the standard InputStream.
Anyone could solve this problem ?
I try the following code :
// Keep listening to the InputStream while connected
while (!isInterrupted)
{
try
{
//Clear buffer
buffer = new byte[1024];
// Read from the InputStream
if (mmInStream != null && mmInStream.available() > 0)
{
if (isInterrupted)
break;
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Act_Main.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
}
else
{
try
{
synchronized (this)
{
this.wait(100);
}
if (isInterrupted)
break;
}
catch(InterruptedException ex)
{
Log.e(TAG, "WAIT_EXCEPTION:"+ ex.getMessage());
}
}
}
catch(Exception ex)
{
Log.e(TAG, "disconnected", ex);
connectionLost();
break;
}
}
And I changed the isInterrupted boolean in the cancel() method. Here is my stop() method:
/**
* Stop all threads
*/
public synchronized void stop()
{
isStop = true ;
if (D)
Log.d(TAG, "stop");
if(mConnectThread != null)
{
mConnectThread.cancel();
mConnectThread = null;
}
if(mConnectedThread != null)
{
mConnectedThread.cancel();
mConnectedThread = null;
}
setState(STATE_NONE);
}

Categories

Resources