如何在安卓设备上编程配对蓝牙设备

18

我正在为我的应用程序尝试以编程方式配对蓝牙设备。我能够显示要配对的设备的配对对话框,然后可以输入PIN码。但是,当我按下“配对”按钮时,对话框被移除并且没有任何操作。

我只需要支持Android 2.0及以上版本的设备。

目前,我正在使用以下代码开始配对进程:


public void pairDevice(BluetoothDevice device) {
        String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST";
        Intent intent = new Intent(ACTION_PAIRING_REQUEST);
        String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE";
        intent.putExtra(EXTRA_DEVICE, device);
        String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT";
        int PAIRING_VARIANT_PIN = 0;
        intent.putExtra(EXTRA_PAIRING_VARIANT, PAIRING_VARIANT_PIN);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }

在开始配对请求之前,我停止扫描新设备。

我的应用具有以下蓝牙权限:

  • android.permission.BLUETOOTH_ADMIN
  • android.permission.BLUETOOTH

2
我花了几天时间寻找解决这个确切问题的方法。看起来谷歌认为强制配对是一个安全问题,所以你在这里列出的ACTION类型实际上并不存在。我在这里找到了你引用的类:http://developer.oesf.biz/em/developer/reference/cinnamon/android/bluetooth/BluetoothDevice.html#ACTION_PAIRING_REQUEST 但它不在官方文档中: http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html - moonlightcheese
@FireFLy,你有什么解决方案吗? - Pawan
使用反射,您可以从 BluetoothDevice 类调用 createBond 方法。解决方案: 请参见此帖子:如何在 Android 上以编程方式取消配对或删除配对的蓝牙设备(https://dev59.com/Tmkw5IYBdhLWcg3w2-Ik#11147911)?还有一个取消配对的解决方案。 - Shridutt Kothari
10个回答

9
我成功地通过一个作为服务运行的应用程序和设置应用程序的修改版本,自动请求与键盘设备进行配对过程。这个服务会检查特定类型的设备是否存在,并在没有外部控制(没有返回/主页/确认按钮)的情况下,将控制器配对到启动完成时,直到需要输入PIN码为止。
首先,我创建了一个服务,在启动时启动一个活动(使用android.intent.action.BOOT_COMPLETEDandroid.permission.RECEIVE_BOOT_COMPLETED),并在onReceive回调中定期检查1344类设备(键盘,是唯一输入数据的方式)的存在。
public void onReceive(Context context, Intent intent) 
...
    BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
...
if(dev.getBluetoothClass().getDeviceClass() == 1344){...}

筛选过后,我选择第一个可用的键盘,然后将蓝牙地址传递给设置应用程序:

Intent btSettingsIntent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
btSettingsIntent.putExtra("btcontroller", dev.getAddress());
startActivityForResult(btSettingsIntent, 1);

寻找最佳位置来调用配对过程是一件棘手的事情。只使用HTML可能会很困难。
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, PAIRING_VARIANT_PIN);

我遇到了一个配对窗口,但一旦关闭就无法使用设备。

深入研究了com.Android.settings.Bluetooth类,我找到了解决方法。

createDevicePreference(CachedBluetoothDevice cachedDevice) 

在DeviceListPreferenceFragment中。

从那里,我会将以前选择的蓝牙地址与可用的地址进行比较,一旦成功匹配,我就会调用

cachedDevice.startPairing();

我知道,这很棘手并需要访问Android源代码,但在自定义环境中它可以工作。
我希望这能有所帮助。

这是一条回答问题的指令,请提供需要翻译的英文内容。 - Caner

5

以下是我的答案:

在 onCreate() 中写入以下代码:

    registerReceiver(incomingPairRequestReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST));

那么请创建变量。
private final BroadcastReceiver incomingPairRequestReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)) {
            BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            //pair from device: dev.getName()
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                dev.setPairingConfirmation(true);
                //successfull pairing
            } else {
                //impossible to automatically perform pairing,
                //your Android version is below KITKAT
            }
        }
    }
};

6
setPairingConfirmation(boolean confirm)方法需要BLUETOOTH_PRIVILEGED权限,但该权限不适用于第三方应用程序。 请参阅Android Developers - Roses

4
很遗憾,我认为您最好能够为用户打开“设置/无线和网络/蓝牙设置”,如下所示:
    Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
    startActivityForResult(intent, REQUEST_PAIR_DEVICE);

使用 kiosk 模式会使此过程非常困难,因此我选择打开蓝牙设置。 - ChRoNoN

3

1
已被删除,自JB aka 4.1.x起。 - Ewoks
2
它并没有被移除,在我的4.3 Nexus 4上仍然可以使用,并且在源代码中仍然存在(http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/bluetooth/BluetoothDevice.java)。 - AJ87uk

2
反射是不可靠的,不同制造商可以按照自己的意愿更改这些底层方法!我在我们这里的10个设备上测试了许多不同的应用程序,这些反射方法只对大约75%的设备完全有效。如果你想要一个能够适用于所有人的应用程序,在使用反射时一定要非常小心-尝试使用云测试来测试你的应用程序在100+个设备上的表现并检查其失败率。
在API 19(KitKat 4.4)以上的版本中根本不需要使用反射。
BluetoothDevice有新的方法CreateBond。
 private void pairDevice(BluetoothDevice device) {

            device.createBond();
    }

developer.android.com/reference/android/bluetooth/BluetoothDevice.html


云测试工具是否可以测试蓝牙? - severin.julien
1
最近没有看过,去年我尝试过,但是我联系的所有云测试公司都不允许我进行这种类型的测试。 - aiden_fry

0
我正在使用这个类来连接我的客户端智能手机和服务器设备:
private class ConnectThread extends Thread
{
    private final BluetoothSocket mmSocket;

    private final UUID WELL_KNOWN_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");

    public ConnectThread(BluetoothDevice device)
    {
        // Use a temporary object that is later assigned to mmSocket,because
        // mmSocket is final
        BluetoothSocket tmp = null;

        // Get a BluetoothSocket to connect with the given BluetoothDevice
        try
        {
            tmp = device.createRfcommSocketToServiceRecord(WELL_KNOWN_UUID);
            //This is the trick
            Method m = device.getClass().getMethod("createRfcommSocket", new Class[] { int.class });
            tmp = (BluetoothSocket) m.invoke(device, 1);
        } catch (Exception e)
        {
            e.printStackTrace();
        }

        mmSocket = tmp;
    }

    public void run()
    {
        DebugLog.i(TAG, "Trying to connect...");
        // Cancel discovery because it will slow down the connection
        mBluetoothAdapter.cancelDiscovery();

        try
        {
            // Connect the device through the socket. This will block
            // until it succeeds or throws an exception
            mmSocket.connect();
            DebugLog.i(TAG, "Connection stablished");
        } catch (IOException connectException)
        {
            // Unable to connect; close the socket and get out
            DebugLog.e(TAG, "Fail to connect!", connectException);
            try
            {
                mmSocket.close();
            } catch (IOException closeException)
            {
                DebugLog.e(TAG, "Fail to close connection", closeException);
            }
            return;
        }
    }

    /** Will cancel an in-progress connection, and close the socket */
    public void cancel()
    {
        try
        {
            mmSocket.close();
        } catch (IOException e)
        {
        }
    }
}

首先,获取您想要连接的BluetoothDevice对象(列出已配对设备或发现设备)。然后执行以下操作:
ConnectThread ct = new ConnectThread(device);
ct.start();

由于connect()是一个阻塞调用,因此这个连接过程应该在与主活动线程分开的线程中进行。请参考Android Developers获取更详细的信息。


我想通过蓝牙将一台设备发送的图像传输到另一台设备上,但是我在接收线程(Accept Thread)中遇到了问题...该怎么办? - Umang Kothari
@Umang,我的回答只是关于配对设备的。在它们之间交换信息是另一回事。我建议你创建一个详细说明你问题的帖子。 - DragonT
你能看一下这个链接吗: http://stackoverflow.com/questions/23648942/send-text-message-from-one-device-to-another-via-bluetooth-in-android - Umang Kothari

0

也许你需要使用startActivityForResult而不是仅仅使用startActivity?

另一个选择是查看BluetoothChat应用程序示例并启动一个RFComm连接套接字,一旦你启动套接字,配对请求将自动出现,无需发送单独的配对意图。这样你就不需要处理配对了。

http://developer.android.com/resources/samples/BluetoothChat/index.html


-1

这是我得到它的方式:

Bluetooth device = mBtAdapter.getRemoteDevice(address);
//address:11:23:FF:cc:22 
Method m = device.getClass()        
 .getMethod("createBond", (Class[]) null);
         m.invoke(device, (Object[]) null); // send pairing dialog request

After pairing//
       connectDevice(address);

-1

我发现使用不同的PAIRING_VARIANT_PIN值会导致不同的配对UI行为。

请参阅此页面: http://code.google.com/p/backport-android-bluetooth/source/browse/trunk/backport-android-bluetooth201/src/backport/android/bluetooth/BluetoothDevice.java?spec=svn67&r=67

我怀疑你遇到的问题是两个设备都是蓝牙2.1,这种情况下配对请求应该在两个设备上显示一个6位数字密码。

我能够实现的最佳结果是使用PAIRING_VARIANT_PIN = 0。当我的应用程序提示时,我输入了1234作为PIN码,然后在我的目标设备上出现了一个6位数字密码。配对UI完成了,就这样。

要么你需要找出如何使用其他配对变量或配对变量PIN来启动蓝牙2.1配对请求,要么你没有正确捕获正在运行的活动的结果。

考虑到我已经花费了很多时间尝试做到这一点,我决定我的最终用户在使用我的应用程序之前必须使用安卓设置进行配对。


@Peter O. Stack Overflow 通知我你已经编辑了这篇文章。由于这是我的第一篇文章,我有一些问题,如果你不介意的话:
  • 我记不得我原来的消息了,所以我看不到你删除了什么。有办法可以做到这一点吗?
  • 你有没有可能解决这个问题?
  • 是否有一种直接向用户发送消息的方法,而不是像我现在这样做? 提前感谢。
- user1007074
  1. 点击“编辑”后面的日期以查看编辑历史。
  2. 不,我没有解决问题的方法,因为我对这个主题不是很熟悉。
  3. 不直接通过私信。但如果任一用户拥有至少100声望值,而另一个用户拥有至少20声望值,则前者可以创建一个画廊聊天室并邀请另一个用户进入。
- Peter O.

-3

除了我的评论之外,顺便说一句,即使这些ACTION类型存在,你也不会这样使用它们。以下是一个例子:

Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
intent.putExtra(EXTRA_DEVICE, device);
int PAIRING_VARIANT_PIN = 272;
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, PAIRING_VARIANT_PIN);
sendBroadcast(intent);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接