|
@@ -0,0 +1,334 @@
|
|
|
+package com.wdkl.app.ncs.callingbed.service
|
|
|
+
|
|
|
+import android.app.*
|
|
|
+import android.bluetooth.*
|
|
|
+import android.bluetooth.le.AdvertiseCallback
|
|
|
+import android.bluetooth.le.AdvertiseData
|
|
|
+import android.bluetooth.le.AdvertiseSettings
|
|
|
+import android.bluetooth.le.BluetoothLeAdvertiser
|
|
|
+import android.content.Context
|
|
|
+import android.content.Intent
|
|
|
+import android.os.*
|
|
|
+import android.util.Log
|
|
|
+import com.wdkl.app.ncs.callingbed.R
|
|
|
+import com.wdkl.app.ncs.callingbed.ble.BLE_Constants
|
|
|
+import com.wdkl.ncs.android.lib.utils.showMessage
|
|
|
+import java.util.*
|
|
|
+
|
|
|
+class BleService : Service() {
|
|
|
+ private val TAG = BleService::class.java.simpleName
|
|
|
+
|
|
|
+ internal var myBinder = ServiceBinder()
|
|
|
+
|
|
|
+ private var notificationManager: NotificationManager? = null
|
|
|
+ private var notificationId: String = "channelId0"
|
|
|
+ private var notificationName: String = "channelName"
|
|
|
+
|
|
|
+ private val bleHandler by lazy { Handler(Looper.getMainLooper()) }
|
|
|
+
|
|
|
+ //蓝牙ble配网相关,只有在设备激活页面有效,如果已经正常进入主页面则认为已经正常联网
|
|
|
+ private var mBluetoothLeAdvertiser: BluetoothLeAdvertiser? = null // BLE广播
|
|
|
+ private var mBluetoothGattServer: BluetoothGattServer? = null // BLE服务端
|
|
|
+
|
|
|
+ override fun onCreate() {
|
|
|
+ super.onCreate()
|
|
|
+
|
|
|
+ notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?
|
|
|
+
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
+ val channel = NotificationChannel(
|
|
|
+ notificationId,
|
|
|
+ notificationName,
|
|
|
+ NotificationManager.IMPORTANCE_HIGH
|
|
|
+ )
|
|
|
+ channel.enableVibration(true)
|
|
|
+ channel.enableLights(true)
|
|
|
+ channel.setBypassDnd(true)
|
|
|
+ channel.setShowBadge(true)
|
|
|
+ channel.setSound(null, null)
|
|
|
+ notificationManager!!.createNotificationChannel(channel)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ bleHandler.postDelayed({
|
|
|
+ configBle()
|
|
|
+ }, 15000)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun getNotification(): Notification {
|
|
|
+ val builder: Notification.Builder = Notification.Builder(this)
|
|
|
+ .setSmallIcon(R.drawable.ic_launch_48)
|
|
|
+ .setContentTitle("Ble service")
|
|
|
+ .setContentText("running...")
|
|
|
+ .setOnlyAlertOnce(true)
|
|
|
+
|
|
|
+ //设置Notification的ChannelID,否则不能正常显示
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
+ builder.setChannelId(notificationId)
|
|
|
+ }
|
|
|
+ val notification: Notification = builder.build()
|
|
|
+ return notification
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
+ startForeground(110, getNotification())//开前台服务
|
|
|
+ }
|
|
|
+
|
|
|
+ Log.i(TAG, "onStartCommand:")
|
|
|
+ //return START_STICKY//当服务被异常终止时,重启服务
|
|
|
+ return super.onStartCommand(intent, flags, startId)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onBind(intent: Intent): IBinder {
|
|
|
+ return myBinder
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onDestroy() {
|
|
|
+ super.onDestroy()
|
|
|
+
|
|
|
+ bleHandler.removeCallbacksAndMessages(null)
|
|
|
+ if (mBluetoothLeAdvertiser != null) {
|
|
|
+ mBluetoothLeAdvertiser!!.stopAdvertising(mAdvertiseCallback)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mBluetoothGattServer != null) {
|
|
|
+ mBluetoothGattServer!!.close()
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
+ stopForeground(true)// 停止前台服务--参数:表示是否移除之前的通知
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ inner class ServiceBinder : Binder() {
|
|
|
+ fun doThings() {}
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ ///////////////********************蓝牙ble配网相关****************************///////////////
|
|
|
+ private fun configBle() {
|
|
|
+ bleHandler.removeCallbacksAndMessages(null)
|
|
|
+ val bluetoothManager = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager
|
|
|
+ val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
|
|
|
+ //打开蓝牙
|
|
|
+ if (bluetoothAdapter != null) {
|
|
|
+ if (!bluetoothAdapter.isEnabled) {
|
|
|
+ Log.e(TAG, "蓝牙未开启")
|
|
|
+ //直接开启蓝牙
|
|
|
+ bluetoothAdapter.enable()
|
|
|
+
|
|
|
+ //15s后再检查
|
|
|
+ bleHandler.postDelayed({
|
|
|
+ configBle()
|
|
|
+ }, 15000)
|
|
|
+ } else {
|
|
|
+ // ============启动BLE蓝牙广播 =================
|
|
|
+ Log.d(TAG, "蓝牙已开启")
|
|
|
+ //广播设置(必须)
|
|
|
+ val settings = AdvertiseSettings.Builder()
|
|
|
+ .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) //广播模式: 低功耗,平衡,低延迟
|
|
|
+ .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) //发射功率级别: 极低,低,中,高
|
|
|
+ .setConnectable(true) //能否连接,广播分为可连接广播和不可连接广播
|
|
|
+ .build()
|
|
|
+
|
|
|
+ //广播数据(必须,广播启动就会发送)
|
|
|
+ val advertiseData = AdvertiseData.Builder()
|
|
|
+ .setIncludeDeviceName(true) //包含蓝牙名称
|
|
|
+ .setIncludeTxPowerLevel(true) //包含发射功率级别
|
|
|
+ .addManufacturerData(1, byteArrayOf(23, 33)) //设备厂商数据,自定义
|
|
|
+ .build()
|
|
|
+
|
|
|
+ //扫描响应数据(可选,当客户端扫描时才发送)
|
|
|
+ val scanResponse = AdvertiseData.Builder()
|
|
|
+ .addManufacturerData(2, byteArrayOf(66, 66)) //设备厂商数据,自定义
|
|
|
+ .addServiceUuid(ParcelUuid(BLE_Constants.UUID_SERVICE)) //服务UUID
|
|
|
+ //.addServiceData(ParcelUuid(BLE_Constants.UUID_SERVICE), byteArrayOf(2)) //服务数据,自定义
|
|
|
+ .build()
|
|
|
+
|
|
|
+ mBluetoothLeAdvertiser = bluetoothAdapter.bluetoothLeAdvertiser
|
|
|
+ mBluetoothLeAdvertiser?.startAdvertising(
|
|
|
+ settings,
|
|
|
+ advertiseData,
|
|
|
+ scanResponse,
|
|
|
+ mAdvertiseCallback
|
|
|
+ )
|
|
|
+
|
|
|
+ // 注意:必须要开启可连接的BLE广播,其它设备才能发现并连接BLE服务端!
|
|
|
+ // =============启动BLE蓝牙服务端=====================================================================================
|
|
|
+ val service = BluetoothGattService(BLE_Constants.UUID_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY)
|
|
|
+
|
|
|
+ //添加可读+通知characteristic
|
|
|
+ val characteristicRead = BluetoothGattCharacteristic(
|
|
|
+ BLE_Constants.UUID_CHAR_READ_NOTIFY,
|
|
|
+ BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_NOTIFY,
|
|
|
+ BluetoothGattCharacteristic.PERMISSION_READ
|
|
|
+ )
|
|
|
+
|
|
|
+ characteristicRead.addDescriptor(
|
|
|
+ BluetoothGattDescriptor(
|
|
|
+ BLE_Constants.UUID_DESC_NOTITY,
|
|
|
+ BluetoothGattCharacteristic.PERMISSION_WRITE
|
|
|
+ )
|
|
|
+ )
|
|
|
+ service.addCharacteristic(characteristicRead)
|
|
|
+
|
|
|
+ //添加可写characteristic
|
|
|
+ val characteristicWrite = BluetoothGattCharacteristic(
|
|
|
+ BLE_Constants.UUID_CHAR_WRITE,
|
|
|
+ BluetoothGattCharacteristic.PROPERTY_WRITE,
|
|
|
+ BluetoothGattCharacteristic.PERMISSION_WRITE
|
|
|
+ )
|
|
|
+ service.addCharacteristic(characteristicWrite)
|
|
|
+
|
|
|
+ if (bluetoothManager != null) {
|
|
|
+ mBluetoothGattServer = bluetoothManager.openGattServer(this, mBluetoothGattServerCallback)
|
|
|
+ }
|
|
|
+ mBluetoothGattServer?.addService(service)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // BLE广播Callback
|
|
|
+ private val mAdvertiseCallback = object: AdvertiseCallback() {
|
|
|
+ override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
|
|
|
+ Log.d(TAG, "BLE广播开启成功")
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onStartFailure(errorCode: Int) {
|
|
|
+ Log.e(TAG, "BLE广播开启失败: $errorCode")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // BLE服务端Callback
|
|
|
+ private val mBluetoothGattServerCallback = object: BluetoothGattServerCallback() {
|
|
|
+
|
|
|
+ override fun onConnectionStateChange(device: BluetoothDevice?, status: Int, newState: Int) {
|
|
|
+ Log.i(TAG,
|
|
|
+ String.format(
|
|
|
+ "onConnectionStateChange:%s,%s,%s,%s",
|
|
|
+ device!!.name,
|
|
|
+ device.address,
|
|
|
+ status,
|
|
|
+ newState
|
|
|
+ )
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onServiceAdded(status: Int, service: BluetoothGattService) {
|
|
|
+ Log.i(TAG, String.format("onServiceAdded:%s,%s", status, service.uuid))
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onCharacteristicReadRequest(
|
|
|
+ device: BluetoothDevice,
|
|
|
+ requestId: Int,
|
|
|
+ offset: Int,
|
|
|
+ characteristic: BluetoothGattCharacteristic
|
|
|
+ ) {
|
|
|
+ Log.i(TAG, String.format(
|
|
|
+ "onCharacteristicReadRequest:%s,%s,%s,%s,%s",
|
|
|
+ device.name,
|
|
|
+ device.address,
|
|
|
+ requestId,
|
|
|
+ offset,
|
|
|
+ characteristic.uuid
|
|
|
+ )
|
|
|
+ )
|
|
|
+ val response = "CHAR_" + (Math.random() * 100).toInt() //模拟数据
|
|
|
+
|
|
|
+ mBluetoothGattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, response.toByteArray()) // 响应客户端
|
|
|
+
|
|
|
+ Log.i(TAG, "客户端读取Characteristic[" + characteristic.getUuid() + "]:\n" + response)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onCharacteristicWriteRequest(
|
|
|
+ device: BluetoothDevice,
|
|
|
+ requestId: Int,
|
|
|
+ characteristic: BluetoothGattCharacteristic,
|
|
|
+ preparedWrite: Boolean,
|
|
|
+ responseNeeded: Boolean,
|
|
|
+ offset: Int,
|
|
|
+ value: ByteArray
|
|
|
+ ) {
|
|
|
+ // 获取客户端发过来的数据
|
|
|
+ val requestStr = String(value)
|
|
|
+ Log.i(TAG, String.format(
|
|
|
+ "onCharacteristicWriteRequest:%s,%s,%s,%s,%s,%s,%s,%s",
|
|
|
+ device.name,
|
|
|
+ device.address,
|
|
|
+ requestId,
|
|
|
+ characteristic.uuid,
|
|
|
+ preparedWrite,
|
|
|
+ responseNeeded,
|
|
|
+ offset,
|
|
|
+ requestStr
|
|
|
+ )
|
|
|
+ )
|
|
|
+ mBluetoothGattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value) // 响应客户端
|
|
|
+ Log.i(TAG, "客户端写入Characteristic[" + characteristic.getUuid() + "]:\n" + requestStr)
|
|
|
+
|
|
|
+ //todo: 这里收到配网数据,待处理
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onDescriptorReadRequest(
|
|
|
+ device: BluetoothDevice,
|
|
|
+ requestId: Int,
|
|
|
+ offset: Int,
|
|
|
+ descriptor: BluetoothGattDescriptor
|
|
|
+ ) {
|
|
|
+ Log.i(TAG,
|
|
|
+ String.format(
|
|
|
+ "onDescriptorReadRequest:%s,%s,%s,%s,%s",
|
|
|
+ device.name,
|
|
|
+ device.address,
|
|
|
+ requestId,
|
|
|
+ offset,
|
|
|
+ descriptor.uuid
|
|
|
+ )
|
|
|
+ )
|
|
|
+ val response = "DESC_" + (Math.random() * 100).toInt() //模拟数据
|
|
|
+ mBluetoothGattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, response.toByteArray()) // 响应客户端
|
|
|
+ Log.i(TAG, "客户端读取Descriptor[" + descriptor.getUuid() + "]:\n" + response)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onDescriptorWriteRequest(
|
|
|
+ device: BluetoothDevice,
|
|
|
+ requestId: Int,
|
|
|
+ descriptor: BluetoothGattDescriptor,
|
|
|
+ preparedWrite: Boolean,
|
|
|
+ responseNeeded: Boolean,
|
|
|
+ offset: Int,
|
|
|
+ value: ByteArray
|
|
|
+ ) {
|
|
|
+ // 获取客户端发过来的数据
|
|
|
+ val valueStr = Arrays.toString(value)
|
|
|
+ Log.i(TAG, String.format(
|
|
|
+ "onDescriptorWriteRequest:%s,%s,%s,%s,%s,%s,%s,%s",
|
|
|
+ device.name,
|
|
|
+ device.address,
|
|
|
+ requestId,
|
|
|
+ descriptor.uuid,
|
|
|
+ preparedWrite,
|
|
|
+ responseNeeded,
|
|
|
+ offset,
|
|
|
+ valueStr
|
|
|
+ )
|
|
|
+ )
|
|
|
+ mBluetoothGattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value) // 响应客户端
|
|
|
+ Log.i(TAG, "客户端写入Descriptor[" + descriptor.getUuid() + "]:\n" + valueStr)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onExecuteWrite(device: BluetoothDevice, requestId: Int, execute: Boolean) {
|
|
|
+ Log.i(TAG, String.format("onExecuteWrite:%s,%s,%s,%s", device.getName(), device.getAddress(), requestId, execute))
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onNotificationSent(device: BluetoothDevice, status: Int) {
|
|
|
+ Log.i(TAG, String.format("onNotificationSent:%s,%s,%s", device.getName(), device.getAddress(), status))
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onMtuChanged(device: BluetoothDevice, mtu: Int) {
|
|
|
+ Log.i(TAG, String.format("onMtuChanged:%s,%s,%s", device.getName(), device.getAddress(), mtu));
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|