I'm trying to pair my pi3 with android things to a BLE device.
Following
https://developer.android.com/things/sdk/apis/bluetooth.html#device-pairing
I set a pairingCallback:
mBluetoothConnectionManager = BluetoothConnectionManager.getInstance();
mBluetoothConnectionManager.registerPairingCallback(mBluetoothPairingCallback);
this is my pairing callback:
private BluetoothPairingCallback mBluetoothPairingCallback = new BluetoothPairingCallback() {
#Override
public void onPairingInitiated(BluetoothDevice bluetoothDevice,
PairingParams pairingParams) {
// Handle incoming pairing request or confirmation of outgoing pairing request
Log.d(TAG,"onPairingInitiated");
handlePairingRequest(bluetoothDevice, pairingParams);
}
#Override
public void onPaired(BluetoothDevice bluetoothDevice) {
// Device pairing complete
Log.d(TAG,"onPaired");
}
#Override
public void onUnpaired(BluetoothDevice bluetoothDevice) {
// Device unpaired
Log.d(TAG,"onUnpaired");
}
#Override
public void onPairingError(BluetoothDevice bluetoothDevice,
PairingError pairingError) {
// Something went wrong!
Log.d(TAG,"onPairingError "+getPairingErrorVerbose(pairingError.getErrorCode()));
}
};
and in handlePairingRequest:
private void handlePairingRequest(final BluetoothDevice bluetoothDevice, PairingParams pairingParams) {
switch (pairingParams.getPairingType()) {
case PairingParams.PAIRING_VARIANT_DISPLAY_PIN:
case PairingParams.PAIRING_VARIANT_DISPLAY_PASSKEY:
// Display the required PIN to the user
Log.d(TAG, "Display Passkey - " + pairingParams.getPairingPin());
break;
case PairingParams.PAIRING_VARIANT_PIN:
case PairingParams.PAIRING_VARIANT_PIN_16_DIGITS:
// Obtain PIN from the user
//String pin = ...;
// Pass the result to complete pairing
//mBluetoothConnectionManager.finishPairing(bluetoothDevice, pin);
break;
case PairingParams.PAIRING_VARIANT_CONSENT:
case PairingParams.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
// Complete the pairing process
mBluetoothConnectionManager.finishPairing(bluetoothDevice);
break;
}
}
I get a PairingParams.PAIRING_VARIANT_CONSENT and proceed with finishPairing.
My issue is that finishPairing changes the status from BOND_BONDING to BOND_BONDED but my device pairing is missing something and the device is not connected as it is in standard android (I have the same app for android mobile and the pairing is ok when the user accepts the pairing in the BluetoothPairingDialog).
It seems that in BluetoothPairingDialog it is performed mDevice.setPairingConfirmation(true);
and its result is different from mBluetoothConnectionManager.finishPairing(bluetoothDevice);
Am I missing something during the pairing?
Thanks a lot.
Related
I'm having issues trying to connect to multiple devices running the same app. The workflow of my app is:
one device calls discover peers (see code A)
once peers are discovered I display an AlertDialog that allows the user to select which peers they want to connect to (see code B)
once the user selects the devices they want to connect to I attempt to loop through the WifiP2pDeviceList and call the connect method on each of the passed in device (I always set the intent of the current device as the group owner) (see code C)
once connection is made I transfer some data....
Issue: In step 3, when I call the connect method, the code connects to the first device without any problems, but when it gets to the second loop iteration to connect to the second or third device that was selected I get a failure with reason code 2 (Busy). Why is this happening? When I only connect to one device it all works fine, only when I attempt to connect to more than one is when I have the problems? What am I doing wrong? I can't find any examples of how to connect to multiple devices...any help is greatly appreciated.
Code A: (discover peers)
private WifiP2pManager mManager;
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
onInitiateDiscovery();
}
#Override
public void onFailure(int reasonCode) {
Toast.makeText(getActivity(), "Discovery Failed: " + getReascodeText(reasonCode), Toast.LENGTH_SHORT).show();
}
});
Code B: (select peers you want to connect to)
public void onPeersAvailable(WifiP2pDeviceList peers) {
final ArrayList<Integer> itemsSelected = new ArrayList<>();
// Out with the old, in with the new.
mPeers.clear();
mPeers.addAll(peers.getDeviceList());
CharSequence[] cs = StringUtils.getDeviceNames(mPeers);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Who do you want to share with?");
builder.setMultiChoiceItems(cs, null, new DialogInterface.OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int selectedItemId, boolean isChecked) {
if (isChecked) {
itemsSelected.add(selectedItemId);
} else if (itemsSelected.contains(selectedItemId)) {
itemsSelected.remove(Integer.valueOf(selectedItemId));
}
}
}).setPositiveButton("Done!", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
WifiP2pDevice wifiP2pDevice = mPeers.get(itemsSelected.get(0));
List<WifiP2pDevice> devices = extractSelectedDevices(itemsSelected,mPeers);
numConnections = devices.size();
connect(devices);
}
});
mPeerSelectionDialog = builder.create();
mPeerSelectionDialog.show();
}
Code C: (connect to selected devices)
public void connect(List<WifiP2pDevice> devices) {
for(WifiP2pDevice device: devices) {
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
config.groupOwnerIntent = 15;
mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
// WiFiDirectBroadcastReceiver will notify us. Ignore for now.
System.out.println("successfully connected!!");
Log.d(MultiImageSelectorFragment.TAG, ">>>>>>>>>>>>>>>>>>>>>!!Successfully Connected!<<<<<<<<<<<");
}
#Override
public void onFailure(int reason) {
Toast.makeText(getActivity(), "Connect failed. Retry.", Toast.LENGTH_SHORT).show();
Log.d(MultiImageSelectorFragment.TAG, ">>>>>>>>>>>>>>>>>>>>>!!Failed connection, rasoncode:"+reason+" !<<<<<<<<<<<");
}
});
}
That's correct, after the connection to the first device it will fail to connect with busy error code. The reason is that the WiFiP2P at your device didn't finish yet the first device connection process (even though you've accepted the connection and it got established, it still takes some time to create the group and release the resources).
To overcome this issue, you can start another thread with some delay (from my tests at least 10-15 seconds) to attempt to connect to the second device.
Goodluck.
Does anyone have a complete working example of how to programmatically pair with a BLE (not Bluetooth Classic) device that uses passkey entry (i.e. a 6-digit PIN) or Numeric Comparison on Android 4.4 or later? By 'programmatically' I mean I tell Android the PIN - the user isn't prompted.
There are many similar questions about this on SO but they are either a) about Bluetooth Classic, b) old (before setPin() and createBond() were public), or c) unanswered.
My understanding is as follows.
You connect to the device and discover its services.
You try to read a 'protected' characteristic.
The device returns an authentication error.
Android somehow initiates pairing and you tell it the PIN.
You can now read the characteristic.
I have created a device using mBed running on the nRF51-DK and given it a single characteristic.
I set up the security parameters like so:
ble.securityManager().init(
true, // Enable bonding (though I don't really need this)
true, // Require MitM protection. I assume you don't get a PIN prompt without this, though I'm not 100% sure.
SecurityManager::IO_CAPS_DISPLAY_ONLY, // This makes it us the Passkey Entry (PIN) pairing method.
"123456"); // Static PIN
And then in the characteristic I used
requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
Now when I try to read it with the Nordic Master Control Panel, I get a pairing request notification like this:
And I can put this PIN in, and then MCP says I'm bonded, and can read the characteristic.
However, in my app I would like to avoid having the user enter the PIN, since I know it already. Does anyone have a complete recent example of how to do this?
Edit: By the way this is the most relevant question I found on SO, but the answer there doesn't seem to work.
I almost have it working. It pairs programmatically but I can't get rid of the "Pairing request" notification. Some answers to this question claim to be able to hide it just after it is shown using the hidden method cancelPairingUserInput() but that doesn't seem to work for me.
Edit: Success!
I eventually resorted to reading the source code of BluetoothPairingRequest and the code that sends the pairing request broadcast and realised I should be intercepting the ACTION_PAIRING_REQUEST. Fortunately it is an ordered intent broadcast so you can intercept it before the system does.
Here's the procedure.
Register to receive BluetoothDevice.ACTION_PAIRING_REQUEST changed broadcast intents. Use a high priority!
Connect to the device.
Discover services.
If you have disconnected by now, it's probably because the bond information is incorrect (e.g. the peripheral purged it). In that case, delete the bond information using a hidden method (seriously Google), and reconnect.
Try to read a characteristic that requires encryption MitM protection.
In the ACTION_PAIRING_REQUEST broadcast receiver, check that the pairing type is BluetoothDevice.PAIRING_VARIANT_PIN and if so, call setPin() and abortBroadcast(). Otherwise you can just let the system handle it, or show an error or whatever.
Here is the code.
/* This implements the BLE connection logic. Things to watch out for:
1. If the bond information is wrong (e.g. it has been deleted on the peripheral) then
discoverServices() will cause a disconnect. You need to delete the bonding information and reconnect.
2. If the user ignores the PIN request, you get the undocumented GATT_AUTH_FAILED code.
*/
public class ConnectActivityLogic extends Fragment
{
// The connection to the device, if we are connected.
private BluetoothGatt mGatt;
// This is used to allow GUI fragments to subscribe to state change notifications.
public static class StateObservable extends Observable
{
private void notifyChanged() {
setChanged();
notifyObservers();
}
};
// When the logic state changes, State.notifyObservers(this) is called.
public final StateObservable State = new StateObservable();
public ConnectActivityLogic()
{
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Tell the framework to try to keep this fragment around
// during a configuration change.
setRetainInstance(true);
// Actually set it in response to ACTION_PAIRING_REQUEST.
final IntentFilter pairingRequestFilter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
pairingRequestFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY - 1);
getActivity().getApplicationContext().registerReceiver(mPairingRequestRecevier, pairingRequestFilter);
// Update the UI.
State.notifyChanged();
// Note that we don't actually need to request permission - all apps get BLUETOOTH and BLUETOOTH_ADMIN permissions.
// LOCATION_COARSE is only used for scanning which I don't need (MAC is hard-coded).
// Connect to the device.
connectGatt();
}
#Override
public void onDestroy()
{
super.onDestroy();
// Disconnect from the device if we're still connected.
disconnectGatt();
// Unregister the broadcast receiver.
getActivity().getApplicationContext().unregisterReceiver(mPairingRequestRecevier);
}
// The state used by the UI to show connection progress.
public ConnectionState getConnectionState()
{
return mState;
}
// Internal state machine.
public enum ConnectionState
{
IDLE,
CONNECT_GATT,
DISCOVER_SERVICES,
READ_CHARACTERISTIC,
FAILED,
SUCCEEDED,
}
private ConnectionState mState = ConnectionState.IDLE;
// When this fragment is created it is given the MAC address and PIN to connect to.
public byte[] macAddress()
{
return getArguments().getByteArray("mac");
}
public int pinCode()
{
return getArguments().getInt("pin", -1);
}
// Start the connection process.
private void connectGatt()
{
// Disconnect if we are already connected.
disconnectGatt();
// Update state.
mState = ConnectionState.CONNECT_GATT;
State.notifyChanged();
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress());
// Connect!
mGatt = device.connectGatt(getActivity(), false, mBleCallback);
}
private void disconnectGatt()
{
if (mGatt != null)
{
mGatt.disconnect();
mGatt.close();
mGatt = null;
}
}
// See https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/stack/include/gatt_api.h
private static final int GATT_ERROR = 0x85;
private static final int GATT_AUTH_FAIL = 0x89;
private android.bluetooth.BluetoothGattCallback mBleCallback = new BluetoothGattCallback()
{
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
super.onConnectionStateChange(gatt, status, newState);
switch (newState)
{
case BluetoothProfile.STATE_CONNECTED:
// Connected to the device. Try to discover services.
if (gatt.discoverServices())
{
// Update state.
mState = ConnectionState.DISCOVER_SERVICES;
State.notifyChanged();
}
else
{
// Couldn't discover services for some reason. Fail.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
// If we try to discover services while bonded it seems to disconnect.
// We need to debond and rebond...
switch (mState)
{
case IDLE:
// Do nothing in this case.
break;
case CONNECT_GATT:
// This can happen if the bond information is incorrect. Delete it and reconnect.
deleteBondInformation(gatt.getDevice());
connectGatt();
break;
case DISCOVER_SERVICES:
// This can also happen if the bond information is incorrect. Delete it and reconnect.
deleteBondInformation(gatt.getDevice());
connectGatt();
break;
case READ_CHARACTERISTIC:
// Disconnected while reading the characteristic. Probably just a link failure.
gatt.close();
mState = ConnectionState.FAILED;
State.notifyChanged();
break;
case FAILED:
case SUCCEEDED:
// Normal disconnection.
break;
}
break;
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
super.onServicesDiscovered(gatt, status);
// Services have been discovered. Now I try to read a characteristic that requires MitM protection.
// This triggers pairing and bonding.
BluetoothGattService nameService = gatt.getService(UUIDs.NAME_SERVICE);
if (nameService == null)
{
// Service not found.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
return;
}
BluetoothGattCharacteristic characteristic = nameService.getCharacteristic(UUIDs.NAME_CHARACTERISTIC);
if (characteristic == null)
{
// Characteristic not found.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
return;
}
// Read the characteristic.
gatt.readCharacteristic(characteristic);
mState = ConnectionState.READ_CHARACTERISTIC;
State.notifyChanged();
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS)
{
// Characteristic read. Check it is the right one.
if (!UUIDs.NAME_CHARACTERISTIC.equals(characteristic.getUuid()))
{
// Read the wrong characteristic. This shouldn't happen.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
return;
}
// Get the name (the characteristic I am reading just contains the device name).
byte[] value = characteristic.getValue();
if (value == null)
{
// Hmm...
}
disconnectGatt();
mState = ConnectionState.SUCCEEDED;
State.notifyChanged();
// Success! Save it to the database or whatever...
}
else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION)
{
// This is where the tricky part comes
if (gatt.getDevice().getBondState() == BluetoothDevice.BOND_NONE)
{
// Bonding required.
// The broadcast receiver should be called.
}
else
{
// ?
}
}
else if (status == GATT_AUTH_FAIL)
{
// This can happen because the user ignored the pairing request notification for too long.
// Or presumably if they put the wrong PIN in.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
}
else if (status == GATT_ERROR)
{
// I thought this happened if the bond information was wrong, but now I'm not sure.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
}
else
{
// That's weird.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
}
}
};
private final BroadcastReceiver mPairingRequestRecevier = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction()))
{
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
if (type == BluetoothDevice.PAIRING_VARIANT_PIN)
{
device.setPin(Util.IntToPasskey(pinCode()));
abortBroadcast();
}
else
{
L.w("Unexpected pairing type: " + type);
}
}
}
};
public static void deleteBondInformation(BluetoothDevice device)
{
try
{
// FFS Google, just unhide the method.
Method m = device.getClass().getMethod("removeBond", (Class[]) null);
m.invoke(device, (Object[]) null);
}
catch (Exception e)
{
L.e(e.getMessage());
}
}
}
I also faced the same problem and after all the research, I figured out the below solution to pair to a BLE without any manual intervention.
(Tested and working!!!)
I am basically looking for a particular Bluetooth device (I know MAC address) and pair with it once found. The first thing to do is to create pair request using a broadcast receiver and handle the request as below.
IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(broadCastReceiver,intentFilter);
You need to write the broadcastReceiver and handle it as below.
String BLE_PIN = "1234"
private BroadcastReceiver broadCastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action))
{
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
bluetoothDevice.setPin(BLE_PIN.getBytes());
Log.e(TAG,"Auto-entering pin: " + BLE_PIN);
bluetoothDevice.createBond();
Log.e(TAG,"pin entered and request sent...");
}
}
};
Voila! You should be able to pair to Bluetooth device without ANY MANUAL INTERVENTION.
Hope this helps :-) Please make it right answer if it works for you.
I am developing an Android Application that connects to a BLE Device and reads the Gatt Services and Gatt Characteristics. I used the BluetoothLeGatt sample project from the Android Development Site as my reference.
So far, I am able to programmatically connect to a device (I took note of my Device's Address to be able to do this) and filter out the specific Gatt Service I want to read and that Services' specific Characteristics by taking note of the UUID of both the Service and the Characteristics. The sample provided by Google also updates whenever there's a message sent from my BLE Device to my Android Application. Overall, I have no problems at this end.
However, upon reading up further on GATT, I found that it is possible to connect to multiple BLE devices (all slaves OR servers - being the ones that send the data) using a single Android Application (as master OR client - as the one who receives said data). So what I tried to do was to have 2 BLE Devices (different Address), took note of their Address, and then my application tries to connect to them once the application sees that those 2 addresses are up and running.
In code, I call this function when I see my 2 BLE Devices:
private void connectToDevice(){
mDeviceName = deviceList.get(currentIndex).getName();
mDeviceAddress = deviceList.get(currentIndex).getAddress();
Log.e(TAG, "connecting to device name = " + mDeviceName);
mBluetoothLeService.connect(mDeviceAddress);
}
Where currentIndex is initially set to zero. Then once I get a successful connection, I do:
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
Log.e(TAG, "connected");
mConnected = true;
if(currentIndex < deviceList.size()-1) currentIndex ++;
connectToDevice();
}
}
};
Where I check if I still have devices to connect to in my deviceList, if so, increment my counter and then connect until I exhaust everything in my list.
However, I seem to have no success at all using this method.
Kindly note that switching connection (round robin) between my devices isn't an option. This will be an issue when I have a lot of devices and it's important to get their messages real time without delays. This said, I have to have a live connection to my devices.
Has anyone tried to connect to multiple BLE Devices in Android? I'm not sure on how to proceed on this.
Indeed it is possible to connect to more than one peripheral from your Android device. However, it will make your code much more complex since you will need to manage each connection and responses.
For each connection you would have to implement a BluetoothGatt with it's callbacks. I tested it many months ago with a dummy test and as I said, it worked well and I was able to connect to different peripherals. However, if you chain many commands there seem to be some overlapping issues described in this thread.
As asked here is the relevant code : (Here the ArrayList contains the founded peripheral devices)
for(int i=0;i< Utility.selectedDeviceList.size();i++) {
Log.d(Utility.TAG,"state"+ Utility.selectedDeviceList.get(i).getmConnectionState());
if (Utility.selectedDeviceList.get(i).getmConnectionState() != Utility.CONNECTED) {
Log.d(Utility.TAG,"Connecting LeSerive::" + Utility.selectedDeviceList.get(i).getAddress());
Utility.mBluetoothLeService.connect(i, Utility.selectedDeviceList.get(i).getAddress());
}
}
This for loop is a part of runnable interface which is called inside a handler having a looper.
public void run() {
Looper.prepare();
Looper mLooper = Looper.myLooper();
Log.d(Utility.TAG,"BLE Thread Started::");
mHandler = new Handler(mLooper) {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Utility.BLE_SYNC:
Log.d(Utility.TAG,"BLE Sync Connecting::");
mHandler.post(SynState);
break;
}
};
Looper.loop();
}
I used this approach because their is lot of communication between peripherals to send and receive the data from them.
This is the connect method which inside a Service :
public boolean connect(int tag,final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(Utility.TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
Utility.selectedDeviceList.get(tag).setmConnectionState(Utility.CONNECTING);
if( Utility.selectedDeviceList.get(tag).getmBluetoothGatt()==null){
Log.w(Utility.TAG, "new connect :: "+ Utility.selectedDeviceList.get(tag).getAddress());
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.w(Utility.TAG, "Device not found. Unable to connect.");
return false;
}
try {
Utility.selectedDeviceList.get(tag).setmBluetoothGatt(device.connectGatt(this, false, mGattCallback));
}
catch (Exception e)
{
e.printStackTrace();
Log.d(Utility.TAG,"ConnectGatt exception caught");
}
}
return true;
}
This is the mGattCallBack :
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.d(Utility.TAG, "onServicesDiscovered");
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) {
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.d(Utility.TAG,">>onCharacteristicWrite");
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {
}
};
Hope it clears few things for you
It is possible to connect to multiple devices at a time. in my experience it works pretty stable and the number of devices you can connect to (stable) depends on your hardware. I found out that best practise (for me) was to create one separate service for the scanning stuff and one service for each Bluetoothconnection. it's important not to use bound services because the termination of a connection is not stible when binding to it.
With this pattern you can control your connection easily. To transport data out of your service you can use a broadcastreceiver, for example if you want to display the data in your main activity. Termination of the connection is pretty important so stop the service and in onDestroy call
mConnectedGatt.disconnect();
ble_device=null;
For the Scanning part I've used a List of Strings where I saved all the mac Adresses I want to find. When i found one device I deleted it from the list and if the list is empty it stopped the scanner service. To transmit my found device I used a broadcastreceiver and sent it to my main Activity. There I transmitted it to the right service.
Hope this helps
I am trying to connect two device to each other by wifi direct. I set the group owner intent to 15, like this :
config.groupOwnerIntent = 15;
but it does not work at all. Also I try to remove all of previous groups that are saved in devices in case of having impact on GO selection.
All the time the other device is getting to be GO. Do you have any idea what could be the problem?
Basically if you would want other device to be Group owner, then use the CreateGroup function to make a group on that device.
The problem is that config.groupOwnerIntent = 15; works with same brands (for example, if both your devices are Samsungs). With different devices sometimes this bug appears.
Also it depends on your connection:
manager.connect(channel, config, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
// WiFiDirectBroadcastReceiver will notify us. Ignore for now.
}
#Override
public void onFailure(int reason) {
Toast.makeText(context, "Connect failed. Retry.",
Toast.LENGTH_SHORT).show();
}
});
or
manager.createGroup(channel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
// Device is ready to accept incoming connections from peers.
}
#Override
public void onFailure(int reason) {
Toast.makeText(WiFiDirectActivity.this, "P2P group creation failed. Retry.",
Toast.LENGTH_SHORT).show();
}
});
Your code doesn't help if you use the second one.
Having same problem with Samsung and Xiaomi now, always same group owner. If a test on both Samsungs everything is okay.
How to pair a Bluetooth Low Energy(BLE) device with Android to read encrypted data.
Using the information in the Android BLE page, I am able to discover the device, connect to it, discover services and read un-encrypted characteristics.
When I try to read an encrypted characteristic (one that will cause iOS to show a popup asking to pair and then complete the read) I am getting an error code 5, which corresponds to Insufficient Authentication.
I am not sure how to get the device paired or how to provide the authentication information for the read to complete.
I toyed with BluetoothGattCharacteristics by trying to add descriptors, but that did not work either.
Any help is appreciated!
When you get the GATT_INSUFFICIENT_AUTHENTICATION error, the system starts the bonding process for you. In the example below I'm trying to enable notifications and indications on glucose monitor. First I'm enabling the notifications on Glucose Measurement characteristic which can cause the error to appear.
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
if (GM_CHARACTERISTIC.equals(descriptor.getCharacteristic().getUuid())) {
mCallbacks.onGlucoseMeasurementNotificationEnabled();
if (mGlucoseMeasurementContextCharacteristic != null) {
enableGlucoseMeasurementContextNotification(gatt);
} else {
enableRecordAccessControlPointIndication(gatt);
}
}
if (GM_CONTEXT_CHARACTERISTIC.equals(descriptor.getCharacteristic().getUuid())) {
mCallbacks.onGlucoseMeasurementContextNotificationEnabled();
enableRecordAccessControlPointIndication(gatt);
}
if (RACP_CHARACTERISTIC.equals(descriptor.getCharacteristic().getUuid())) {
mCallbacks.onRecordAccessControlPointIndicationsEnabled();
}
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
// this is where the tricky part comes
if (gatt.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
mCallbacks.onBondingRequired();
// I'm starting the Broadcast Receiver that will listen for bonding process changes
final IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mContext.registerReceiver(mBondingBroadcastReceiver, filter);
} else {
// this situation happens when you try to connect for the second time to already bonded device
// it should never happen, in my opinion
Logger.e(TAG, "The phone is trying to read from paired device without encryption. Android Bug?");
// I don't know what to do here
// This error was found on Nexus 7 with KRT16S build of Andorid 4.4. It does not appear on Samsung S4 with Andorid 4.3.
}
} else {
mCallbacks.onError(ERROR_WRITE_DESCRIPTOR, status);
}
};
Where the mBondingBroadcastReceiver is:
private BroadcastReceiver mBondingBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
final int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
final int previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1);
Logger.d(TAG, "Bond state changed for: " + device.getAddress() + " new state: " + bondState + " previous: " + previousBondState);
// skip other devices
if (!device.getAddress().equals(mBluetoothGatt.getDevice().getAddress()))
return;
if (bondState == BluetoothDevice.BOND_BONDED) {
// Continue to do what you've started before
enableGlucoseMeasurementNotification(mBluetoothGatt);
mContext.unregisterReceiver(this);
mCallbacks.onBonded();
}
}
};
Remember to unregister the broadcast receiver when exiting the activity. It may have not been unregistered by the receicver itself.
You might need to check the Kernel smp.c file, which method of paring it invoke for paring. 1) passkey 2)Just work or etc . i guess if it will be able to invoke MIMT and passkey level of security , there will not be any authentication issue. Make sure all flags is set to invoke the SMP passkey methods. track by putting some print in smp.c file.
A solution which works in ICS : with btmgmt tool in android and hooking it in encryption APIs. with passkey or any other methods. it works. You might need to add the passkey APIs in btmgmt from latest bluez code.
i think new android 4.4 provide pairing method. same problem already i am facing so wait for update and hope over problem solved createBond() method .
http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#setPairingConfirmation%28boolean%29