* Copyright (c) 2024-2026 Huawei Device Co., Ltd.
* 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.
*/
package ohos.ace.plugin.bluetoothplugin;
import ohos.ace.plugin.bluetoothplugin.BluetoothHelper;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import java.util.HashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import ohos.ace.adapter.ALog;
public class CustomBluetoothManager {
private static final String LOG_TAG = "CustomBluetoothManager";
private static final int NOT_FIND = 0;
private static final Object INSTANCE_LOCK = new Object();
private static volatile CustomBluetoothManager INSTANCE = null;
private Context context_ = null;
private BluetoothManager bluetoothManager_ = null;
private BluetoothAdapter bluetoothAdapter_ = null;
private BluetoothLeScanner bluetoothLeScanner_ = null;
private CustomBluetoothScanCallback bluetoothScanCallback_ = new CustomBluetoothScanCallback();
private HashMap<Integer, Integer> bluetoothAdvertisingStatusMap_ = new HashMap<>();
private HashMap<Integer, AdvertiseCallback> bluetoothAdvertiseCallbackMap_ = new HashMap<>();
private HashMap<Integer, CustomGattServer> bluetoothGattServerMap_ = new HashMap<>();
private HashMap<Integer, BluetoothGattClient> bluetoothGattClientMap_ = new HashMap<Integer, BluetoothGattClient>();
private HashMap<String, BluetoothDevice> bluetoothDeviceMap_ = new HashMap<String, BluetoothDevice>();
private Lock bluetoothAdvertiserMapLock_ = new ReentrantLock();
private Lock bluetoothDeviceMapLock_ = new ReentrantLock();
private Lock bluetoothGattClientMapLock_ = new ReentrantLock();
private Lock bluetoothGattServerMapLock_ = new ReentrantLock();
private CustomBluetoothManager(Context context) {
if (context == null) {
ALog.e(LOG_TAG, "Constructor failure, context is null");
return;
}
try {
context_ = context;
bluetoothManager_ = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager_ == null) {
ALog.e(LOG_TAG, "Constructor failure, bluetoothManager_ is null");
}
} catch (IllegalStateException e) {
ALog.e(LOG_TAG, "Constructor failure, IllegalStateException err is " + e);
}
}
* Get CustomBluetoothManager object.
*
* @return The CustomBluetoothManager object.
*/
public static CustomBluetoothManager getInstance(Context context) {
if (INSTANCE != null) {
return INSTANCE;
}
synchronized (INSTANCE_LOCK) {
if (INSTANCE != null) {
return INSTANCE;
}
INSTANCE = new CustomBluetoothManager(context);
return INSTANCE;
}
}
public Context getContext() {
return context_;
}
public BluetoothAdapter getBluetoothAdapter() {
if (bluetoothAdapter_ != null) {
return bluetoothAdapter_;
} else {
if (bluetoothManager_ != null) {
bluetoothAdapter_ = bluetoothManager_.getAdapter();
return bluetoothAdapter_;
} else {
ALog.e(LOG_TAG, "getBluetoothAdapter failed, bluetoothManager is null");
return null;
}
}
}
public BluetoothLeScanner getBluetoothLeScanner() {
if (bluetoothLeScanner_ != null) {
return bluetoothLeScanner_;
} else {
if (bluetoothAdapter_ != null && bluetoothAdapter_.isEnabled()) {
bluetoothLeScanner_ = bluetoothAdapter_.getBluetoothLeScanner();
return bluetoothLeScanner_;
} else {
ALog.e(LOG_TAG, "getBluetoothLeScanner failed, bluetoothAdapter is error");
return null;
}
}
}
public boolean bluetoothAdapterIsEnabled() {
if (bluetoothAdapter_ == null) {
return false;
} else {
return bluetoothAdapter_.isEnabled();
}
}
public void addAdvertiseCallback(int advHandle, AdvertiseCallback advertiseCallback) {
bluetoothAdvertiserMapLock_.lock();
if (bluetoothAdvertiseCallbackMap_ != null) {
bluetoothAdvertiseCallbackMap_.put(advHandle, advertiseCallback);
}
bluetoothAdvertiserMapLock_.unlock();
}
public AdvertiseCallback getAdvertiseCallback(int advHandle) {
return (bluetoothAdvertiseCallbackMap_ != null) ? bluetoothAdvertiseCallbackMap_.get(advHandle) : null;
}
public void removeAdvertiseCallback(int advHandle) {
bluetoothAdvertiserMapLock_.lock();
if (bluetoothAdvertiseCallbackMap_ != null) {
bluetoothAdvertiseCallbackMap_.remove(advHandle);
}
bluetoothAdvertiserMapLock_.unlock();
}
public void setAdvertisingStatus(int advHandle, int status) {
bluetoothAdvertiserMapLock_.lock();
if (bluetoothAdvertisingStatusMap_ != null) {
bluetoothAdvertisingStatusMap_.put(advHandle, status);
}
bluetoothAdvertiserMapLock_.unlock();
}
public int getAdvertisingStatus(int advHandle) {
return (bluetoothAdvertisingStatusMap_ != null && bluetoothAdvertisingStatusMap_.containsKey(advHandle))
? bluetoothAdvertisingStatusMap_.get(advHandle)
: NOT_FIND;
}
public void removeAdvertisingStatus(int advHandle) {
bluetoothAdvertiserMapLock_.lock();
if (bluetoothAdvertisingStatusMap_ != null) {
bluetoothAdvertisingStatusMap_.remove(advHandle);
}
bluetoothAdvertiserMapLock_.unlock();
}
public int registerBluetoothGattServer(int appId, Context context) {
BluetoothErrorCode errCode = BluetoothErrorCode.BT_NO_ERROR;
if (bluetoothManager_ == null) {
return BluetoothErrorCode.BT_ERR_INTERNAL_ERROR.getId();
}
bluetoothGattServerMapLock_.lock();
try {
CustomGattServerCallback gattServerCallback = new CustomGattServerCallback();
if (gattServerCallback == null) {
errCode = BluetoothErrorCode.BT_ERR_INTERNAL_ERROR;
} else {
gattServerCallback.setApplicationId(appId);
BluetoothGattServer bluetoothGattServer = bluetoothManager_.openGattServer(context, gattServerCallback);
CustomGattServer customGattServer;
if (bluetoothGattServer == null || bluetoothGattServerMap_ == null ||
(customGattServer = new CustomGattServer(bluetoothGattServer)) == null) {
errCode = BluetoothErrorCode.BT_ERR_INTERNAL_ERROR;
} else {
bluetoothGattServerMap_.put(appId, customGattServer);
}
}
return errCode.getId();
} catch (IllegalArgumentException e) {
ALog.e(LOG_TAG, "registerBluetoothGattServer failure, IllegalArgumentException err is " + e);
return BluetoothErrorCode.BT_ERR_INTERNAL_ERROR.getId();
} finally {
bluetoothGattServerMapLock_.unlock();
}
}
public BluetoothGattServer getGattServer(int appId) {
CustomGattServer customGattServer;
return (bluetoothGattServerMap_ != null && (customGattServer = bluetoothGattServerMap_.get(appId)) != null)
? customGattServer.getBluetoothGattServer()
: null;
}
public CustomGattServer getCustomGattServer(int appId) {
return (bluetoothGattServerMap_ != null) ? bluetoothGattServerMap_.get(appId) : null;
}
public void removeBluetoothGattService(int appId, int handle) {
CustomGattServer customGattServer;
bluetoothGattServerMapLock_.lock();
if (bluetoothGattServerMap_ != null && (customGattServer = bluetoothGattServerMap_.get(appId)) != null) {
customGattServer.removeBluetoothGattService(handle);
}
bluetoothGattServerMapLock_.unlock();
}
public BluetoothGattService getBluetoothGattService(int appId, int handle) {
CustomGattServer customGattServer;
return (bluetoothGattServerMap_ != null && (customGattServer = bluetoothGattServerMap_.get(appId)) != null)
? customGattServer.getBluetoothGattService(handle)
: null;
}
public void gattServerClose(int appId) {
bluetoothGattServerMapLock_.lock();
if (bluetoothGattServerMap_ != null) {
bluetoothGattServerMap_.remove(appId);
}
bluetoothGattServerMapLock_.unlock();
}
public BluetoothGattCharacteristic getBluetoothGattCharacteristic(int appId, int characterHandle) {
CustomGattServer customGattServer;
return (bluetoothGattServerMap_ != null && (customGattServer = bluetoothGattServerMap_.get(appId)) != null)
? customGattServer.getBluetoothGattCharacter(characterHandle)
: null;
}
public boolean addBluetoothGattClient(int appId, BluetoothGattClient bluetoothGattClient) {
this.bluetoothGattClientMapLock_.lock();
try {
if (bluetoothGattClientMap_ != null && bluetoothGattClientMap_.containsKey(appId)) {
ALog.e(LOG_TAG, "The BluetoothGattClient Already exists, appId is " + String.valueOf(appId));
return false;
} else {
bluetoothGattClientMap_.put(appId, bluetoothGattClient);
ALog.i(LOG_TAG, "The BluetoothGattClient add successfully, appId is " + String.valueOf(appId));
return true;
}
} finally {
this.bluetoothGattClientMapLock_.unlock();
}
}
public BluetoothGattClient findBluetoothGattClient(int appId) {
if (bluetoothGattClientMap_ != null) {
return bluetoothGattClientMap_.get(appId);
} else {
return null;
}
}
public void deleteBluetoothGattClient(int appId) {
this.bluetoothGattClientMapLock_.lock();
try {
if (bluetoothGattClientMap_ != null && bluetoothGattClientMap_.containsKey(appId)) {
bluetoothGattClientMap_.remove(appId);
} else {
ALog.e(LOG_TAG, "delete BluetoothGattClient failed, appId is " + String.valueOf(appId));
}
} finally {
this.bluetoothGattClientMapLock_.unlock();
}
}
public BluetoothDevice findBluetoothDevice(String address) {
this.bluetoothDeviceMapLock_.lock();
try {
if (bluetoothDeviceMap_.containsKey(address)) {
return bluetoothDeviceMap_.get(address);
} else {
return null;
}
} finally {
this.bluetoothDeviceMapLock_.unlock();
}
}
public void addBluetoothDevice(String address, BluetoothDevice bluetoothDevice) {
this.bluetoothDeviceMapLock_.lock();
try {
if (bluetoothDeviceMap_ != null) {
bluetoothDeviceMap_.put(address, bluetoothDevice);
} else {
ALog.e(LOG_TAG, "add BluetoothDevice failed.");
}
} finally {
this.bluetoothDeviceMapLock_.unlock();
}
}
public void deleteBluetoothDevice() {
this.bluetoothDeviceMapLock_.lock();
try {
if (bluetoothDeviceMap_ != null && !bluetoothDeviceMap_.isEmpty()) {
bluetoothDeviceMap_.clear();
} else {
ALog.e(LOG_TAG, "delete BluetoothDevice failed.");
}
} finally {
this.bluetoothDeviceMapLock_.unlock();
}
}
public ScanCallback getScanCallback(int scannerId) {
if (bluetoothScanCallback_ != null && bluetoothScanCallback_.checkScannerId(scannerId)) {
bluetoothScanCallback_.setScannerId(scannerId);
}
return bluetoothScanCallback_;
}
public class CustomBluetoothScanCallback extends ScanCallback {
private int scannerId_ = 0;
public void setScannerId(int id) {
scannerId_ = id;
}
public boolean checkScannerId(int id) {
return (scannerId_ == id) ? false : true;
}
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
BluetoothDevice bluetoothDevice = result.getDevice();
if (bluetoothDevice.getName() != null) {
if (!bluetoothDevice.getName().isEmpty() && !bluetoothDevice.getName().equalsIgnoreCase("null")) {
addBluetoothDevice(bluetoothDevice.getAddress(), bluetoothDevice);
}
}
BluetoothPlugin.nativeOnScanResult(
BluetoothHelper.encapsulationBluetoothLeScanResults(result), scannerId_);
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
ALog.e(LOG_TAG, "StartScan callback onScanFailed; errorCode is " + errorCode);
}
}
public class CustomGattServerCallback extends BluetoothGattServerCallback {
private int applicationId_ = 0;
public void setApplicationId(int appId) {
applicationId_ = appId;
}
@Override
public void onMtuChanged(BluetoothDevice device, int mtu) {
super.onMtuChanged(device, mtu);
String deviceData = BluetoothHelper.convertBluetoothDeviceToJString(device);
Intent intent = new Intent(BluetoothPlugin.BLE_SERVER_MTU_CHANGE);
intent.putExtra("applicationId", applicationId_);
intent.putExtra("device", deviceData);
intent.putExtra("mtu", mtu);
BluetoothPlugin.sendBleBroadcast(intent);
}
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
String deviceData = BluetoothHelper.convertBluetoothDeviceToJString(device);
Intent intent = new Intent(BluetoothPlugin.BLE_SERVER_CONNECTION_STATE_CHANGE);
intent.putExtra("applicationId", applicationId_);
intent.putExtra("device", deviceData);
intent.putExtra("state", newState);
BluetoothPlugin.sendBleBroadcast(intent);
}
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
if (bluetoothGattServerMap_ != null) {
String deviceData = BluetoothHelper.convertBluetoothDeviceToJString(device);
String serviceUuid = characteristic.getService().getUuid().toString();
String characteristicUuid = characteristic.getUuid().toString();
int characteristicHandle = bluetoothGattServerMap_.get(applicationId_).getCharacteristicHandleByUuid(
serviceUuid, characteristicUuid);
String characteristicData = BluetoothHelper.convertCharacteristicToJString(
characteristic, characteristicHandle);
bluetoothGattServerMap_.get(applicationId_).setRequestId(requestId);
Intent intent = new Intent(BluetoothPlugin.BLE_SERVER_READ_CHARACTERISTIC);
intent.putExtra("applicationId", applicationId_);
intent.putExtra("device", deviceData);
intent.putExtra("characteristic", characteristicData);
BluetoothPlugin.sendBleBroadcast(intent);
}
}
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded,
offset, value);
if (bluetoothGattServerMap_ != null) {
bluetoothGattServerMap_.get(applicationId_).setRequestId(requestId);
String deviceData = BluetoothHelper.convertBluetoothDeviceToJString(device);
String serviceUuid = characteristic.getService().getUuid().toString();
String characteristicUuid = characteristic.getUuid().toString();
int characteristicHandle = bluetoothGattServerMap_.get(applicationId_).getCharacteristicHandleByUuid(
serviceUuid, characteristicUuid);
String characteristicData = BluetoothHelper.convertCharacteristicToJString(characteristic,
characteristicHandle);
Intent intent = new Intent(BluetoothPlugin.BLE_SERVER_WRITE_CHARACTERISTIC);
intent.putExtra("applicationId", applicationId_);
intent.putExtra("device", deviceData);
intent.putExtra("responseNeeded", responseNeeded ? 1 : 0);
intent.putExtra("characteristic", characteristicData);
BluetoothPlugin.sendBleBroadcast(intent);
}
}
@Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
int offset, BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
if (bluetoothGattServerMap_ != null) {
String deviceData = BluetoothHelper.convertBluetoothDeviceToJString(device);
String serviceUuid = descriptor.getCharacteristic().getService().getUuid().toString();
String descriptorUuid = descriptor.getUuid().toString();
int descriptorHandle = bluetoothGattServerMap_.get(applicationId_).getDescriptorHandleByUuid(
serviceUuid, descriptorUuid);
String descriptorData = BluetoothHelper.convertDescriptorToJSONString(descriptor, descriptorHandle);
bluetoothGattServerMap_.get(applicationId_).setRequestId(requestId);
Intent intent = new Intent(BluetoothPlugin.BLE_SERVER_READ_DESCRIPTOR);
intent.putExtra("applicationId", applicationId_);
intent.putExtra("device", deviceData);
intent.putExtra("descriptor", descriptorData);
BluetoothPlugin.sendBleBroadcast(intent);
}
}
@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset,
byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite,
responseNeeded, offset, value);
if (bluetoothGattServerMap_ != null) {
bluetoothGattServerMap_.get(applicationId_).setRequestId(requestId);
String deviceData = BluetoothHelper.convertBluetoothDeviceToJString(device);
String serviceUuid = descriptor.getCharacteristic().getService().getUuid().toString();
String descriptorUuid = descriptor.getUuid().toString();
int descriptorHandle = bluetoothGattServerMap_.get(applicationId_).getDescriptorHandleByUuid(
serviceUuid, descriptorUuid);
if (value != null) {
descriptor.setValue(value);
}
String descriptorData = BluetoothHelper.convertDescriptorToJSONString(descriptor, descriptorHandle,
value);
Intent intent = new Intent(BluetoothPlugin.BLE_SERVER_WRITE_DESCRIPTOR);
intent.putExtra("applicationId", applicationId_);
intent.putExtra("device", deviceData);
intent.putExtra("descriptor", descriptorData);
BluetoothPlugin.sendBleBroadcast(intent);
}
}
@Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
if (bluetoothGattServerMap_ != null) {
String deviceData = BluetoothHelper.convertBluetoothDeviceToJString(device);
Intent intent = new Intent(BluetoothPlugin.BLE_SERVER_NOTIFICATION_SENT);
intent.putExtra("applicationId", applicationId_);
intent.putExtra("device", deviceData);
intent.putExtra("status", status);
BluetoothPlugin.sendBleBroadcast(intent);
}
}
}
}