1. 程式人生 > >Android4.0藍芽開啟流程分析

Android4.0藍芽開啟流程分析

 在前面的UI分析的文章中我們已經發現,其實不管是設定中的開關和fragment之後的開關最終都是關聯到BluetoothEnabler中去的,所以,我們直接去看這個裡面對於開關的處理,開關的處理當然就是onCheckedChanged這個函數了,哈哈~~直接分析。。

1、藍芽開啟的按鍵處理

public void>1.1 setBluetoothEnabled分析

         在按鈕開啟的時候,就是通過這個函式來和framework層的adapter進行互動的。

public void setBluetoothEnabled(boolean enabled) {
		//根據傳入的enabled值,決定是開啟還是關閉
		//我們這邊必然就是呼叫enable了
		//具體分析見1.1.1
        boolean success = enabled
                ? mAdapter.enable()
                : mAdapter.disable();
		//這裡就是設定狀態為正在開啟
		//注意的是這裡success是enable或者disable的返回值,不是enabled的值哦,呵呵~~
        if (success) {
			//所以,會在enable或者disable返回後面設定狀態。
			//當然這個返回並不是說藍芽開啟成功了,因為藍芽開啟的操作是非同步的
			//詳細見1.1.2
            setBluetoothStateInt(enabled
                ? BluetoothAdapter.STATE_TURNING_ON
                : BluetoothAdapter.STATE_TURNING_OFF);
        } else {
            if (Utils.V) {
                Log.v(TAG, "setBluetoothEnabled call, manager didn't return " +
                        "success 
for
enabled: " + enabled); } //若是失敗了,還是要同步一下狀態的。 //就是把adapter的狀態和當前狀態進行一下同步 syncBluetoothState(); } }

1.1.1 bluetoothAdapter的enable函式分析

                  其實我們會發現,這個函式最終還是呼叫的bluetoothService中的enable函式,所以,這裡就不詳細說了,直接去看bluetoothService中的enable

/** Bring up BT and persist BT>1.1.2 setBluetoothStateInt

這個函式是上層對藍芽狀態改變的操作,就是設定成不同的狀態:

synchronized void setBluetoothStateInt(int state) {
	//其實針對turning>2.深入理解藍芽狀態變換所做的工作

         我們在藍芽狀態變換的那篇文章中仔細的分析了藍芽開啟所涉及的一些操作,然而當時的重點還是分析了狀態之間的變換,並沒有分析真正去做了些什麼,這篇文章下面的內容將會深入去分析一下這些內容。

         現在我們先假設quick swtich是關閉的,也就是說在我們開啟之前藍芽的狀態是位於poweroff的,我們就從這邊開始來分析好了:

private class PowerOff extends State {
        @Override
        public void enter() {
            if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);
        }
        @Override
        public boolean processMessage(Message message) {
            log("PowerOff process message: " + message.what);

            boolean retValue = http://blog.csdn.net/u011960402/article/details/HANDLED;
            switch(message.what) {
                case USER_TURN_ON:
                    // starts turning>2.1 bluetoothAdapter狀態轉換廣播的處理分析

         我們先來看一下究竟是發出的那個broadcast,然後再來看有哪些地方註冊了這個broadcast的receiver。

private void broadcastState(int newState) {

        log("Bluetooth state " + mPublicState + " -> " + newState);
		// mPublicState這是用來儲存目前狀態的變數,會先進行比較,看是否真的改變了
		//若是沒有改變也就不需要做什麼了
        if (mPublicState == newState) {
            return;
        }
		//否則就需要傳送BluetoothAdapter.ACTION_STATE_CHANGED的broadcast
        Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
		//兩個引數
		//一個就是原有的狀態,一個是新的狀態
        intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mPublicState);
        intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
        mPublicState = newState;

        mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
}

2.1.1註冊的BluetoothAdapter.ACTION_STATE_CHANGED的receiver

         1)BluetoothEnabler中的receiver:

         這個地方主要是對按鈕的處理,我們知道BluetoothEnabler主要是對bluetooth的開啟和關閉進行處理的,所以很容易就聯想到,在開啟的時候按鈕反灰之類的操作應該是這裡來實現的,我們來看具體的程式碼

public void>         2)BluetoothEventManager中的handler

          我們前面分析過BluetoothEventManager是用來從BluetoothAPI中接收broadcast,並把他們分析到對應的UI執行緒中去。

         這句程式碼表明瞭對broadcast的處理:

 // Bluetooth>         A、BluetoothEventManager中註冊的callback的分析

         我們從程式碼中搜索一下會發現,真正註冊到這裡的callback只有一個,就是DeviceListPreferenceFragment,它的程式碼如下:

   public void>在前面,我們看到的就是BluetoothSettings是擴充套件這個類的,然而不幸的時候,BluetoothSettings中有自己的onResume函式,並不會走到這裡,所以,我們再來看看這裡的callback還有別的地方會用到麼?

         我們發現除了BletoothSettings還有一個地方,就是DevicePickerFragment也是擴充套件這個類的,然而同樣的,它也重寫了onResume函式,哈哈,原來的這個callback沒有人註冊啊,所以也就不會呼叫了。呵呵~~

         至此BluetoothEventManger中的handler就分析完成了,不幸的是好像什麼工作都沒有做。好吧,我們過去了。

         3)其它。

其實還有一些別的地方也註冊了這個broadcast,但是至少目前在開啟的時候他們還沒有註冊,所以,這裡就暫時不分析了,我們到了具體的情景時在去具體分析好了。比如以下地方:

1)BluetoothA2dpService,BluetoothController.Java,PhoneStatusBarPolicy.java等,這些地方基本都是一些狀態位的重置。

2)BluetoothOppBtEnablingActivity.java,BluetoothOppReceiver.java,BluetoothPbapReceiver.java,BluetoothHeadsetService.java這些地方都是對一些profile的處理,我們在具體分析到這裡的時候再來具體看。

3)BluetoothNameDialogFragment,這個只有在打開了修改名字才會使用到。

4)RequestPermissionActivity,這個是別的應用要求搜尋或者開啟藍芽的時候才會使用。

5)SettingsAppWidgetProvider,視窗小部件中進行一些功耗相關的控制的時候會用到。

以上幾個,我們暫時就不會進行分析了,避免分叉得太厲害。

所以,從這裡可以看到,基本在正在開啟這個狀態,就只有對按鈕的一個反灰的操作了,其它的就沒有什麼別的特殊的了。

2.2 prepareBluetooth的分析

         從註釋來看,這個函式就是開啟藍芽模組了,包括載入fw等一系列操作,各家的方案的區別可能就是體現在這裡了,所以,需要根據不同的廠商來進行不同的設定了。理所當然的是,這裡就有了各自的verdon operation了。需要注意的是他是不能discoverable和connectable,這也是因為在quick swtich開啟的情況下,讓外界並不能發現我們是開啟的。

private boolean prepareBluetooth() {
			//這是一個jni層的操作,涉及的內容及其廣泛,我們會在3中進行詳細介紹
            if (mBluetoothService.enableNative() != 0) {
                return false;
            }

            // try to start event loop, give 2 attempts
			//啟動event loop,event loop是framework層用來接收來自jni層各種event的程式碼,他會根據不同的event來所不同的操作
            int retryCount = 2;
            boolean eventLoopStarted = false;
		//這裡會嘗試進行開始兩次,每次會有一個eventloop run的檢查,會檢查5次,每次間隔100ms,所以這裡最長大概會有1s左右的時間,事實上,我們並不會有這麼長的時間,呵呵~~
            while ((retryCount-- > 0) && !eventLoopStarted) {
			//詳細見2.2.1
                mEventLoop.start();
                // it may take a moment for the other thread to do its
                // thing.  Check periodically for a while.
                int pollCount = 5;
                while ((pollCount-- > 0) && !eventLoopStarted) {
                    if (mEventLoop.isEventLoopRunning()) {
                        eventLoopStarted = true;
                        break;
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        log("prepareBluetooth sleep interrupted: " + pollCount);
                        break;
                    }
                }
            }
//沒有能夠啟動eventloop會直接把藍芽disable掉的
            if (!eventLoopStarted) {
                mBluetoothService.disableNative();
                return false;
            }

            // get BluetoothService ready
	//緊接著的BluetoothService中的一些處理,詳細見2.2.2
            if (!mBluetoothService.prepareBluetooth()) {
                mEventLoop.stop();
                mBluetoothService.disableNative();
                return false;
            }
		//這裡會在10s之後傳送一個timeout的msg,以用來恢復狀態機,詳細見我狀態機變化的那篇文章的分析。
            sendmessageDelayed(PREPARE_BLUETOOTH_TIMEOUT, PREPARE_BLUETOOTH_TIMEOUT_TIME);
            return true;
        }
}

2.2.1 Eventloop的啟動

         BluetoothEventLoop在狀態機初始化的時候就已經建構了,這裡就不再分析了。我們直接從start函式來看,看eventloop究竟做了什麼:

 /* package */ void start() {
		//看是否已經在run了
        if (!isEventLoopRunningNative()) {
            if (DBG) log("Starting Event Loop thread");
			//開始eventloop
            startEventLoopNative();
        }
}

很快大家就發現,這原來還是要到jni層啊,看來我們還是要好好看進去哦,不知道這個和enableNative會不會有關係,畢竟他是在前面啊,所以,這裡我們還是不著急分析,把它放到enableNative分析完成之後再去看。詳細分析見4。

2.2.2 BluetoothService中的PrepareBluetooth函式 

/* package */ synchronized boolean prepareBluetooth() {
		//建立native的data,就是一些資料的關聯,沒有什麼實在的東西
        if (!setupNativeDataNative()) {
            return false;
        }
		//那powered的property設為false,就是把discoverable和connectable設為false了,為什麼這麼做,上面已經講過了
        switchConnectable(false);
		//更新sdp列表,這個我們需要重點看一下,詳細分析見下面的A
        updateSdpRecords();
        return true;
    }

A、updateSdpRecords分析

private synchronized void updateSdpRecords() {
        ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>();

        // Add the default records
		//支援HSP的AG端
        uuids.add(BluetoothUuid.HSP_AG);
		//支援opp
        uuids.add(BluetoothUuid.ObexObjectPush);
	//看是否支援voice,這裡是支援的話,就加入HFP的AG和pbap
        if (mContext.getResources().
                getBoolean(com..internal.R.bool.config_voice_capable)) {
            uuids.add(BluetoothUuid.Handsfree_AG);
            uuids.add(BluetoothUuid.PBAP_PSE);
        }

        // Add SDP records for profiles maintained by Android userspace
		//加入支援的uuid,這裡詳細分析見下面的2.2.2.1
        addReservedSdpRecords(uuids);

        // Enable profiles maintained by Bluez userspace.
		//使能包含的profile,這個又通過jni層去實現了,所以,我們同樣放到後面2.2.2.2中分析
        setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE,
                BluetoothPanProfileHandler.NAP_BRIDGE);

        // Add SDP records for profiles maintained by Bluez userspace
		//上面那些uuid是通過bluez去控制的,所以我們會有一些互動
		//下面這幾個是上層直接控制就可以了
		//audiosource,avrcpTarget和NAP的角色
        uuids.add(BluetoothUuid.AudioSource);
        uuids.add(BluetoothUuid.AvrcpTarget);
        uuids.add(BluetoothUuid.NAP);

        // Cannot cast uuids.toArray directly since ParcelUuid is parcelable
		//因為uuids是parcel格式的,所以,我們一個一個讀出來好了,加入到mAdapterUuids中
        mAdapterUuids = new ParcelUuid[uuids.size()];
        for (int i = 0; i < uuids.size(); i++) {
            mAdapterUuids[i] = uuids.get(i);
        }
    }

2.2.2.1 uuid的記錄

SDP相關的註冊

  private synchronized void addReservedSdpRecords(final ArrayList<ParcelUuid> uuids) {
        //Register SDP records.
        int[] svcIdentifiers = new int[uuids.size()];
        for (int i = 0; i < uuids.size(); i++) {
//從128bit的uuid得到16bit/32bit的內容
            svcIdentifiers[i] = BluetoothUuid.getServiceIdentifierFromParcelUuid(uuids.get(i));
        }
			//這個就到了jni層的分析了,我們在後面會詳細分析
        mAdapterSdpHandles = addReservedServiceRecordsNative(svcIdentifiers);
    }

至此,jni層之上的所有操作都已經全部分析完成了,後面我們在進行分析jni層之下的內容,那又將是一個非常龐大的體系。