You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I can scan on GAP , connect at GATT layer and discover services which are, however, cannot be broadcasted to DeviceControlActivity as this activity goes into pause directly after receiving ACTION_GATT_CONNECTED broadcast.
I'm unsure why this happens with the one bluetooth perpheral and not with my smartwatch.
DeviceScanActivity: Deprecated startLeScan() and stopLeScan() replaced with startScan() and stopScan()
BluetoothLeService.java: setCharacteristicNotification() changed to notify on characteristics from my peripheral device (following this post)
Declaration of own services and characteristics in SampleGattAttributes in addition to the existing heart rate unit declarations
DeviceControlActivity: Call to setCharacteristicNotification adapted and extractData added (following this post)
All these changes, however, should not explain why DeviceControlActivity suddently goes to onPause() even though the screen is not changed. The activity is still in foreground.
Mind you this only happens with my own peripheral device and not with my smartwatch.
Any pointers are much appreciated. I'm running out of ideas.
On the left side we see the example where onPause() is called immediately after receiving a broadcast ACTION_GATT_CONNECTED.
This seems to be the reason that the services broadcast is not received. Why does it happen depending on the peripheral device?
/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */packagecom.example.android.bluetoothlegatt;
importandroid.Manifest;
importandroid.app.Activity;
importandroid.app.ListActivity;
importandroid.bluetooth.BluetoothAdapter;
importandroid.bluetooth.BluetoothDevice;
importandroid.bluetooth.BluetoothManager;
importandroid.bluetooth.le.BluetoothLeScanner;
importandroid.bluetooth.le.ScanCallback;
importandroid.bluetooth.le.ScanFilter;
importandroid.bluetooth.le.ScanResult;
importandroid.bluetooth.le.ScanSettings;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.content.pm.PackageManager;
importandroid.os.Build;
importandroid.os.Bundle;
importandroid.os.Handler;
importandroid.os.ParcelUuid;
importandroid.util.Log;
importandroid.view.LayoutInflater;
importandroid.view.Menu;
importandroid.view.MenuItem;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.BaseAdapter;
importandroid.widget.ListView;
importandroid.widget.TextView;
importandroid.widget.Toast;
importandroidx.annotation.RequiresApi;
importandroidx.core.app.ActivityCompat;
importandroidx.core.content.ContextCompat;
importjava.util.ArrayList;
importjava.util.List;
importjava.util.UUID;
/** * Activity for scanning and displaying available Bluetooth LE devices. */publicclassDeviceScanActivityextendsListActivity {
privatestaticfinalStringTAG = "MY_DEBUG" + DeviceScanActivity.class.getSimpleName();
privatestaticfinalintPERMISSIONS_REQUEST_CODE = 100;
privateLeDeviceListAdaptermLeDeviceListAdapter;
privateBluetoothAdaptermBluetoothAdapter;
privateBluetoothLeScannermbluetoothLeScanner;
ScanCallbackmBtScanCallback;
privatebooleanmScanning;
privateHandlermHandler;
privatestaticfinalintREQUEST_ENABLE_BT = 1;
// Stops scanning after 10 seconds.privatestaticfinallongSCAN_PERIOD = 10000;
privatefinalString[] permissions = { //only dangerous permissions are run-time permissionsManifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.RECORD_AUDIO};
// with the SDK 31 and higher (Android 12) we need to have also BLUETOOTH_SCAN and BLUETOOTH_CONNECT permissions@RequiresApi(api = Build.VERSION_CODES.S)
privatefinalString[] permissionsForS = { //only dangerous permissions are run-time permissionsManifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.RECORD_AUDIO};
@OverridepublicvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
handlePermissions();
getActionBar().setTitle(R.string.title_devices);
mHandler = newHandler();
// Use this check to determine whether BLE is supported on the device. Then you can// selectively disable BLE-related features.if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to// BluetoothAdapter through BluetoothManager.finalBluetoothManagerbluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Checks if Bluetooth is supported on the device.if (mBluetoothAdapter == null) {
Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
finish();
return;
}
mbluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
}
@OverridepublicbooleanonCreateOptionsMenu(Menumenu) {
getMenuInflater().inflate(R.menu.main, menu);
if (!mScanning) {
menu.findItem(R.id.menu_stop).setVisible(false);
menu.findItem(R.id.menu_scan).setVisible(true);
menu.findItem(R.id.menu_refresh).setActionView(null);
} else {
menu.findItem(R.id.menu_stop).setVisible(true);
menu.findItem(R.id.menu_scan).setVisible(false);
menu.findItem(R.id.menu_refresh).setActionView(
R.layout.actionbar_indeterminate_progress);
}
returntrue;
}
@OverridepublicbooleanonOptionsItemSelected(MenuItemitem) {
switch (item.getItemId()) {
caseR.id.menu_scan:
mLeDeviceListAdapter.clear();
scanLeDevice(true);
break;
caseR.id.menu_stop:
scanLeDevice(false);
break;
}
returntrue;
}
@OverrideprotectedvoidonResume() {
super.onResume();
Log.v(TAG, "onResume() " );
// Initializes list view adapter.mLeDeviceListAdapter = newLeDeviceListAdapter();
setListAdapter(mLeDeviceListAdapter);
scanLeDevice(true);
}
@OverrideprotectedvoidonActivityResult(intrequestCode, intresultCode, Intentdata) {
// User chose not to enable Bluetooth.if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
finish();
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
@OverrideprotectedvoidonPause() {
super.onPause();
scanLeDevice(false);
mLeDeviceListAdapter.clear();
}
@OverrideprotectedvoidonListItemClick(ListViewl, Viewv, intposition, longid) {
Log.v(TAG, "onListItemclick -> sending intent DeviceControlActivity.class");
finalBluetoothDevicedevice = mLeDeviceListAdapter.getDevice(position);
if (device == null) return;
finalIntentintent = newIntent(this, DeviceControlActivity.class);
intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName());
intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());
if (mScanning) {
// mBluetoothAdapter.stopLeScan(mLeScanCallback);mbluetoothLeScanner.stopScan(mBtScanCallback); //Todo:MY_DEBUG: Verify; remove the stopLeScan -> just replace with stopScan() enough?mScanning = false;
}
startActivity(intent);
}
privatevoidscanLeDevice(finalbooleanenable) {
if (enable) {
// Stops scanning after a pre-defined scan period.mHandler.postDelayed(newRunnable() {
@Overridepublicvoidrun() {
mScanning = false;
mbluetoothLeScanner.stopScan(mBtScanCallback);
invalidateOptionsMenu();
Log.v(TAG, "scanLeDevice - stopped after timeout");
}
}, SCAN_PERIOD);
Log.v(TAG, "scanLeDevice - started");
mScanning = true;
//mBluetoothAdapter.startLeScan(mLeScanCallback);mBtScanCallback = newBTScanCallback();
List<ScanFilter> scanFilters = newArrayList<>();
ScanFilter.Builderfilter_builder = newScanFilter.Builder();
filter_builder.setServiceUuid(newParcelUuid(UUID.fromString(SampleGattAttributes.MY_BTLE_DEVICE_SERVICE_UUID)));
scanFilters.add(filter_builder.build());
//Todo: MY_DEBUG: Remove limitation to first match as it's for testing purpose//ScanSettings.Builder settings_builder = new ScanSettings.Builder().setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH);ScanSettings.Buildersettings_builder = newScanSettings.Builder();
//settings_builder.setLegacy(false); //disable finding legacy -> min API 26 requiredScanSettingsscanSettings = settings_builder.build();
//mbluetoothLeScanner.startScan(scanFilters, scanSettings, mBtScanCallback);mbluetoothLeScanner.startScan(mBtScanCallback); //unfiltered rsults
} else {
mScanning = false;
mbluetoothLeScanner.stopScan(mBtScanCallback);
}
invalidateOptionsMenu();
}
// Adapter for holding devices found through scanning.privateclassLeDeviceListAdapterextendsBaseAdapter {
privateArrayList<BluetoothDevice> mLeDevices;
privateLayoutInflatermInflator;
publicLeDeviceListAdapter() {
super();
mLeDevices = newArrayList<BluetoothDevice>();
mInflator = DeviceScanActivity.this.getLayoutInflater();
}
publicvoidaddDevice(BluetoothDevicedevice) {
if(!mLeDevices.contains(device)) {
mLeDevices.add(device);
}
}
publicBluetoothDevicegetDevice(intposition) {
returnmLeDevices.get(position);
}
publicvoidclear() {
mLeDevices.clear();
}
@OverridepublicintgetCount() {
returnmLeDevices.size();
}
@OverridepublicObjectgetItem(inti) {
returnmLeDevices.get(i);
}
@OverridepubliclonggetItemId(inti) {
returni;
}
@OverridepublicViewgetView(inti, Viewview, ViewGroupviewGroup) {
Log.v(TAG, "getView in private class LeDeviceListAdapter called");
ViewHolderviewHolder;
// General ListView optimization code.if (view == null) {
view = mInflator.inflate(R.layout.listitem_device, null);
viewHolder = newViewHolder();
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
BluetoothDevicedevice = mLeDevices.get(i);
finalStringdeviceName = device.getName();
if (deviceName != null && deviceName.length() > 0)
viewHolder.deviceName.setText(deviceName);
elseviewHolder.deviceName.setText(R.string.unknown_device);
viewHolder.deviceAddress.setText(device.getAddress());
returnview;
}
}
privateclassBTScanCallbackextendsScanCallback {
/** * Callback when a BLE advertisement has been found. * * @param callbackType Determines how this callback was triggered. Could be one of {@link * ScanSettings#CALLBACK_TYPE_ALL_MATCHES}, {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST} * @param result A Bluetooth LE scan result. */@OverridepublicvoidonScanResult(intcallbackType, ScanResultresult) {
super.onScanResult(callbackType, result);
Log.v(TAG, "~~~~~~~~~~~~~~~~~ \n\t onScanResult: " + " " + result.getDevice().getName() + " " + result.getDevice().getAddress() + " " + result.getDevice().getUuids() + "\n\tresult.getScanRecord().getServiceUuids(): " + result.getScanRecord().getServiceUuids()+ "\n\n " + result.toString());
if (result.getScanRecord().getServiceUuids()==null) {
Log.v(TAG, "result.getScanRecord().getServiceUuids()==null" +
"<<<---------------------");
return;
}
mLeDeviceListAdapter.addDevice(result.getDevice());
mLeDeviceListAdapter.notifyDataSetChanged();
UUIDfoundUuid = result.getScanRecord().getServiceUuids().get(0).getUuid();
UUIDexpectedUUid = UUID.fromString(SampleGattAttributes.MY_BTLE_DEVICE_SERVICE_UUID);
if (foundUuid.equals(expectedUUid)) {
Log.v(TAG, "MY_BTLE_DEVICE with BTLE Nordic UART Service found <<<---------------------");
//gattConnection(result.getDevice());mLeDeviceListAdapter.addDevice(result.getDevice());
mLeDeviceListAdapter.notifyDataSetChanged();
} else {
Log.v(TAG, "some other device without UART service found");
}
}
/** * Callback when scan could not be started. * * @param errorCode Error code (one of SCAN_FAILED_*) for scan failure. */@OverridepublicvoidonScanFailed(interrorCode) {
super.onScanFailed(errorCode);
Log.v(TAG, "\t onScanFailed called ..");
}
}
//Todo: MY_DEBUG: The following callback method is deprecated now -> to be removed// Device scan callback.privateBluetoothAdapter.LeScanCallbackmLeScanCallback =
newBluetoothAdapter.LeScanCallback() {
@OverridepublicvoidonLeScan(finalBluetoothDevicedevice, intrssi, byte[] scanRecord) {
runOnUiThread(newRunnable() {
@Overridepublicvoidrun() {
Log.v(TAG, "BluetoothAdapter.LeScanCallback -> result received");
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
staticclassViewHolder {
TextViewdeviceName;
TextViewdeviceAddress;
}
privatevoidhandlePermissions() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ActivityCompat.requestPermissions(this, permissionsForS,
PERMISSIONS_REQUEST_CODE);
} else {
ActivityCompat.requestPermissions(this, permissions,
PERMISSIONS_REQUEST_CODE);
}
} else {
Log.v(TAG, "permission has already been granted");
}
}
/** * Call back method is used to check for granted permissions and provide information in case * permissions are not granted but are necessary for the app functionality * * @param requestCode requestCode used for requesting the permissions * @param permissions Permissions that were requested * @param grantResults result of the permission request */@OverridepublicvoidonRequestPermissionsResult(intrequestCode, String[] permissions,
int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
casePERMISSIONS_REQUEST_CODE:
// If request is cancelled, the result arrays are empty.if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.v(TAG, "All requested permissions granted. Continue..");
} else {
Log.e(TAG, "Not all permissions are granted. Accept the permissions request for full app functionality..");
}
return;
}
}
}
DeviceControlActivity.java
/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */packagecom.example.android.bluetoothlegatt;
importandroid.app.Activity;
importandroid.bluetooth.BluetoothGattCharacteristic;
importandroid.bluetooth.BluetoothGattService;
importandroid.content.BroadcastReceiver;
importandroid.content.ComponentName;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.content.IntentFilter;
importandroid.content.ServiceConnection;
importandroid.os.Bundle;
importandroid.os.IBinder;
importandroid.util.Log;
importandroid.view.Menu;
importandroid.view.MenuItem;
importandroid.view.View;
importandroid.widget.ExpandableListView;
importandroid.widget.SimpleExpandableListAdapter;
importandroid.widget.TextView;
importjava.util.ArrayList;
importjava.util.HashMap;
importjava.util.List;
/** * For a given BLE device, this Activity provides the user interface to connect, display data, * and display GATT services and characteristics supported by the device. The Activity * communicates with {@code BluetoothLeService}, which in turn interacts with the * Bluetooth LE API. */publicclassDeviceControlActivityextendsActivity {
privatefinalstaticStringTAG = "MY_DEBUG: " + DeviceControlActivity.class.getSimpleName();
publicstaticfinalStringEXTRAS_DEVICE_NAME = "DEVICE_NAME";
publicstaticfinalStringEXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";
privateTextViewmConnectionState;
privateTextViewmDataField;
privateStringmDeviceName;
privateStringmDeviceAddress;
privateExpandableListViewmGattServicesList;
privateBluetoothLeServicemBluetoothLeService;
privateArrayList<ArrayList<BluetoothGattCharacteristic>> mGattCharacteristics =
newArrayList<ArrayList<BluetoothGattCharacteristic>>();
privatebooleanmConnected = false;
privateBluetoothGattCharacteristicmNotifyCharacteristic;
privatefinalStringLIST_NAME = "NAME";
privatefinalStringLIST_UUID = "UUID";
// Code to manage Service lifecycle.privatefinalServiceConnectionmServiceConnection = newServiceConnection() {
@OverridepublicvoidonServiceConnected(ComponentNamecomponentName, IBinderservice) {
Log.v(TAG, "onServiceConnected() ");
mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
if (!mBluetoothLeService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
// Automatically connects to the device upon successful start-up initialization.mBluetoothLeService.connect(mDeviceAddress);
}
@OverridepublicvoidonServiceDisconnected(ComponentNamecomponentName) {
Log.v(TAG, "onServiceDiscnnected() ");
mBluetoothLeService = null;
}
};
// Handles various events fired by the Service.// ACTION_GATT_CONNECTED: connected to a GATT server.// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.// ACTION_DATA_AVAILABLE: received data from the device. This can be a result of read// or notification operations.privatefinalBroadcastReceivermGattUpdateReceiver = newBroadcastReceiver() {
@OverridepublicvoidonReceive(Contextcontext, Intentintent) {
Log.v(TAG, "BroadcastReceiver: mGattUpdateReceiver onReceive()- Intent.action: " + intent.getAction());
finalStringaction = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
mConnected = true;
updateConnectionState(R.string.connected);
invalidateOptionsMenu();
} elseif (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
mConnected = false;
updateConnectionState(R.string.disconnected);
invalidateOptionsMenu();
clearUI();
} elseif (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
// Show all the supported services and characteristics on the user interface.mBluetoothLeService.setCharacteristicNotification(SampleGattAttributes.MY_BTLE_DEVICE_SERVICE_UUID, SampleGattAttributes.MY_BTLE_DEVICE_STATUS_UUID, true); //Todo: MY_DEBUG: check whether the characteristic and service is correct for notification!displayGattServices(mBluetoothLeService.getSupportedGattServices());
} elseif (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
extractData(intent.getByteArrayExtra(BluetoothLeService.EXTRA_DATA));
displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
}
else {
Log.v(TAG, "unhandled action received .." + action);
}
}
};
privatevoidextractData(byte[] input_data){
System.out.println("Extracting Data");;
Log.i("broadcastUpdate", "Data Length = " + input_data.length);
if (input_data != null && input_data.length > 0) {
finalStringBuilderstringBuilder = newStringBuilder(input_data.length);
for (bytebyteChar : input_data) {
stringBuilder.append(String.format("%02X ", byteChar));
}
Log.i("broadcastUpdate", "Received Data = " + stringBuilder.toString());
}
}
// If a given GATT characteristic is selected, check for supported features. This sample// demonstrates 'Read' and 'Notify' features. See// http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete// list of supported characteristic features.privatefinalExpandableListView.OnChildClickListenerservicesListClickListner =
newExpandableListView.OnChildClickListener() {
@OverridepublicbooleanonChildClick(ExpandableListViewparent, Viewv, intgroupPosition,
intchildPosition, longid) {
Log.v(TAG, "onChildClick registered on characteristic --> trying to read and get the notification");
if (mGattCharacteristics != null) {
finalBluetoothGattCharacteristiccharacteristic =
mGattCharacteristics.get(groupPosition).get(childPosition);
finalintcharaProp = characteristic.getProperties();
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) { //Todo: MY_DEBUG: Why is this property doing bitwise OR instead of AND &? -> Should check for read property exclusively, not?// If there is an active notification on a characteristic, clear// it first so it doesn't update the data field on the user interface.if (mNotifyCharacteristic != null) {
mBluetoothLeService.setCharacteristicNotification(characteristic.getService().getUuid().toString(), characteristic.getUuid().toString(),
false);
mNotifyCharacteristic = null;
}
mBluetoothLeService.readCharacteristic(characteristic);
}
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
Log.v(TAG, "(charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0 ---------> set notification to TRUE .... notifications should come now...");
mNotifyCharacteristic = characteristic;
mBluetoothLeService.setCharacteristicNotification(characteristic.getService().getUuid().toString(),characteristic.getUuid().toString(), true);
}
returntrue;
}
returnfalse;
}
};
privatevoidclearUI() {
mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);
mDataField.setText(R.string.no_data);
}
@OverridepublicvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gatt_services_characteristics);
finalIntentintent = getIntent();
mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);
// Sets up UI references.
((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
mGattServicesList = (ExpandableListView) findViewById(R.id.gatt_services_list);
mGattServicesList.setOnChildClickListener(servicesListClickListner);
mConnectionState = (TextView) findViewById(R.id.connection_state);
mDataField = (TextView) findViewById(R.id.data_value);
getActionBar().setTitle(mDeviceName);
getActionBar().setDisplayHomeAsUpEnabled(true);
IntentgattServiceIntent = newIntent(this, BluetoothLeService.class);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
}
@OverrideprotectedvoidonResume() {
super.onResume();
Log.v(TAG, "DeviceControlActivity - onResume(): ");
registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
if (mBluetoothLeService != null) {
finalbooleanresult = mBluetoothLeService.connect(mDeviceAddress);
Log.d(TAG, "Connect request result=" + result);
}
}
@OverrideprotectedvoidonPause() {
super.onPause();
Log.v(TAG, "DeviceControlActivity - onPause(): ");
unregisterReceiver(mGattUpdateReceiver);
}
@OverrideprotectedvoidonDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
mBluetoothLeService = null;
}
@OverridepublicbooleanonCreateOptionsMenu(Menumenu) {
getMenuInflater().inflate(R.menu.gatt_services, menu);
if (mConnected) {
menu.findItem(R.id.menu_connect).setVisible(false);
menu.findItem(R.id.menu_disconnect).setVisible(true);
} else {
menu.findItem(R.id.menu_connect).setVisible(true);
menu.findItem(R.id.menu_disconnect).setVisible(false);
}
returntrue;
}
@OverridepublicbooleanonOptionsItemSelected(MenuItemitem) {
switch(item.getItemId()) {
caseR.id.menu_connect:
mBluetoothLeService.connect(mDeviceAddress);
returntrue;
caseR.id.menu_disconnect:
mBluetoothLeService.disconnect();
returntrue;
caseandroid.R.id.home:
onBackPressed();
returntrue;
}
returnsuper.onOptionsItemSelected(item);
}
privatevoidupdateConnectionState(finalintresourceId) {
runOnUiThread(newRunnable() {
@Overridepublicvoidrun() {
mConnectionState.setText(resourceId);
}
});
}
privatevoiddisplayData(Stringdata) {
if (data != null) {
mDataField.setText(data);
}
}
// Demonstrates how to iterate through the supported GATT Services/Characteristics.// In this sample, we populate the data structure that is bound to the ExpandableListView// on the UI.privatevoiddisplayGattServices(List<BluetoothGattService> gattServices) {
Log.v(TAG, "displayGattServices - called ");
if (gattServices == null || gattServices.size()==0) return;
Log.v(TAG, "displayGattServices - gattServices.size()/first characteristic UUID: " + gattServices.size() + "/" + gattServices.get(0).getCharacteristics().get(0).getUuid());
Stringuuid = null;
StringunknownServiceString = getResources().getString(R.string.unknown_service);
StringunknownCharaString = getResources().getString(R.string.unknown_characteristic);
ArrayList<HashMap<String, String>> gattServiceData = newArrayList<HashMap<String, String>>();
ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
= newArrayList<ArrayList<HashMap<String, String>>>();
mGattCharacteristics = newArrayList<ArrayList<BluetoothGattCharacteristic>>();
// Loops through available GATT Services.for (BluetoothGattServicegattService : gattServices) {
Log.v(TAG, "gattServide: " + gattService.getCharacteristics().size());
HashMap<String, String> currentServiceData = newHashMap<String, String>();
uuid = gattService.getUuid().toString();
currentServiceData.put(
LIST_NAME, SampleGattAttributes.lookup(uuid, unknownServiceString));
currentServiceData.put(LIST_UUID, uuid);
gattServiceData.add(currentServiceData);
ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
newArrayList<HashMap<String, String>>();
List<BluetoothGattCharacteristic> gattCharacteristics =
gattService.getCharacteristics();
ArrayList<BluetoothGattCharacteristic> charas =
newArrayList<BluetoothGattCharacteristic>();
// Loops through available Characteristics.for (BluetoothGattCharacteristicgattCharacteristic : gattCharacteristics) {
Log.v(TAG, "gattCharacteristic: " + gattCharacteristic);
charas.add(gattCharacteristic);
HashMap<String, String> currentCharaData = newHashMap<String, String>();
uuid = gattCharacteristic.getUuid().toString();
currentCharaData.put(
LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString));
currentCharaData.put(LIST_UUID, uuid);
gattCharacteristicGroupData.add(currentCharaData);
}
mGattCharacteristics.add(charas);
gattCharacteristicData.add(gattCharacteristicGroupData);
}
SimpleExpandableListAdaptergattServiceAdapter = newSimpleExpandableListAdapter(
this,
gattServiceData,
android.R.layout.simple_expandable_list_item_2,
newString[] {LIST_NAME, LIST_UUID},
newint[] { android.R.id.text1, android.R.id.text2 },
gattCharacteristicData,
android.R.layout.simple_expandable_list_item_2,
newString[] {LIST_NAME, LIST_UUID},
newint[] { android.R.id.text1, android.R.id.text2 }
);
mGattServicesList.setAdapter(gattServiceAdapter);
}
privatestaticIntentFiltermakeGattUpdateIntentFilter() {
Log.v(TAG, "makeGattUpdateIntentFilter() ");
finalIntentFilterintentFilter = newIntentFilter();
intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
Log.i("IntentFilter", "all GATT states registered");
returnintentFilter;
}
}
BluetoothLeService.java
/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */packagecom.example.android.bluetoothlegatt;
importandroid.app.Service;
importandroid.bluetooth.BluetoothAdapter;
importandroid.bluetooth.BluetoothDevice;
importandroid.bluetooth.BluetoothGatt;
importandroid.bluetooth.BluetoothGattCallback;
importandroid.bluetooth.BluetoothGattCharacteristic;
importandroid.bluetooth.BluetoothGattDescriptor;
importandroid.bluetooth.BluetoothGattService;
importandroid.bluetooth.BluetoothManager;
importandroid.bluetooth.BluetoothProfile;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.os.Binder;
importandroid.os.IBinder;
importandroid.util.Log;
importjava.util.List;
importjava.util.UUID;
/** * Service for managing connection and data communication with a GATT server hosted on a * given Bluetooth LE device. */publicclassBluetoothLeServiceextendsService {
privatefinalstaticStringTAG = "MY_DEBUG: " + BluetoothLeService.class.getSimpleName();
privateBluetoothManagermBluetoothManager;
privateBluetoothAdaptermBluetoothAdapter;
privateStringmBluetoothDeviceAddress;
privateBluetoothGattmBluetoothGatt;
privateintmConnectionState = STATE_DISCONNECTED;
privatestaticfinalintSTATE_DISCONNECTED = 0;
privatestaticfinalintSTATE_CONNECTING = 1;
privatestaticfinalintSTATE_CONNECTED = 2;
publicfinalstaticStringACTION_GATT_CONNECTED =
"com.example.bluetooth.le.ACTION_GATT_CONNECTED";
publicfinalstaticStringACTION_GATT_DISCONNECTED =
"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
publicfinalstaticStringACTION_GATT_SERVICES_DISCOVERED =
"com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
publicfinalstaticStringACTION_DATA_AVAILABLE =
"com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
publicfinalstaticStringEXTRA_DATA =
"com.example.bluetooth.le.EXTRA_DATA";
publicfinalstaticUUIDUUID_HEART_RATE_MEASUREMENT =
UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
// Implements callback methods for GATT events that the app cares about. For example,// connection change and services discovered.privatefinalBluetoothGattCallbackmGattCallback = newBluetoothGattCallback() {
@OverridepublicvoidonConnectionStateChange(BluetoothGattgatt, intstatus, intnewState) {
Log.v(TAG, "BluetoothGattCallback: onConnectionStateChange - " + "status: " + status + " newState: " + newState);
StringintentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
Log.i(TAG, "mConnectionState - " + mConnectionState);
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());
} elseif (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
@OverridepublicvoidonServicesDiscovered(BluetoothGattgatt, intstatus) {
Log.v(TAG, "BluetoothGattCallback: onServicesDiscovered - " + "status: " + status );
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
Log.w(TAG, "onServicesDiscovered received - sending broadcaseUpdte(ACTION_GATT_SERVICES_DISCOVERED)" + status);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@OverridepublicvoidonCharacteristicRead(BluetoothGattgatt,
BluetoothGattCharacteristiccharacteristic,
intstatus) {
Log.v(TAG, "BluetoothGattCallback: onCharacteristicRead: - " + "status: " + status + "characteristic UUID: " + characteristic.getUuid());
Log.v(TAG, "onCharacteristicRead: ");
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.v(TAG, "onCharacteristicRead: GATT_SUCCESS -> ACTION_DATA_AVAILABLE for characteristic" + characteristic.getUuid());
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
@OverridepublicvoidonCharacteristicChanged(BluetoothGattgatt,
BluetoothGattCharacteristiccharacteristic) {
Log.v(TAG, "onCharacteristicChanged: -> ACTION_DATA_AVAILABLE for characteristic" + characteristic.getUuid());
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
privatevoidbroadcastUpdate(finalStringaction) {
Log.v(TAG, "broadcastUpdate: -> action: " + action);
finalIntentintent = newIntent(action);
sendBroadcast(intent);
}
//Todo: MY_DEBUG: Have to write the data in MY_BTLE_DEVICE specific data formatprivatevoidbroadcastUpdate(finalStringaction,
finalBluetoothGattCharacteristiccharacteristic) {
finalIntentintent = newIntent(action);
Log.v(TAG, "broadcastUpdate: -> action: " + action + "characteristic: " +characteristic.getUuid());
// 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.xmlif (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
intflag = characteristic.getProperties();
intformat = -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.");
}
finalintheartRate = characteristic.getIntValue(format, 1);
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
} else {
Log.v(TAG, "broadcastUpdate: " + action + " - characteristic: " + characteristic.getUuid());
// For all other profiles, writes the data formatted in HEX.finalbyte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
finalStringBuilderstringBuilder = newStringBuilder(data.length);
for(bytebyteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
intent.putExtra(EXTRA_DATA, newString(data) + "\n" + stringBuilder.toString());
}
}
sendBroadcast(intent);
}
publicclassLocalBinderextendsBinder {
BluetoothLeServicegetService() {
returnBluetoothLeService.this;
}
}
@OverridepublicIBinderonBind(Intentintent) {
returnmBinder;
}
@OverridepublicbooleanonUnbind(Intentintent) {
// After using a given device, you should make sure that BluetoothGatt.close() is called// such that resources are cleaned up properly. In this particular example, close() is// invoked when the UI is disconnected from the Service.close();
returnsuper.onUnbind(intent);
}
privatefinalIBindermBinder = newLocalBinder();
/** * Initializes a reference to the local Bluetooth adapter. * * @return Return true if the initialization is successful. */publicbooleaninitialize() {
// For API level 18 and above, get a reference to BluetoothAdapter through// BluetoothManager.if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager.");
returnfalse;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
returnfalse;
}
returntrue;
}
/** * Connects to the GATT server hosted on the Bluetooth LE device. * * @param address The device address of the destination device. * * @return Return true if the connection is initiated successfully. The connection result * is reported asynchronously through the * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} * callback. */publicbooleanconnect(finalStringaddress) {
if (mBluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
returnfalse;
}
// Previously connected device. Try to reconnect.if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
&& mBluetoothGatt != null) {
Log.v(TAG, "mBluetoothDeviceAddress: " + mBluetoothDeviceAddress + "address: " + address + "mBluetoothGatt: " +mBluetoothGatt.toString());
Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGatt.connect()) {
mConnectionState = STATE_CONNECTING;
Log.v(TAG, "mConnectionState: " + mConnectionState);
returntrue;
} else {
Log.v(TAG, "connect() returned false" );
returnfalse;
}
}
finalBluetoothDevicedevice = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.w(TAG, "Device not found. Unable to connect.");
returnfalse;
}
Log.v(TAG, "remote device address: " + device.getAddress() + " name: " + device.getName());
// We want to directly connect to the device, so we are setting the autoConnect// parameter to false.mBluetoothGatt = device.connectGatt(this, true, mGattCallback); //Todo: MY_DEBUG: set autoConnect to true for final solution -> just for manual testing false is usedLog.d(TAG, "Trying to create a new connection.");
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
Log.v(TAG,"mBluetoothDeviceAddress: " + mBluetoothDeviceAddress + "mConnectionState: " + mConnectionState + mBluetoothGatt.getServices());
returntrue;
}
/** * Disconnects an existing connection or cancel a pending connection. The disconnection result * is reported asynchronously through the * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} * callback. */publicvoiddisconnect() {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.disconnect();
}
/** * After using a given BLE device, the app must call this method to ensure resources are * released properly. */publicvoidclose() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
/** * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} * callback. * * @param characteristic The characteristic to read from. */publicvoidreadCharacteristic(BluetoothGattCharacteristiccharacteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
/** * Enables or disables notification on a give characteristic. * * @param service_uuid * @param characteristic_uuid Characteristic to act on. * @param enabled If true, enable notification. False otherwise. */publicvoidsetCharacteristicNotification(Stringservice_uuid, Stringcharacteristic_uuid,
booleanenabled) {
Log.v(TAG, "setCharacteristicNotification called - ");
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
BluetoothGattServiceservice = mBluetoothGatt.getService(UUID.fromString(service_uuid));
if (service == null) return;
BluetoothGattCharacteristiccharacteristic = service.getCharacteristic(UUID.fromString(characteristic_uuid));
if (characteristic==null) return;
Log.v(TAG, "setCharacteristicNotification() called in service with \"enabled = \"" + enabled);
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptordescriptor = characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.MY_BTLE_DEVICE_STATUS_UUID));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
/** * Retrieves a list of supported GATT services on the connected device. This should be * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully. * * @return A {@code List} of supported services. */publicList<BluetoothGattService> getSupportedGattServices() {
if (mBluetoothGatt == null) returnnull;
Log.v(TAG, "mBluetoothGatt.getServices() => " + mBluetoothGatt.discoverServices());
returnmBluetoothGatt.getServices();
}
}
SampleGattAttributes.java
/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */packagecom.example.android.bluetoothlegatt;
importjava.util.HashMap;
/** * This class includes a small subset of standard GATT attributes for demonstration purposes. */publicclassSampleGattAttributes {
privatestaticHashMap<String, String> attributes = newHashMap();
publicstaticStringMY_BTLE_DEVICE_STATUS_UUID = "00002a37-0000-1000-8000-00805f9b34fb";
publicstaticStringHEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb";
publicstaticStringCLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
publicstaticStringMY_BTLE_DEVICE_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-00805f9b34fb";
publicstaticStringMY_BTLE_DEVICE_RX_UUID = "6e400002-b5a3-f393-e0a9-00805f9b34fb";
publicstaticStringMY_BTLE_DEVICE_TX_UUID = "6e400003-b5a3-f393-e0a9-00805f9b34fb";
static {
// Sample Services.attributes.put("0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate Service");
attributes.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information Service");
// Sample Characteristics.attributes.put(HEART_RATE_MEASUREMENT, "Heart Rate Measurement");
attributes.put("00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name String");
attributes.put(MY_BTLE_DEVICE_SERVICE_UUID, "MY_BTLE_DEVICE Service");
attributes.put(MY_BTLE_DEVICE_RX_UUID, "MY_BTLE_DEVICE Receive");
attributes.put(MY_BTLE_DEVICE_TX_UUID, "MY_BTLE_DEVICE Transmit");
attributes.put(MY_BTLE_DEVICE_STATUS_UUID, "MY_BTLE_DEVICE Status");
}
publicstaticStringlookup(Stringuuid, StringdefaultName) {
Stringname = attributes.get(uuid);
returnname == null ? defaultName : name;
}
}
For the sake of completion, here is also the manifest.
AndroidManifest.xml
<?xmlversion="1.0" encoding="UTF-8"?><!-- Copyright 2013 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "ASIS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.--><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.android.bluetoothlegatt"android:versionCode="1"android:versionName="1.0">
<!-- Min/targetSDKversions (<uses-sdk>) managedbybuild.gradle -->
<!-- DeclarethisrequiredfeatureifyouwanttomaketheappavailabletoBLE-capabledevicesonly. Ifyouwanttomakeyourappavailabletodevicesthatdon't support BLE,
youshouldomitthisinthemanifest. Instead, determineBLEcapabilitybyusingPackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) -->
<!-- <uses-featureandroid:name="android.hardware.bluetooth_le"android:required="true"/>-->
<uses-permissionandroid:name="android.permission.FOREGROUND_SERVICE" />
<uses-permissionandroid:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- RequestlegacyBluetoothpermissionsonolderdevices. -->
<uses-permissionandroid:name="android.permission.BLUETOOTH"android:maxSdkVersion="30" />
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"android:maxSdkVersion="30" />
<!-- NeededonlyifyourapplooksforBluetoothdevices.
Ifyourappdoesn't use Bluetooth scan results to derive physical
locationinformation, youcanstronglyassertthatyourappdoesn't derive physical location. -->
<uses-permissionandroid:name="android.permission.BLUETOOTH_SCAN"/>
<!-- Neededonlyifyourappcommunicateswithalready-pairedBluetoothdevices. -->
<uses-permissionandroid:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permissionandroid:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<applicationandroid:label="@string/app_name"android:icon="@drawable/ic_launcher"android:theme="@android:style/Theme.Holo.Light">
<activityandroid:name=".DeviceScanActivity"android:label="@string/app_name"android:exported="true">
<intent-filter>
<actionandroid:name="android.intent.action.MAIN"/>
<categoryandroid:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activityandroid:name=".DeviceControlActivity"/>
<serviceandroid:name=".BluetoothLeService"android:enabled="true"/>
</application>
</manifest>
The text was updated successfully, but these errors were encountered:
I'm working with BluetoothLeGatt project.
I can scan on GAP , connect at GATT layer and discover services which are, however, cannot be broadcasted to DeviceControlActivity as this activity goes into pause directly after receiving ACTION_GATT_CONNECTED broadcast.
I'm unsure why this happens with the one bluetooth perpheral and not with my smartwatch.
I've followed these official documentation links:
Following changes are done in the original code:
DeviceScanActivity: Deprecated startLeScan() and stopLeScan() replaced with startScan() and stopScan()
BluetoothLeService.java: setCharacteristicNotification() changed to notify on characteristics from my peripheral device (following this post)
Declaration of own services and characteristics in SampleGattAttributes in addition to the existing heart rate unit declarations
DeviceControlActivity: Call to setCharacteristicNotification adapted and extractData added (following this post)
All these changes, however, should not explain why DeviceControlActivity suddently goes to onPause() even though the screen is not changed. The activity is still in foreground.
Mind you this only happens with my own peripheral device and not with my smartwatch.
Any pointers are much appreciated. I'm running out of ideas.
On the left side we see the example where onPause() is called immediately after receiving a broadcast ACTION_GATT_CONNECTED.
This seems to be the reason that the services broadcast is not received. Why does it happen depending on the peripheral device?
Logcat with my device shows onPause issue:
Logcat_My_device_with_onPause_issue_in_DeviceControlActivity.txt
DeviceScanActivity .java
DeviceControlActivity.java
BluetoothLeService.java
SampleGattAttributes.java
For the sake of completion, here is also the manifest.
AndroidManifest.xml
The text was updated successfully, but these errors were encountered: