android-service - 自动连接到 BLE 设备

标签 android-service bluetooth-lowenergy android-bluetooth


我正在开发一个与 BLE 设备通信的应用程序,目前我正在尝试
创建一个以该应用程序启动并自动连接到 TI 的 CC2541
keyfob 的服务。

问题是关贸总协定服务器似乎每次都失败....
我不知道我的代码有什么问题,因为通过谷歌 API 和我看到的一些教程
似乎所有的部分都在他们的地方,但仍然没有任何作用...... =(

这是我的服务 -

package com.example.bluetoothgatt;

import java.util.UUID;

import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

public class BLE extends Service implements BluetoothAdapter.LeScanCallback {

    private final IBinder mBinder = new BluetoothLeBinder();
    private final static String TAG = "BLE";
    private static final String DEVICE_NAME = "Keyfobdemo";
    private BluetoothManager mBluetoothManager;
    public BluetoothGatt mConnectedGatt;
    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothDevice mDevice;
    private String mDeviceAddress;

    private int mConnectionState = STATE_DISCONNECTED;
    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_CONNECTED = 2;

    /*******************************
     ******************************* 
     ****** Service Inherited ****** Methods **********
     *******************************/

    @Override
    public void onCreate() {
        super.onCreate();
        mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        Thread discoverDevices = new Thread(mStartRunnable);
        discoverDevices.setPriority(discoverDevices.MAX_PRIORITY);
        discoverDevices.start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        close();
        return super.onUnbind(intent);
    }

    // Implements callback methods for GATT events that the app cares about.
    // For example, connection change and services discovered.
    private final BluetoothGattExecutor mExecutor = new BluetoothGattExecutor() {

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,
                int newState) {
            super.onConnectionStateChange(gatt, status, newState);

            if (newState == BluetoothProfile.STATE_CONNECTED) {
                mConnectionState = STATE_CONNECTED;
                mConnectedGatt = gatt;
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                mConnectionState = STATE_DISCONNECTED;
                Log.i(TAG, "Disconnected from GATT server.");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);

            if (status == BluetoothGatt.GATT_SUCCESS) {
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);

            if (status == BluetoothGatt.GATT_SUCCESS) {
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
        }
    };

    /**
     * Return a reference for the current class
     */
    public class BluetoothLeBinder extends Binder {
        BLE getService() {
            return BLE.this;
        }
    }

    private Runnable mStartRunnable = new Runnable() {
        @Override
        public void run() {
            startScan();
        }
    };

    private void startScan() {
        if (mConnectionState == STATE_DISCONNECTED) {
            mBluetoothAdapter.startLeScan(this);
            mHandler.postDelayed(mStopRunnable, 2500);
        }
    }

    private Runnable mStopRunnable = new Runnable() {
        @Override
        public void run() {
            stopScan();
        }
    };

    private void stopScan() {
        mBluetoothAdapter.stopLeScan(this);
    }

    @Override
    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
        /*
         * We are looking for SensorTag devices only, so validate the name that
         * each device reports before adding it to our collection
         */
        if (DEVICE_NAME.equals(device.getName())) {
            mDevice = device;
            mDeviceAddress = mDevice.getAddress();
            connect(mDeviceAddress);
            mConnectionState = STATE_CONNECTING;
            if(device.getBondState() == BluetoothDevice.BOND_BONDED) {

            } else if (device.getBondState() == BluetoothDevice.BOND_BONDING) {

            } else if(device.getBondState() == BluetoothDevice.BOND_NONE) {
                connect(device.getAddress());
            }
        }
    }

    /**
     * Connects to the GATT server hosted on the Bluetooth LE device.
     * 
     * @param address
     *            The device address of the destination device.
     * 
     * @return Return true if the connection is initiated successfully. The
     *         connection result is reported asynchronously through the
     *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
     *         callback.
     */
    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG,
                    "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }

        // Previously connected device. Try to reconnect.
        if (mDeviceAddress != null && address.equals(mDeviceAddress)
                && mConnectedGatt != null) {
            Log.d(TAG,
                    "Trying to use an existing BluetoothGatt for connection.");
            if (mConnectedGatt.connect()) {
                mConnectionState = STATE_CONNECTING;
                return true;
            } else {
                return false;
            }
        }

        final BluetoothDevice device = mBluetoothAdapter
                .getRemoteDevice(address);
        if (device == null) {
            Log.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        // We want to directly connect to the device, so we are setting the
        // autoConnect
        // parameter to false.
        mConnectedGatt = device.connectGatt(this, false, mExecutor);
        Log.d(TAG, "Trying to create a new connection.");
        mDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }

    /**
     * Disconnects an existing connection or cancel a pending connection. The
     * disconnection result is reported asynchronously through the
     * BluetoothGattCallback >>
     * onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)
     * callback.
     */
    public void disconnect() {
        if (mBluetoothAdapter == null || mConnectedGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mConnectedGatt.disconnect();
    }

    /**
     * After using a given BLE device, the app must call this method to ensure
     * resources are released properly.
     */
    public void close() {
        if (mConnectedGatt == null) {
            return;
        }
        mConnectedGatt.close();
        mConnectedGatt = null;
    }

    private final UUID IMMEDIATE_ALERT_UUID = UUID
            .fromString("00001802-0000-1000-8000-00805f9b34fb");
    private final UUID ALERT_LEVEL_UUID = UUID
            .fromString("00002a06-0000-1000-8000-00805f9b34fb");

    public void Buzz(BluetoothGatt gatt, int level) {
        BluetoothGattService alertService = gatt
                .getService(IMMEDIATE_ALERT_UUID);
        if (alertService == null) {
            Log.d(TAG, "Immediate Alert service not found!");
            return;
        }
        BluetoothGattCharacteristic alertLevel = alertService
                .getCharacteristic(ALERT_LEVEL_UUID);
        if (alertLevel == null) {
            Log.d(TAG, "Alert Level charateristic not found!");
            return;
        }
        alertLevel.setValue(level, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
        gatt.writeCharacteristic(alertLevel);
        Log.d(TAG, "Alert");
    }

    private final UUID BATTERY_SERVICE_UUID = UUID
            .fromString("0000180F-0000-1000-8000-00805f9b34fb");
    private final UUID BATTERY_LEVEL_UUID = UUID
            .fromString("00002a19-0000-1000-8000-00805f9b34fb");

    public int getbattery(BluetoothGatt mBluetoothGatt) {

        BluetoothGattService batteryService = mConnectedGatt
                .getService(BATTERY_SERVICE_UUID);
        if (batteryService == null) {
            Log.d(TAG, "Battery service not found!");
            return 0;
        }

        BluetoothGattCharacteristic batteryLevel = batteryService
                .getCharacteristic(BATTERY_LEVEL_UUID);
        if (batteryLevel == null) {
            Log.d(TAG, "Battery level not found!");
            return 0;
        }
        mBluetoothGatt.readCharacteristic(batteryLevel);
        return batteryLevel.getIntValue(
                BluetoothGattCharacteristic.FORMAT_SINT8, 0);
    }

    /*
     * We have a Handler to process event results on the main thread
     */
    private static final int MSG_PROGRESS = 201;
    private static final int MSG_DISMISS = 202;
    private static final int MSG_CLEAR = 301;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            BluetoothGattCharacteristic characteristic;
            switch (msg.what) {
            case MSG_PROGRESS:
                break;
            case MSG_DISMISS:
                break;
            case MSG_CLEAR:
                break;
            }
        }
    };

    public void MakeBuzz() {
        Thread t = new Thread(new Runnable() {

            @Override
            public void run() {
                mConnectedGatt = mDevice.connectGatt(getApplicationContext(),
                        true, mExecutor);
                BluetoothGattService alertService = mConnectedGatt
                        .getService(IMMEDIATE_ALERT_UUID);
                int x = getbattery(mConnectedGatt);
                Buzz(mConnectedGatt, 2);
            }
        });
        t.start();
    }
}

这是应用程序类 -

package com.example.bluetoothgatt;


import android.app.Application;
import android.content.Intent;

public class ApplicationBleTest extends Application {
    // Application variables
    public final String SMOKE_TALK_PACKAGE_NAME = "com.smoketalk";
    private BluetoothLEService mBleService;
    private static int MODE_PRIVATE;

    /**
     * Application OnCreate event initiate the class parameters
     */
    public void onCreate() {
        super.onCreate();
        getApplicationContext().startService(new Intent(this, BLE.class));
    }
}

这是主要事件(我试图让 key 扣警报在单击按钮时发出嗡嗡声)

package com.example.bluetoothgatt;

import com.example.bluetoothgatt.BluetoothLowEnergyService.BluetoothLeBinder;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

/**
 * Created by Dave Smith Double Encore, Inc. MainActivity
 */
public class MainActivity extends Activity {

    BluetoothLowEnergyService mBluetoothService;
    boolean isBound = false;
    Button buzz;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, BluetoothLowEnergyService.class);
        bindService(intent, mBleServiceConnection, Context.BIND_AUTO_CREATE);
        buzz = (Button) findViewById(R.id.btn1);
        buzz.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                mBluetoothService.MakeBuzz();
            }
        });
    }

    private ServiceConnection mBleServiceConnection = new ServiceConnection() {

        public void onServiceConnected(ComponentName className, IBinder service) {
            BluetoothLeBinder binder = (BluetoothLeBinder) service;
            mBluetoothService = binder.getService();
            isBound = true;
        }

        public void onServiceDisconnected(ComponentName arg0) {
            isBound = false;
        }

    };
}

还有 list 文件 -

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.bluetoothgatt"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="true" />

    <uses-sdk
        android:minSdkVersion="18"
        android:targetSdkVersion="18" />

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED"/>

    <application
        android:name="com.example.bluetoothgatt.ApplicationBleTest"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="SensorTag Weather" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="com.example.bluetoothgatt.BLE" />
    </application>

</manifest>

最后一个是主要事件的布局 -

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:padding="@dimen/activity_horizontal_margin"
        android:text="Android BLE Test"
        android:textSize="42sp" />

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="56dp"
        android:text="Buzz" />

</RelativeLayout>

任何帮助将不胜感激,因为我不知道出了什么问题... =(

最佳答案

对于初学者,我建议注释掉绑定(bind)代码(onLeScan 方法中 if(device.getBondState().. 之后的所有内容)整个绑定(bind)过程在 4.3 上不稳定(至少是 Nexus 设备)并且4.4. 您应该能够发现设备,并且使用 BluetoothDevice 用户选择您应该在停止发现后调用 ConnectGatt。这将尝试连接到设备上的 Gatt 服务器。如果连接成功,您应该会在 connectionStateChange 上收到一个回调,指示连接成功。

绑定(bind)背后的概念与设备配对和交换 key 有关(如果您的特征已加密)。通常您应该能够连接到 Gatt 服务器而无需绑定(bind),但是一旦连接,如果您尝试读取加密特征,它将失败。

关于android-service - 自动连接到 BLE 设备,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21916775/

相关文章:

android - 如何防止服务应用程序在启动时启动其 ui Activity ?

android - 如何设置 Android 闹钟在特定日期之前触发

android - 蓝牙 LE 连接时间/缓存

android - BLE 设备每次与新设备绑定(bind)时是否会生成新的 LTK、CSRK 和 IRK?

android - 是否有可能在设备休眠时调用回调而无需获取唤醒锁

android - 应用程序关闭后粘性服务重新启动

android - 确定由哪个进程调用 Application.onCreate()

linux - 将 BlueZ Stack 用作外设(广告商)

Android蓝牙MAC获取的是02 :00:00:00:00:00

Android 上的 Android 蓝牙交叉传输 key 派生支持