android 4.0 藍芽分析之一

慢慢的燃燒發表於2017-03-24
原址
SystemServer啟動開始講起,在SystemServer啟動的時,會啟動一個BluetoothServiceBluetoothA2DPService的例項:

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

相關文章