I have the BleFindMeClient working on the HTC 1X+ in conjunction with the TI Mini Keyfob. I'm trying to extend it slightly to read the battery level (not register for battery updates). I can do it from the BTool, but my Android prog fails, and I get the debug message:
getCharacteristic - Service data not found
What does this mean? And where would I find out what this and other error messages mean?
Clearly I can write characteristics because I can set the alarm. But there is something rather basic that I have not grasped about reading characteristics, but I can't find example code.
Could someone throw me a better code fragment please, or spot something dumb in the following?
public class BatteryProfileClient extends BleClientProfile {
private static String TAG = "BatteryProfileClient";
static public BleGattID myUuid = new BleGattID("0000180f-0000-1000-8000-00805f9b34fb");
private static final BleGattID BATTERY_CHARACTERISTIC = new BleGattID("00002a19-0000-1000-8000-00805f9b34fb");
private BatteryServiceClient mBatteryServiceClient = new BatteryServiceClient();
public BatteryProfileClient(Context context) {
super(context, myUuid);
Log.d(TAG, "Instantiated");
ArrayList<BleClientService> services = new ArrayList<BleClientService>();
services.add(mBatteryServiceClient);
init(services, null);
}
public void batt(BluetoothDevice device) {
BleCharacteristic battLevelCharacteristic = mBatteryServiceClient.getCharacteristic(device, BATTERY_CHARACTERISTIC);
byte b[] = battLevelCharacteristic.getValue();
Log.d(TAG, "battery " + b);
}
}
Dont know if im late, but that should solve your problem -
public int getbattery(BluetoothGatt mBluetoothGatt) {
BluetoothGattService batteryService = mBluetoothGatt
.getService(BATTERY_SERVICE_UUID);
if (batteryService == null) {
Log.d(TAG, "Battery service not found!");
return 0;
}
BluetoothGattCharacteristic batteryLevel = batteryService
.getCharacteristic(BATTERY_LEVEL_UUID);
if (batteryLevel == null) {
Log.d(TAG, "Battery level not found!");
return 0;
}
mBluetoothGatt.readCharacteristic(batteryLevel);
return batteryLevel.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT8, 0);
}
Visit https://stackoverflow.com/questions/19539535/how-to-get-the-battery-level-after-connect-to-the-ble-device
same problem
use BluetoothGattCallback
for callBack and read Battery Value
Related
I am struggling on how to capture the audio stream from connected USB microphone. I have tried to use the MediaCapture with MediaRecorder.AudioSource.MIC as source which worked but recording quality isn't quite usable for me and you can't be sure if the audio source is really USB or the built in device microphone. What I need is to use the USB audio feature but so far I am unable to make any progress.
To use third-party libraries is overkill for me since I need to only receive the stream of audio data from the microphone, the rest of the processing is already done and working, only the audio source is the issue.
What I need to do is:
Check if there is USB microphone connected to the device.
Find out what characteristics this device has (supported sampling rates, channels etc.)
Record audio data
What I've done so far:
Generate a list of connected USB device which class is UsbConstants.USB_CLASS_AUDIO
private static final String ACTION_USB_PERMISSION = PACKAGE_NAME + ".USB_PERMISSION";
private UsbManager mUsbManAndroid;
private Map<String, UsbDevice> mAndroidDeviceMap;
private PendingIntent mPermissionIntent;
private ArrayList<UsbDeviceListItem> getUSBDevicesList() {
// Enumerate USB devices
mAndroidDeviceMap = mUsbManAndroid.getDeviceList();
ArrayList<UsbDeviceListItem> usbDevicesList = new ArrayList<>();
for (String key : mAndroidDeviceMap.keySet()) {
UsbDevice device = mAndroidDeviceMap.get(key);
// Check the device class
if (device.getDeviceClass() == UsbConstants.USB_CLASS_AUDIO) {
usbDevicesList.add(usbDeviceToListItem(key, device));
} else if (device.getDeviceClass() == UsbConstants.USB_CLASS_PER_INTERFACE) {
UsbInterface interface;
for (int i = 0; i < device.getInterfaceCount(); i++) {
// Check if at least one interface is audio
interface = device.getInterface(i);
if (interface != null && interface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO) {
usbDevicesList.add(usbDeviceToSysBusUsbDevice(key, device));
break;
}
}
}
}
/////////////////////////////////////////////////////////
// Here goes some code to identify the device using
// linux shell commands if device SDK version is older
// than 21 (Lollipop). In older versions of Android
// we can't get device's Vendor and Device names using
// Android API, we need to use some linux shell commands.
/////////////////////////////////////////////////////////
return usbDevicesList;
}
Request permission for selected usb device from the list:
mUsbDeviceList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
UsbDeviceListItem usbDeviceItem = (UsbDeviceListItem) mUsbDeviceList.getItemAtPosition(i);
UsbDevice device = mAndroidDeviceMap.get(usbDeviceItem.getDevicePath());
manager.requestPermission(device, mPermissionIntent);
}
});
Permission broadcast receiver:
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if(device != null){
streamFromUsbDevice(device)
}
}
else {
Toast.makeText(SensorActivity.this, "Permission denied for device " + device,
Toast.LENGTH_SHORT).show();
}
}
}
}
};
Sample method for reading data from the USB device
private void streamFromUSBDevice(UsbDevice device) {
UsbEndpoint endpoint;
UsbInterface usbInterface;
////////////////////////////////////////////////////////////////
// Here was code for finding the first audio interface with its
// endpoint. But because I failed to make it work I was manually
// getting them by index.
////////////////////////////////////////////////////////////////
usbInterface = device.getInterface(2);
endpoint = usbInterface.getEndpoint(0);
if (endpoint == null) {
Log.i(TAG, getString(R.string.endpoint_not_found));
notifyUser(R.string.endpoint_not_found);
return;
}
Log.i(TAG, R.string.connecting_to_usb_device);
notifyUser(R.string.connecting_to_usb_device);
UsbDeviceConnection connection = manager.openDevice(device);
connection.claimInterface(usbInterface, true);
while (true) {
if (!isRecording) {
// Close the connection to the usb device
connection.close();
notifyUser(R.string.status_idle);
break;
}
UsbRequest request = new UsbRequest();
request.initialize(connection, endpoint);
byte[] buffer = new byte[endpoint.getMaxPacketSize()];
final ByteBuffer buf = ByteBuffer.wrap(buffer);
Log.i(TAG, "Requesting queue...");
if (!request.queue(buf, buffer.length)) {
Log.e(TAG, getString(R.string.error_queue_request)));
notifyUser(R.string.error_queue_request);
isRecording = false;
break;
}
Log.i(TAG, "Requesting response...");
final UsbRequest response = connection.requestWait();
if (response == null) {
Log.e(TAG, "Null response!");
notifyUser(R.string.null_response);
isRecording = false;
break;
}
final int nRead = buf.position();
if (nRead > 0) {
Log.i(TAG, "Streaming " + nRead + " bytes to UI");
Bundle args = new Bundle();
args.putShortArray(ARG_RECORDED_DATA, byte2short(buffer));
sendMessageToUI(SHOW_AUDIO_DATA_PACKET, args, null);
} else {
Log.e(TAG, "No data in buffer!");
notifyUser(R.string_empty_buffer);
isRecording = false;
break;
}
}
}
What I am getting from this is that request.queue() is always returning false.
I have also attempted to use the connection.bulkTransfer(endpoint, buffer, buffer.length, 0); method but the result is always -1.
If someone was in similar situation please help.
P.S. The error I am receiving in the log is: UsbRequestJNI: request is closed in native_queue.
I saw that Android system has a bad behavior with Wifi roaming.
We have a Wifi centralized network with many AP with a signle SSID.
The Adroid Phones wont roams seamlessly.
An Android Phone tries to stay connected to an AP until the signal reaches zero even if there are others AP (with the same SSID) with a good signal!
When the signal is zero, finally it performs an assosiation to another AP (with a good signal). But with this behavior the phone loses all the TCP Connections!
For example:
the phone is connected in WiFi to AP1
the phone moves in the building and now hears two signals from AP1 and from AP2.
When the signal form AP2 is stronger than the signal from AP1, i want that the phone do a reassosiation (not an assosiation) to AP2.
The idea is:
Perform a WifiManager.startScan()
Get the results WifiManager.getScanResults()
Find the best AP in the results
Perform a reassosiation to the best AP
Repeat every 30 seconds.
I talk about reassosiation because i don't want that the phone loses the TCP Connections.
There is a way to do this ?
Thank you,
Salvo
You cannot do this as you describe. A client cannot determine the state of the TCP connection on it's own. Your network must also move the communication channel from one AP to another. This can be done with the right network controllers.
Also, you should look at IEEE 802.11k -
https://en.wikipedia.org/wiki/IEEE_802.11k-2008
Add below permissions;
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
Register for below intent;
private WifiBroadcastReceiver wifiBroadcastReceiver = new WifiBroadcastReceiver();
Then in routine;
registerReceiver(wifiBroadcastReceiver, new IntentFilter("android.net.wifi.SCAN_RESULTS"));
Use the below class to change the reassociation;
public class WifiBroadcastReceiver extends BroadcastReceiver {
private WiFiManager manager = null;//set the value in constructor
private WifiConfiguration connectedConfiguration = null;//set the value in constructor
private int connectedNetId;
private void updateConnectedConfiguration(String ssid) {
configs = manager.getConfiguredNetworks();
int nid = 0;
for (WifiConfiguration cnf : configs) {
if (cnf.SSID.substring(1, cnf.SSID.length() - 1).equals(ssid)) {
connectedConfiguration = cnf;
connectedNetId = nid;
}
nid++;
}
}
public void onReceive(Context c, Intent intent) {
List<ScanResult> results = manager.getScanResults();
WifiInfo info = manager.getConnectionInfo();
ScanResult stronger = null;
for (ScanResult scanResult : results) {
try {
if (scanResult.SSID.equals(info.getSSID())) {
if (stronger == null) {
if (WifiManager.compareSignalLevel(info.getRssi() + 5, scanResult.level) < 0) {
stronger = scanResult;
}
} else if (WifiManager.compareSignalLevel(stronger.level, scanResult.level) < 0) {
stronger = scanResult;
}
}
} catch (Exception e) {
}
}
if (stronger != null && !stronger.BSSID.equals(info.getBSSID())) {
updateConnectedConfiguration(info.getSSID());
if (connectedConfiguration != null) {
connectedConfiguration.BSSID = stronger.BSSID;
manager.updateNetwork(connectedConfiguration);
manager.saveConfiguration();
manager.enableNetwork(connectedNetId, true);
manager.reassociate();
info = manager.getConnectionInfo();
//showNotification("\nConnecting " + stronger.SSID, stronger.BSSID + " " + stronger.level + "dBm");
}
}
}
}
I'm trying to connect programmatically my device to for example on my Headsets... I had KitKat version and all worked perfect (Bluetooth always was connecting without problems autommatically) but since I've updated to Lolipop it doesn't. I'd like to know if there is any way to connect any paired device of my Android phone to Bluetooth when it turns on.
Since now I've this code (gets the Device name and Device Address) because I thought with it I could connect doing something like device.connect(MAC-Address); but it didn't work...
BluetoothAdapter bluetoothAdapter
= BluetoothAdapter.getDefaultAdapter();
Set < BluetoothDevice > pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device: pairedDevices) {
mDeviceName.add(device.getName());
mDeviceMAC.add(device.getAddress());
}
}
bluetoothClass.setDeviceName(mDeviceName);
bluetoothClass.setDeviceMac(mDeviceMAC);
Question
On my MotoG (KitKat) if I turn my Bluetooth it connects autommatically to device (if it's near and paired ofc...) but on my LG G3 I must go to Configuration/Bluetooth/Paired devices/ and there tap the device to connect... and I want to avoid this... should be possible?
I would like to know if there is any possibility to connect to specific Bluetooth just adding the Device name or Device MAC... More or less like Android does when I click on my device to connect it connects autommatically... I just want to get that CLICK event.
I know that Android should connect autommatically to a paired device but there's any exceptions that doesn not ... the only way to pair it it's doing the click... that's why I'm wondering if it's there a way to do it...
I've read and tested kcoppock answer but it still don't work ..
Any suggestion?
EDIT
The main thing that I wanted to do is to connect my Bluetooth autommatically but since I've read on Hey you answer... I figured it out and I know it's an Android bug, so the thing that I would like to do is select the paired devices and then click on the device that I want to connect (Without doing any Intent) and connect it, instead to go Configuration/Bluetooth/....
Btw I've read any answers on StackOverflow and I found something with Sockets are they used to connect Bluetooth?Could be it a solution?
Edit to answer latest question
You can avoid using an intent to search for paired devices. When connecting to a device that is not paired, a notification will pop up asking to pair the devices. Once paired this message should not show again for these devices, the connection should be automatic (according to how you have written your program).
I use an intent to enable bluetooth, and to make my device discoverable, I then set up my code to connect, and press a button to connect. In your case, you will need to ensure your accessories are discoverable also. In my case I use a unique UUID, and both devices must recognise this to connect. This can only be used if you are programming both devices, whether both are android or one android and one other device type.
Try this, and see if it solves your problem.
This answer is to the original question before it was edited to be another question.
I've edited my answer for clarity as I can see from the comments it is misleading. Your question has two parts.
On my MotoG (KitKat) if I turn my Bluetooth it connects autommatically
to device (if it's near and paired ofc...) but on my LG G3 I must go
to Configuration/Bluetooth/Paired devices/ and there tap the device to
connect... and I want to avoid this... should be possible?
This is less of a programming issue and more of a platform issue.
There is a well documented bug in Android 5.0 with Bluetooth not automatically connecting and many other BT issues. These issues continue with all the updates on 5.0. versions and is not fixed until the 5.1. upgrade.
http://www.digitaltrends.com/mobile/android-lollipop-problems/11/
http://forums.androidcentral.com/lg-g3/473064-bluetooth-streaming-choppy-lg-3-lollipop.html
First port of call is to update to 5.1
These issues have been addressed in the Lollipop update 5.1
http://www.reddit.com/r/Android/comments/306m3y/lollipop_51_bluetooth/
Edit:
I don't believe this is going to fix your problem of the automatic pairing, you wanted to know how to use BTGatt.
I've seen if I type device. to check what can I do it let me
connectGatt() means /.../
But I can't figure it out how to do this...
To use BluetoothGatt
https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html
This class provides Bluetooth GATT functionality to enable
communication with Bluetooth Smart or Smart Ready devices.
/.../
GATT capable devices can be discovered using the Bluetooth device
discovery or BLE scan process.
https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html
Here is a great example of how to use BluetoothGatt (it uses hear rate):
https://github.com/googlesamples/android-BluetoothLeGatt/blob/master/Application/src/main/java/com/example/android/bluetoothlegatt/BluetoothLeService.java
I have reproduced some of the code here, in case the link dies.
It basically follows similar lines to a regular bluetooth connection. You need to discover and find supported devices.
Monitor state, etc.
These are the two most pertinent features to gatt.
The callback:
// Implements callback methods for GATT events that the app cares about. For example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
broadcastUpdate(intentAction);
Log.i(TAG, "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
The broadcast:
private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
// This is special handling for the Heart Rate Measurement profile. Data parsing is
// carried out as per profile specifications:
// http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d(TAG, "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d(TAG, "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
} else {
// For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
}
}
sendBroadcast(intent);
}
This question also has some relevant code that may help cut it down when learning:
BLuetooth Gatt Callback not working with new API for Lollipop
Now here's the rub. Are your devices bluetooth smart or smart ready?
This link gives a great list of smart devices. You will also find out when you implement your program.
http://www.bluetooth.com/Pages/Bluetooth-Smart-Devices-List.aspx
This is how i made this work using Java Reflection and BluetoothProfile:
Attributes:
private boolean mIsConnect = true;
private BluetoothDevice mDevice;
private BluetoothA2dp mBluetoothA2DP;
private BluetoothHeadset mBluetoothHeadset;
private BluetoothHealth mBluetoothHealth;
Call:
mBluetoothAdapter.getProfileProxy(getApplicationContext() , mProfileListener, BluetoothProfile.A2DP);
mBluetoothAdapter.getProfileProxy(getApplicationContext() , mProfileListener, BluetoothProfile.HEADSET);
mBluetoothAdapter.getProfileProxy(getApplicationContext() , mProfileListener, BluetoothProfile.HEALTH);
Listener:
private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.A2DP) {
mBluetoothA2DP = (BluetoothA2dp) proxy;
try {
if (mIsConnect) {
Method connect = BluetoothA2dp.class.getDeclaredMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(mBluetoothA2DP, mDevice);
} else {
Method disconnect = BluetoothA2dp.class.getDeclaredMethod("disconnect", BluetoothDevice.class);
disconnect.setAccessible(true);
disconnect.invoke(mBluetoothA2DP, mDevice);
}
}catch (Exception e){
} finally {
}
} else if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
try {
if (mIsConnect) {
Method connect = BluetoothHeadset.class.getDeclaredMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(mBluetoothHeadset, mDevice);
} else {
Method disconnect = BluetoothHeadset.class.getDeclaredMethod("disconnect", BluetoothDevice.class);
disconnect.setAccessible(true);
disconnect.invoke(mBluetoothHeadset, mDevice);
}
}catch (Exception e){
} finally {
}
} else if (profile == BluetoothProfile.HEALTH) {
mBluetoothHealth = (BluetoothHealth) proxy;
try {
if (mIsConnect) {
Method connect = BluetoothHealth.class.getDeclaredMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(mBluetoothHealth, mDevice);
} else {
Method disconnect = BluetoothHealth.class.getDeclaredMethod("disconnect", BluetoothDevice.class);
disconnect.setAccessible(true);
disconnect.invoke(mBluetoothHealth, mDevice);
}
}catch (Exception e){
} finally {
}
}
}
public void onServiceDisconnected(int profile) {
}
};
I hope this helps anyone trying to connect to Bluetooth Audio devices and headsets.
I'm trying to write an Android app that mimics functionality already present in an iOS app I wrote. I am interfacing with 2 different BLE devices:
Blood Pressure Cuff
Weight Scale
On iOS, I have both devices working well and reporting data. On Android, I can't get it to work. After hours of research and testing, I think the basic issue I'm trying to solve is this:
On iOS, I call the following code to enable the BLE device to notify my iOS device when it has data to report:
#pragma mark - CBPeripheralDelegate Protocol methods
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
for (CBCharacteristic *characteristic in [service characteristics]) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}
That's it. The notes for this method in iOS say the following:
If the specified characteristic is configured to allow both notifications and indications, calling this method enables notifications only.
Based on that (and the fact that it works in iOS), I'm figuring that the configuration descriptor for the characteristic for which I want notifications should be configured like this:
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
With that in mind, my BLEDevice class looks like this:
public abstract class BLEDevice {
protected BluetoothAdapter.LeScanCallback mLeScanCallback;
protected BluetoothGattCallback mBluetoothGattCallback;
protected byte[] mBytes;
protected Context mContext;
protected GotReadingCallback mGotReadingCallback;
protected String mDeviceName;
public final static UUID UUID_WEIGHT_SCALE_SERVICE
= UUID.fromString(GattAttributes.WEIGHT_SCALE_SERVICE);
public final static UUID UUID_WEIGHT_SCALE_READING_CHARACTERISTIC
= UUID.fromString(GattAttributes.WEIGHT_SCALE_READING_CHARACTERISTIC);
public final static UUID UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC
= UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
public final static UUID UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR
= UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);
abstract void processReading();
interface GotReadingCallback {
void gotReading(Object reading);
}
public BLEDevice(Context context, String deviceName, GotReadingCallback gotReadingCallback) {
mContext = context;
BluetoothManager btManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
final BluetoothAdapter btAdapter = btManager.getAdapter();
if (btAdapter != null && !btAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
mContext.startActivity(enableIntent);
}
mDeviceName = deviceName;
mBluetoothGattCallback = new BluetoothGattCallback() {
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
byte[] data = characteristic.getValue();
mBytes = data;
Log.d("BluetoothGattCallback.onCharacteristicChanged", "data: " + data.toString());
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
// this will get called when a device connects or disconnects
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
if (mBytes != null) {
processReading();
}
}
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.d("onDescriptorWrite", "descriptor: " + descriptor.getUuid() + ". characteristic: " + descriptor.getCharacteristic().getUuid() + ". status: " + status);
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
// this will get called after the client initiates a BluetoothGatt.discoverServices() call
BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
if (service != null) {
BluetoothGattCharacteristic characteristic;
characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
if (characteristic != null) {
gatt.setCharacteristicNotification(characteristic, true);
}
characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
if (characteristic != null) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
}
}
};
mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
Log.d("LeScanCallback", device.toString());
if (device.getName().contains("{Device Name}")) {
BluetoothGatt bluetoothGatt = device.connectGatt(mContext, false, mBluetoothGattCallback);
btAdapter.stopLeScan(mLeScanCallback);
}
}
};
btAdapter.startLeScan(mLeScanCallback);
}
}
NOTE: It might be important to know that these 2 devices function in the following way:
The BLE device is turned on an a measurement is initiated on the device.
Once the measurement has been taken, the BLE device attempts to initiate a BLE connection.
Once the BLE connection is made, the device pretty much immediately sends the data, sometimes sending a couple of data packets. (If previous data measurements haven't been successfully sent over BLE, it keeps them in memory and sends all of them, so I only really care about the final data packet.)
Once the final data packet is sent, the BLE device disconnects rapidly.
If the BLE device fails to send data (as is currently happening on the Android app), the BLE device disconnects pretty rapidly.
In my LogCat, I see a lot of output that's exactly like I'd expect.
I see a list of services like I expect, including the data service I want.
I see a list of characteristics like I expect, including the data characteristic I want.
I see a list of descriptors like I expect, including the "configuration" (0x2902) descriptor.
The most recent failure I'm experiencing is a status of "128" being reported in onCharacteristicWrite. The comments to question #3 (below) seem to indicate this is a resource issue.
I've looked at the following questions:
Android BLE onCharacteristicChanged not called
Android BLE, read and write characteristics
Android 4.3 onDescriptorWrite returns status 128
Here's why they don't give me what I need:
This question's answer was not to read the descriptor's value. I'm not doing that, so that can't be what's getting in the way.
This is basically an overview of the various methods that are available, which I think I now understand. The big key in this question/answer is not to write multiple times to different descriptors, but I'm also not doing that. I only care about the one characteristic.
This question/answer seems to be related to BLE resource limitations, but I don't think this applies. I'm only connecting this one device and I'm trying to do a very, very simple data transfer. I don't think I'm hitting resource ceilings.
I've tried a bunch of examples and tutorials, including Google's Android sample code. None of them seem to enable the BLE device to notify my Android device of data updates. It's obviously not the device, since the iOS version works. So, what is the iOS code doing in the background to get the notifications to work and what code on the Android side will mimic that functionality?
EDIT/UPDATE
Based on #yonran's comments, I updated my code by changing the onServicesDiscovered implementation to this:
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
// this will get called after the client initiates a BluetoothGatt.discoverServices() call
BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
if (service != null) {
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
if (characteristic != null) {
if (gatt.setCharacteristicNotification(characteristic, true) == true) {
Log.d("gatt.setCharacteristicNotification", "SUCCESS!");
} else {
Log.d("gatt.setCharacteristicNotification", "FAILURE!");
}
BluetoothGattDescriptor descriptor = characteristic.getDescriptors().get(0);
if (0 != (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)) {
// It's an indicate characteristic
Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is INDICATE");
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
} else {
// It's a notify characteristic
Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is NOTIFY");
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
}
}
}
That does seem to have changed some things a little bit. Here's the current Logcat, following that code change:
D/BluetoothGatt﹕ setCharacteristicNotification() - uuid: <UUID> enable: true
D/gatt.setCharacteristicNotification﹕ SUCCESS!
D/onServicesDiscovered﹕ Characteristic (<UUID>) is INDICATE
D/BluetoothGatt﹕ writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb
D/BluetoothGatt﹕ onDescriptorWrite() - Device=D0:5F:B8:01:6C:9E UUID=<UUID>
D/onDescriptorWrite﹕ descriptor: 00002902-0000-1000-8000-00805f9b34fb. characteristic: <UUID>. status: 0
D/BluetoothGatt﹕ onClientConnectionState() - status=0 clientIf=6 device=D0:5F:B8:01:6C:9E
So, it would appear that I'm now setting everything up properly (since setCharacteristicNotification returns true and the onDescriptorWrite status is 0). However, onCharacteristicChanged still never fires.
I've been able to successfully catch onCharacteristicChanged() with multiple services and characteristics by:
Writing descriptor values in the broadcastReceiver() in the main loop after service discovery is finished.
private final BroadcastReceiver UARTStatusChangeReceiver = new BroadcastReceiver() {
//more code...
if (action.equals(uartservice.ACTION_GATT_SERVICES_DISCOVERED)) {
mService.enableTXNotification();
}
and
By adding a delay between descriptor value settings
public void enableTXNotification(){
/*
if (mBluetoothGatt == null) {
showMessage("mBluetoothGatt null" + mBluetoothGatt);
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
*/
/**
* Enable Notifications for the IO service and characteristic
*
*/
BluetoothGattService IOService = mBluetoothGatt.getService(IO_SERVICE_UUID);
if (IOService == null) {
showMessage("IO service not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_IO);
return;
}
BluetoothGattCharacteristic IOChar = IOService.getCharacteristic(IO_CHAR_UUID);
if (IOChar == null) {
showMessage("IO charateristic not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_IO);
return;
}
mBluetoothGatt.setCharacteristicNotification(IOChar,true);
BluetoothGattDescriptor descriptorIO = IOChar.getDescriptor(CCCD);
descriptorIO.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptorIO);
/**
* For some reason android (or the device) can't handle
* writing one descriptor after another properly. Without
* the delay only the first characteristic can be caught in
* onCharacteristicChanged() method.
*/
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* Enable Indications for the RXTX service and characteristic
*/
BluetoothGattService RxService = mBluetoothGatt.getService(RXTX_SERVICE_UUID);
if (RxService == null) {
showMessage("Rx service not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
BluetoothGattCharacteristic RxChar = RxService.getCharacteristic(RXTX_CHAR_UUID);
if (RxChar == null) {
showMessage("Tx charateristic not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
mBluetoothGatt.setCharacteristicNotification(RxChar,true);
BluetoothGattDescriptor descriptor = RxChar.getDescriptor(CCCD);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE );
mBluetoothGatt.writeDescriptor(descriptor);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* Enable Notifications for the Battery service and Characteristic?
*/
BluetoothGattService batteryService = mBluetoothGatt.getService(BATTERY_SERVICE_UUID);
if (batteryService == null) {
showMessage("Battery service not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_BATTERY);
return;
}
BluetoothGattCharacteristic batteryChar = batteryService.getCharacteristic(BATTERY_CHAR_UUID);
if (batteryChar == null) {
showMessage("Battery charateristic not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_BATTERY);
return;
}
}
I was facing the same problem.
that's because when the device is sending the indicate value, your application is charged in another process and that's why you never get the indicate value which make the onCharacteristicChanged never fires.
to resolve your problem try to put all traitement in a service. and just call functions from your activity.
I want to send a MMS using the stock MMS source. Before more explanation, I want to say that it does work on some android versions but 4.0.3 and 4.0.4.
In my service, I ask the device to enable the MMS network feature using the following code:
createWakeLock();
int result = mConnMgr.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, PhoneEx.FEATURE_ENABLE_MMS);
Log.v(TAG, "beginMmsConnectivity: result=" + result);
switch (result) {
case PhoneEx.APN_ALREADY_ACTIVE:
case PhoneEx.APN_REQUEST_STARTED:
acquireWakeLock();
return result;
}
throw new IOException("Cannot establish MMS connectivity");
On some devices (Xperia T running 4.0.3), it throws an exception because result equals PhoneEx.APN_TYPE_NOT_AVAILABLE. The MMS is enabled in my phone settings and I can send one with the stock mms app.
On other devices (HTC Desire S running 4.0.4), the problem is located a bit further, in the ConnectivityBroadcastReceiver. Here is my code:
private class ConnectivityBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
String action = intent.getAction();
mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
mOtherNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
networkAttempt++;
if (networkAttempt < APN_RETRY_ATTEMPTS) {
// Check availability of the mobile network.
if ((mNetworkInfo == null) || (mNetworkInfo.getType() != ConnectivityManager.TYPE_MOBILE_MMS)) {
// ERROR is located here, it returns TYPE_MOBILE :s
Log.v(TAG, " type is not TYPE_MOBILE_MMS, bail");
return;
}
}
...
As you can see in the comment, mNetworkInfo.getType() returns TYPE_MOBILE but I expect TYPE_MOBILE_MMS.
So, my question is the following: Did I make something wrong ? Or, Is there another way to do that ?
Ps: It works on devices running Android 2.3 to 3.2 and 4.1 and above.
It appears on some devices, with some providers, the TYPE_MOBILE isn't the default MMS gateway, you have to use another one. Here is the solution I found that solved my problem.
I hope this could help someone else.
// Take a wake lock so we don't fall asleep before the message is downloaded.
createWakeLock();
// Let's try every type
int result = -1;
int[] apnTypes = new int[] {ConnectivityManager.TYPE_MOBILE, ConnectivityManager.TYPE_MOBILE_MMS, ConnectivityManager.TYPE_MOBILE_DUN, ConnectivityManager.TYPE_MOBILE_HIPRI, ConnectivityManager.TYPE_MOBILE_SUPL};
for (int i=0; i<apnTypes.length; i++)
{
result = mConnMgr.startUsingNetworkFeature(apnTypes[i], PhoneEx.FEATURE_ENABLE_MMS);
Log.v(TAG, "beginMmsConnectivity: result=" + result);
switch (result)
{
case PhoneEx.APN_ALREADY_ACTIVE:
case PhoneEx.APN_REQUEST_STARTED:
acquireWakeLock();
return result;
}
}
// None found
throw new IOException("Cannot establish MMS connectivity");