天天看點

[Android源碼分析]藍牙打開流程分析——jni層之上的方方面面

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

1、藍牙打開的按鍵處理

public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        // Show toast message if Bluetooth is not allowed in airplane mode
//其實這個就是飛行模式打開的時候,就顯示一個“正處于飛行模式下”
//不過這種情況很少遇到的,因為在飛行模式的時候,其實按鈕時反灰的,基本走不到這裡
//不過,在那個下拉菜單中點選打開藍牙的時候還是會遇到的,大家可以去試試看
        if (isChecked &&
                !WirelessSettings.isRadioAllowed(mContext, Settings.System.RADIO_BLUETOOTH)) {
            Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
            // Reset switch to off
            buttonView.setChecked(false);
        }
//隻要藍牙是正常的,這裡必然就不是null喽。。
        if (mLocalAdapter != null) {
            mLocalAdapter.setBluetoothEnabled(isChecked);
        }
		//把switch反灰,在打開的過程中,開關一直是灰的
        mSwitch.setEnabled(false);
    }
           

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 on in settings */
    public boolean enable() {
			//這個沒什麼好說的,直接看下面
        return enable(true);
    }

    /**
     * Enable this Bluetooth device, asynchronously.
     * This turns on/off the underlying hardware.
     *
     * @param saveSetting If true, persist the new state of BT in settings
     * @return True on success (so far)
     */
		//注意這裡是異步的打開藍牙
    public synchronized boolean enable(boolean saveSetting) {
		//檢查權限,這就是我們為什麼要在AndroidManifest中加入各種權限了
        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                "Need BLUETOOTH_ADMIN permission");

        // Airplane mode can prevent Bluetooth radio from being turned on.
			//看是否是飛行模式打開了
        if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
            return false;
        }
//就是wifi和bt能否共存,若不能,則需要檢查wifi是否打開,打開了就不能打開bt了,一般情況下,我們不會進入這樣的case,後面類似的内容不再解釋
        if (mBootCompleted && !supportBtWifiCoexit) {
            if (mWifiManager == null)
                mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
            if (mWifiManager != null
                    && (WifiManager.WIFI_STATE_DISABLED != mWifiManager.getWifiState()
                    || WifiManager.WIFI_AP_STATE_DISABLED != mWifiManager.getWifiApState())) {
                return false;
            }
        }
	//發送USER_TURN_ON的msg,哈哈,這個我們在藍牙狀态機改變的那篇文章中已經相信解釋過了,是以這裡就和那邊關聯起來了。
        mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);	
 
        return true;
    }
           

1.1.2 setBluetoothStateInt

這個函數是上層對藍牙狀态改變的操作,就是設定成不同的狀态:

synchronized void setBluetoothStateInt(int state) {
	//其實針對turning on和turning of并沒做什麼特殊的東西
//就是設定mState的狀态
        mState = state;

        if (mProfileManager != null) {
            if (state == BluetoothAdapter.STATE_ON) {
			//這兩個地方我們在打開和關閉完成之後再來分析
                // if mProfileManager hasn't been constructed yet, it will
                // get the adapter UUIDs in its constructor when it is.
                mProfileManager.setBluetoothStateOn();
            } else if (state == BluetoothAdapter.STATE_OFF) {
                //closeProfileProxy to unbindService
                mProfileManager.setBluetoothStateOff();
            }
        }
}
           

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 = HANDLED;
            switch(message.what) {
                case USER_TURN_ON:
                    // starts turning on BT module, broadcast this out
					//這裡就是比較關鍵了,先發送一個狀态改變的廣播
					//哪裡會進行這個廣播的處理?詳見2.1
                    broadcastState(BluetoothAdapter.STATE_TURNING_ON);
					//到warmup狀态
                    transitionTo(mWarmUp);
					//藍牙的一系列初始化操作,詳細見2.2
                    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;

           

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 onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            Log.i("BluetoothEnabler", "action = " + action);
            if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
				//得到目前的狀态
                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
				//對狀态的改變進行處理,見下面。
                handleStateChanged(state);	
	A,handleStateChanged的分析
	這個函數主要用來處理狀體改變後的開關的變化,主要就是打開過程中的反灰,打開之後的設定為打開(就是那個高亮),關閉之後的按鈕到左邊的那些操作。
	    void handleStateChanged(int state) {
        switch (state) {
            case BluetoothAdapter.STATE_TURNING_ON:
			//正在打開的過程中,按鈕不可選中
                mSwitch.setEnabled(false);
                break;
            case BluetoothAdapter.STATE_ON:
				//打開了,就是可以選中,checked設為true
                mSwitch.setChecked(true);
                mSwitch.setEnabled(true);
                break;
            case BluetoothAdapter.STATE_TURNING_OFF:
			//關閉過程和打開過程是一樣的
                mSwitch.setEnabled(false);
                break;
			//關閉的話,就把按鈕的checked設為false
            case BluetoothAdapter.STATE_OFF:
                mSwitch.setChecked(false);
			//同時根據是否是飛行模式,來設定按鈕能否選中
			//這也就是我們打開飛行模式後,若是藍牙是開着的,那麼打開按鈕是不能再被選中的
                mAirplaneMode.set(isAirplaneModeOn());
                if (!mAirplaneMode.get()) {
                    mSwitch.setEnabled(true);
                }
                break;
            default:
			//預設就是關閉可選中
                mSwitch.setChecked(false);
                mSwitch.setEnabled(true);
        }
	//這個東西不再分析了
        if (mSupportBtWifiCoexist == false && isWifiAndWifiApStateDisabled() == false) {
            mSwitch.setChecked(false);
            mSwitch.setEnabled(false);
        }
    }
           

         2)BluetoothEventManager中的handler

          我們前面分析過BluetoothEventManager是用來從BluetoothAPI中接收broadcast,并把他們分析到對應的UI線程中去。

         這句代碼表明了對broadcast的處理:

// Bluetooth on/off broadcasts
        addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());
	它的實作如下:
    private class AdapterStateChangedHandler implements Handler {
        public void onReceive(Context context, Intent intent,
                BluetoothDevice device) {
			//得到新的狀态
            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
                                    BluetoothAdapter.ERROR);
            // update local profiles and get paired devices
			//根據狀态來重新整理本地的profile,但是,他主要是對on和off的state進行操作的,是以,我們暫時不分析,因為會涉及到turning on中的一些處理。
            mLocalAdapter.setBluetoothStateInt(state);
            // send callback to update UI and possibly start scanning
            synchronized (mCallbacks) {
			//調用注冊的callbacks的onBluetoothStateChanged函數
			//這個callback是通過registerCallback來進行注冊的,我們在下面的A中進行分析
                for (BluetoothCallback callback : mCallbacks) {
                    callback.onBluetoothStateChanged(state);
                }
            }
        }
    }
           

         A、BluetoothEventManager中注冊的callback的分析

         我們從代碼中搜尋一下會發現,真正注冊到這裡的callback隻有一個,就是DeviceListPreferenceFragment,它的代碼如下:

public void onResume() {
……
        mLocalManager.getEventManager().registerCallback(this);
}
           

在前面,我們看到的就是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.android.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層之下的内容,那又将是一個非常龐大的體系。

若您覺得該文章對您有幫助,請在下面用滑鼠輕輕按一下“頂”,哈哈~~·

繼續閱讀