我正在开发一个在Android上运行的外设模式BLE应用程序。有几个很好的功能示例描述了如何开发外设模式应用程序here和here。
示例应用程序与我需要开发的系统之间存在一个细微差别。在示例应用程序中,GATT服务器在外围模式设备上运行。外围设备通告,集中扫描并查找设备并向GATT服务器发送请求。
在我的系统中,GATT服务器将位于中央模式设备上。外围设备将收到连接请求,需要找到请求设备的地址并启动与中央模式设备上的GATT服务器的连接。
命名和详细操作
MyApp(BLE外围设备;广告商,GATT客户端)
TgtDev(BLE中心;扫描仪,运行GATT服务器)
我的申请代码如下所示。我希望在收到连接请求时收到MyApp的意图。看起来这应该可以使用registerReceiver()的适当的intent过滤器。但是,我没有看到在发出连接请求时调用BroadcastReceiver。
提供了GattCallback()函数的实现,但未使用。要注册回调,我需要连接设备的地址。
有关我在这里缺少什么的建议吗?
编辑:添加有关系统和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 #0x0
申请代码
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);
/*
* Bluetooth in Android 4.3+ is accessed via the BluetoothManager, rather than
* the old static BluetoothAdapter.getInstance()
*/
mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
}
@Override
protected void onResume() {
super.onResume();
// extraneous code; check if bluetooth is on, advertising is supported etc.
registerReceiver(intentReceiver, makeGattUpdateIntentFilter());
mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
startAdvertising();
}
@Override
protected void onPause() {
super.onPause();
stopAdvertising();
if (intentReceiver != null)
{
unregisterReceiver(intentReceiver);
}
}
/*
* Callback handles all incoming requests from GATT clients.
* From connections to read/write requests.
*/
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");
}
}
};
/*
* Initialize the advertiser
*/
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);
}
/*
* Terminate the advertiser
*/
private void stopAdvertising() {
if (mBluetoothLeAdvertiser == null) return;
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
}
/*
* Callback handles events from the framework describing
* if we were successful in starting the advertisement requests.
*/
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;
}
}