I am developing a peripheral mode BLE application that runs on Android. There are several excellent functional examples that describe how to develop peripheral applications here and.
There is one slight difference between the sample applications and the system that I have to develop. In sample applications, the GATT server runs on a peripheral device. The peripheral device advertises, performs a central scan and locates the device and sends a request to the GATT server.
On my system, the GATT server will be in central mode. The peripheral device will receive a connection request, you need to find the address of the requesting device and initiate a connection to the GATT server on the central mode device.
Nomenclature and detailed operation
MyApp (BLE Peripheral, Advertiser, GATT Client)
TgtDev (central BLE center, scanner, GATT server)
- TgtDev constantly scans and searches for ads with a specific UUID.
- MyApp is launched and begins to advertise its presence.
- TgtDev detects MyApp and sends a connection request.
- Upon receipt of a connection request, MyApp will ask the user to accept or decline the connection request. At this point, he will need to read the address of the device requesting the connection.
- If the user accepts, MyApp will request a connection to the GATT server on TgtDev.
- , MyApp .
. MyApp . , registerReceiver(). , , BroadcastReceiver , .
GattCallback() , . , .
, ?
EDIT: Android.
Nexus 6 5.1. MyApp targetSdkVersion 21. TgtDev , MyApp . BLE.
.
I/ActivityManager( 802): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.user.advertiser/.PeripheralActivity (has extras)} from uid 10034 on display 0
V/WindowManager( 802): addAppToken: AppWindowToken{2ff62ab token=Token{225f8cfa ActivityRecord{f3dc325 u0 com.example.user.advertiser/.PeripheralActivity t89}}} to stack=1 task=89 at 0
V/WindowManager( 802): Adding window Window{2d18b520 u0 Starting com.example.user.advertiser} at 3 of 9 (after Window{14f690b7 u0 com.google.android.googlequicksearchbox/com.google.android.launcher.GEL})
D/BluetoothManagerService( 802): checkIfCallerIsForegroundUser: valid=true callingUser=0 parentUser=-10000 foregroundUser=0
D/BluetoothManagerService( 802): checkIfCallerIsForegroundUser: valid=true callingUser=0 parentUser=-10000 foregroundUser=0
D/BluetoothLeAdvertiser( 4018): onClientRegistered() - status=0 clientIf=5
V/WindowManager( 802): Adding window Window{16d3a650 u0 com.example.user.advertiser/com.example.user.advertiser.PeripheralActivity} at 3 of 10 (before Window{2d18b520 u0 Starting com.example.user.advertiser})
I/ActivityManager( 802): Displayed com.example.user.advertiser/.PeripheralActivity: +129ms
E/bt-att ( 3236): MTU request PDU with MTU size 517
W/bt-att ( 3236): Call back not found for application conn_id=3
W/bt-att ( 3236): Call back not found for application conn_id=4
W/bt-att ( 3236): Call back not found for application conn_id=5
W/bt-btif ( 3236): info:x0
E/BluetoothRemoteDevices( 3236): aclStateChangeCallback: Device is NULL
W/bt-btif ( 3236): bta_gattc_conn_cback() - cif=3 connected=0 conn_id=3 reason=0x0016
W/bt-btif ( 3236): bta_gattc_conn_cback() - cif=4 connected=0 conn_id=4 reason=0x0016
W/bt-btif ( 3236): bta_gattc_conn_cback() - cif=5 connected=0 conn_id=5 reason=0x0016
E/bt-btm ( 3236): btm_sec_disconnected - Clearing Pending flag
E/BluetoothRemoteDevices( 3236): aclStateChangeCallback: Device is NULL
W/PackageManager( 1045): Failure retrieving resources for com.example.user.advertiser: Resource ID
public class PeripheralActivity extends Activity {
private static final String TAG = "PeripheralActivity";
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
private ArrayList<BluetoothDevice> mConnectedDevices;
private ArrayAdapter<BluetoothDevice> mConnectedDevicesAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ListView list = new ListView(this);
setContentView(list);
mConnectedDevices = new ArrayList<BluetoothDevice>();
mConnectedDevicesAdapter = new ArrayAdapter<BluetoothDevice>(this,
android.R.layout.simple_list_item_1, mConnectedDevices);
list.setAdapter(mConnectedDevicesAdapter);
mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(intentReceiver, makeGattUpdateIntentFilter());
mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
startAdvertising();
}
@Override
protected void onPause() {
super.onPause();
stopAdvertising();
if (intentReceiver != null)
{
unregisterReceiver(intentReceiver);
}
}
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.i(TAG, "onConnectionStateChange "
+DeviceProfile.getStatusDescription(status)+" "
+DeviceProfile.getStateDescription(newState));
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "connected");
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "disconnected");
} else if (newState == BluetoothProfile.STATE_CONNECTING) {
Log.i(TAG, "attempting to connect");
}
}
private BroadcastReceiver intentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent receivedIntent) {
Log.v(TAG, "Entered intent receiver");
if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(receivedIntent.getAction()))
{
Log.v(TAG, "Received Pairing request");
}
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(receivedIntent.getAction()))
{
Log.v(TAG, "connected");
}
if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(receivedIntent.getAction()))
{
Log.v(TAG, "disconnected");
}
if (BluetoothDevice.ACTION_FOUND.equals(receivedIntent.getAction()))
{
Log.v(TAG, "action found");
BluetoothDevice foundDevice = receivedIntent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.v(TAG, foundDevice.getName()+" "+foundDevice.getAddress()+" was found");
BluetoothDevice foundDeviceClass = receivedIntent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
Log.v(TAG, "BT class: "+foundDeviceClass.toString());
}
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(receivedIntent.getAction()))
{
Log.v(TAG, "bond state changed");
}
}
};
private void startAdvertising() {
if (mBluetoothLeAdvertiser == null) return;
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
.setConnectable(true)
.setTimeout(0)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
.build();
AdvertiseData data = new AdvertiseData.Builder()
.setIncludeDeviceName(true)
.addServiceUuid(new ParcelUuid(DeviceProfile.SERVICE_UUID))
.build();
mBluetoothLeAdvertiser.startAdvertising(settings, data, mAdvertiseCallback);
}
private void stopAdvertising() {
if (mBluetoothLeAdvertiser == null) return;
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
}
private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
Log.i(TAG, "Peripheral Advertise Started.");
}
@Override
public void onStartFailure(int errorCode) {
Log.w(TAG, "Peripheral Advertise Failed: ");
}
};
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
return intentFilter;
}
}