【问题标题】:Bluetooth Pairing with DisplayOnly no passkey box appears蓝牙与 DisplayOnly 配对不出现密码框
【发布时间】:2018-09-05 19:38:56
【问题描述】:

我正在尝试为 linux 配置蓝牙芯片以与 android 手机配对。我将有一个可以显示 6 位数密码的显示器,因此我正在尝试将 Bluetoothctl 代理设置为 DisplayOnly。

根据这篇文章中的表格https://www.silabs.com/community/wireless/bluetooth/knowledge-base.entry.html/2016/07/11/bt121_-_legacy_pairi-MnCo 如果响应者是 DisplayOnly,发起者 (Android) 是 KeyboardDisplay,它应该使用 Passkey 输入,响应者显示代码,发起者输入代码。

实际发生的情况是 Android 在尝试配对时什么也不显示,而 bluetoothctl 输出显示数字比较响应是/否:即使我指定我没有任何输入 (DisplayOnly)

如何让 bluez 代理真正作为 DisplayOnly 工作,以便它显示一个密码供我在 Android 上输入?

【问题讨论】:

    标签: android bluetooth bluez


    【解决方案1】:

    了解 URL 中提到的配对选项是完美的,但 BlueZ 的工作方式不同。此处简单讨论,请参考Bluetooth SIG 的Bluetooth Pairing blog 的解释。

    为简化起见,下图是从上述博客中复制而来的。

    所以输入可以是“No Input”、“Yes/No”和“Keyboard”三种形式。输出可以是“无输出”和“数值输出”。

    在您的情况下,Android 是您要输入显示在响应者中的密钥的发起者。所以 Android 设备的输入是“键盘”,响应者是“数字输出”能力。

    要实现您的情况,您需要在响应者中指定“DisplayOnly”(您已经正确)并在输入Android设备中指定“KeyboardOnly”或“KeyboardDisplay”。

    但默认情况下,BlueZ 不会将“KeyboardDisplay”视为单独的选项,而是将其转换/视为“DisplayYesNO”,see here in mgmt.txt API 以获取更多详细信息。因此,您的 android 输入设备充当“DisplayYesNo”并导致混乱。

    所以你需要在响应端使用“DisplayOnly”,在发起端使用“KeyboardOnly”。要使用自定义代理(不使用 bluetoothctl 的代理)试验此用例,请使用以下示例示例(注意:未完全实现,来自 stdin 的 fscanf 是错误的:-(等,)

    /*
     * gcc `pkg-config --cflags glib-2.0 gio-2.0` -Wall -Wextra -o ./bin/agent ./agent.c `pkg-config --libs glib-2.0 gio-2.0`
     */
    #include <glib.h>
    #include <gio/gio.h>
    #include <stdio.h>
    
    GMainLoop *loop;
    GDBusConnection *con;
    #define AGENT_PATH  "/org/bluez/AutoPinAgent"
    
    static void bluez_agent_method_call(GDBusConnection *conn,
                        const gchar *sender,
                        const gchar *path,
                        const gchar *interface,
                        const gchar *method,
                        GVariant *params,
                        GDBusMethodInvocation *invocation,
                        void *userdata)
    {
        int pass;
        int entered;
        char *opath;
        GVariant *p= g_dbus_method_invocation_get_parameters(invocation);
    
        g_print("Agent method call: %s.%s()\n", interface, method);
        if(!strcmp(method, "RequestPinCode")) {
            ;
        }
        else if(!strcmp(method, "DisplayPinCode")) {
            ;
        }
        else if(!strcmp(method, "RequestPasskey")) {
            g_print("Getting the Pin from user: ");
            fscanf(stdin, "%d", &pass);
            g_print("\n");
            g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", pass));
        }
        else if(!strcmp(method, "DisplayPasskey")) {
            g_variant_get(params, "(ouq)", &opath, &pass, &entered);
            g_print("Path: %s Pass: %d Entered: %d\n", opath, pass, entered);
            g_dbus_method_invocation_return_value(invocation, NULL);
        }
        else if(!strcmp(method, "RequestConfirmation")) {
            g_variant_get(params, "(ou)", &opath, &pass);
            g_print("Path: %s Pass: %d\n", opath, pass);
            g_dbus_method_invocation_return_value(invocation, NULL);
        }
        else if(!strcmp(method, "RequestAuthorization")) {
            ;
        }
        else if(!strcmp(method, "AuthorizeService")) {
            ;
        }
        else if(!strcmp(method, "Cancel")) {
            ;
        }
        else
            g_print("We should not come here, unknown method\n");
    }
    
    static const GDBusInterfaceVTable agent_method_table = {
        .method_call = bluez_agent_method_call,
    };
    
    int bluez_register_agent(GDBusConnection *con)
    {
        GError *error = NULL;
        guint id = 0;
        GDBusNodeInfo *info = NULL;
    
        static const gchar bluez_agent_introspection_xml[] =
            "<node name='/org/bluez/SampleAgent'>"
            "   <interface name='org.bluez.Agent1'>"
            "       <method name='Release'>"
            "       </method>"
            "       <method name='RequestPinCode'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='s' name='pincode' direction='out' />"
            "       </method>"
            "       <method name='DisplayPinCode'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='s' name='pincode' direction='in' />"
            "       </method>"
            "       <method name='RequestPasskey'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='u' name='passkey' direction='out' />"
            "       </method>"
            "       <method name='DisplayPasskey'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='u' name='passkey' direction='in' />"
            "           <arg type='q' name='entered' direction='in' />"
            "       </method>"
            "       <method name='RequestConfirmation'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='u' name='passkey' direction='in' />"
            "       </method>"
            "       <method name='RequestAuthorization'>"
            "           <arg type='o' name='device' direction='in' />"
            "       </method>"
            "       <method name='AuthorizeService'>"
            "           <arg type='o' name='device' direction='in' />"
            "           <arg type='s' name='uuid' direction='in' />"
            "       </method>"
            "       <method name='Cancel'>"
            "       </method>"
            "   </interface>"
            "</node>";
    
        info = g_dbus_node_info_new_for_xml(bluez_agent_introspection_xml, &error);
        if(error) {
            g_printerr("Unable to create node: %s\n", error->message);
            g_clear_error(&error);
            return 0;
        }
    
        id = g_dbus_connection_register_object(con, 
                AGENT_PATH,
                info->interfaces[0],
                &agent_method_table,
                NULL, NULL, &error);
        g_dbus_node_info_unref(info);
        //g_dbus_connection_unregister_object(con, id);
        /* call register method in AgentManager1 interface */
        return id;
    }
    
    static int bluez_agent_call_method(const gchar *method, GVariant *param)
    {
        GVariant *result;
        GError *error = NULL;
    
        result = g_dbus_connection_call_sync(con,
                "org.bluez",
                "/org/bluez",
                "org.bluez.AgentManager1",
                method,
                param,
                NULL,
                G_DBUS_CALL_FLAGS_NONE,
                -1,
                NULL,
                &error);
        if(error != NULL) {
            g_print("Register %s: %s\n", AGENT_PATH, error->message);
            return 1;
        }
    
        g_variant_unref(result);
        return 0;
    }
    
    static int bluez_register_autopair_agent(const char *cap)
    {
        int rc;
    
        rc = bluez_agent_call_method("RegisterAgent", g_variant_new("(os)", AGENT_PATH, cap));
        if(rc)
            return 1;
    
        rc = bluez_agent_call_method("RequestDefaultAgent", g_variant_new("(o)", AGENT_PATH));
        if(rc) {
            bluez_agent_call_method("UnregisterAgent", g_variant_new("(o)", AGENT_PATH));
            return 1;
        }
    
        return 0;
    }
    
    
    static void cleanup_handler(int signo)
    {
        if (signo == SIGINT) {
            g_print("received SIGINT\n");
            g_main_loop_quit(loop);
        }
    }
    
    int main(int argc, char **argv)
    {
        int id;
        int rc;
    
        if(argc < 2)
            return 1;
    
        if(signal(SIGINT, cleanup_handler) == SIG_ERR)
            g_print("can't catch SIGINT\n");
    
        con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
        if(con == NULL) {
            g_print("Not able to get connection to system bus\n");
            return 1;
        }
    
        loop = g_main_loop_new(NULL, FALSE);
    
        id = bluez_register_agent(con);
        if(id == 0)
            goto fail;
    
        rc = bluez_register_autopair_agent(argv[1]);
        if(rc) {
            g_print("Not able to register default autopair agent\n");
            goto fail;
        }
    
        g_main_loop_run(loop);
    
    fail:
        g_dbus_connection_unregister_object(con, id);
        g_object_unref(con);
        return 0;
    }
    

    要对此进行实验,您必须在 bluetoothctl "agent off" 中禁用代理并打开代理,

    Responder: ./bin/agent "DisplayOnly"
    Initiator: ./bin/agent "KeyboardOnly"
    

    当您尝试从发起方配对时,DisplayPasskey 将在响应方中调用并显示 6 位 PIN,发起方将调用 RequestPasskey 并从标准输入获取输入 PIN。

    【讨论】:

    • 哇,感谢您详细的回答。我一直在考虑添加一个自定义代理,但目标操作系统是 tinycore,所以我在编译开箱即用的代理时遇到了问题,但我会继续尝试看看这是否可行!
    • @Tyler:谢谢。如果它真的回答了您的问题,请接受它:-)
    • 所以我在响应者上运行自定义代理,但如果发起者是 Android,我该如何在发起者上运行它?我没有看到只制作Android键盘的方法,请指教
    • @Tyler 我不确定 Android 端。
    • 好的,谢谢,我接受了这个答案,因为它回答了为什么不使用密码输入的问题,我将打开一个新问题,将 android 设置为仅限键盘
    猜你喜欢
    • 2013-06-25
    • 2016-09-24
    • 2012-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多