zoukankan      html  css  js  c++  java
  • Android 8 蓝牙 A2DP流程

    记录一下蓝牙A2DP的流程

    
    
    packagesappsSettingssrccomandroidsettingsluetoothBluetoothPairingDetail.java
        @Override
        void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
            disableScanning();
            super.onDevicePreferenceClick(btPreference);
        }   
        
    packagesappsSettingssrccomandroidsettingsluetoothDeviceListPreferenceFragment.java
        void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
            btPreference.onClicked();
        }
            
    packagesappsSettingssrccomandroidsettingsluetoothBluetoothDevicePreference.java
        void onClicked() {
            Context context = getContext();
            // 获取连接状态
            int bondState = mCachedDevice.getBondState();
    
            final MetricsFeatureProvider metricsFeatureProvider =
                    FeatureFactory.getFactory(context).getMetricsFeatureProvider();
            // 已经连接
            if (mCachedDevice.isConnected()) {
                // 断开连接
                metricsFeatureProvider.action(context,
                        MetricsEvent.ACTION_SETTINGS_BLUETOOTH_DISCONNECT);
                askDisconnect();
                // 以前连接过,不需要再配对,直接进行连接
            } else if (bondState == BluetoothDevice.BOND_BONDED) {
                metricsFeatureProvider.action(context,
                        MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT);
                mCachedDevice.connect(true);
                // 没连接过,进行配对,需要连接的双方都同意之后才能连接
            } else if (bondState == BluetoothDevice.BOND_NONE) {
                metricsFeatureProvider.action(context,
                        MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR);
                if (!mCachedDevice.hasHumanReadableName()) {
                    metricsFeatureProvider.action(context,
                            MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
                }
                pair();
            }
        }
        
        
    
    匹配过的设备,进行连接
        frameworksasepackagesSettingsLibsrccomandroidsettingslibluetoothCachedBluetoothDevice.java
            public void connect(boolean connectAllProfiles) {
            // 是否配对过
            if (!ensurePaired()) {
                return;
            }
    
            mConnectAttempted = SystemClock.elapsedRealtime();
            connectWithoutResettingTimer(connectAllProfiles);
        }
        
            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) {
                // connectAllProfile传进来的是true
                if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
                    if (profile.isPreferred(mDevice)) {
                        ++preferredProfiles;
                        connectInt(profile);			// 选择对应的profile进行连接
                    }
                }
            }
            if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
    
            if (preferredProfiles == 0) {
                connectAutoConnectableProfiles();
            }
        }
    
        private void connectAutoConnectableProfiles() {
            if (!ensurePaired()) {
                return;
            }
            // Reset the only-show-one-error-dialog tracking variable
            mIsConnectingErrorPossible = true;
    
            for (LocalBluetoothProfile profile : mProfiles) {
                if (profile.isAutoConnectable()) {
                    profile.setPreferred(mDevice, true);
                    connectInt(profile);
                }
            }
        }
    	
    frameworksasepackagesSettingsLibsrccomandroidsettingslibluetoothCachedBluetoothDevice.java
    	    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);
        }
    	
    
    A2dp的profile	
    frameworksasepackagesSettingsLibsrccomandroidsettingslibluetoothA2dpProfile.java
    public class A2dpProfile implements LocalBluetoothProfile {
    
        public boolean connect(BluetoothDevice device) {
            if (mService == null) return false;
            List<BluetoothDevice> sinks = getConnectedDevices();
            if (sinks != null) {
                for (BluetoothDevice sink : sinks) {
                    if (sink.equals(device)) {
                        Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
                        continue;
                    }
                }
            }
    		// 连接
            return mService.connect(device);
        }
    	
    	
    frameworksasecorejavaandroidluetoothBluetoothA2dp.java
    	 @GuardedBy("mServiceLock") private IBluetoothA2dp mService;
    	 
    	public boolean connect(BluetoothDevice device) {
            if (DBG) log("connect(" + device + ")");
            try {
                mServiceLock.readLock().lock();
                if (mService != null && isEnabled() &&
                    isValidDevice(device)) {
                    return mService.connect(device);	// 调用aidl中的方法
                }
                if (mService == null) Log.w(TAG, "Proxy not attached to service");
                return false;
            } catch (RemoteException e) {
                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                return false;
            } finally {
                mServiceLock.readLock().unlock();
            }
        }
    	
    	
    	
    Binder进程间通信,服务端实现方法如下。	
    packagesappsBluetoothsrccomandroidluetootha2dpA2dpService.java
    	private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub 
        implements IProfileServiceBinder {
    		
    	        public boolean connect(BluetoothDevice device) {
                A2dpService service = getService();
                if (service == null) return false;
                //do not allow new connections with active multicast
                if (service.isMulticastOngoing(device)) {
                    Log.i(TAG,"A2dp Multicast is Ongoing, ignore Connection Request");
                    return false;
                }
                return service.connect(device);
            }
    
    
        public boolean connect(BluetoothDevice device) {
            if (DBG) Log.d(TAG, "Enter connect");
            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                           "Need BLUETOOTH ADMIN permission");
    
            if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
                return false;
            }
            ParcelUuid[] featureUuids = device.getUuids();
            if ((BluetoothUuid.containsAnyUuid(featureUuids, A2DP_SOURCE_UUID)) &&
                !(BluetoothUuid.containsAllUuids(featureUuids ,A2DP_SOURCE_SINK_UUIDS))) {
                Log.e(TAG,"Remote does not have A2dp Sink UUID");
                return false;
            }
    
            int connectionState = BluetoothProfile.STATE_DISCONNECTED;
            synchronized(mBtA2dpLock) {
                if (mStateMachine != null) {
                    connectionState = mStateMachine.getConnectionState(device);
                }
            }
            if (connectionState == BluetoothProfile.STATE_CONNECTED ||
                connectionState == BluetoothProfile.STATE_CONNECTING) {
                return false;
            }
    		// 发送消息给状态机
            synchronized(mBtA2dpLock) {
                if (mStateMachine != null) {
                    mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device);
                }
            }
            if (DBG) Log.d(TAG, "Exit connect");
            return true;
        }		
    	
    	
    	状态机初始状态Disconnect
    packagesappsBluetoothsrccomandroidluetootha2dpA2dpStateMachine.java
    final class A2dpStateMachine extends StateMachine {
    	
    	
    	           switch(message.what) {
                    case CONNECT:
                        BluetoothDevice device = (BluetoothDevice) message.obj;
                        // 发送广播,状态改变
                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
                                       BluetoothProfile.STATE_DISCONNECTED);
                        // 进行连接
                        if (!connectA2dpNative(getByteAddress(device)) ) {
    						// 连接完成,失败就再次发广播,状态有connecting->disconnect 
                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
                                           BluetoothProfile.STATE_CONNECTING);
                            break;
                        }
    
                        synchronized (A2dpStateMachine.this) {
                            mTargetDevice = device;
                            transitionTo(mPending);         // 切换状态机状态
                        }
                        // TODO(BT) remove CONNECT_TIMEOUT when the stack
                        //          sends back events consistently
                        Message m = obtainMessage(CONNECT_TIMEOUT);
                        m.obj = device;
                        sendMessageDelayed(m, CONNECT_TIMEOUT_SEC);
                        break;
    					
    					
    packagesappsBluetoothjnicom_android_bluetooth_a2dp.cpp
    static jboolean connectA2dpNative(JNIEnv* env, jobject object,
                                      jbyteArray address) {
      ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
      if (!sBluetoothA2dpInterface) return JNI_FALSE;
    
      jbyte* addr = env->GetByteArrayElements(address, NULL);
      if (!addr) {
        jniThrowIOException(env, EINVAL);
        return JNI_FALSE;
      }
    
      bt_status_t status = sBluetoothA2dpInterface->connect((RawAddress*)addr);
      if (status != BT_STATUS_SUCCESS) {
        ALOGE("Failed HF connection, status: %d", status);
      }
      env->ReleaseByteArrayElements(address, addr, 0);
      return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    }
    
    初始化
    static void initNative(JNIEnv* env, jobject object,
                           jobjectArray codecConfigArray,
                           jint maxA2dpConnection,
                           jint multiCastState) {
    					   
     sBluetoothA2dpInterface =
          (btav_source_interface_t*)btInf->get_profile_interface(
              BT_PROFILE_ADVANCED_AUDIO_ID);
      if (sBluetoothA2dpInterface == NULL) {
        ALOGE("Failed to get Bluetooth A2DP Interface");
        return;
      }
      
      
      
    systemttifsrcluetooth.cc
      static const void* get_profile_interface(const char* profile_id) {
      LOG_INFO(LOG_TAG, "%s: id = %s", __func__, profile_id);
    
      /* sanity check */
      if (interface_ready() == false) return NULL;
    
      /* check for supported profile interfaces */
      if (is_profile(profile_id, BT_PROFILE_HANDSFREE_ID))
        return btif_hf_get_interface();
    
      if (is_profile(profile_id, BT_PROFILE_HANDSFREE_CLIENT_ID))
        return btif_hf_client_get_interface();
    
      if (is_profile(profile_id, BT_PROFILE_SOCKETS_ID))
        return btif_sock_get_interface();
    
      if (is_profile(profile_id, BT_PROFILE_PAN_ID))
        return btif_pan_get_interface();
    	// audio有两个,一个是作为source用
      if (is_profile(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID))
        return btif_av_get_src_interface();
    	// audio, 作为sink用
      if (is_profile(profile_id, BT_PROFILE_ADVANCED_AUDIO_SINK_ID))
        return btif_av_get_sink_interface();
    	
    	
    	
    systemttifsrctif_av.cc
    const btav_source_interface_t* btif_av_get_src_interface(void) {
      BTIF_TRACE_EVENT("%s", __func__);
      return &bt_av_src_interface;
    }
    
    static const btav_source_interface_t bt_av_src_interface = {
        sizeof(btav_source_interface_t),
        init_src,
        src_connect_sink,	// 音频源连接sink,这个应该是对音频进行编码,然后输出
        disconnect,
        codec_config_src,
        cleanup_src,
        allow_connection,
        select_audio_device,
    };
    
    static const btav_sink_interface_t bt_av_sink_interface = {
        sizeof(btav_sink_interface_t),
        init_sink,
        sink_connect_src,	// sink连接音频源, sink接受音频数据,解码进行播放
        disconnect,
        cleanup_sink,
        update_audio_focus_state,
        update_audio_track_gain,
    };
    
    
    
    连接成功之后,底层发送事件。
        packagesappsBluetoothsrccomandroidluetootha2dpA2dpStateMachine.java
    	private void onConnectionStateChanged(int state, byte[] address) {
            log("Enter onConnectionStateChanged() ");
            StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
            event.valueInt = state;
            event.device = getDevice(address);
            sendMessage(STACK_EVENT, event);
            log("Exit onConnectionStateChanged() ");
        }
    	
    	
    	private class Disconnected extends State {
    	
                    case STACK_EVENT:
                        StackEvent event = (StackEvent) message.obj;
                        switch (event.type) {
                            case EVENT_TYPE_CONNECTION_STATE_CHANGED:
                                processConnectionEvent(event.valueInt, event.device);
                                break;
                            default:
                                loge("Unexpected stack event: " + event.type);
                                break;
    	
           private void processConnectionEvent(int state, BluetoothDevice device) {
                log("processConnectionEvent state = " + state +
                        ", device = " + device);
                switch (state) {
                ...
                case CONNECTION_STATE_CONNECTED:
                    logw("A2DP Connected from Disconnected state");
                    if (okToConnect(device)) {
                        logi("Incoming A2DP accepted");
                        synchronized (A2dpStateMachine.this) {
                            if (!mConnectedDevicesList.contains(device)) {
                                mConnectedDevicesList.add(device);
                                log( "device " + device.getAddress() +
                                        " is adding in Disconnected state");
                            }
                            mCurrentDevice = device;
                            transitionTo(mConnected);
                        }
                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
                                                 BluetoothProfile.STATE_DISCONNECTED);
                    } else {
                        //reject the connection and stay in Disconnected state itself
                        logi("Incoming A2DP rejected");
                        disconnectA2dpNative(getByteAddress(device));
                    }
                    break;	
    
    				    private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
            log("Enter broadcastConnectionState() ");
            int delay = 0;
            if (mDummyDevice == null) {
               Log.i(TAG, "Setting the dummy device for audio service: " + device);
               String dummyAddress = "FA:CE:FA:CE:FA:CE";
               mDummyDevice = mAdapter.getRemoteDevice(dummyAddress);
            }
    
            if ((newState == BluetoothProfile.STATE_CONNECTED) ||
                (newState == BluetoothProfile.STATE_DISCONNECTING)) {
                if (mConnectedDevicesList.size() == 1) {
                    Log.d("A2dpStateMachine", "broadcasting connection state");
                    delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(mDummyDevice,
                                                           newState, BluetoothProfile.A2DP);
                } else {
                    Log.d("A2dpStateMachine", "DualA2dp: not broadcasting connected/disconnecting state");
                }
            } else if ((newState == BluetoothProfile.STATE_DISCONNECTED) ||
                       (newState == BluetoothProfile.STATE_CONNECTING)) {
                if (mConnectedDevicesList.size() == 0) {
                    Log.d("A2dpStateMachine", "broadcasting connection state");
                    delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(mDummyDevice,
                                                           newState, BluetoothProfile.A2DP);
                } else {
                    Log.d("A2dpStateMachine", "DualA2dp: not broadcasting connecting/disconnected state");
                }
            }
    
            if (mCodecNotifPending && newState == BluetoothProfile.STATE_CONNECTED) {
                Log.i(TAG, "There was pending codec config change, dispatching now. " + "device: " + device);
                Log.i(TAG, "Sending broadcast with device " + mDummyDevice);
    
                Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
                intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, mCodecStatus);
                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    
                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDummyDevice);
                mAudioManager.handleBluetoothA2dpDeviceConfigChange(mDummyDevice);
                mCodecNotifPending = false;
    
                mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM);
            }
    
            Log.i(TAG,"mLastDelay: " + mLastDelay + " Current_Delay: " + delay);
            if (mIntentBroadcastHandler.hasMessages(MSG_CONNECTION_STATE_CHANGED)) {
                Log.i(TAG," Braodcast handler has the pending messages: " );
                if (mLastDelay > delay) {
                    Log.i(TAG,"Last delay is greater than the current delay: " );
                    delay = mLastDelay;
                }
            }
            mLastDelay = delay;
    
            Log.i(TAG,"connection state change: " + device + " newState: " + newState + " prevState:" + prevState);
    
            mWakeLock.acquire();
    		// 发送广播
            mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage(
                MSG_CONNECTION_STATE_CHANGED, prevState, newState, device), delay);
            log("Exit broadcastConnectionState() ");
        }
    	broadcastConnectionState中会向AudioManager中设置A2DP的连接状态,返回值用来延时发送广播。AudioManager设置A2DP的连接状态非常重要,这样音频系统根据当前状态,判断音频从哪里发出(蓝牙a2dp、扬声器、耳机)。
    	
    

    Liu Tao
    2019-3-28

  • 相关阅读:
    css:水平居中和垂直居中
    python celery多worker、多队列、定时任务
    python 单例模式
    单例模式
    django 缓存信号等
    redis常用的命令总结
    在python项目中导出项目依赖的模块信息
    面向对象:类中的特殊成员
    python写算法中的栈
    CatLog_小鱼要加油
  • 原文地址:https://www.cnblogs.com/helloworldtoyou/p/10617252.html
Copyright © 2011-2022 走看看