Android4.4(MT8685)源碼藍牙解析--連接

在BluetoothSetting的onResume生命週期方法中會執行updateContent方法,當藍牙狀態爲開時,會執行其中這麼一段代碼:

LocalBluetoothManager.getInstance(getActivity()).setDiscoverableEnabler(
                            mDiscoverableEnabler);

這裏我們只要關注LocalBluetoothManager這個類,進入這個類看看:

 public static synchronized LocalBluetoothManager getInstance(Context context) {
        if (sInstance == null) {
            LocalBluetoothAdapter adapter = LocalBluetoothAdapter.getInstance();
            if (adapter == null) {
                return null;
            }
            // This will be around as long as this process is
            Context appContext = context.getApplicationContext();
            sInstance = new LocalBluetoothManager(adapter, appContext);
        }

        return sInstance;
    }

這裏採用了單例模式,進入構造方法:

private LocalBluetoothManager(LocalBluetoothAdapter adapter, Context context) {
        mContext = context;
        mLocalAdapter = adapter;

        mCachedDeviceManager = new CachedBluetoothDeviceManager(context);
        mEventManager = new BluetoothEventManager(mLocalAdapter,
                mCachedDeviceManager, context);
        mProfileManager = new LocalBluetoothProfileManager(context,
                mLocalAdapter, mCachedDeviceManager, mEventManager);
    }

分別實例化了CachedBluetoothDeviceManager、BluetoothEventManager、LocalBluetoothProfileManager對象,其中CachedBluetoothDeviceManager和BluetoothEventManager的對象時作爲LocalBluetoothProfileManager構造方法的參數,我們進入LocalBluetoothProfileManager中看看:
LocalBluetoothProfileManager(Context context,
            LocalBluetoothAdapter adapter,
            CachedBluetoothDeviceManager deviceManager,
            BluetoothEventManager eventManager) {
        mContext = context;

        mLocalAdapter = adapter;
        mDeviceManager = deviceManager;
        mEventManager = eventManager;
        // pass this reference to adapter and event manager (circular dependency)
        mLocalAdapter.setProfileManager(this);
        mEventManager.setProfileManager(this);

        ParcelUuid[] uuids = adapter.getUuids();

        // uuids may be null if Bluetooth is turned off
        if (uuids != null) {
            Xlog.d(TAG, "bluetooth adapter uuid: ");
            for (ParcelUuid uuid : uuids) {
                Xlog.v(TAG, "  " + uuid);
            }
            updateLocalProfiles(uuids);
        }
}

進入到updateLocalProfiles方法中:

 void updateLocalProfiles(ParcelUuid[] uuids) {
        // A2DP
        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSource)) {
            if (mA2dpProfile == null) {
                Log.d(TAG, "Adding local A2DP profile");
                mA2dpProfile = new A2dpProfile(mContext, mLocalAdapter, mDeviceManager, this);
                addProfile(mA2dpProfile, A2dpProfile.NAME,
                        BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
            }
        } else if (mA2dpProfile != null) {
            Log.w(TAG, "Warning: A2DP profile was previously added but the UUID is now missing.");
        }

        // Headset / Handsfree
        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) ||
            BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) {
            if (mHeadsetProfile == null) {
                Log.d(TAG, "Adding local HEADSET profile");
                mHeadsetProfile = new HeadsetProfile(mContext, mLocalAdapter,
                        mDeviceManager, this);
                addProfile(mHeadsetProfile, HeadsetProfile.NAME,
                        BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
            }
        } else if (mHeadsetProfile != null) {
            Log.w(TAG, "Warning: HEADSET profile was previously added but the UUID is now missing.");
        }

        // OPP
        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
            if (mOppProfile == null) {
                Log.d(TAG, "Adding local OPP profile");
                mOppProfile = new OppProfile();
                // Note: no event handler for OPP, only name map.
                mProfileNameMap.put(OppProfile.NAME, mOppProfile);
            }
        } else if (mOppProfile != null) {
            Log.w(TAG, "Warning: OPP profile was previously added but the UUID is now missing.");
        }
        mEventManager.registerProfileIntentReceiver();

        // There is no local SDP record for HID and Settings app doesn't control PBAP
    }

這個方法的作用是初始化或更新本地的profile 對象,注意,這裏還有另一個非常重要的方法,setBluetoothStateOn

// Called from LocalBluetoothAdapter when state changes to ON
    void setBluetoothStateOn() {       
        //M: when BT is turned on and these profiles have never been initialized, initialize it @{
        if(mHidProfile == null) {
            mHidProfile = new HidProfile(mContext, mLocalAdapter, mDeviceManager, this);
            addProfile(mHidProfile, HidProfile.NAME,
                    BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
        }
        if(mPanProfile == null) {
            mPanProfile = new PanProfile(mContext);
            addPanProfile(mPanProfile, PanProfile.NAME,
                    BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
        }

        if(mMapProfile == null) {
            if(DEBUG) Log.d(TAG, "Adding local MAP profile");
            mMapProfile = new MapProfile(mContext, mLocalAdapter,
                    mDeviceManager, this);
            addProfile(mMapProfile, MapProfile.NAME,
                    BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
        }
        
        if(mPbapProfile == null) {
            mPbapProfile = new PbapServerProfile(mContext);
        }
        if(mFtpProfile == null) {
            mFtpProfile = new FtpProfile(mContext);
            addProfile(mFtpProfile, FtpProfile.NAME,
                    BluetoothFtp.ACTION_STATE_CHANGED);
        }       
        //@}
        
        ParcelUuid[] uuids = mLocalAdapter.getUuids();
        if (uuids != null) {
            updateLocalProfiles(uuids);
        }
        mEventManager.readPairedDevices();
    }

這個方法會在LocalBluetoothAdapter類中,當藍牙狀態爲開時調用,這個方法是初始化所有還未初始化的profile。分析到這裏,似乎和藍牙的連接沒啥關係,不是的,上面的分析是鋪墊,因爲藍牙涉及到多種不同的協議,當連接不同類型的藍牙設備時,就要用不同的profile去連接,比如遊戲手柄,就是用HidProfile。

下面我們來看看連接部分的代碼,回到BluetoothSettings中的onDevicePreferenceClick方法,這個方法最終會調用父類的onDevicePreferenceClick方法:

void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
        btPreference.onClicked();
    }

進入到BluetoothDevicePreference的onClicked方法中:

void onClicked() {
        int bondState = mCachedDevice.getBondState();

        if (mCachedDevice.isConnected()) {
            askDisconnect();
        } else if (bondState == BluetoothDevice.BOND_BONDED) {
            Xlog.d(TAG, mCachedDevice.getName() + " connect");
            mCachedDevice.connect(true);
        } else if (bondState == BluetoothDevice.BOND_NONE) {
            pair();
        }
    }

這裏首先獲取當前被點擊設備的狀態,當狀態爲已配對時,就對你進行連接操作,進入CachedBluetoothDevice的connect方法

void connect(boolean connectAllProfiles) {
        if (!ensurePaired()) {
            return;
        }

        mConnectAttempted = SystemClock.elapsedRealtime();
        connectWithoutResettingTimer(connectAllProfiles);
    }

進入connectWithoutResettingTimer方法

private void connectWithoutResettingTimer(boolean connectAllProfiles) {
        // Try to initialize the profiles if they were not.
        if (mProfiles.isEmpty()) {
            // if mProfiles is empty, then do not invoke updateProfiles. This causes a race
            // condition with carkits during pairing, wherein RemoteDevice.UUIDs have been updated
            // from bluetooth stack but ACTION.uuid is not sent yet.
            // Eventually ACTION.uuid will be received which shall trigger the connection of the
            // various profiles
            // If UUIDs are not available yet, connect will be happen
            // upon arrival of the ACTION_UUID intent.
            Log.d(TAG, "No profiles. Maybe we will connect later");
            return;
        }

        // Reset the only-show-one-error-dialog tracking variable
        mIsConnectingErrorPossible = true;

        int preferredProfiles = 0;
        for (LocalBluetoothProfile profile : mProfiles) {
            if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
                if (profile.isPreferred(mDevice)) {
                    ++preferredProfiles;
                    connectInt(profile);
                }
            }
        }
        if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);

        if (preferredProfiles == 0) {
            connectAutoConnectableProfiles();
        }
    }

這裏會遍歷之前初始化的所有profile,並將它們依次作爲參數傳入connectInt方法中,進入這個方法

synchronized void connectInt(LocalBluetoothProfile profile) {
        if (!ensurePaired()) {
            return;
        }
        if (profile.connect(mDevice)) {
            if (Utils.D) {
                Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
            }
            return;
        }
        Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
    }

這裏就會調用profile的connect方法了,下面以HidProfile爲例進行講解,進入HidProfile的connect方法

public boolean connect(BluetoothDevice device) {
        if (mService == null) return false;
        return mService.connect(device);
    }

這個mService是BluetoothInputDevice對象,進入它的connect方法

public boolean connect(BluetoothDevice device) {
        if (DBG) log("connect(" + device + ")");
        if (mService != null && isEnabled() && isValidDevice(device)) {
            try {
                return mService.connect(device);
            } catch (RemoteException e) {
                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                return false;
            }
        }
        if (mService == null) Log.w(TAG, "Proxy not attached to service");
        return false;
    }

這裏又是一個mSerivce,這個mSerivce其實是一個IBluetoothInputDevice接口對象,那我們只要找到相應的實現,HidSerivce,進入connect方法

boolean connect(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        log("begin to connect,device=" + device);
        if (getConnectionState(device) != BluetoothInputDevice.STATE_DISCONNECTED) {
            Log.e(TAG, "Hid Device not disconnected: " + device);
            return false;
        }
        if (getPriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
            Log.e(TAG, "Hid Device PRIORITY_OFF: " + device);
            return false;
        }

        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT, device);
        mHandler.sendMessage(msg);
        return true;
    }

這裏會發送一個消息,我們看哪個地方接收這個消息

private final Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_CONNECT:
                {
                    BluetoothDevice device = (BluetoothDevice) msg.obj;
                    if (!connectHidNative(Utils.getByteAddress(device)) ) {
                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
                        break;
                    }
                    mTargetDevice = device;
                }
                    break;
                case MESSAGE_DISCONNECT:
                {
                    BluetoothDevice device = (BluetoothDevice) msg.obj;
                    if (!disconnectHidNative(Utils.getByteAddress(device)) ) {
                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
                        break;
                    }
                }
                    break;
                case MESSAGE_CONNECT_STATE_CHANGED:
                {
                    BluetoothDevice device = getDevice((byte[]) msg.obj);
                    int halState = msg.arg1;
                    Integer prevStateInteger = mInputDevices.get(device);
                    int prevState = (prevStateInteger == null) ?
                        BluetoothInputDevice.STATE_DISCONNECTED :prevStateInteger;
                    if(DBG) Log.d(TAG, "MESSAGE_CONNECT_STATE_CHANGED newState:"+
                        convertHalState(halState)+", prevState:"+prevState);
                if (okToBroadcastConnectState(device, halState, prevState)) {
                        broadcastConnectionState(device, convertHalState(halState));
                    }
                    if (halState != CONN_STATE_CONNECTING) {
                        mTargetDevice = null;
                    }
                    else {
                        // CONN_STATE_CONNECTING is received only during
                        // local initiated connection.
                        mTargetDevice = device;
                    }
                }
                    break;
                case MESSAGE_GET_PROTOCOL_MODE:
                {
                    BluetoothDevice device = (BluetoothDevice) msg.obj;
                    if(!getProtocolModeNative(Utils.getByteAddress(device)) ) {
                        Log.e(TAG, "Error: get protocol mode native returns false");
                    }
                }
                break;

                case MESSAGE_ON_GET_PROTOCOL_MODE:
                {
                    BluetoothDevice device = getDevice((byte[]) msg.obj);
                    int protocolMode = msg.arg1;
                    broadcastProtocolMode(device, protocolMode);
                }
                break;
                case MESSAGE_VIRTUAL_UNPLUG:
                {
                    BluetoothDevice device = (BluetoothDevice) msg.obj;
                    if(!virtualUnPlugNative(Utils.getByteAddress(device))) {
                        Log.e(TAG, "Error: virtual unplug native returns false");
                    }
                }
                break;
                case MESSAGE_SET_PROTOCOL_MODE:
                {
                    BluetoothDevice device = (BluetoothDevice) msg.obj;
                    byte protocolMode = (byte) msg.arg1;
                    log("sending set protocol mode(" + protocolMode + ")");
                    if(!setProtocolModeNative(Utils.getByteAddress(device), protocolMode)) {
                        Log.e(TAG, "Error: set protocol mode native returns false");
                    }
                }
                break;
                case MESSAGE_GET_REPORT:
                {
                    BluetoothDevice device = (BluetoothDevice) msg.obj;
                    Bundle data = msg.getData();
                    byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE);
                    byte reportId = data.getByte(BluetoothInputDevice.EXTRA_REPORT_ID);
                    int bufferSize = data.getInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE);
                    if(!getReportNative(Utils.getByteAddress(device), reportType, reportId, bufferSize)) {
                        Log.e(TAG, "Error: get report native returns false");
                    }
                }
                break;
                case MESSAGE_SET_REPORT:
                {
                    BluetoothDevice device = (BluetoothDevice) msg.obj;
                    Bundle data = msg.getData();
                    byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE);
                    String report = data.getString(BluetoothInputDevice.EXTRA_REPORT);
                    if(!setReportNative(Utils.getByteAddress(device), reportType, report)) {
                        Log.e(TAG, "Error: set report native returns false");
                    }
                }
                break;
                case MESSAGE_SEND_DATA:
                {
                    BluetoothDevice device = (BluetoothDevice) msg.obj;
                    Bundle data = msg.getData();
                    String report = data.getString(BluetoothInputDevice.EXTRA_REPORT);
                    if(!sendDataNative(Utils.getByteAddress(device), report)) {
                        Log.e(TAG, "Error: send data native returns false");
                    }
                }
                break;
                case MESSAGE_ON_VIRTUAL_UNPLUG:
                {
                    BluetoothDevice device = getDevice((byte[]) msg.obj);
                    int status = msg.arg1;
                    broadcastVirtualUnplugStatus(device, status);
                }
                break;
            }
        }
    };

又見到了本地方法connectHidNative,進入到相應的jni方法中,在Com_android_bluetooth_hid.cpp文件中connectHidNative

static jboolean connectHidNative(JNIEnv *env, jobject object, jbyteArray address) {
    bt_status_t status;
    jbyte *addr;
    jboolean ret = JNI_TRUE;
    if (!sBluetoothHidInterface) return JNI_FALSE;

    addr = env->GetByteArrayElements(address, NULL);
    if (!addr) {
        ALOGE("Bluetooth device address null");
        return JNI_FALSE;
    }

    if ((status = sBluetoothHidInterface->connect((bt_bdaddr_t *) addr)) !=
         BT_STATUS_SUCCESS) {
        ALOGE("Failed HID channel connection, status: %d", status);
        ret = JNI_FALSE;
    }
    env->ReleaseByteArrayElements(address, addr, 0);

    return ret;
}

又是sBluetoothHidInterface,它的實現是在MTK封裝的藍牙庫中,連接完畢後會返回連接的信息,在connection_state_callback方法中

static void connection_state_callback(bt_bdaddr_t *bd_addr, bthh_connection_state_t state) {
    jbyteArray addr;

    CHECK_CALLBACK_ENV
    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if (!addr) {
        ALOGE("Fail to new jbyteArray bd addr for HID channel state");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }
    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged, addr, (jint) state);
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    sCallbackEnv->DeleteLocalRef(addr);
}

這個方法會回調HIDService中的onConnectStateChanged方法

private void onConnectStateChanged(byte[] address, int state) {
        log("onConnectStateChanged:address=" + address + " state=" + state);
        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
        msg.obj = address;
        msg.arg1 = state;
        mHandler.sendMessage(msg);
    }

這裏又是發送一個消息,接收的地方

private final Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
case MESSAGE_CONNECT_STATE_CHANGED:
                {
                    BluetoothDevice device = getDevice((byte[]) msg.obj);
                    int halState = msg.arg1;
                    Integer prevStateInteger = mInputDevices.get(device);
                    int prevState = (prevStateInteger == null) ?
                        BluetoothInputDevice.STATE_DISCONNECTED :prevStateInteger;
                    if(DBG) Log.d(TAG, "MESSAGE_CONNECT_STATE_CHANGED newState:"+
                        convertHalState(halState)+", prevState:"+prevState);
                if (okToBroadcastConnectState(device, halState, prevState)) {
                        broadcastConnectionState(device, convertHalState(halState));
                    }
                    if (halState != CONN_STATE_CONNECTING) {
                        mTargetDevice = null;
                    }
                    else {
                        // CONN_STATE_CONNECTING is received only during
                        // local initiated connection.
                        mTargetDevice = device;
                    }
                }
                    break;
}


這裏會通過broadcastConnectionState廣播藍牙的連接結果,

private void broadcastConnectionState(BluetoothDevice device, int newState) {
        Integer prevStateInteger = mInputDevices.get(device);
        log("broadcastConnectionState:device=" + device + " prevState=" + prevStateInteger + "->" + "newState=" + newState);
        int prevState = (prevStateInteger == null) ? BluetoothInputDevice.STATE_DISCONNECTED : 
                                                     prevStateInteger;
        if (prevState == newState) {
            Log.w(TAG, "no state change: " + newState);
            return;
        }
        mInputDevices.put(device, newState);

        /* Notifying the connection state change of the profile before sending the intent for
           connection state change, as it was causing a race condition, with the UI not being
           updated with the correct connection state. */
        log("Connection state " + device + ": " + prevState + "->" + newState);
        notifyProfileConnectionStateChanged(device, BluetoothProfile.INPUT_DEVICE,
                                            newState, prevState);
        Intent intent = new Intent(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
        sendBroadcast(intent, BLUETOOTH_PERM);
    }
這裏會想系統廣播最終連接的結果,至此,藍牙連接分析完畢。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章