먼저 내 앱이 BLE로 누가의 데이터를 읽을 수 없습니다. BlueNRG-MS 모듈을 사용하여 기기에 연결하고 있습니다. 최대 Marshmallow onCharacteristicChanged 메서드가 호출되었으며 데이터를받을 수 있습니다. 그러나 7.0.0 이상의 onCharacteristicChanged 메서드는 호출되지 않습니다.android nougat (7.0.0 이상)의 BLE (Bluetooth Low Energy)에서 데이터를 읽을 수 없습니다.
나는 이것을 검색하고 누군가이 코드를 추가하라고 알려줍니다.
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_UUID);
if (descriptor != null) {
descriptor.setValue(enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
하지만 정확히 무엇을 CLIENT_UUID에 넣어야할지 모르겠습니다.
그리고 내 모든 코드입니다.
public class BleManager {
private static final String TAG = "BleManager";
static final private UUID CCCD_ID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
public static final int STATE_ERROR = -1;
public static final int STATE_NONE = 0; // Initialized
public static final int STATE_IDLE = 1; // Not connected
public static final int STATE_SCANNING = 2; // Scanning
public static final int STATE_CONNECTING = 13; // Connecting
public static final int STATE_CONNECTED = 16; // Connected
public static final int MESSAGE_STATE_CHANGE = 1;
public static final int MESSAGE_READ = 2;
public static final int MESSAGE_WRITE = 3;
public static final int MESSAGE_DEVICE_NAME = 4;
public static final int MESSAGE_TOAST = 5;
public static final long SCAN_PERIOD = 5*1000;
private static Context mContext = null;
private static BleManager mBleManager = null;
private final Handler mHandler;
private final BluetoothAdapter mBluetoothAdapter;
private BluetoothAdapter.LeScanCallback mLeScanCallback = null;
private ArrayList<BluetoothDevice> mDeviceList = new ArrayList<BluetoothDevice>();
private BluetoothDevice mDefaultDevice = null;
private BluetoothGatt mBluetoothGatt = null;
private ArrayList<BluetoothGattService> mGattServices
= new ArrayList<BluetoothGattService>();
private ArrayList<BluetoothGattCharacteristic> mGattCharacteristics
= new ArrayList<BluetoothGattCharacteristic>();
private ArrayList<BluetoothGattCharacteristic> mWritableCharacteristics
= new ArrayList<BluetoothGattCharacteristic>();
private BluetoothGattCharacteristic mDefaultChar = null;
private int mState = -1;
/**
* Constructor. Prepares a new Bluetooth session.
* @param context The UI Activity Context
* @param h A Listener to receive messages back to the UI Activity
*/
private BleManager(Context context, Handler h) {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;
mHandler = h;
mContext = context;
if(mContext == null)
return;
}
public synchronized static BleManager getInstance(Context c, Handler h) {
if(mBleManager == null)
mBleManager = new BleManager(c, h);
return mBleManager;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public synchronized void finalize() {
if (mBluetoothAdapter != null) {
mState = STATE_IDLE;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
disconnect();
}
mDefaultDevice = null;
mBluetoothGatt = null;
mDefaultService = null;
mGattServices.clear();
mGattCharacteristics.clear();
mWritableCharacteristics.clear();
if(mContext == null)
return;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void stopScanning() {
if(mState < STATE_CONNECTING) {
mState = STATE_IDLE;
mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_IDLE, 0).sendToTarget();
}
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private int checkGattServices(List<BluetoothGattService> gattServices) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.d(TAG, "# BluetoothAdapter not initialized");
return -1;
}
for (BluetoothGattService gattService : gattServices) {
Log.d(TAG, "# GATT Service: "+gattService.toString());
Toast.makeText(mContext, "" + gattService.toString(), Toast.LENGTH_SHORT).show();
mGattServices.add(gattService);
List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
Toast.makeText(mContext, "" + gattCharacteristic.toString(), Toast.LENGTH_SHORT).show();
mGattCharacteristics.add(gattCharacteristic);
Log.d(TAG, "# GATT Char: "+gattCharacteristic.toString());
boolean isWritable = isWritableCharacteristic(gattCharacteristic);
if(isWritable) {
mWritableCharacteristics.add(gattCharacteristic);
}
boolean isReadable = isReadableCharacteristic(gattCharacteristic);
if(isReadable) {
readCharacteristic(gattCharacteristic);
}
if(isNotificationCharacteristic(gattCharacteristic)) {
setCharacteristicNotification(gattCharacteristic, true);
if(isWritable && isReadable) {
mDefaultChar = gattCharacteristic;
}
}
}
}
return mWritableCharacteristics.size();
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private boolean isWritableCharacteristic(BluetoothGattCharacteristic chr) {
if(chr == null) return false;
final int charaProp = chr.getProperties();
if (((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) |
(charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) > 0) {
Log.d(TAG, "# Found writable characteristic");
return true;
} else {
Log.d(TAG, "# Not writable characteristic");
return false;
}
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private boolean isReadableCharacteristic(BluetoothGattCharacteristic chr) {
if(chr == null) return false;
final int charaProp = chr.getProperties();
if((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
Log.d(TAG, "# Found readable characteristic");
return true;
} else {
Log.d(TAG, "# Not readable characteristic");
return false;
}
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private boolean isNotificationCharacteristic(BluetoothGattCharacteristic chr) {
if(chr == null) return false;
final int charaProp = chr.getProperties();
if((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
Log.d(TAG, "# Found notification characteristic");
return true;
} else {
Log.d(TAG, "# Not notification characteristic");
return false;
}
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.d(TAG, "# BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.d(TAG, "# BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD_ID);
if (descriptor != null) {
descriptor.setValue(enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
public void setScanCallback(BluetoothAdapter.LeScanCallback cb) {
mLeScanCallback = cb;
}
public int getState() {
return mState;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean scanLeDevice(final boolean enable) {
boolean isScanStarted = false;
if (enable) {
if(mState == STATE_SCANNING)
return false;
if(mBluetoothAdapter.startLeScan(mLeScanCallback)) {
mState = STATE_SCANNING;
mDeviceList.clear();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
stopScanning();
}
}, SCAN_PERIOD);
mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_SCANNING, 0).sendToTarget();
isScanStarted = true;
}
} else {
if(mState < STATE_CONNECTING) {
mState = STATE_IDLE;
mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_IDLE, 0).sendToTarget();
}
stopScanning();
}
return isScanStarted;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean connectGatt(Context c, boolean bAutoReconnect, BluetoothDevice device) {
if(c == null || device == null)
return false;
mGattServices.clear();
mGattCharacteristics.clear();
mWritableCharacteristics.clear();
mBluetoothGatt = device.connectGatt(c, bAutoReconnect, mGattCallback);
mDefaultDevice = device;
mState = STATE_CONNECTING;
mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_CONNECTING, 0).sendToTarget();
return true;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean connectGatt(Context c, boolean bAutoReconnect, String address) {
if(c == null || address == null)
return false;
if(mBluetoothGatt != null && mDefaultDevice != null
&& address.equals(mDefaultDevice.getAddress())) {
if (mBluetoothGatt.connect()) {
mState = STATE_CONNECTING;
return true;
}
}
BluetoothDevice device =
BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
if (device == null) {
Log.d(TAG, "# Device not found. Unable to connect.");
return false;
}
mGattServices.clear();
mGattCharacteristics.clear();
mWritableCharacteristics.clear();
mBluetoothGatt = device.connectGatt(c, bAutoReconnect, mGattCallback);
mDefaultDevice = device;
mState = STATE_CONNECTING;
mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_CONNECTING, 0).sendToTarget();
return true;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void disconnect() {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.d(TAG, "# BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.disconnect();
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean write(BluetoothGattCharacteristic chr, byte[] data) {
if (mBluetoothGatt == null) {
Log.d(TAG, "# BluetoothGatt not initialized");
return false;
}
BluetoothGattCharacteristic writableChar = null;
if(chr == null) {
if(mDefaultChar == null) {
for(BluetoothGattCharacteristic bgc : mWritableCharacteristics) {
if(isWritableCharacteristic(bgc)) {
writableChar = bgc;
}
}
if(writableChar == null) {
Log.d(TAG, "# Write failed - No available characteristic");
return false;
}
} else {
if(isWritableCharacteristic(mDefaultChar)) {
Log.d(TAG, "# Default GattCharacteristic is PROPERY_WRITE | PROPERTY_WRITE_NO_RESPONSE");
writableChar = mDefaultChar;
} else {
Log.d(TAG, "# Default GattCharacteristic is not writable");
mDefaultChar = null;
return false;
}
}
} else {
if (isWritableCharacteristic(chr)) {
Log.d(TAG, "# user GattCharacteristic is PROPERY_WRITE | PROPERTY_WRITE_NO_RESPONSE");
writableChar = chr;
} else {
Log.d(TAG, "# user GattCharacteristic is not writable");
return false;
}
}
writableChar.setValue(data);
writableChar.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
mBluetoothGatt.writeCharacteristic(writableChar);
mDefaultChar = writableChar;
return true;
}
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mState = STATE_CONNECTED;
Log.d(TAG, "# Connected to GATT server.");
mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_CONNECTED, 0).sendToTarget();
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mState = STATE_IDLE;
Log.d(TAG, "# Disconnected from GATT server.");
mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_IDLE, 0).sendToTarget();
mBluetoothGatt = null;
mGattServices.clear();
mDefaultService = null;
mGattCharacteristics.clear();
mWritableCharacteristics.clear();
mDefaultChar = null;
mDefaultDevice = null;
}
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "# New GATT service discovered.");
checkGattServices(gatt.getServices());
} else {
Log.d(TAG, "# onServicesDiscovered received: " + status);
}
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// We've received data from remote
Log.d(TAG, "# Read characteristic: "+characteristic.toString());
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
stringBuilder.append(data);
Log.d(TAG, stringBuilder.toString());
mHandler.obtainMessage(MESSAGE_READ, byteArrayToHex(data)).sendToTarget();
}
if(mDefaultChar == null && isWritableCharacteristic(characteristic)) {
mDefaultChar = characteristic;
}
}
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// We've received data from remote
Log.d(TAG, "# onCharacteristicChanged: "+characteristic.toString());
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
//for(byte byteChar : data)
// stringBuilder.append(String.format("%02X ", byteChar));
stringBuilder.append(data);
Log.d(TAG, stringBuilder.toString());
mHandler.obtainMessage(MESSAGE_READ, byteArrayToHex(data)).sendToTarget();
}
if(mDefaultChar == null && isWritableCharacteristic(characteristic)) {
mDefaultChar = characteristic;
}
}
};
String byteArrayToHex(byte[] a) {
StringBuilder sb = new StringBuilder();
for(final byte b: a)
sb.append(String.format("%02x", b&0xff));
return sb.toString();
}}