【问题标题】:Android - connecting to multiple bluetooth devices without pairingAndroid - 无需配对即可连接多个蓝牙设备
【发布时间】:2012-05-07 04:02:00
【问题描述】:

我有一堆不可发现的设备,但我知道它们的 MAC 地址。目前我可以在我的 ConnectThread 中使用它连接到多个设备:

Method m = device.getClass().getMethod("createRfcommSocket",new Class[] { int.class });

tmp = (BluetoothSocket) m.invoke(device, 1);

问题是我想为 2.3 之前的设备支持不安全的 RFCOMM。然后我 found this 在另一个答案上让我这样做:

tmp = InsecureBluetooth.createRfcommSocketToServiceRecord(device,MY_UUID, true);

无需配对即可完美连接到单个设备。

我的问题是我如何才能两全其美并在 InsecureBluetooth 类上使用反射?或者它是否需要在 InsecureBluetooth 类中完成,如果是这样,怎么做?下面是 InsecureBluetooth 类的相关部分:

private static BluetoothSocket createRfcommSocketToServiceRecord(
        BluetoothDevice device, int port, UUID uuid, boolean encrypt)
        throws IOException {
    try {
        BluetoothSocket socket = null;
        Constructor<BluetoothSocket> constructor = BluetoothSocket.class
                .getDeclaredConstructor(int.class, int.class, boolean.class,
                        boolean.class, BluetoothDevice.class, int.class, ParcelUuid.class);
        if (constructor == null)
            throw new RuntimeException("can't find the constructor for socket");

        constructor.setAccessible(true);
        Field f_rfcomm_type = BluetoothSocket.class
                .getDeclaredField("TYPE_RFCOMM");
        f_rfcomm_type.setAccessible(true);
        int rfcomm_type = (Integer) f_rfcomm_type.get(null);
        socket = constructor.newInstance(new Object[] { rfcomm_type, -1, false,
                true, device, port, uuid != null ? new ParcelUuid(uuid) : null });
        return socket;
    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    } catch (NoSuchFieldException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        if (e.getCause() instanceof IOException) {
            throw (IOException) e.getCause();
        }
        throw new RuntimeException(e.getCause());
    }
}

public static BluetoothSocket createRfcommSocketToServiceRecord(
        BluetoothDevice device, UUID uuid, boolean encrypt) throws IOException {
    return createRfcommSocketToServiceRecord(device, -1, uuid, encrypt);
}

public static BluetoothSocket createRfcommSocket(BluetoothDevice device,
        int port, boolean encrypt) throws IOException {
    return createRfcommSocketToServiceRecord(device, port, null, encrypt);
}

【问题讨论】:

    标签: android bluetooth rfcomm insecure-connection


    【解决方案1】:

    您可以使用以下类来创建不安全的蓝牙套接字连接。它会自动处理反射以便为低于 2.3.3 的 SDK 提供此功能

    import java.io.IOException;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.UUID;
    
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothServerSocket;
    import android.bluetooth.BluetoothSocket;
    import android.os.Binder;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.ParcelUuid;
    import android.util.Log;
    
    /**
     * Class to offer unsecure bluetooth socket connection.<br />
     * It was intended to be used when building the app with API version below 10 (API < Android2.3.3)
     * @author waa
     *
     */
    @SuppressWarnings("all")
    public class UnpairedBluetooth {
        static private class InUse extends RuntimeException {
        }
    
        public static BluetoothServerSocket listenUsingRfcommWithServiceRecord(BluetoothAdapter adapter, String name, UUID uuid, boolean encrypt) throws IOException {
            try {
                Class c_rfcomm_channel_picker = null;
                Class[] children = BluetoothAdapter.class.getDeclaredClasses();
                for(Class c : children) {
                    Log.e("TO", "class " + c.getCanonicalName());
                    if(c.getCanonicalName().equals(BluetoothAdapter.class.getName() + ".RfcommChannelPicker")) {
                        c_rfcomm_channel_picker = c;
                        break;
                    }
                }
                if(c_rfcomm_channel_picker == null)
                    throw new RuntimeException("can't find the rfcomm channel picker class");
    
                Constructor constructor = c_rfcomm_channel_picker.getDeclaredConstructor(UUID.class);
                if(constructor == null)
                    throw new RuntimeException("can't find the constructor for rfcomm channel picker");
                Object rfcomm_channel_picker = constructor.newInstance(new Object[] {uuid});
                Method m_next_channel = c_rfcomm_channel_picker.getDeclaredMethod("nextChannel", new Class[] {});
                m_next_channel.setAccessible(true);
    
                BluetoothServerSocket socket = null;
    
                int channel;
                int errno;
                while (true) {
                    channel = (Integer)m_next_channel.invoke(rfcomm_channel_picker, new Object[] {});
    
                    if (channel == -1) {
                        throw new IOException("No available channels");
                    }
    
                    try {
                        socket = listenUsingRfcomm(channel, encrypt);
                        break;
                    } catch(InUse e) {
                        continue;
                    }
                }
    
                Field f_internal_service = adapter.getClass().getDeclaredField("mService");
                f_internal_service.setAccessible(true);
                Object internal_service = f_internal_service.get(adapter);
    
                Method m_add_rfcomm_service_record = internal_service.getClass().getDeclaredMethod("addRfcommServiceRecord", new Class[] {String.class, ParcelUuid.class, int.class, IBinder.class});
                m_add_rfcomm_service_record.setAccessible(true);
    
                int handle = (Integer)m_add_rfcomm_service_record.invoke(internal_service, new Object[] { name, new ParcelUuid(uuid), channel, new Binder() } );
    
                if (handle == -1) {
                    try {
                        socket.close();
                    } catch (IOException e) {}
                    throw new IOException("Not able to register SDP record for " + name);
                }
                Field f_internal_handler = adapter.getClass().getDeclaredField("mHandler");
                f_internal_handler.setAccessible(true);
                Object internal_handler = f_internal_handler.get(adapter);
    
                Method m_set_close_handler = socket.getClass().getDeclaredMethod("setCloseHandler", new Class[] {Handler.class, int.class});
                m_set_close_handler.setAccessible(true);
    
                m_set_close_handler.invoke(socket, new Object[] { internal_handler, handle});
                return socket;
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            } catch (NoSuchFieldException e) {
                throw new RuntimeException(e);
            } catch(IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch(InstantiationException e) {
                throw new RuntimeException(e);
            } catch(InvocationTargetException e) {
                if(e.getCause() instanceof IOException) {
                    throw (IOException)e.getCause();
                }
                throw new RuntimeException(e.getCause());
            }   
        }
        private static BluetoothServerSocket listenUsingRfcomm(/*BluetoothAdapter adapter, */ int port, boolean encrypt, boolean reuse) throws IOException, InUse {
            BluetoothServerSocket socket = null;
            try {
                Constructor<BluetoothServerSocket> constructor = BluetoothServerSocket.class.getDeclaredConstructor(int.class, boolean.class, boolean.class, int.class);
                if(constructor == null)
                    throw new RuntimeException("can't find the constructor");
                constructor.setAccessible(true);
                Field f_rfcomm_type = BluetoothSocket.class.getDeclaredField("TYPE_RFCOMM");
                f_rfcomm_type.setAccessible(true);
                int rfcomm_type = (Integer)f_rfcomm_type.get(null);
    
                Field f_e_addr_in_use = BluetoothSocket.class.getDeclaredField("EADDRINUSE");
                f_e_addr_in_use.setAccessible(true);
                int e_addr_in_use = (Integer)f_e_addr_in_use.get(null);
    
                socket = constructor.newInstance(new Object[] { rfcomm_type, false, encrypt, port } );
    
                Field f_internal_socket = socket.getClass().getDeclaredField("mSocket");
                f_internal_socket.setAccessible(true);
                Object internal_socket = f_internal_socket.get(socket);
                Method m_bind_listen = internal_socket.getClass().getDeclaredMethod("bindListen", new Class[] {});
                m_bind_listen.setAccessible(true);
                Object result = m_bind_listen.invoke(internal_socket, new Object[] {});
    
                int errno = (Integer)result;
                if(reuse && errno == e_addr_in_use) {
                    throw new InUse();
                } else if (errno != 0) {
                    try {
                        socket.close();
                    } catch (IOException e) {}
                    internal_socket.getClass().getMethod("throwErrnoNative", new Class[] {int.class}).invoke(internal_socket, new Object[] { errno });
                }
                return socket;
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            } catch (NoSuchFieldException e) {
                throw new RuntimeException(e);
            } catch(IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch(InstantiationException e) {
                throw new RuntimeException(e);
            } catch(InvocationTargetException e) {
                if(e.getCause() instanceof IOException) {
                    throw (IOException)e.getCause();
                }
                throw new RuntimeException(e.getCause());
            }
        }
        public static BluetoothServerSocket listenUsingRfcomm(int port, boolean encrypt) throws IOException {
            return listenUsingRfcomm(port, encrypt, false);
        }
        private static BluetoothSocket createRfcommSocketToServiceRecord(BluetoothDevice device, int port, UUID uuid, boolean encrypt) throws IOException {
            try {
                BluetoothSocket socket = null;
                Constructor<BluetoothSocket> constructor = BluetoothSocket.class.getDeclaredConstructor(
                        int.class, int.class, boolean.class, boolean.class, BluetoothDevice.class, int.class, ParcelUuid.class);
                if(constructor == null)
                    throw new RuntimeException("can't find the constructor for socket");
    
                constructor.setAccessible(true);
                Field f_rfcomm_type = BluetoothSocket.class.getDeclaredField("TYPE_RFCOMM");
                f_rfcomm_type.setAccessible(true);
                int rfcomm_type = (Integer)f_rfcomm_type.get(null);
    //          socket = constructor.newInstance(new Object[] { rfcomm_type, -1, false, true, device, port, uuid != null ? new ParcelUuid(uuid) : null} );
                socket = constructor.newInstance(new Object[] { rfcomm_type, -1, false, encrypt, device, port, uuid != null ? new ParcelUuid(uuid) : null} );
                return socket;
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            } catch (NoSuchFieldException e) {
                throw new RuntimeException(e);
            } catch(IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch(InstantiationException e) {
                throw new RuntimeException(e);
            } catch(InvocationTargetException e) {
                if(e.getCause() instanceof IOException) {
                    throw (IOException)e.getCause();
                }
                throw new RuntimeException(e.getCause());
            }
        }
        public static BluetoothSocket createRfcommSocketToServiceRecord(BluetoothDevice device, UUID uuid, boolean encrypt) throws IOException{
            return createRfcommSocketToServiceRecord(device, -1, uuid, encrypt);
        }
        public static BluetoothSocket createRfcommSocket(BluetoothDevice device, int port, boolean encrypt) throws IOException {
            return createRfcommSocketToServiceRecord(device, port, null, encrypt);
        }
    }
    

    更多信息,请参考this链接。

    【讨论】:

    • 是的,这与我在问题中包含的类和链接相同。问题是,当我在每个设备上执行 while 循环时,它会覆盖我在使用此方法时已经连接到的设备。我将更新我的问题以解释更多我的意思。
    • 我认为您可以一次连接到一台设备以获得相同的蓝牙配置文件。不是吗?
    • 是的,但是我想同时连接多个设备,并在后台对每个设备进行数据检索。
    • 或许this可以帮助你
    猜你喜欢
    • 2018-05-26
    • 1970-01-01
    • 2016-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多