android 4.0 蓝牙分析之一

    xiaoxiao2021-03-25  33

    原址 SystemServer 启动开始讲起,在 SystemServer 启动的时,会启动一个 BluetoothService BluetoothA2DPService 的实例:

    Code:

     //     Skip Bluetooth if we have an emulator kernel 223             // TODO: Use a more reliable check to see if this product should 224             // support Bluetooth - see bug 988521 225             if (SystemProperties.get("ro.kernel.qemu").equals("1")) { 226                 Slog.i(TAG, "No Bluetooh Service (emulator)"); 227             } else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) { 228                 Slog.i(TAG, "No Bluetooth Service (factory test)"); 229             } else { 230                 Slog.i(TAG, "Bluetooth Service"); 231                 bluetooth = new BluetoothService(context); 232                 ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, bluetooth); 233                 bluetooth.initAfterRegistration(); 234                 bluetoothA2dp = new BluetoothA2dpService(context, bluetooth); 235                 ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE, 236                                           bluetoothA2dp); 237                 bluetooth.initAfterA2dpRegistration(); 238  239                 int airplaneModeOn = Settings.System.getInt(mContentResolver, 240                         Settings.System.AIRPLANE_MODE_ON, 0); 241                 int bluetoothOn = Settings.Secure.getInt(mContentResolver, 242                     Settings.Secure.BLUETOOTH_ON, 0); 243                 if (airplaneModeOn == 0 && bluetoothOn != 0) { 244                     bluetooth.enable(); 245                 } 246             }

     

    SystemServer.Java 里,在addService()时

    bluetooth = new BluetoothService(context);

    ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, bluetooth);

                 bluetooth.initAfterRegistration();

    bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);

    bluetooth.initAfterA2dpRegistration();

     

    addService后,执行了红色initAfterRegistration()方法,该方法里发送了一个消息

    mBluetoothState.sendMessage(BluetoothAdapterStateMachine.TURN_HOT),进入BluetoothAdapterStateMachine之后,TURN_HOT的处理有两处,到底是哪一处的处理呢,我们到BluetoothAdapterStateMachine的构造函数里去看,在BluetoothAdapterStateMachine的构造函数里,设置了初始化状态为setInitialState(mPowerOff);因此addService后的TURN_HOT,

    进入的是PowerOff里的TURN_HOT

     

    1.1          蓝牙的状态

    蓝牙状态如下:

    ·        Power off

    这就是蓝牙模块没有初始化的状态,这时候硬件模块是出于没有上电的状态。

    ·        Warm up

    这个状态就是给设备上电,使设备能够从没电到待机状态。

    ·        Hot off

    Hot off我个人理解就是在模块上电了,出于一种待命的状态,如果收到了turn_on_coninue的命令时候就会去将蓝牙模块切换到工作状态。如果接收到了turn_cold的命令时候,设备就会断电进入power off状态。

    ·        Switching

    这也是一个中间状态,需要继续接收命令。

    ·        Bluetooth on

    这时蓝牙模块出于正常工作的状态。

    1.2          蓝牙的使能

    在上层应用中,蓝牙界面类是BluetoothSettins.java,在actionBar上还有一个开关,另外MENU里也有四个菜单项。

    实现蓝牙开关逻辑处理的类是BluetoothEnabler.java,当我们打开或关闭开关时,会执行onCheckedChanged()方法,

            if (mLocalAdapter != null) {

                mLocalAdapter.setBluetoothEnabled(isChecked);

            }

    这里,LocalBluetoothAdaptersetBluetoothEnabled方法,然后调用了BluetoothAdapter.java enable(), 进而调用到了IBluetooth enable(),这里的IBluetooth对应的有一个IBluetooth.aidl,

     

    由此可以知道,是通过进程间通信,调用到了BluetoothService.java里的enable()BluetoothService.java里的enable()里,我们很高兴看到如下代码:

    mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);

     

    这里就是发送了一个USER_TURN_ON消息,而处理该消息的地方是在processMessage()里,然后再根据状态,确认是在哪个状态对该消息进行了处理,processMessage()对该消息的处理如下:

    Code:

    case USER_TURN_ON:

          // starts turning on BT module, broadcast this out

          broadcastState(BluetoothAdapter.STATE_TURNING_ON);

          transitionTo(mWarmUp);

             if (prepareBluetooth()) {

              // this is user request, save the setting

              if ((Boolean) message.obj) {

                 persistSwitchSetting(true);

              }

          // We will continue turn the BT on all the way to the BluetoothOn state

               deferMessage(obtainMessage(TURN_ON_CONTINUE));

                   } else {

                            Log.e(TAG, "failed to prepare bluetooth, abort turning on");

                            transitionTo(mPowerOff);

                            broadcastState(BluetoothAdapter.STATE_OFF);

                        }

             break;

    上一小节中讲到的蓝牙的状态,透过代码可以看到,是通过transitionTo()方法来切换蓝牙的状态的。

    1.3          调用流程

    方法调用流程如下:

    BluetoothSetting.java ------>

     BluetoothEnable.java( onCheckedChanged() ) ------>

     LocalBlutoothAdapter.java ( setBluetoothEnable() ) ------>

    BluetoothAdapter.java( enable())  ------>

    IBluetooth.aidl( enable() )  ------>

    BluetoothService.java( enable() )  ------>

    BluetoothAdapterStateMachine.java( enableNative() )  ------>

    android_server_BluetoothService.cpp

    2.         蓝牙的扫描 

    2.1          蓝牙扫描

    蓝牙扫描的流程,结构比较清晰,根据代码,分析打描的流程如下:

           上层应用层代码调用startScanning()方法,这个方法会LocalBluetoothAdapter.javastartScanning()方法,进而调用到framework里的BluetoothAdapter.java里,关于这点,是和打开蓝牙的流程是一致的,需要说有的是,调用LocalBluetoothAdapter.java里面的方法,最终都是调用到了BluetoothAdapter.java里,后面有其它类似的方法也是如此。

           BluetoothAdapter.java里,则是startDiscovery()方法来扫描蓝牙设备的,使用的也是IBluetooth.aidl的文件来实现进程间通信,进而调用到BluetoothService.java里的startDiscovery(),到了这里,之后就是本地方法startDiscoveryNative()了,本地方法调用到了android_server_BluetoothService.cpp里。

     

     

     

    2.2          调用流程

    方法调用流程如下:

    BluetoothSetting.java ------>

    LocalBlutoothAdapter.java (startScanning () ) ------>

    BluetoothAdapter.java(startDiscovery ())  ------>

    IBluetooth.aidl(startDiscovery () )  ------>

    BluetoothService.java(startDiscovery () )  ------>

    BluetoothAdapterStateMachine.java(startDiscoveryNative () )  ------>

    android_server_BluetoothService.cpp

    2.3          扫描结果

    当扫描到了设备时,在android_server_BluetoothEventLoop.cpp里有一个方法:

     static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,

    void *data)

    这个方法里各种事件的处理,其中扫描到蓝牙设备后的处理是

    Code:

      if (dbus_message_is_signal(msg,

                                   "org.bluez.Adapter",

                                   "DeviceFound")) {

            char *c_address;

            DBusMessageIter iter;

            jobjectArray str_array = NULL;

            if (dbus_message_iter_init(msg, &iter)) {

                dbus_message_iter_get_basic(&iter, &c_address);

                if (dbus_message_iter_next(&iter))

                    str_array =

                        parse_remote_device_properties(env, &iter);

            }

            if (str_array != NULL) {

                env->CallVoidMethod(nat->me,

                                    method_onDeviceFound,

                                    env->NewStringUTF(c_address),

                                    str_array);

            } else

                LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);

            goto success;

    }

     

    这里会有一个标识“DeviceFound”,调用到方法则是  method_onDeviceFound,接下来的处理是

    method_onDeviceFound = env->GetMethodID(clazz, "onDeviceFound",

                                                "(Ljava/lang/String;[Ljava/lang/String;)V");

    这样,通过JNI C++的方法调到了上层java代码中的BluetoothEventLoop.java的方法onDeviceFound()中,最终调用addDevice(),从而添加一个扫描到的蓝牙设备,并最终添加到BluetoothDeviceProperties.java中去了。

    Code:

    private void addDevice(String address, String[] properties) {

            BluetoothDeviceProperties deviceProperties =

                    mBluetoothService.getDeviceProperties();

    deviceProperties.addProperties(address, properties);

    ……

    }

     

    2.4          进程间通信

    扫描和使能在BluetoothAdapter.java中,在调用enable()startDiscovery()时,都调用到了IBluetooth.aidl中,在Android中,. aidl是用于进程间通信的。

    AIDL进程间通信,在BluetoothAdapter.java里,enable()方法如下:

    Code:

       public boolean enable() {

            try {

                return mService.enable();

            } catch (RemoteException e) {Log.e(TAG, "", e);}

            return false;

    }

    这里,我们发现IBluetooth service = IBluetooth.Stub.asInterface(b),在frameworks\base\core\java\android\bluetooth目录下,有一个IBluetooth.aidl,这里是用到了进程间通信,在android里,进程间通信常用aidl来实现,最终IBluetooth调用到了哪个地方,我们看下IBluetooth的实现, BluetoothService最终实现了IBluetoothBluetoothService extends IBluetooth.Stub),因此调用enable方法是调用这里的enable()方法。

    3.         蓝牙的配对与连接

    3.1          蓝牙的配对

    3.1.1    master

    扫描到可用的蓝牙设备后,在BluetoothDevicePreference.java里,点击列表中的某一蓝牙设备,会根据各个设备的bondState,会有不同的流程:

    Code:

      int bondState = mCachedDevice.getBondState();

            if (mCachedDevice.isConnected()) {

                askDisconnect();

            } else if (bondState == BluetoothDevice.BOND_BONDED) {

                mCachedDevice.connect(true);

            } else if (bondState == BluetoothDevice.BOND_NONE) {

                pair();

            }

     

    即如果已边接,则会断开边接disconnect(profile),如果状态是已配对,则会去连接connect(true),如果状态是NONE,则会先配对pair()。这里配对和连接需要注意的是,

    都会进入startPairing(),进一步进入调用到BluetoothServcie.java里的createBond();

    这里做所的工作就是配对。

     

    3.1.2      slave

    被要求配对的一方,在BluetoothPairingRequest.java这个广播里,会接收到来自底层的一个配对请求,接收到BluetoothDevice.ACTION_PAIRING_REQUESTACTION,并弹出提示框(BluetoothPairingDialog.java),提示用户配对。

    3.1.3    取消配对

    DeviceProfilesSettings.java中,取消配对直接调用unpairDevice,最终会调用到CachedBluetoothDevice.javaunpair()方法,取消配对时,会直接断开连接了,调用disconnect(),取消配对时,也会根据状态,分别做不同的处理

    Code:

    int state = getBondState();

            if (state == BluetoothDevice.BOND_BONDING) {

                mDevice.cancelBondProcess();

            }

            if (state != BluetoothDevice.BOND_NONE) {

                final BluetoothDevice dev = mDevice;

                if (dev != null) {

                    final boolean successful = dev.removeBond();

                    if (successful) {

                        if (Utils.D) {

                            Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null));

                        }

                    } else if (Utils.V) {

                        Log.v(TAG, "Framework rejected command immediately:REMOVE_BOND " +

                                describe(null));

                    }

                }

            }

    3.2          蓝牙的连接

    配对后即可进行A2DP, FTP等操作,不同的业务,都会走各自的连接,如BluetoothHeadset.java/BluetoothA2dp.java都有各自的connect()方法,待后续各自的profile里,单独分析各自的连接功能。

    3.3          相关的界面类:

    BluetoothSettings.java

    DeviceListPreferenceFragment.java

    DeviceProfilesSettings.java

    BluetoothPairingRequest.java

    BluetoothPairingDialog.java

    4.         重命名蓝牙设备

    4.1          调用流程

    重命名蓝牙设备是一个从上到下单线的流程,在BluetoothNameDialogFragment.java这个dialog里,直接调用setName(),一直往下调到BluetoothAdapter.java里的setName()里。

    5.         蓝牙可见时间

    5.1          调用流程

    可被检测到的主要逻辑处理在BluetoothDiscoverableEnabler.java里,这个流程较为简单,直接在BluetoothDiscoverableEnabler.java这个类里调用setEnable(),然后一步一步调用到BluetoothAdapter.java里的mService.setDiscoverableTimeout(timeout),进而调用到BluetoothService.java里的setDiscoverableTimeout()。

    6.         接收到的文件

    6.1          调用流程

    当用户点击“接收到的文件”菜单时,会直接发送一个广播

    Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES);

                    getActivity().sendBroadcast(intent);

    这个广播将会被BluetoothOppReceiver.java接收到,进一步跳转到BluetoothOppTransferHistory.java中,列出所有接收到的文件。

    Intent in = new Intent(context, BluetoothOppTransferHistory.class);

                in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);

                in.putExtra("direction", BluetoothShare.DIRECTION_INBOUND);

                in.putExtra(Constants.EXTRA_SHOW_ALL_FILES, true);

                context.startActivity(in);

    接收到的文件功能实现是OPP里实现的,具体在OPP的分析中,会有进一步详细的分析。

    7.          界面

    蓝牙设置只有两个界面,其他的都是一些 dialog menu.

    7.1          蓝牙设置界面

    进入蓝牙设置的界面:BluetoothSettings.java

    已配对的蓝牙,进入后,可进行重命名,取消配对的界面:DeviceProfilesSettings

    转载请注明原文地址: https://ju.6miu.com/read-149988.html

    最新回复(0)