In the 9.2 Release Notes of Google Play services I found the following:
Nearby
This release removes the requirement that clients use the Proximity Beacon API to manage their beacons. Nearby will now return parsed BLE advertisements directly, allowing clients to use their own solution to interpret those advertisements.
That sounds to me, as if I should be able to subscribe to Nearby Messages with Strategy.BLE_ONLY and get information about BLE beacons (which includes iBeacons) in the vicinity without "owning"/registering the beacons vie Proximity Beacon API.
Do I understand that correctly? Does anyone have a helpful link to a sample or other resources that might help implementing this feature?
Subscribe with a MessageFilter that uses includeIBeaconIds.
The code would look something like:
MessageFilter filter = new MessageFilter.Builder()
.includeIBeaconIds(MY_IBEACON_PROXIMITY_UUID, null, null)
.build();
SubscribeOptions options = new SubscribeOptions.Builder()
.setFilter(filter)
.setStrategy(Strategy.BLE_ONLY)
.build();
MessageListener listener = new MessageListener() {
#Override
public void onFound(Message message) {
IBeaconId beaconId = IBeaconId.from(message);
Log.i("Test", "Saw iBeacon " + beaconId);
}
};
Nearby.Messages.subscribe(apiClient, messageListener, options);
Does anyone have a helpful link to a sample or other resources that might help implementing this feature?
Listening to iBeacon from an Android device is possible as confirmed in this SO thread and this too.
You can make use of the AltBeacon/android-beacon-library.
It allows Android devices to use beacons much like iOS devices do. An
app can request to get notifications when one or more beacons appear
or disappear. An app can also request to get a ranging update from one
or more beacons at a frequency of approximately 1Hz.
Don't forget to read about Getting Beacon Messages from the Google Docs too.
Here's a snippet when subscribing in the foreground:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
mMessageListener = new MessageListener() {
#Override
public void onFound(Message message) {
String messageAsString = new String(message.getContent());
Log.d(TAG, "Found message: " + messageAsString);
}
#Override
public void onLost(Message message) {
String messageAsString = new String(message.getContent());
Log.d(TAG, "Lost sight of message: " + messageAsString);
}
}
}
Related
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'm trying to send String from my Moto 360 to Android App! but I don't understand how to implement the code in documentation, can anyone help me please with a little explanation of the steps to follow to send data !
http://developer.android.com/training/wearables/data-layer/messages.html
Take a look at a samples, this line exactly shows how to send a message:
http://developer.android.com/samples/DataLayer/Application/src/com.example.android.wearable.datalayer/MainActivity.html#l335
private void sendStartActivityMessage(String node) {
Wearable.MessageApi.sendMessage(
mGoogleApiClient, node, START_ACTIVITY_PATH, new byte[0]).setResultCallback(
new ResultCallback<SendMessageResult>() {
#Override
public void onResult(SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e(TAG, "Failed to send message with status code: "
+ sendMessageResult.getStatus().getStatusCode());
}
}
}
);
}
If that's too much information, a simpler tutorial for MessageApi should be just a google search away. Try this one: http://www.binpress.com/tutorial/a-guide-to-the-android-wear-message-api/152 it looks reasonable.
I currently have an android wear watchface developed. I however would now like to create a settings section on the host app that would allow the user to customize the watchface. I am new to the android development so I am curious on the correct way to do this.
Is there a way to update a sharedpreference on the host and then push or sync that with the sharedpreference on the wear device? Or is there a totally different way I should be looking at this?
You can use the DataApi or MessageApi to sync your watchface configuration between Phone and Watch devices.
Please take a look at the documentation and choose the one more appropriate to your needs:
https://developer.android.com/training/wearables/data-layer/index.html
https://developer.android.com/training/wearables/data-layer/data-items.html
https://developer.android.com/training/wearables/data-layer/messages.html
Here is an example with the use of DataApi.
Everything pushed to the DataApi is shared between devices and available of both of them. You can change this data on both sides and the other side will be notified about such change immediately (when devices are connected to each other). You can also read this data at any moment (for example when user will choose your watchface on the Watch - the configuration data will be already waiting for you there).
On the phone side:
public class WatchfaceConfigActivity extends Activity {
private GoogleApiClient mGoogleApiClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(new ConnectionCallbacks() {
#Override
public void onConnected(Bundle connectionHint) {
}
#Override
public void onConnectionSuspended(int cause) {
}
})
.addOnConnectionFailedListener(new OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult result) {
}
})
.addApi(Wearable.API)
.build();
mGoogleApiClient.connect();
}
and every time you want to sync new fconfiguration with the Android Wear device you have to put a DataRequest via Wearable DataApi:
private void syncConfiguration() {
if(mGoogleApiClient==null)
return;
final PutDataMapRequest putRequest = PutDataMapRequest.create("/CONFIG");
final DataMap map = putRequest.getDataMap();
map.putInt("mode", 1);
map.putInt("color", Color.RED);
map.putString("string_example", "MyWatchface");
Wearable.DataApi.putDataItem(mGoogleApiClient, putRequest.asPutDataRequest());
}
}
On the Watch side:
You need to create a class that extends WearableListenerService:
public class DataLayerListenerService extends WearableListenerService {
#Override
public void onDataChanged(DataEventBuffer dataEvents) {
super.onDataChanged(dataEvents);
final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
for(DataEvent event : events) {
final Uri uri = event.getDataItem().getUri();
final String path = uri!=null ? uri.getPath() : null;
if("/CONFIG".equals(path)) {
final DataMap map = DataMapItem.fromDataItem(event.getDataItem()).getDataMap();
// read your values from map:
int mode = map.getInt("mode");
int color = map.getInt("color");
String stringExample = map.getString("string_example");
}
}
}
}
and declare it in your AndroidManifest:
<service android:name=".DataLayerListenerService" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
Notice that this is only an example of usage. Maybe (instead of registering an instance of WearableListenerService) there will be better for you to create an instance of mGoogleApiClient inside your Watchface directly and add a DataListener there:
Wearable.DataApi.addListener(mGoogleApiClient, new DataListener() {
#Override
public void onDataChanged(DataEventBuffer dataEvents) {
// read config here and update the watchface
}
});
Maybe you don't need shared data - then you can communicate using MessageApi and send messages only when new configuration is saved or then watch wants to read current configuration from phone.
There isn't a shared preferences across the mobile and wear modules per se, but you can send messages and/or update assets that a listener will detect. For example, whenever you change a preference on the phone, you could also send a message to the watch using the Message API. On the watch, you should implement a WearableListenerService with an onMessageReceived method, in which you can parse the message and take an appropriate action, such as setting a preference on the watch.
Check out the Android Developers training guide: https://developer.android.com/training/wearables/data-layer/index.html
I'm currently attempting to use AppSync to sync a piece of data between an android app and a pebble app. However, I seem to not be able to get the pebble to realize that any data is being transferred - that is, no logs are being produced where they should be. What is really bothering me is that this is essentially the code found in the pebble weather example. I've pasted the relevant bits of code below - could someone possibly look it over and suggest where any issues may be? I've made sure that the UUIDs in both programs (pebble app and android app) are the same, and that they are on the same network, and that the pebble is actually connected to the phone, and that the android function is actually being called and all.
Snippet of pebble app code:
static void sync_error_callback(DictionaryResult dict_error, AppMessageResult app_message_error, void *context) {
APP_LOG(APP_LOG_LEVEL_DEBUG, "App Message Sync Error: %d", app_message_error);
}
static void sync_tuple_changed_callback(const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) {
APP_LOG(APP_LOG_LEVEL_DEBUG, new_tuple->value->cstring);
}
void home_screen_load() {
// set up each one of the SimpleMenuItems
Tuplet initial_values[] = {
TupletCString(0x0, "Initial 1")
};
app_sync_init(&sync, sync_buffer, sizeof(sync_buffer), initial_values, ARRAY_LENGTH(initial_values), sync_tuple_changed_callback, sync_error_callback, NULL);
}
Snippet of android app:
final UUID PEBBLE_APP_UUID = UUID.fromString("10549fd4-1fe4-4d30-8a18-6f2f8149f8fd");
public void sendDataToWatch(String toSend) {
// Build up a Pebble dictionary containing the weather icon and the current temperature in degrees celsius
PebbleDictionary data = new PebbleDictionary();
data.addString(0x0, toSend);
PebbleKit.sendDataToPebble(getApplicationContext(), PEBBLE_APP_UUID, data);
}
To debug this type of problem, you should set a inbox_dropped handler and see if you get anything there.
After initializing AppMessage and AppSync, call:
app_message_register_inbox_dropped(appmsg_in_dropped);
And add this function:
static void appmsg_in_dropped(AppMessageResult reason, void *context) {
APP_LOG(APP_LOG_LEVEL_DEBUG, "In dropped: %s", translate_error(reason));
}
Take a look at this question for the source of the translate_error function.
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