美文网首页Android 蓝牙Android技术知识Android知识
Android 蓝牙(十一)Pan蓝牙共享网络(源码分析)

Android 蓝牙(十一)Pan蓝牙共享网络(源码分析)

作者: 朋永 | 来源:发表于2017-07-20 17:38 被阅读114次

    转载请注明出处:http://blog.csdn.net/vnanyesheshou/article/details/72638637

    本文主要分析Andorid蓝牙共享网络的使用、连接流程等。
    基于Android4.3源码


    1 简介##

    Bluetooth PAN全称:Bluetooth Personal Area Networking,蓝牙个人区域网,是Bluetooth技术的一种重要应用,其核心思想就是用Bluetooth无线技术取代传统的有线电缆,组建个人化信息网络,实现个人范围的资源和信息共享(也就是网络共享)。
    主要应用场景:手机与手机、PC与PC、PC与手机之间的网络共享。
    三种角色:

    NAP(Network Access Point): 如果你的蓝牙设备支持NAP,那么你可以通过共享网络连接来给别的PAN Network内的PC提供上网功能。
    GN(Group Ad-hoc Network): 使你可以在小局域网内给其它设备提供数据转发的功能。
    PANU(PAN User):与NAP,GN相对的角色,使用NAP,GN提供的功能的设备。

    Android上支持作为NAP和PANU角色。


    2 NAP##

    NAP全称NetworkAccessPoint(网络接入点)。网络接入点又称为网络访问点,是带有一个或多个蓝牙射频的装置,作为LAN、GSM等网络和蓝牙网络之间的网桥、代理或路由器的设备。网络接入点为每个相连的蓝牙设备提供了网络服务,如LAN上共享的资源。
    要想蓝牙共享网络,首先设备需要连接wifi或者开启流量。然后设置中开启“蓝牙共享网络”。


    蓝牙共享网络

    该界面对应着TetherSettings。
    路径:packages/apps/Settings/src/com/android/settings/TetherSettings.java
    在其onCreate时,会获取BluetoothPan代理对象。

    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    if (adapter != null) {
        //获取pan代理对象
        adapter.getProfileProxy(activity.getApplicationContext(), 
                mProfileServiceListener,
                BluetoothProfile.PAN);
    }
    

    getProfileProxy是异步的,获取成功、失败会回调mProfileServiceListener。代码如下:

    private BluetoothProfile.ServiceListener mProfileServiceListener =
        new BluetoothProfile.ServiceListener() {
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            //获取成功
            mBluetoothPan.set((BluetoothPan) proxy);
        }
        public void onServiceDisconnected(int profile) {
            //获取失败
            mBluetoothPan.set(null);
        }
    };
    

    打开蓝牙共享会调用到startTethering()。如果蓝牙状态为关闭,则先开启蓝牙。保证蓝牙为开启状态,然后打开蓝牙共享。

    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    //判断蓝牙状态
    if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
        mBluetoothEnableForTether = true;
        adapter.enable(); //打开蓝牙
        mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
        mBluetoothTether.setEnabled(false);
    } else {
        BluetoothPan bluetoothPan = mBluetoothPan.get();
        //打开蓝牙共享
        if (bluetoothPan != null) bluetoothPan.setBluetoothTethering(true);
        mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
    }
    

    bluetoothPan.setBluetoothTethering(true)跳到Bluetooth应用中,
    代码路径:packages/apps/Bluetooth/src/com/android/bluetooth/pan/PanService.java
    先调用到内部类BluetoothPanBinder的setBluetoothTethering方法。

    public void setBluetoothTethering(boolean value) {
        PanService service = getService();
        if (service == null) return;
        service.setBluetoothTethering(value);
    }
    

    该方法中很明显是去调用PanService的setBluetoothTethering方法。

    void setBluetoothTethering(boolean value) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
        if(mTetherOn != value) {
            mTetherOn = value;  //保存状态
            //在更改状态时,删除任何现有的panu、pan-nap连接
            List<BluetoothDevice> DevList = getConnectedDevices();
            for(BluetoothDevice dev : DevList)
                disconnect(dev);
        }
    }
    

    该函数主要是将状态值保存喜爱,然后将之前的连接都断开。
    打开网络共享后就可以进行连接互联网共享了,当A设备打开网络共享后,点击互联网访问,连接B设备一直失败,这样是错误的,应该B设备主动连接该设备,而不是该设备连接其他设备。

    当别的设备与NAP设备(该设备)连接成功,NAP会回调com_android_bluetooth_pan.cpp中的connection_state_callback(),然后回调PanService中的onConnectStateChanged(),然后跳到handlePanDeviceStateChange中,在该函数中由于远端设备为PANU,本地设备为NAP,则会调用enableTethering,用来配置ip地址等相关信息(具体还不是很清楚)。handlePanDeviceStateChange会向为发送广播,携带蓝牙共享的相关状态。


    3 PANU##

    PANU Personal Area Networking user,个人区域网用户。
    要想连接别的设备的蓝牙共享网络,首先需要配对,配对成功后,在已配对界面点击“互联网访问”来连接远端设备,

    互联网访问
    该操作经过一系列调用,跳到PanProfile中的connect方法中。具体如何跳到PanProfile中的connect方法,可以参考如下文章。
    http://blog.csdn.net/vnanyesheshou/article/details/71106622
    http://blog.csdn.net/vnanyesheshou/article/details/71811288
    PanProfile中的connect会跳到Bluetooth应用中的PanService中。
    在PanService的connect函数中判断与该设备的连接状态是否断开,没有断开则返回false。断开着向handler发送消息,返回true。handler中处理该消息如下:
    BluetoothDevice device = (BluetoothDevice) msg.obj;
    //进行pan连接
    if (!connectPanNative(Utils.getByteAddress(device),
            BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) {
        //连接失败,发送状态
        handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_CONNECTING,
                BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
        handlePanDeviceStateChange(device, null,
                BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE,
                BluetoothPan.REMOTE_NAP_ROLE);
        break;
    }
    

    调用connectPanNative进行pan的连接,返回false则表示失败,向外发送广播(CONNECTING、DISCONNECTED);返回true则表示该操作成功,等待连接状态回调。
    connectPanNative函数中的参数,看出本地设备作为PANU角色,远端作为NAP角色,所以上面作为NAP时主动连接其他设备失败。
    connectPanNative为native方法,其会跳到com_android_bluetooth_pan,然后向hardware层调用。

    连接状态回调与上相同。都会回调到handlePanDeviceStateChange中。

    //网络相关,暂不清楚是怎么回事。
    LinkProperties lp = new LinkProperties();
    lp.setInterfaceName(iface);             mTetherAc.sendMessage(NetworkStateTracker.EVENT_NETWORK_CONNECTED, lp);
    

    然后向外发送连接状态的广播。该广播只能判断pan协议的连接状态,并不能代表是否能正常共享网络。因为NAP端设备可能没有连接网络,或者分配ip地址等没有成功。

    public boolean isPanConnected(Context context){
        ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo btInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_BLUETOOTH);
        if(btInfo!=null){
            return btInfo.isConnected();
        }
        return false;
    }
    

    上述代码可以判断与NAP设备的网络是否连接,但并不能保证可以上网,连接成功后,NAP设备不管可不可以上网,上述代码都返回true。暂时还没有找到其他方法。

    欢迎大家关注、评论、点赞
    你们的支持是我坚持的动力。

    欢迎关注我的微信公众号

    相关文章

      网友评论

        本文标题:Android 蓝牙(十一)Pan蓝牙共享网络(源码分析)

        本文链接:https://www.haomeiwen.com/subject/cwdjkxtx.html