[desc]:同步蓝牙模块代码

[author]:wangyimiao
master
yimiao 3 years ago
parent d05ddca018
commit 2e58b77dcc

@ -25,13 +25,25 @@
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat"/> android:theme="@style/Theme.AppCompat"/>
<service <service
android:name="com.common.bluetooth.service.BLEReceiveService" android:name="com.common.bluetooth.service.ble.BLEReceiveService"
android:exported="true" android:exported="true"
android:enabled="true"> android:enabled="true">
</service> </service>
<service <service
android:name="com.common.bluetooth.service.BLEClientService" android:name="com.common.bluetooth.service.ble.BLEClientService"
android:exported="true"
android:enabled="true">
</service>
<service
android:name="com.common.bluetooth.service.bt.BTCReceiverService"
android:exported="true"
android:enabled="true">
</service>
<service
android:name="com.common.bluetooth.service.bt.BTCClientService"
android:exported="true" android:exported="true"
android:enabled="true"> android:enabled="true">
</service> </service>

@ -2,6 +2,7 @@ package com.common.bluetooth
import android.content.Context import android.content.Context
import android.provider.Settings import android.provider.Settings
import java.nio.charset.StandardCharsets
import java.util.* import java.util.*
/** /**
@ -13,6 +14,8 @@ import java.util.*
object BtConstants { object BtConstants {
const val BT_NAME = "innovation bt" const val BT_NAME = "innovation bt"
val DEFAULT_CHARSET = StandardCharsets.UTF_8
/** /**
* 蓝牙类型 * 蓝牙类型
*/ */
@ -80,6 +83,10 @@ object BtConstants {
*/ */
const val MAX_MTU = 35 const val MAX_MTU = 35
/**
* 经典蓝牙连接UUID
*/
val CLASSIC_BT_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
val UUID_SERVICE = UUID.fromString("66f564dc-121f-3e7f-80b1-f005d3f194c9") val UUID_SERVICE = UUID.fromString("66f564dc-121f-3e7f-80b1-f005d3f194c9")
val UUID_CHARACTERISTIC_NOTIFY = UUID.fromString("66f564dd-121f-3e7f-80b1-f005d3f194c9") val UUID_CHARACTERISTIC_NOTIFY = UUID.fromString("66f564dd-121f-3e7f-80b1-f005d3f194c9")
val UUID_CHARACTERISTIC_WRITE = UUID.fromString("66f564de-121f-3e7f-80b1-f005d3f194c9") val UUID_CHARACTERISTIC_WRITE = UUID.fromString("66f564de-121f-3e7f-80b1-f005d3f194c9")

@ -22,6 +22,7 @@ import com.common.bluetooth.bean.CommonMsg;
import com.common.bluetooth.bean.KeyboardEvent; import com.common.bluetooth.bean.KeyboardEvent;
import com.common.bluetooth.callback.BLEClientListener; import com.common.bluetooth.callback.BLEClientListener;
import com.common.bluetooth.databinding.ActivityMainBinding; import com.common.bluetooth.databinding.ActivityMainBinding;
import com.common.bluetooth.utils.BtUtils;
import com.common.bluetooth.view.BtDeviceListAdapter; import com.common.bluetooth.view.BtDeviceListAdapter;
import com.google.gson.Gson; import com.google.gson.Gson;
@ -131,10 +132,23 @@ public class BtDemoActivity extends AppCompatActivity {
} }
})); }));
}); });
mBinding.btcServerBuild.setOnClickListener(v -> {
BtManager.INSTANCE.initServer(this, BtConstants.BLUETOOTH_TYPE.CLASSIC);
BtManager.INSTANCE.setMsgReceiverListener(msg -> {
Log.d(TAG, new String(msg));
runOnUiThread(new Runnable() {
@Override
public void run() {
mBinding.btcReceiveContent.setText(new String(msg));
}
});
});
});
} }
private void checkBluetooth() { private void checkBluetooth() {
BtManager.INSTANCE.initClient(this, BtConstants.BLUETOOTH_TYPE.BLE); BtManager.INSTANCE.initClient(this, BtConstants.BLUETOOTH_TYPE.CLASSIC);
BtManager.INSTANCE.btPermissionCheck(this, PERMISSION_REQUEST_LOCATION); BtManager.INSTANCE.btPermissionCheck(this, PERMISSION_REQUEST_LOCATION);
} }

@ -11,20 +11,25 @@ import android.content.ServiceConnection
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.util.Log.d import android.util.Log.*
import android.util.Log.e
import androidx.core.content.ContextCompat.checkSelfPermission import androidx.core.content.ContextCompat.checkSelfPermission
import com.common.bluetooth.BtConstants.BLUETOOTH_TYPE import com.common.bluetooth.BtConstants.BLUETOOTH_TYPE
import com.common.bluetooth.adapter.BluetoothClientBLEAdapter import com.common.bluetooth.service.ble.BluetoothClientBLEAdapter
import com.common.bluetooth.bean.CommonMsg import com.common.bluetooth.bean.CommonMsg
import com.common.bluetooth.callback.BLEClientListener import com.common.bluetooth.callback.BLEClientListener
import com.common.bluetooth.callback.BleMsgReceiverListener import com.common.bluetooth.callback.BtMsgListener
import com.common.bluetooth.callback.BtServiceListener
import com.common.bluetooth.callback.MsgReceiverListener
import com.common.bluetooth.interfaces.IBluetoothClient import com.common.bluetooth.interfaces.IBluetoothClient
import com.common.bluetooth.service.BLEClientService import com.common.bluetooth.service.ble.BLEClientService
import com.common.bluetooth.service.BLEClientService.BLEClientBinder import com.common.bluetooth.service.ble.BLEClientService.BLEClientBinder
import com.common.bluetooth.service.BLEReceiveService import com.common.bluetooth.service.ble.BLEReceiveService
import com.common.bluetooth.service.BLEReceiveService.ReceiverBinder import com.common.bluetooth.service.ble.BLEReceiveService.ReceiverBinder
import com.common.bluetooth.service.BluetoothLeClient import com.common.bluetooth.service.ble.BluetoothLeClient
import com.common.bluetooth.service.bt.BTCClientService
import com.common.bluetooth.service.bt.BTCReceiverService
import com.common.bluetooth.service.bt.BluetoothClassicClient
import com.common.bluetooth.service.bt.BluetoothClientClassicAdapter
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observer import io.reactivex.rxjava3.core.Observer
import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.disposables.Disposable
@ -45,34 +50,59 @@ object BtManager {
private var mBtClient: IBluetoothClient? = null private var mBtClient: IBluetoothClient? = null
/** /**
* 客户端服务 * BLE客户端服务
*/ */
private var clientService: BLEClientService? = null private var clientService: BLEClientService? = null
/** /**
* 服务端服务 * 经典蓝牙客户端服务
*/
private var classicClientService: BTCClientService? = null
/**
* BLE服务端服务
*/ */
private var serverService: BLEReceiveService? = null private var serverService: BLEReceiveService? = null
/**
* 经典蓝牙服务端服务
*/
private var classicServerService: BTCReceiverService? = null
/** /**
* 消息接收监听 * 消息接收监听
*/ */
var msgReceiverListener: BleMsgReceiverListener? = null var msgReceiverListener: MsgReceiverListener? = null
/** /**
* 蓝牙客户端监听 * 蓝牙客户端监听
*/ */
var clientListener: BLEClientListener? = null var clientListener: BLEClientListener? = null
/**
* 蓝牙服务链接监听
*/
var btServiceListener: BtServiceListener? = null
/**
* 是否绑定了服务端
*/
private var isBindBleServer: AtomicBoolean = AtomicBoolean(false)
/**
* 是否绑定了客户端
*/
private var isBindBleClient: AtomicBoolean = AtomicBoolean(false)
/** /**
* 是否绑定了服务端 * 是否绑定了服务端
*/ */
private var isBindServer: AtomicBoolean = AtomicBoolean(false) private var isBindClassicServer: AtomicBoolean = AtomicBoolean(false)
/** /**
* 是否绑定了客户端 * 是否绑定了客户端
*/ */
private var isBindClient: AtomicBoolean = AtomicBoolean(false) private var isBindClassicClient: AtomicBoolean = AtomicBoolean(false)
/** /**
* 当前使用的编码 默认是UTF-8 * 当前使用的编码 默认是UTF-8
@ -89,55 +119,111 @@ object BtManager {
* 初始化蓝牙服务端模块 * 初始化蓝牙服务端模块
*/ */
fun initServer(context: Context, btType: BLUETOOTH_TYPE) { fun initServer(context: Context, btType: BLUETOOTH_TYPE) {
mBtClient = BluetoothClientBLEAdapter(BluetoothLeClient.getInstance(context)) if (btType == BLUETOOTH_TYPE.BLE) {
mBtClient = BluetoothClientBLEAdapter(
BluetoothLeClient.getInstance(context)
)
initBleReceiverService(context)
} else if (btType == BLUETOOTH_TYPE.CLASSIC) {
mBtClient = BluetoothClientClassicAdapter(BluetoothClassicClient(context))
initClassicReceiverService(context)
}
mBtClient!!.checkBluetoothDevice(btType) mBtClient!!.checkBluetoothDevice(btType)
initReceiverService(context)
} }
/** /**
* 初始化蓝牙客户端模块 * 初始化蓝牙客户端模块
*/ */
fun initClient(context: Activity, btType: BLUETOOTH_TYPE) { fun initClient(context: Activity, btType: BLUETOOTH_TYPE) {
mBtClient = BluetoothClientBLEAdapter(BluetoothLeClient.getInstance(context)) if (btType == BLUETOOTH_TYPE.BLE) {
mBtClient = BluetoothClientBLEAdapter(
BluetoothLeClient.getInstance(context)
)
initBleClientService(context)
} else if (btType == BLUETOOTH_TYPE.CLASSIC) {
mBtClient = BluetoothClientClassicAdapter(
BluetoothClassicClient(context)
)
initClassicClientService(context)
}
mBtClient!!.checkBluetoothDevice(btType) mBtClient!!.checkBluetoothDevice(btType)
initClientService(context)
} }
/** /**
* 初始化接收端服务 * 初始化接收端服务
*/ */
private fun initReceiverService(context: Context) { private fun initBleReceiverService(context: Context) {
val intent = Intent(context, BLEReceiveService::class.java) val intent = Intent(context, BLEReceiveService::class.java)
// 标志位BIND_AUTO_CREATE是的服务中onCreate得到执行,onStartCommand不会执行 // 标志位BIND_AUTO_CREATE是的服务中onCreate得到执行,onStartCommand不会执行
context.bindService(intent, receiverConn, Context.BIND_AUTO_CREATE) context.bindService(intent, bleReceiverConn, Context.BIND_AUTO_CREATE)
isBindServer.set(true) isBindBleServer.set(true)
}
private fun initClassicReceiverService(context: Context) {
val intent = Intent(context, BTCReceiverService::class.java)
// 标志位BIND_AUTO_CREATE是的服务中onCreate得到执行,onStartCommand不会执行
context.bindService(intent, classicReceiverConn, Context.BIND_AUTO_CREATE)
isBindClassicServer.set(true)
} }
/** /**
* 初始化客户端服务 * 初始化客户端服务
*/ */
private fun initClientService(context: Context) { private fun initBleClientService(context: Context) {
val intent = Intent(context, BLEClientService::class.java) val intent = Intent(context, BLEClientService::class.java)
// 标志位BIND_AUTO_CREATE是的服务中onCreate得到执行,onStartCommand不会执行 // 标志位BIND_AUTO_CREATE是的服务中onCreate得到执行,onStartCommand不会执行
context.bindService(intent, clientConn, Context.BIND_AUTO_CREATE) context.bindService(intent, bleClientConn, Context.BIND_AUTO_CREATE)
isBindClient.set(true) isBindBleClient.set(true)
}
private fun initClassicClientService(context: Context) {
val intent = Intent(context, BTCClientService::class.java)
// 标志位BIND_AUTO_CREATE是的服务中onCreate得到执行,onStartCommand不会执行
context.bindService(intent, classicClientConn, Context.BIND_AUTO_CREATE)
isBindClassicClient.set(true)
} }
private val receiverConn: ServiceConnection = object : ServiceConnection { private val bleReceiverConn: ServiceConnection by lazy {
object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) { override fun onServiceConnected(name: ComponentName, service: IBinder) {
serverService = (service as ReceiverBinder).service serverService = (service as ReceiverBinder).service
serverService?.setMsgReceiveListener(object : BleMsgReceiverListener { serverService?.setMsgReceiveListener(object : MsgReceiverListener {
override fun onMsgReceive(msg: ByteArray) { override fun onMsgReceive(msg: ByteArray) {
msgReceiverListener?.onMsgReceive(msg) msgReceiverListener?.onMsgReceive(msg)
} }
}) })
btServiceListener?.onServiceReady()
}
override fun onServiceDisconnected(name: ComponentName) {
serverService = null
btServiceListener?.onServiceDisConnected()
}
}
}
private val classicReceiverConn: ServiceConnection by lazy {
object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
classicServerService = (service as BTCReceiverService.ClassicReceiverBinder).service
classicServerService?.setMsgReceiveListener(object : BtMsgListener() {
override fun socketNotify(state: Int, msg: String) {
msgReceiverListener?.onMsgReceive(msg.toByteArray())
}
})
btServiceListener?.onServiceReady()
} }
override fun onServiceDisconnected(name: ComponentName) {} override fun onServiceDisconnected(name: ComponentName) {
classicServerService = null
btServiceListener?.onServiceDisConnected()
}
}
} }
private val clientConn: ServiceConnection = object : ServiceConnection { private val bleClientConn: ServiceConnection by lazy {
object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) { override fun onServiceConnected(name: ComponentName, service: IBinder) {
clientService = (service as BLEClientBinder).service clientService = (service as BLEClientBinder).service
clientService?.setClientListener(object : BLEClientListener { clientService?.setClientListener(object : BLEClientListener {
@ -155,11 +241,58 @@ object BtManager {
clientListener?.onNotifyMsgReceive(msg) clientListener?.onNotifyMsgReceive(msg)
} }
}) })
btServiceListener?.onServiceReady()
} }
override fun onServiceDisconnected(name: ComponentName) { override fun onServiceDisconnected(name: ComponentName) {
clientService = null clientService = null
clientListener?.onResult(CommonMsg(BtConstants.DISCONNECT, "disconnect from server")) clientListener?.onResult(
CommonMsg(
BtConstants.DISCONNECT,
"disconnect from server"
)
)
btServiceListener?.onServiceDisConnected()
}
}
}
private val classicClientConn: ServiceConnection by lazy {
object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
classicClientService = (service as BTCClientService.ClassicClientBinder).service
classicClientService?.setMsgReceiveListener(object : BtMsgListener() {
override fun socketNotify(state: Int, msg: String) {
when (state) {
CONNECTED -> {
curConnectMac = msg
clientListener?.onResult(
CommonMsg(
BtConstants.CONNECT_SUCCESS,
msg
)
)
}
DISCONNECTED -> {
curConnectMac = ""
clientListener?.onResult(CommonMsg(BtConstants.DISCONNECT, msg))
}
MSG -> {
clientListener?.onNotifyMsgReceive(msg.toByteArray())
}
else -> {
e(TAG, "got error state:$state")
}
}
}
})
btServiceListener?.onServiceReady()
}
override fun onServiceDisconnected(name: ComponentName) {
classicClientService = null
btServiceListener?.onServiceDisConnected()
}
} }
} }
@ -205,10 +338,16 @@ object BtManager {
* @param mac MAC地址 * @param mac MAC地址
*/ */
fun connect(mac: String) { fun connect(mac: String) {
if (clientService == null) { if (clientService != null) {
e(TAG, "writeMsg pls init first")
} else {
clientService!!.connect(mac, true) clientService!!.connect(mac, true)
} else {
w(TAG, "init clientService first")
}
if (classicClientService != null) {
classicClientService!!.connect(mac)
} else {
w(TAG, "init classicClientService first")
} }
} }
@ -241,11 +380,17 @@ object BtManager {
* @param msg 传输的内容 * @param msg 传输的内容
*/ */
fun write(msg: ByteArray) { fun write(msg: ByteArray) {
if (clientService == null) { if (clientService != null) {
e(TAG, "writeMsg pls init first")
} else {
clientService!!.write(msg) clientService!!.write(msg)
} }
if (classicClientService != null) {
classicClientService!!.write(msg)
}
if (classicServerService != null) {
classicServerService!!.write(msg)
}
} }
/** /**
@ -301,19 +446,28 @@ object BtManager {
*/ */
fun sendNotify(msg: ByteArray) { fun sendNotify(msg: ByteArray) {
serverService?.sendNotify(msg) serverService?.sendNotify(msg)
classicServerService?.write(msg)
} }
/** /**
* 释放链接 * 释放链接
*/ */
fun release(context: Context?) { fun release(context: Context?) {
if (isBindServer.get()) { if (isBindBleServer.get()) {
context?.unbindService(receiverConn) context?.unbindService(bleReceiverConn)
isBindServer.set(false) isBindBleServer.set(false)
}
if (isBindClassicServer.get()) {
context?.unbindService(classicReceiverConn)
isBindClassicServer.set(false)
}
if (isBindBleClient.get()) {
context?.unbindService(bleClientConn)
isBindBleClient.set(false)
} }
if (isBindClient.get()) { if (isBindClassicClient.get()) {
context?.unbindService(clientConn) context?.unbindService(classicClientConn)
isBindClient.set(false) isBindClassicServer.set(false)
} }
} }
} }

@ -0,0 +1,33 @@
package com.common.bluetooth.callback
/**
* 经典蓝牙监听
*
* @author wangym
* @since 2021-12-1
*/
abstract class BtMsgListener {
/**
* 通知
* @param state 消息类型
* @param msg 消息内容
*/
open fun socketNotify(state: Int, msg: String) {}
companion object {
/**
* 断开连接
*/
const val DISCONNECTED = 0
/**
* 连接成功
*/
const val CONNECTED = 1
/**
* 消息
*/
const val MSG = 2
}
}

@ -0,0 +1,19 @@
package com.common.bluetooth.callback
/**
* BT服务监听
*
* @author wangym
* @since 2021-12-9
*/
interface BtServiceListener {
/**
* 服务准备完毕
*/
fun onServiceReady()
/**
* 服务断开
*/
fun onServiceDisConnected()
}

@ -3,7 +3,7 @@ package com.common.bluetooth.callback
/** /**
* BLE数据接收监听 * BLE数据接收监听
*/ */
interface BleMsgReceiverListener { interface MsgReceiverListener {
/** /**
* 接收的消息 * 接收的消息
* *

@ -24,7 +24,7 @@ interface IBluetoothClient {
* false 如果当前正在进行扫描操作则会抛出 [com.common.bluetooth.exception.BluetoothSearchConflictException] 错误 * false 如果当前正在进行扫描操作则会抛出 [com.common.bluetooth.exception.BluetoothSearchConflictException] 错误
* @return 扫描结果的列表无重复设备 * @return 扫描结果的列表无重复设备
*/ */
fun search(millis: Int, cancel: Boolean): Observable<BluetoothDevice> fun search(millis: Long, cancel: Boolean): Observable<BluetoothDevice>
/** /**
* 停止扫描 * 停止扫描
@ -38,7 +38,7 @@ interface IBluetoothClient {
* @param mac 需要连接蓝牙设备的地址 * @param mac 需要连接蓝牙设备的地址
* @return 成功返回连接设备的地址 * @return 成功返回连接设备的地址
*/ */
fun connect(mac: String?): Observable<String> fun connect(mac: String): Observable<String>
/** /**
* 断开蓝牙连接, 释放蓝牙连接占用的蓝牙服务 * 断开蓝牙连接, 释放蓝牙连接占用的蓝牙服务

@ -0,0 +1,120 @@
package com.common.bluetooth.service
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.pm.PackageManager
import android.util.Log
import com.common.bluetooth.BtConstants.BLUETOOTH_TYPE
import com.common.bluetooth.interfaces.IBluetoothSearch
import com.common.bluetooth.service.bt.BluetoothClassicBase
import com.common.bluetooth.service.bt.BluetoothClassicSearcher
/**
* 通用蓝牙
*
* @author wangym
* @since 2021-12-1
*/
open class BluetoothCommon(val mContext: Context) {
val TAG = "BluetoothCommon"
var mBluetoothAdapter: BluetoothAdapter? = null
var mBluetoothManager: BluetoothManager? = null
var mBluetoothSearcher: IBluetoothSearch? = null
/**
* 检查蓝牙设备是否支持
*
* @param btType 蓝牙类型
*/
fun checkBtDevice(btType: BLUETOOTH_TYPE): Boolean {
if (btType === BLUETOOTH_TYPE.BLE) {
// 检查当前手机是否支持ble 蓝牙
if (!mContext.packageManager
.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
) {
Log.e(TAG, "not support ble device")
return false
}
}
// For API level 18 and above, get a reference to BluetoothAdapter
// through BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager = mContext
.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager.")
return false
}
}
if (mBluetoothAdapter == null) {
mBluetoothAdapter = mBluetoothManager?.adapter
if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.")
return false
}
}
return true
}
/**
* 通过MAC地址获取设备对象
*
* @param mac mac地址
* @return 设备对象
*/
fun getBtDeviceByMac(mac: String?): BluetoothDevice? {
if (mBluetoothAdapter == null) {
Log.e(TAG, "getBtDeviceByMac mBluetoothAdapter is null")
return null
}
return mBluetoothAdapter!!.getRemoteDevice(mac)
}
/**
* 初始化BluetoothAdapter
* Initializes a reference to the local Bluetooth adapter.
*
* @return Return true if the initialization is successful.
*/
fun openBt(): Boolean {
if (mBluetoothAdapter == null) {
Log.e(TAG, "BluetoothManager do not init")
return false
}
return mBluetoothAdapter!!.isEnabled() || mBluetoothAdapter!!.enable()
}
fun closeBt(): Boolean {
if (mBluetoothAdapter == null) {
Log.e(TAG, "BluetoothManager do not init")
return false
}
return mBluetoothAdapter!!.disable()
}
/**
* 获取已配对蓝牙设备信息
*/
fun getBondedDevices(): Set<BluetoothDevice>? {
return mBluetoothAdapter?.bondedDevices
}
/**
* 获取蓝牙扫描对象
*
* @return 蓝牙扫描对象
*/
fun getBluetoothSearcher(): IBluetoothSearch {
if (mBluetoothSearcher == null) {
synchronized(BluetoothClassicBase::class.java) {
if (mBluetoothSearcher == null) {
mBluetoothSearcher = BluetoothClassicSearcher(mContext, mBluetoothAdapter)
}
}
}
return mBluetoothSearcher!!
}
}

@ -1,519 +0,0 @@
/*
* Copyright (C) 2014 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.
*/
package com.common.bluetooth.service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import com.common.bluetooth.BtConstants;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
/**
* This class does all the work for setting up and managing Bluetooth
* connections with other devices. It has a thread that listens for
* incoming connections, a thread for connecting with a device, and a
* thread for performing data transmissions when connected.
*/
public class BluetoothConnectService {
// Debugging
private static final String TAG = "BluetoothConnectService";
// Name for the SDP record when creating server socket
private static final String NAME_SECURE = "BluetoothChatSecure";
private static final String NAME_INSECURE = "BluetoothChatInsecure";
// Unique UUID for this application
private final UUID CURRENT_UUID;
// Member fields
private final BluetoothAdapter mAdapter;
private final Handler mHandler;
private AcceptThread mSecureAcceptThread;
private AcceptThread mInsecureAcceptThread;
private ConnectThread mConnectThread;
private ConnectedThread mConnectedThread;
private int mState;
private int mNewState;
// Constants that indicate the current connection state
public static final int STATE_NONE = 0; // we're doing nothing
public static final int STATE_LISTEN = 1; // now listening for incoming connections
public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
public static final int STATE_CONNECTED = 3; // now connected to a remote device
/**
* Constructor. Prepares a new BluetoothChat session.
*
* @param context The UI Activity Context
* @param handler A Handler to send messages back to the UI Activity
*/
public BluetoothConnectService(Context context, Handler handler) {
CURRENT_UUID = BtConstants.INSTANCE.getUUid(context);
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;
mNewState = mState;
mHandler = handler;
}
/**
* Update UI title according to the current state of the chat connection
*/
private synchronized void updateUserInterfaceTitle() {
mState = getState();
Log.d(TAG, "updateUserInterfaceTitle() " + mNewState + " -> " + mState);
mNewState = mState;
// Give the new state to the Handler so the UI Activity can update
mHandler.obtainMessage(BtConstants.MESSAGE_STATE_CHANGE, mNewState, -1).sendToTarget();
}
/**
* Return the current connection state.
*/
public synchronized int getState() {
return mState;
}
/**
* Start the chat service. Specifically start AcceptThread to begin a
* session in listening (server) mode. Called by the Activity onResume()
*/
public synchronized void start() {
Log.d(TAG, "start");
// Cancel any thread attempting to make a connection
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
// Start the thread to listen on a BluetoothServerSocket
if (mSecureAcceptThread == null) {
mSecureAcceptThread = new AcceptThread(false);
mSecureAcceptThread.start();
}
if (mInsecureAcceptThread == null) {
mInsecureAcceptThread = new AcceptThread(false);
mInsecureAcceptThread.start();
}
// Update UI title
updateUserInterfaceTitle();
}
/**
* Start the ConnectThread to initiate a connection to a remote device.
*
* @param device The BluetoothDevice to connect
*/
public synchronized void connect(BluetoothDevice device) {
Log.d(TAG, "connect to: " + device);
// Cancel any thread attempting to make a connection
if (mState == STATE_CONNECTING) {
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
// Start the thread to connect with the given device
mConnectThread = new ConnectThread(device, false);
mConnectThread.start();
// Update UI title
updateUserInterfaceTitle();
}
/**
* Start the ConnectedThread to begin managing a Bluetooth connection
*
* @param socket The BluetoothSocket on which the connection was made
* @param device The BluetoothDevice that has been connected
*/
public synchronized void connected(BluetoothSocket socket, BluetoothDevice
device, final String socketType) {
Log.d(TAG, "connected, Socket Type:" + socketType);
// Cancel the thread that completed the connection
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
// Cancel the accept thread because we only want to connect to one device
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
if (mInsecureAcceptThread != null) {
mInsecureAcceptThread.cancel();
mInsecureAcceptThread = null;
}
// Start the thread to manage the connection and perform transmissions
mConnectedThread = new ConnectedThread(socket, socketType);
mConnectedThread.start();
// Send the name of the connected device back to the UI Activity
Message msg = mHandler.obtainMessage(BtConstants.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(BtConstants.DEVICE_NAME, device.getName());
msg.setData(bundle);
mHandler.sendMessage(msg);
// Update UI title
updateUserInterfaceTitle();
}
/**
* Stop all threads
*/
public synchronized void stop() {
Log.d(TAG, "stop");
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
if (mInsecureAcceptThread != null) {
mInsecureAcceptThread.cancel();
mInsecureAcceptThread = null;
}
mState = STATE_NONE;
// Update UI title
updateUserInterfaceTitle();
}
/**
* Write to the ConnectedThread in an unsynchronized manner
*
* @param out The bytes to write
* @see ConnectedThread#write(byte[])
*/
public void write(byte[] out) {
// Create temporary object
ConnectedThread r;
// Synchronize a copy of the ConnectedThread
synchronized (this) {
if (mState != STATE_CONNECTED) return;
r = mConnectedThread;
}
// Perform the write unsynchronized
r.write(out);
}
/**
* Indicate that the connection attempt failed and notify the UI Activity.
*/
private void connectionFailed() {
// Send a failure message back to the Activity
Message msg = mHandler.obtainMessage(BtConstants.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(BtConstants.TOAST, "Unable to connect device");
msg.setData(bundle);
mHandler.sendMessage(msg);
mState = STATE_NONE;
// Update UI title
updateUserInterfaceTitle();
// Start the service over to restart listening mode
BluetoothConnectService.this.start();
}
/**
* Indicate that the connection was lost and notify the UI Activity.
*/
private void connectionLost() {
// Send a failure message back to the Activity
Message msg = mHandler.obtainMessage(BtConstants.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(BtConstants.TOAST, "Device connection was lost");
msg.setData(bundle);
mHandler.sendMessage(msg);
mState = STATE_NONE;
// Update UI title
updateUserInterfaceTitle();
// Start the service over to restart listening mode
BluetoothConnectService.this.start();
}
/**
* This thread runs while listening for incoming connections. It behaves
* like a server-side client. It runs until a connection is accepted
* (or until cancelled).
*/
private class AcceptThread extends Thread {
// The local server socket
private final BluetoothServerSocket mmServerSocket;
private String mSocketType;
public AcceptThread(boolean secure) {
BluetoothServerSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
// Create a new listening server socket
try {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
CURRENT_UUID);
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
}
mmServerSocket = tmp;
mState = STATE_LISTEN;
}
public void run() {
Log.d(TAG, "Socket Type: " + mSocketType +
"BEGIN mAcceptThread" + this);
setName("AcceptThread" + mSocketType);
BluetoothSocket socket = null;
// Listen to the server socket if we're not connected
while (mState != STATE_CONNECTED) {
try {
// This is a blocking call and will only return on a
// successful connection or an exception
socket = mmServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
break;
}
// If a connection was accepted
if (socket != null) {
synchronized (BluetoothConnectService.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
// Situation normal. Start the connected thread.
connected(socket, socket.getRemoteDevice(),
mSocketType);
break;
case STATE_NONE:
case STATE_CONNECTED:
// Either not ready or already connected. Terminate new socket.
try {
socket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close unwanted socket", e);
}
break;
}
}
}
}
Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
}
public void cancel() {
Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
try {
mmServerSocket.close();
} catch (IOException e) {
Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
}
}
}
/**
* This thread runs while attempting to make an outgoing connection
* with a device. It runs straight through; the connection either
* succeeds or fails.
*/
private class ConnectThread extends Thread {
private BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private String mSocketType;
public ConnectThread(BluetoothDevice device, boolean secure) {
mmDevice = device;
BluetoothSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
tmp = device.createRfcommSocketToServiceRecord(
CURRENT_UUID);
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
}
mmSocket = tmp;
mState = STATE_CONNECTING;
}
public void run() {
Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
setName("ConnectThread" + mSocketType);
// Always cancel discovery because it will slow down a connection
mAdapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket.connect();
} catch (IOException e) {
// Unable to connect; close the socket and return.
Log.e(TAG, e.toString());
try {
mmSocket.close();
} catch (IOException ie) {
connectionFailed();
}
return;
}
// Reset the ConnectThread because we're done
synchronized (BluetoothConnectService.this) {
mConnectThread = null;
}
// Start the connected thread
connected(mmSocket, mmDevice, mSocketType);
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
}
}
}
/**
* This thread runs during a connection with a remote device.
* It handles all incoming and outgoing transmissions.
*/
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket, String socketType) {
Log.d(TAG, "create ConnectedThread: " + socketType);
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
mState = STATE_CONNECTED;
}
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (mState == STATE_CONNECTED) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BtConstants.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
/**
* Write to the connected OutStream.
*
* @param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// Share the sent message back to the UI Activity
mHandler.obtainMessage(BtConstants.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
}

@ -1,172 +0,0 @@
package com.common.bluetooth.service;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import com.common.bluetooth.BtConstants;
import com.common.bluetooth.interfaces.IBluetoothSearch;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
*
*
* @author wangym
* @since 2021-10-19
*/
public class BluetoothLeClient {
@SuppressLint("StaticFieldLeak")
private static volatile BluetoothLeClient mInstance;
private final static String TAG = "BluetoothLeClient";
private final Context mContext;
private static Handler mBluetoothWorker;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothManager mBluetoothManager;
private IBluetoothSearch mBluetoothSearcher;
private final Map<String, BluetoothLeConnector> mGattConnectorMap
= new ConcurrentHashMap<>();
private BluetoothLeClient(Context context) {
mContext = context.getApplicationContext();
HandlerThread thread = new HandlerThread("bluetooth client worker");
thread.start();
mBluetoothWorker = new Handler(thread.getLooper());
}
public static BluetoothLeClient getInstance(Context context) {
if (mInstance == null) {
synchronized (BluetoothLeClient.class) {
if (mInstance == null) {
mInstance = new BluetoothLeClient(context);
}
}
}
return mInstance;
}
/**
*
*
* @param btType
*/
public boolean checkBtDevice(BtConstants.BLUETOOTH_TYPE btType) {
if (btType == BtConstants.BLUETOOTH_TYPE.BLE) {
// 检查当前手机是否支持ble 蓝牙
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Log.e(TAG, "not support ble device");
return false;
}
}
// For API level 18 and above, get a reference to BluetoothAdapter
// through BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) mContext
.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
if (mBluetoothAdapter == null) {
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
}
return true;
}
/**
* BluetoothAdapter
* Initializes a reference to the local Bluetooth adapter.
*
* @return Return true if the initialization is successful.
*/
public boolean openBt() {
if (mBluetoothAdapter == null) {
Log.e(TAG, "BluetoothManager do not init");
return false;
}
return mBluetoothAdapter.isEnabled() || mBluetoothAdapter.enable();
}
public boolean closeBt() {
if (mBluetoothAdapter == null) {
Log.e(TAG, "BluetoothManager do not init");
return false;
}
return mBluetoothAdapter.disable();
}
public IBluetoothSearch getBluetoothSearcher() {
if (mBluetoothSearcher == null) {
synchronized (BluetoothLeClient.class) {
if (mBluetoothSearcher == null) {
if (mBluetoothAdapter == null) {
String err = "cannot create BluetoothLeSearcher instance because not " +
"initialize, please call initialize() method";
Log.e(TAG, err);
return null;
}
mBluetoothSearcher = new BluetoothLeSearcher(mContext, mBluetoothAdapter, mBluetoothWorker);
}
}
}
return mBluetoothSearcher;
}
public BluetoothLeConnector getBluetoothLeConnector(String mac) {
BluetoothLeConnector result;
if ((result = mGattConnectorMap.get(mac)) != null) {
return result;
}
result = new BluetoothLeConnector(mContext, mBluetoothAdapter, mac, mBluetoothWorker);
mGattConnectorMap.put(mac, result);
return result;
}
public void cleanConnector(String mac) {
BluetoothLeConnector result;
if ((result = mGattConnectorMap.get(mac)) != null) {
mGattConnectorMap.remove(mac);
result.disconnect();
result.setOnDataAvailableListener(null);
}
}
/**
*
*
*/
public void cleanAllConnector() {
for (String mac : mGattConnectorMap.keySet()) {
cleanConnector(mac);
}
}
/**
*
*/
public Set<BluetoothDevice> getBondedDevices() {
return mBluetoothAdapter.getBondedDevices();
}
}

@ -1,4 +1,4 @@
package com.common.bluetooth.service; package com.common.bluetooth.service.ble;
import static com.common.bluetooth.BtConstants.CONNECT_SUCCESS; import static com.common.bluetooth.BtConstants.CONNECT_SUCCESS;
@ -13,8 +13,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.common.bluetooth.BtConstants; import com.common.bluetooth.BtConstants;
import com.common.bluetooth.BtUtils; import com.common.bluetooth.utils.BtUtils;
import com.common.bluetooth.adapter.BluetoothClientBLEAdapter;
import com.common.bluetooth.bean.CommonMsg; import com.common.bluetooth.bean.CommonMsg;
import com.common.bluetooth.callback.BLEClientListener; import com.common.bluetooth.callback.BLEClientListener;
import com.common.bluetooth.callback.BaseResultCallback; import com.common.bluetooth.callback.BaseResultCallback;
@ -29,7 +28,6 @@ import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.disposables.Disposable;
/** /**
* BLE * BLE
* *

@ -1,4 +1,4 @@
package com.common.bluetooth.service; package com.common.bluetooth.service.ble;
import android.app.Service; import android.app.Service;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
@ -18,15 +18,12 @@ import android.content.Intent;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.common.bluetooth.BtConstants; import com.common.bluetooth.BtConstants;
import com.common.bluetooth.callback.BleMsgReceiverListener; import com.common.bluetooth.callback.MsgReceiverListener;
import java.nio.charset.StandardCharsets;
/** /**
* BLE * BLE
@ -51,7 +48,7 @@ public class BLEReceiveService extends Service {
/** /**
* *
*/ */
private BleMsgReceiverListener receiverListener; private MsgReceiverListener receiverListener;
/** /**
* *
*/ */
@ -159,7 +156,7 @@ public class BLEReceiveService extends Service {
} }
} }
public void setMsgReceiveListener(BleMsgReceiverListener listener) { public void setMsgReceiveListener(MsgReceiverListener listener) {
receiverListener = listener; receiverListener = listener;
} }

@ -1,4 +1,4 @@
package com.common.bluetooth.adapter; package com.common.bluetooth.service.ble;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGatt;
@ -17,8 +17,6 @@ import com.common.bluetooth.exception.BluetoothSearchConflictException;
import com.common.bluetooth.exception.BluetoothWriteException; import com.common.bluetooth.exception.BluetoothWriteException;
import com.common.bluetooth.interfaces.IBluetoothClient; import com.common.bluetooth.interfaces.IBluetoothClient;
import com.common.bluetooth.interfaces.IBluetoothSearch; import com.common.bluetooth.interfaces.IBluetoothSearch;
import com.common.bluetooth.service.BluetoothLeClient;
import com.common.bluetooth.service.BluetoothLeConnector;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -53,7 +51,7 @@ public class BluetoothClientBLEAdapter implements IBluetoothClient {
} }
@Override @Override
public Observable<BluetoothDevice> search(final int millis, final boolean cancel) { public Observable<BluetoothDevice> search(final long millis, final boolean cancel) {
return Observable.create(new ObservableOnSubscribe<BluetoothDevice>() { return Observable.create(new ObservableOnSubscribe<BluetoothDevice>() {
@Override @Override
public void subscribe(@NonNull final ObservableEmitter<BluetoothDevice> emitter) { public void subscribe(@NonNull final ObservableEmitter<BluetoothDevice> emitter) {

@ -0,0 +1,79 @@
package com.common.bluetooth.service.ble;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import com.common.bluetooth.service.BluetoothCommon;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
*
*
* @author wangym
* @since 2021-10-19
*/
public class BluetoothLeClient extends BluetoothCommon {
@SuppressLint("StaticFieldLeak")
private static volatile BluetoothLeClient mInstance;
private final static String TAG = "BluetoothLeClient";
private final Context mContext;
private static Handler mBluetoothWorker;
private final Map<String, BluetoothLeConnector> mGattConnectorMap
= new ConcurrentHashMap<>();
private BluetoothLeClient(Context context) {
super(context.getApplicationContext());
mContext = context.getApplicationContext();
HandlerThread thread = new HandlerThread("bluetooth client worker");
thread.start();
mBluetoothWorker = new Handler(thread.getLooper());
}
public static BluetoothLeClient getInstance(Context context) {
if (mInstance == null) {
synchronized (BluetoothLeClient.class) {
if (mInstance == null) {
mInstance = new BluetoothLeClient(context);
}
}
}
return mInstance;
}
public BluetoothLeConnector getBluetoothLeConnector(String mac) {
BluetoothLeConnector result;
if ((result = mGattConnectorMap.get(mac)) != null) {
return result;
}
result = new BluetoothLeConnector(mContext, getMBluetoothAdapter(), mac, mBluetoothWorker);
mGattConnectorMap.put(mac, result);
return result;
}
public void cleanConnector(String mac) {
BluetoothLeConnector result;
if ((result = mGattConnectorMap.get(mac)) != null) {
mGattConnectorMap.remove(mac);
result.disconnect();
result.setOnDataAvailableListener(null);
}
}
/**
*
*
*/
public void cleanAllConnector() {
for (String mac : mGattConnectorMap.keySet()) {
cleanConnector(mac);
}
}
}

@ -1,4 +1,4 @@
package com.common.bluetooth.service; package com.common.bluetooth.service.ble;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
@ -23,7 +23,6 @@ import java.util.concurrent.atomic.AtomicLong;
import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.functions.Consumer;
/** /**
* Service for managing connection and data communication with a GATT server * Service for managing connection and data communication with a GATT server
* hosted on a given Bluetooth LE device. * hosted on a given Bluetooth LE device.
@ -417,8 +416,8 @@ public class BluetoothLeConnector {
try { try {
action.accept(gattCharacteristic); action.accept(gattCharacteristic);
} catch (Throwable e) { } catch (Throwable throwable) {
e.printStackTrace(); throwable.printStackTrace();
} }
} }

@ -1,4 +1,4 @@
package com.common.bluetooth.service; package com.common.bluetooth.service.ble;
import android.Manifest; import android.Manifest;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;

@ -0,0 +1,102 @@
package com.common.bluetooth.service.bt;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.common.bluetooth.BtConstants;
import com.common.bluetooth.callback.BtMsgListener;
/**
*
*
* @author wangym
* @since 2021-10-29
*/
public class BTCClientService extends Service {
private static final String TAG = "BTCClientService";
/**
*
*/
private BluetoothClassicClient classicClient;
/**
*
*/
private final BtMsgListener mMsgListener = new BtMsgListener() {
@Override
public void socketNotify(int state, @NonNull String msg) {
super.socketNotify(state, msg);
// 将内容消息透传给外部调用者
if (msgListener != null) {
msgListener.socketNotify(state, msg);
}
}
};
/**
*
*/
private BtMsgListener msgListener = null;
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "initialize BluetoothManager");
classicClient = new BluetoothClassicClient(this);
classicClient.checkBtDevice(BtConstants.BLUETOOTH_TYPE.CLASSIC);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new ClassicClientBinder();
}
public class ClassicClientBinder extends Binder {
public BTCClientService getService() {
return BTCClientService.this;
}
}
public void setMsgReceiveListener(BtMsgListener listener) {
msgListener = listener;
}
public void connect(String mac) {
if (classicClient == null) {
Log.e(TAG, "classicClient not init success");
return;
}
classicClient.setListener(mMsgListener);
classicClient.connect(mac);
}
/**
*
*
* @param msg
*/
public void write(byte[] msg) {
Log.i(TAG, "write msg = " + new String(msg));
if (classicClient != null && msg != null) {
Log.i(TAG, "---send msg start---");
classicClient.sendMsg(msg);
Log.i(TAG, "---send msg end---");
} else {
Log.e(TAG, "got msg null");
}
}
@Override
public boolean onUnbind(Intent intent) {
classicClient.close();
return super.onUnbind(intent);
}
}

@ -0,0 +1,108 @@
package com.common.bluetooth.service.bt;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.common.bluetooth.callback.BtMsgListener;
import java.nio.charset.StandardCharsets;
/**
*
*
* @author wangym
* @since 2021-10-29
*/
public class BTCReceiverService extends Service {
private static final String TAG = "BTCReceiverService";
/**
*
*/
private BluetoothClassicServer classicServer;
/**
*
*/
private final BtMsgListener mMsgListener = new BtMsgListener() {
@Override
public void socketNotify(int state, @NonNull String msg) {
super.socketNotify(state, msg);
// 当客户端断开连接时,需要重新开始监听客户端的连接
if (state == BtMsgListener.DISCONNECTED) {
if (classicServer != null) {
classicServer.listen();
}
} else if (state == BtMsgListener.MSG) {
// 服务端只关心内容消息
// 将内容消息透传给外部调用者
if (msgListener != null) {
msgListener.socketNotify(state, msg);
}
}
}
};
/**
*
*/
private BtMsgListener msgListener = null;
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "initialize BluetoothManager");
classicServer = new BluetoothClassicServer(this);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
if (classicServer != null) {
classicServer.setListener(mMsgListener);
classicServer.listen();
}
return new ClassicReceiverBinder();
}
public class ClassicReceiverBinder extends Binder {
public BTCReceiverService getService() {
return BTCReceiverService.this;
}
}
public void setMsgReceiveListener(BtMsgListener listener) {
msgListener = listener;
}
/**
*
*
* @param msg
*/
public void write(byte[] msg) {
Log.i(TAG, "sendNotify msg = " + new String(msg));
if (classicServer != null && msg != null) {
Log.i(TAG, "---send msg start---");
classicServer.sendMsg(msg);
Log.i(TAG, "---send msg end---");
} else {
Log.e(TAG, "got msg null");
}
}
@Override
public boolean onUnbind(Intent intent) {
if (classicServer != null) {
classicServer.setListener(null);
classicServer.close();
}
return super.onUnbind(intent);
}
}

@ -0,0 +1,259 @@
package com.common.bluetooth.service.bt;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Environment;
import android.util.Log;
import com.common.bluetooth.BtConstants;
import com.common.bluetooth.callback.BtMsgListener;
import com.common.bluetooth.service.BluetoothCommon;
import com.common.bluetooth.utils.BtUtils;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* socket
*
* @author wangym
* @since 2.21-12-2
*/
public class BluetoothClassicBase extends BluetoothCommon {
private static final String TAG = "BluetoothClassicBase";
private static final String FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bluetooth/";
private static final int FLAG_MSG = 0; //消息标记
private static final int FLAG_FILE = 1; //文件标记
protected BluetoothSocket mSocket;
private DataOutputStream mOut;
private BtMsgListener mListener;
/**
*
*/
private final AtomicBoolean isRead = new AtomicBoolean(false);
/**
*
*/
private final AtomicBoolean isSending = new AtomicBoolean(false);
/**
*
*/
private final LinkedBlockingQueue<byte[]> msgQueue = new LinkedBlockingQueue<>();
public BluetoothClassicBase(Context context) {
super(context);
}
/**
*
*
* @param mListener
*/
public void setListener(BtMsgListener mListener) {
this.mListener = mListener;
}
/**
* ()
*/
void loopRead(BluetoothSocket socket) {
mSocket = socket;
try {
if (!mSocket.isConnected()) {
mSocket.connect();
}
String mac = mSocket.getRemoteDevice().getAddress();
notifyUI(BtMsgListener.CONNECTED, mac);
// 将链接成功的MAC地址保存下来
BtUtils.INSTANCE.saveConnectMac(getMContext(), mac);
mOut = new DataOutputStream(mSocket.getOutputStream());
DataInputStream in = new DataInputStream(mSocket.getInputStream());
isRead.set(true);
while (isRead.get()) { //死循环读取
switch (in.readInt()) {
case FLAG_MSG: //读取短消息
String msg = in.readUTF();
Log.d(TAG, "接收短消息:" + msg);
notifyUI(BtMsgListener.MSG, msg);
break;
case FLAG_FILE: //读取文件
new File(FILE_PATH).mkdirs();
String fileName = in.readUTF(); //文件名
long fileLen = in.readLong(); //文件长度
// 读取文件内容
long len = 0;
int r;
byte[] b = new byte[4 * 1024];
FileOutputStream out = new FileOutputStream(FILE_PATH + fileName);
Log.d(TAG, "正在接收文件(" + fileName + "),请稍后...");
while ((r = in.read(b)) != -1) {
out.write(b, 0, r);
len += r;
if (len >= fileLen) {
break;
}
}
Log.d(TAG, "文件接收完成(存放在:" + FILE_PATH + ")");
break;
}
}
} catch (Throwable e) {
close();
}
}
/**
*
*/
public void sendMsg(byte[] msg) {
Log.d(TAG, "send msg");
if (mSocket == null || !mSocket.isConnected()) {
Log.e(TAG, "sendMsg got socket is not connect");
return;
}
msgQueue.add(msg);
if (checkSend()) {
Log.d(TAG, "sending wait");
return;
}
synchronized (BluetoothClassicBase.class) {
isSending.set(true);
// 循环发送消息
while (true) {
if (!doSendMsg()) {
break;
}
}
isSending.set(false);
}
}
/**
*
*/
public boolean doSendMsg() {
final byte[] msgItem = msgQueue.poll();
if (msgItem == null) {
Log.d(TAG, "doTranslate Queue is null");
return false;
}
try {
mOut.writeInt(FLAG_MSG); //消息标记
mOut.writeUTF(new String(msgItem, BtConstants.INSTANCE.getDEFAULT_CHARSET()));
mOut.flush();
Log.d(TAG, "发送短消息:" + new String(msgItem, BtConstants.INSTANCE.getDEFAULT_CHARSET()));
} catch (Throwable e) {
close();
isSending.set(false);
}
return true;
}
/**
*
*/
public void sendFile(final String filePath) {
if (checkSend()) return;
isSending.set(true);
Executors.newCachedThreadPool().execute(new Runnable() {
@Override
public void run() {
try {
FileInputStream in = new FileInputStream(filePath);
File file = new File(filePath);
mOut.writeInt(FLAG_FILE); //文件标记
mOut.writeUTF(file.getName()); //文件名
mOut.writeLong(file.length()); //文件长度
int r;
byte[] b = new byte[4 * 1024];
notifyUI(BtMsgListener.MSG, "正在发送文件(" + filePath + "),请稍后...");
while ((r = in.read(b)) != -1)
mOut.write(b, 0, r);
mOut.flush();
notifyUI(BtMsgListener.MSG, "文件发送完成.");
} catch (Throwable e) {
close();
}
isSending.set(false);
}
});
}
/**
* Socket
*/
public void close() {
Log.d(TAG, "close");
try {
String mac = "";
isRead.set(false);
if (mSocket != null) {
mSocket.close();
mac = mSocket.getRemoteDevice().getAddress();
}
notifyUI(BtMsgListener.DISCONNECTED, mac);
} catch (Throwable e) {
e.printStackTrace();
}
}
/**
* Socket
*/
public void closeSilence(){
Log.d(TAG, "closeSilence");
try {
isRead.set(false);
if (mSocket != null) {
mSocket.close();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
/**
*
*
* @param mac MAC
* @return
*/
public boolean isConnected(String mac) {
BluetoothDevice dev = getBtDeviceByMac(mac);
boolean connected = (mSocket != null && mSocket.isConnected());
if (dev == null) {
return connected;
}
return connected && mSocket.getRemoteDevice().equals(dev);
}
/**
*
*
* @return
*/
private synchronized boolean checkSend() {
return isSending.get();
}
/**
*
*
* @param state
* @param msg
*/
protected void notifyUI(final int state, final String msg) {
if (mListener != null) {
mListener.socketNotify(state, msg);
}
}
}

@ -0,0 +1,56 @@
package com.common.bluetooth.service.bt;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.util.Log;
import com.common.bluetooth.BtConstants;
import com.common.bluetooth.callback.BtMsgListener;
import java.util.concurrent.Executors;
/**
*
*
* @author wangym
* @since 2021-12-1
*/
public class BluetoothClassicClient extends BluetoothClassicBase {
private static final String TAG = "BtClient";
public BluetoothClassicClient(Context context) {
super(context);
}
/**
*
*
* @param mac MAC
*/
public void connect(String mac) {
Log.d(TAG, "start connect mac:" + mac);
BluetoothDevice bluetoothDevice = getBtDeviceByMac(mac);
if (bluetoothDevice == null) {
Log.e(TAG, "connect got device is null : mac is " + mac);
notifyUI(BtMsgListener.DISCONNECTED, mac);
return;
}
if (isConnected(mac)) {
Log.i(TAG, "connected, no need to close socket");
return;
}
closeSilence();
try {
// final BluetoothSocket socket = dev.createRfcommSocketToServiceRecord(SPP_UUID); //加密传输Android系统强制配对弹窗显示配对码
final BluetoothSocket socket = bluetoothDevice.createInsecureRfcommSocketToServiceRecord(
BtConstants.INSTANCE.getCLASSIC_BT_UUID()); //明文传输(不安全),无需配对
// 开启子线程
Executors.newCachedThreadPool().execute(() -> {
loopRead(socket); //循环读取
});
} catch (Throwable e) {
close();
}
}
}

@ -1,4 +1,4 @@
package com.common.bluetooth.service; package com.common.bluetooth.service.bt;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.content.Context; import android.content.Context;

@ -0,0 +1,59 @@
package com.common.bluetooth.service.bt;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import com.common.bluetooth.BtConstants;
import java.util.concurrent.Executors;
/**
* 线
*
* @author wangym
* @version 2021-12-2
*/
public class BluetoothClassicServer extends BluetoothClassicBase {
private static final String TAG = BluetoothClassicServer.class.getSimpleName();
private BluetoothServerSocket mSSocket;
BluetoothClassicServer(Context context) {
super(context);
}
/**
*
*/
public void listen() {
try {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
// mSSocket = adapter.listenUsingRfcommWithServiceRecord(TAG, SPP_UUID); //加密传输Android强制执行配对弹窗显示配对码
mSSocket = adapter.listenUsingInsecureRfcommWithServiceRecord(TAG,
BtConstants.INSTANCE.getCLASSIC_BT_UUID()); //明文传输(不安全),无需配对
// 开启子线程
Executors.newCachedThreadPool().execute(() -> {
try {
BluetoothSocket socket = mSSocket.accept(); // 监听连接
mSSocket.close(); // 关闭监听,只连接一个设备
loopRead(socket); // 循环读取
} catch (Throwable e) {
close();
}
});
} catch (Throwable e) {
close();
}
}
@Override
public void close() {
try {
mSSocket.close();
} catch (Throwable e) {
e.printStackTrace();
}
super.close();
}
}

@ -0,0 +1,98 @@
package com.common.bluetooth.service.bt
import android.bluetooth.BluetoothDevice
import com.common.bluetooth.BtConstants
import com.common.bluetooth.callback.BaseResultCallback
import com.common.bluetooth.interfaces.IBluetoothClient
import io.reactivex.rxjava3.core.Observable
import java.util.UUID
/**
* 经典蓝牙适配器
*
* @author wangym
* @since 2021-12-1
*/
class BluetoothClientClassicAdapter(private var mClient: BluetoothClassicClient) :
IBluetoothClient {
val TAG = "BluetoothClientAdapter"
override fun search(millis: Long, cancel: Boolean): Observable<BluetoothDevice> {
return Observable.create {
mClient.getBluetoothSearcher().startScan(millis, null)
it.onComplete()
}
}
override fun stopSearch() {
mClient.getBluetoothSearcher().stopScan()
}
override fun connect(mac: String): Observable<String> {
return Observable.create {
if (!mClient.isConnected((mac))) {
mClient.connect(mac)
}
it.onNext(mac)
it.onComplete()
}
}
override fun disconnect(mac: String) {
mClient.close()
}
override fun write(
mac: String,
service: UUID,
characteristic: UUID,
values: ByteArray
): Observable<String>? {
return Observable.create {
if (!mClient.isConnected((mac))) {
mClient.sendMsg(values)
}
it.onNext(mac)
it.onComplete()
}
}
override fun checkBluetoothDevice(type: BtConstants.BLUETOOTH_TYPE): Boolean {
return mClient.checkBtDevice(type)
}
override fun openBluetooth() {
mClient.openBt()
}
override fun closeBluetooth(): Boolean {
return mClient.closeBt()
}
override fun getBondedDevices(): Set<BluetoothDevice>? {
return mClient.getBondedDevices()
}
override fun registerNotify(
mac: String,
service: UUID,
characteristic: UUID,
callback: BaseResultCallback<ByteArray>?
): Observable<String> {
return Observable.create {}
}
override fun unRegisterNotify(
mac: String,
service: UUID,
characteristic: UUID
): Observable<String>? {
return null
}
override fun clean(mac: String) {
}
override fun cleanAll() {
}
}

@ -0,0 +1,94 @@
package com.common.bluetooth.service.bt;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
/**
* 广-
*/
public class BtReceiver extends BroadcastReceiver {
private static final String TAG = BtReceiver.class.getSimpleName();
private final Listener mListener;
public BtReceiver(Context cxt, Listener listener) {
mListener = listener;
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//蓝牙开关状态
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//蓝牙开始搜索
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//蓝牙搜索结束
filter.addAction(BluetoothDevice.ACTION_FOUND);//蓝牙发现新设备(未配对的设备)
filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);//在系统弹出配对框之前(确认/输入配对码)
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//设备配对状态改变
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);//最底层连接建立
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);//最底层连接断开
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); //BluetoothAdapter连接状态
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); //BluetoothHeadset连接状态
filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); //BluetoothA2dp连接状态
cxt.registerReceiver(this, filter);
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null)
return;
Log.i(TAG, "===" + action);
BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (dev != null)
Log.i(TAG, "BluetoothDevice: " + dev.getName() + ", " + dev.getAddress());
switch (action) {
case BluetoothAdapter.ACTION_STATE_CHANGED:
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
Log.i(TAG, "STATE: " + state);
break;
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
break;
case BluetoothDevice.ACTION_FOUND:
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MAX_VALUE);
Log.i(TAG, "EXTRA_RSSI:" + rssi);
mListener.foundDev(dev);
break;
case BluetoothDevice.ACTION_PAIRING_REQUEST: //在系统弹出配对框之前,实现自动配对,取消系统配对框
/*try {
abortBroadcast();//终止配对广播,取消系统配对框
boolean ret = dev.setPin("1234".getBytes()); //设置PIN配对码(必须是固定的)
} catch (Exception e) {
e.printStackTrace();
}*/
break;
case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
Log.i(TAG, "BOND_STATE: " + intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 0));
break;
case BluetoothDevice.ACTION_ACL_CONNECTED:
break;
case BluetoothDevice.ACTION_ACL_DISCONNECTED:
break;
case BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED:
Log.i(TAG, "CONN_STATE: " + intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, 0));
break;
case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
Log.i(TAG, "CONN_STATE: " + intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0));
break;
case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
Log.i(TAG, "CONN_STATE: " + intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, 0));
break;
}
}
public interface Listener {
void foundDev(BluetoothDevice dev);
}
}

@ -1,4 +1,4 @@
package com.common.bluetooth package com.common.bluetooth.utils
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
@ -113,6 +113,11 @@ object BtUtils {
*/ */
const val MOD_I_CN = "6" const val MOD_I_CN = "6"
/**
* 设置
*/
const val MOD_S = "7"
/** /**
* 中间包 * 中间包
*/ */

@ -33,13 +33,13 @@ public class BtDeviceListAdapter extends RecyclerView.Adapter<BtDeviceListAdapte
@NonNull @NonNull
@Override @Override
public BtViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public BtDeviceListAdapter.BtViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mBinding = RvBtDeviceItemBinding.inflate(LayoutInflater.from(context)); mBinding = RvBtDeviceItemBinding.inflate(LayoutInflater.from(context));
return new BtViewHolder(mBinding); return new BtViewHolder(mBinding);
} }
@Override @Override
public void onBindViewHolder(@NonNull BtViewHolder holder, int position) { public void onBindViewHolder(@NonNull BtDeviceListAdapter.BtViewHolder holder, int position) {
final int currentIndex = position; final int currentIndex = position;
BluetoothDevice device = bluetoothDevices.get(position); BluetoothDevice device = bluetoothDevices.get(position);
holder.binding.btDeviceNameTv.setText("Device Name:" + device.getName()); holder.binding.btDeviceNameTv.setText("Device Name:" + device.getName());

@ -64,6 +64,19 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp" android:layout_marginStart="24dp"
android:layout_marginEnd="24dp" /> android:layout_marginEnd="24dp" />
<Button
android:id="@+id/btc_server_build"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="经典蓝牙服务端" />
<TextView
android:id="@+id/btc_receive_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp" />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</RelativeLayout> </RelativeLayout>
Loading…
Cancel
Save