Android BLE read Gatt Characteristic - android

I am trying to read some Bluetooth Characteristics in my APP.
Now i have a Problem with what to do after the characteristic changed from my Gatt Server. At first i've tried to use a thread to retrigger the read for the characteristic again and again like this:
new Thread(new Runnable() {
#Override
public void run() {
int[] newData = new int[30];
while(true){
try{
for(int i=0;i<newData.length;i++){
newData[i] = 0;
}
BluetoothGatt tmpGatt = refExtDataClass.getRefBluetoothGatt();
tmpGatt.readCharacteristic(characteristic);
byte[] value = characteristic.getValue();
for(int i=0;i<newData.length;i++){
newData[i] = value[i];
}
refExtDataClass.setNmData(newData);
}catch(Exception e){
break;
}
}
}
}).start();
But the Problem is that it's seems like the data is corrupt at one point (like i've always writing the same data into the characteristic from my MCU side).
Is it allowed to read BLE data like this? Is there any suggested way to read BLE data all the time? Or update it on my App side?
If you Need any additional code, please let me know.

Reading GATT characteristics is an asynchronous operation. The result is not available until you receive the onCharacteristicRead callback.
Anyway, you should rather configure your GATT server to send notifications when it has new data to send rather than polling it all the time.

Related

writeable characteristic for android custom ble service always returns write permission zero when discovered

I'm creating a custom BLE service on Android with a single characteristic that can be read/written. The code looks like this:
public static UUID MY_SERVICE = UUID.fromString("e0ec8d9c-5e4d-470a-b87f-64f433685301");
public static UUID MY_CHARACTERISTIC = UUID.fromString("e0ec8d9c-5e4d-470a-b87f-64f433685302");
/**
* Return a configured {#link BluetoothGattService} instance for the
* Custom Service.
*/
public static BluetoothGattService createCustomBleService() {
BluetoothGattService service = new BluetoothGattService(MY_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
// Current Configuration characteristic
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(MY_CHARACTERISTIC,
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_READ |BluetoothGattCharacteristic.PERMISSION_WRITE);
boolean serviceAdded = service.addCharacteristic(characteristic);
Log.i(TAG, "Building BLE service addCharacteristic returned "+serviceAdded);
return service;
}
The call to addCharacteristic(...) returns true. The service itself is created, can be advertised, and the service and its characteristic are discoverable by clients. Somewhere else in client side code, subsequent to a BLE scan that locates said service, the discovery code that runs looks like this:
for (BluetoothGattService service : gatt.getServices()) {
serviceUuid = service.getUuid().toString();
if( MY_SERVICE.toString().equals(serviceUuid) ) {
List<BluetoothGattCharacteristic> gattCharacteristics = service.getCharacteristics();
for( BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics ) {
characteristicUuid = gattCharacteristic.getUuid().toString();
Log.d(TAG, "onServicesDiscovered() - found characteristic uuid="+characteristicUuid);
int cProps = gattCharacteristic.getProperties();
Log.d(TAG, "onServicesDiscovered() - found characteristic properties "+cProps);
if ((( MY_CHARACTERISTIC.toString().equals(characteristicUuid) ))&&((cProps & BluetoothGattCharacteristic.PROPERTY_WRITE)>0)) {
writeCharacteristic(gatt,gattCharacteristic,"configStringLiteral");
}
}
}
}
When this service discovery code runs, as I mentioned, it finds the custom service and the characteristic that were defined. Any values I set for characteristic properties show up properly at the time of discovery on client side. The characteristic shows as writeable.
The problem is that characteristic write fails always even though the properties say its writable.
Has anyone seen this?... or perhaps I'm doing something dumb and have been looking at it too long.
(BTW the device hosting the custom service at runtime is a Samsung Galaxy 7 and the client is a Galaxy 6 ...or vice versa, same behavior)
Permission info is not sent over BLE when the services are discovered. Therfore the permission property should not be used for remote characteristics.
A client should inspect the characteristic property only to decide what can be done. If it receives an error saying for example encryption needed, the Bluetooth stack shall start encryption and then retry the request.
So, characteristic properties are the correct way to declare to a client what can be done with it. Characteristic permissions only tell the local GATT Server implementation how it should react to incoming GATT requests.

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!).

Recieving multiple packets in one connection Android BLE

When reading a characteristic of ble in android device or even subscribing to it, I receive 2 packets at the same time but I know that these data are not complete because when decoding them i find a part of the data sent by the characteristic so I think that the problem is that the ble write multiple packets to the characteristic at the same connection interval but the android recieve only 2 of them
I need to know how to receive all of them so I can have the full data at the end ?
This is the code of onCharacteristicChanged method
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (HEART_RATE_READING_CHAR.equals(characteristic.getUuid())){
byte[] char_float_value = characteristic.getValue();
String s="";
for(int i=0;i<char_float_value.length;i++){
s=s +String.format("%02x", char_float_value[i])+" ";
}
s = s.substring(0, s.length()-1);
Log.e("_____________", "_____________");
Log.d("TAG", s);
handler.sendMessage(Message.obtain(null,MSG_HEARTRATE,char_float_value[14]));
}
}
And these are the packets i receive at the same time
Packets

BLE Android, can't enable more than 1 notify on read characteristics

I'm developing an Android application which opens a BLE connection between the Android device and a BLE pheripheral (a simple transmitter).
The peripheral is programmed to have multiple reading characterics which I found.
The problem shows up when I try to enable the notification.
The first always returns true, and than it starts to trigger my notify callback, the others always return a false value.
List<BluetoothGattDescriptor> descrittoriDellaChar = getListaDescrittoriDaCharact(charact);
Boolean status = null;
for (int i = 0; i < descrittoriDellaChar.size(); i++) {
BluetoothGattDescriptor TargetDescriptor = descrittoriDellaChar.get(i);
byte[] valore = TargetDescriptor.getValue();
if (valore != BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) {
getCharDiLettura().add(charact);
TargetDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
//TargetDescriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
boolean success=false;
while(success==false) {
success = gattGlobale.writeDescriptor(TargetDescriptor);
}
status = gattGlobale.setCharacteristicNotification(charact, true);
}
}
boolean prio= gattGlobale.requestConnectionPriority(gattGlobale.CONNECTION_PRIORITY_HIGH);
I was using the same method since I had just 1 characteristic to read, and now it doesn't work anymore.
Sending read and write requests one after one other synchronously does not work since android only allows one pending GATT operation at a time (that's the reason it returns false). You must somehow enqueue the work and continue sending the next request once the callback (onCharacteristicRead/onCharacteristicWrite/onDescriptorWrite) of the previous request arrives.

writeCharacteristic() returns true, but does not call onCharacteristicWrite()

I wish to read a characteristic value stored in a device, modify the value, and then write it to the device. For some reason, writeCharacteristic() return true, but the internal device value does not change and onCharacteristicWrite() is not called. In fact, for some reason it is only called if I attempt to write something, and then is called shortly after I close or reopen the app - and doesn't change the device value. I've been looking into this for a few days and it's driving me crazy. It may be worth noting that reading characteristics both manually and via a notification both work fine.
Why is the writeCharacteristic() not going through, and why is onCharacteristicWrite() being called at such an odd time?
I am not sure where the issue stems from. It could be something dumb and simple like calling writeCharacteristic() incorrectly (my implementation is based off the question "Working with BLE Android 4.3 how to write characteristics?"). Would it be possible that the request is being ignored because onCharacteristicRead() is somehow unfinished?
I believe these links appear most helpful for indicating the issue but I haven't been able to pull anything from them myself:
Android BLE API: GATT Notification not received
https://code.google.com/p/android-developer-preview/issues/detail?id=1714
As a walkthrough of my code, an onItemClick() event is hit, which initiates the sequence.
int flag = 1;
((MainActivity)getActivity()).BtService.readFlag(flag);
It calls a function (in a Service) which verifies the Bluetooth connection and reads the characteristic.
public boolean readFlag(int flag){
/*... removed code here verifies that the Bluetooth Gatt is available,
that the Service exists...*/
BluetoothGattCharacteristic characteristic = Service.getCharacteristic(SEND_FLAGS_CHAR);
if (characteristic == null) {
Log.e(TAG, "char not found!");
return false;
}
try {
// Store intended flag value in SharedPreferences, then read the current one.
Editor fEditor = sPrefs.edit();
fEditor.putInt(calibrationSetFlagToSendKey, flag);
fEditor.commit();
mConnectedGatt.readCharacteristic(characteristic);
// Catch response in onCharacteristicRead() callback.
return true;
}
catch (NullPointerException e) {
Log.w("readCharacteristic", "The characteristic could not be read.");
return false;
}
}
onCharacteristicRead() is called, which only calls a Handler to modify and broadcast the result back to MainActivity. trueFlagValue is a byte that is stored globally within the Service (bad practise I know, but I intend to modify this later.)
case MSG_SEND_FLAG:
characteristic = (BluetoothGattCharacteristic) msg.obj;
if (characteristic.getValue() == null) {
Log.w(TAG, "Error obtaining current flags");
return;
}
int recentReadDeviceFlag = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
int initialFlag = sPrefs.getInt(calibrationSetFlagToSendKey, 0);
if (initialFlag == 0) Log.e("Write Debug", "Flag to send is apparently 0");
/*... Arithmetic modifying flag integer value...*/
//Desired value is the OR'd value of the value read and value desired
trueFlagValue = (byte) ((byte) initialFlag | (byte) recentReadDeviceFlag);
Log.d("Read Debug", "OR'd value is " + trueFlagValue +", sending broadcast");
Intent fIntent = new Intent("com.example.appwithble.SEND_FLAGS");
sendBroadcast(fIntent);
break;
The BroadcastReceiver in MainActivity then calls a method in my Service, verifying an asking for a writeCharacteristic(), as was done for readCharacteristic(). Accesses the trueFlagValue set earlier.
else if("com.example.appwithble.SEND_FLAGS".equals(intent.getAction())) {
BtService.performWriteFlag();
}
// ...
public boolean performWriteFlag () {
/*... check Gatt is available and that Service exists...*/
BluetoothGattCharacteristic characteristic = Service.getCharacteristic(SEND_FLAGS_CHAR);
if (characteristic == null) {
Log.e(TAG, "char not found!");
return false;
}
byte[] byteValue = new byte[1];
byteValue[0] = trueFlagValue;
characteristic.setValue(byteValue);
boolean status = mConnectedGatt.writeCharacteristic(characteristic);
return status;
}
My onCharacteristicWrite() callback should then be called, and currently contains only a line of code to log a message. In reality, this is only ever called sometimes, as the app is closed or reopened. The value stored in my peripheral's characteristic never changes.
I am not an expert, but in my BLE app
I first connect to peripheral
then pair to it (by calling createBond() method),
then discover services,
then discover characteristics (and store them in app variables) and
finally write values to characteristics (by using the app variables)
Have you tried that?
Such a rookie mistake.
As I've not experimented with writing data before I assumed something was wrong with the writing process or the device. After looking at the kinds of data coming from by readCharacteristic() and by asking around about the device I am sending to, I started to realise that perhaps the data I am sending is in the wrong format.
It turns out that even though only four different flags need to be sent through (could be done with two bits), the characteristic needs to be formatted at two bytes for my particular device, not one. So if I had made my byte array something like this, it would've worked.
byte[] byteValue = new byte[2];
byteValue[0] = trueFlagValue;
byteValue[1] = (byte)0;
characteristic.setValue(byteValue);
I expect if anyone else had an odd problem where the write is initiated but not completed, then it's likely an issue like this - at least, something in the device is rejecting the write.

Categories

Resources