Loading android/app/src/com/android/bluetooth/a2dp/A2dpService.java +47 −85 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.bluetooth.a2dp; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; import static com.android.bluetooth.Utils.checkCallerTargetSdk; Loading Loading @@ -84,17 +86,18 @@ public class A2dpService extends ProfileService { private static A2dpService sA2dpService; private final A2dpNativeInterface mNativeInterface; private final A2dpCodecConfig mA2dpCodecConfig; private final AdapterService mAdapterService; private final AudioManager mAudioManager; private final DatabaseManager mDatabaseManager; private final CompanionDeviceManager mCompanionDeviceManager; private final Looper mLooper; private final Handler mHandler; private HandlerThread mStateMachinesThread; private final HandlerThread mStateMachinesThread; // Upper limit of all A2DP devices that are Connected or Connecting private final int mMaxConnectedAudioDevices; @VisibleForTesting ServiceFactory mFactory = new ServiceFactory(); private A2dpCodecConfig mA2dpCodecConfig; @GuardedBy("mStateMachines") private BluetoothDevice mActiveDevice; Loading @@ -111,10 +114,8 @@ public class A2dpService extends ProfileService { // Upper limit of all A2DP devices: Bonded or Connected private static final int MAX_A2DP_STATE_MACHINES = 50; // Upper limit of all A2DP devices that are Connected or Connecting private int mMaxConnectedAudioDevices = 1; // A2DP Offload Enabled in platform boolean mA2dpOffloadEnabled = false; private final boolean mA2dpOffloadEnabled; private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback = new AudioManagerAudioDeviceCallback(); Loading @@ -133,62 +134,43 @@ public class A2dpService extends ProfileService { mCompanionDeviceManager = requireNonNull(getSystemService(CompanionDeviceManager.class)); mLooper = requireNonNull(looper); mHandler = new Handler(mLooper); } public static boolean isEnabled() { return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false); } ActiveDeviceManager getActiveDeviceManager() { return mAdapterService.getActiveDeviceManager(); } @Override protected IProfileServiceBinder initBinder() { return new BluetoothA2dpBinder(this); } @Override public void start() { Log.i(TAG, "start()"); if (sA2dpService != null) { throw new IllegalStateException("start() called twice"); } // Step 2: Get maximum number of connected audio devices mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices(); Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices); // Step 3: Start handler thread for state machines // Setup Handler. mStateMachines.clear(); if (!Flags.a2dpServiceLooper()) { mStateMachinesThread = new HandlerThread("A2dpService.StateMachines"); mStateMachinesThread.start(); } else { mStateMachinesThread = null; } // Step 4: Setup codec config mA2dpCodecConfig = new A2dpCodecConfig(this, mNativeInterface); // Step 5: Initialize native interface mNativeInterface.init( mMaxConnectedAudioDevices, mA2dpCodecConfig.codecConfigPriorities(), mA2dpCodecConfig.codecConfigOffloading()); // Step 6: Check if A2DP is in offload mode mA2dpOffloadEnabled = mAdapterService.isA2dpOffloadEnabled(); Log.d(TAG, "A2DP offload flag set to " + mA2dpOffloadEnabled); // Step 7: Register Audio Device callback mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler); // Step 8: Mark service as started setA2dpService(this); } // Step 9: Clear active device removeActiveDevice(false); public static boolean isEnabled() { return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false); } ActiveDeviceManager getActiveDeviceManager() { return mAdapterService.getActiveDeviceManager(); } @Override protected IProfileServiceBinder initBinder() { return new BluetoothA2dpBinder(this); } @Override Loading @@ -211,14 +193,10 @@ public class A2dpService extends ProfileService { // Step 6: Cleanup native interface mNativeInterface.cleanup(); // Step 5: Clear codec config mA2dpCodecConfig = null; // Step 4: Destroy state machines and stop handler thread synchronized (mStateMachines) { for (A2dpStateMachine sm : mStateMachines.values()) { sm.doQuit(); sm.cleanup(); } mStateMachines.clear(); } Loading @@ -227,21 +205,12 @@ public class A2dpService extends ProfileService { try { mStateMachinesThread.quitSafely(); mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS); mStateMachinesThread = null; } catch (InterruptedException e) { // Do not rethrow as we are shutting down anyway } } mHandler.removeCallbacksAndMessages(null); // Step 2: Reset maximum number of connected audio devices mMaxConnectedAudioDevices = 1; } @Override public void cleanup() { Log.i(TAG, "cleanup()"); } public static synchronized A2dpService getA2dpService() { Loading Loading @@ -301,7 +270,7 @@ public class A2dpService extends ProfileService { Log.e(TAG, "Cannot connect to " + device + " : no state machine"); return false; } smConnect.sendMessage(A2dpStateMachine.CONNECT); smConnect.sendMessage(A2dpStateMachine.MESSAGE_CONNECT); return true; } } Loading @@ -321,7 +290,7 @@ public class A2dpService extends ProfileService { Log.e(TAG, "Ignored disconnect request for " + device + " : no state machine"); return false; } sm.sendMessage(A2dpStateMachine.DISCONNECT); sm.sendMessage(A2dpStateMachine.MESSAGE_DISCONNECT); return true; } } Loading Loading @@ -938,8 +907,7 @@ public class A2dpService extends ProfileService { if (sm == null) { if (stackEvent.type == A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { switch (stackEvent.valueInt) { case A2dpStackEvent.CONNECTION_STATE_CONNECTED: case A2dpStackEvent.CONNECTION_STATE_CONNECTING: case STATE_CONNECTED, STATE_CONNECTING -> { // Create a new state machine only when connecting to a device if (!connectionAllowedCheckMaxDevices(device)) { Log.e( Loading @@ -950,9 +918,7 @@ public class A2dpService extends ProfileService { return; } sm = getOrCreateStateMachine(device); break; default: break; } } } } Loading @@ -960,7 +926,7 @@ public class A2dpService extends ProfileService { Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent); return; } sm.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent); sm.sendMessage(A2dpStateMachine.MESSAGE_STACK_EVENT, stackEvent); } } Loading Loading @@ -1045,10 +1011,11 @@ public class A2dpService extends ProfileService { } Log.d(TAG, "Creating a new state machine for " + device); sm = A2dpStateMachine.make( device, new A2dpStateMachine( this, device, mNativeInterface, mA2dpOffloadEnabled, Flags.a2dpServiceLooper() ? mLooper : mStateMachinesThread.getLooper()); mStateMachines.put(device, sm); return sm; Loading Loading @@ -1225,7 +1192,6 @@ public class A2dpService extends ProfileService { } Log.i(TAG, "removeStateMachine: removing state machine for device: " + device); sm.doQuit(); sm.cleanup(); mStateMachines.remove(device); } } Loading Loading @@ -1697,7 +1663,6 @@ public class A2dpService extends ProfileService { ProfileService.println(sb, "mActiveDevice: " + mActiveDevice); } ProfileService.println(sb, "mMaxConnectedAudioDevices: " + mMaxConnectedAudioDevices); if (mA2dpCodecConfig != null) { ProfileService.println(sb, "codecConfigPriorities:"); for (BluetoothCodecConfig codecConfig : mA2dpCodecConfig.codecConfigPriorities()) { ProfileService.println( Loading @@ -1714,9 +1679,6 @@ public class A2dpService extends ProfileService { ProfileService.println(sb, " " + codecConfig); } } } else { ProfileService.println(sb, "mA2dpCodecConfig: null"); } for (A2dpStateMachine sm : mStateMachines.values()) { sm.dump(sb); } Loading android/app/src/com/android/bluetooth/a2dp/A2dpStackEvent.java +3 −19 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.bluetooth.a2dp; import static android.bluetooth.BluetoothProfile.getConnectionStateName; import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; Loading @@ -30,12 +32,6 @@ public class A2dpStackEvent { public static final int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; public static final int EVENT_TYPE_CODEC_CONFIG_CHANGED = 3; // Do not modify without updating the HAL bt_av.h files. // Match up with btav_connection_state_t enum of bt_av.h static final int CONNECTION_STATE_DISCONNECTED = 0; static final int CONNECTION_STATE_CONNECTING = 1; static final int CONNECTION_STATE_CONNECTED = 2; static final int CONNECTION_STATE_DISCONNECTING = 3; // Match up with btav_audio_state_t enum of bt_av.h static final int AUDIO_STATE_REMOTE_SUSPEND = 0; static final int AUDIO_STATE_STOPPED = 1; Loading Loading @@ -82,19 +78,7 @@ public class A2dpStackEvent { private static String eventTypeValueIntToString(int type, int value) { switch (type) { case EVENT_TYPE_CONNECTION_STATE_CHANGED: switch (value) { case CONNECTION_STATE_DISCONNECTED: return "DISCONNECTED"; case CONNECTION_STATE_CONNECTING: return "CONNECTING"; case CONNECTION_STATE_CONNECTED: return "CONNECTED"; case CONNECTION_STATE_DISCONNECTING: return "DISCONNECTING"; default: break; } break; return getConnectionStateName(value); case EVENT_TYPE_AUDIO_STATE_CHANGED: switch (value) { case AUDIO_STATE_REMOTE_SUSPEND: Loading Loading
android/app/src/com/android/bluetooth/a2dp/A2dpService.java +47 −85 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.bluetooth.a2dp; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; import static com.android.bluetooth.Utils.checkCallerTargetSdk; Loading Loading @@ -84,17 +86,18 @@ public class A2dpService extends ProfileService { private static A2dpService sA2dpService; private final A2dpNativeInterface mNativeInterface; private final A2dpCodecConfig mA2dpCodecConfig; private final AdapterService mAdapterService; private final AudioManager mAudioManager; private final DatabaseManager mDatabaseManager; private final CompanionDeviceManager mCompanionDeviceManager; private final Looper mLooper; private final Handler mHandler; private HandlerThread mStateMachinesThread; private final HandlerThread mStateMachinesThread; // Upper limit of all A2DP devices that are Connected or Connecting private final int mMaxConnectedAudioDevices; @VisibleForTesting ServiceFactory mFactory = new ServiceFactory(); private A2dpCodecConfig mA2dpCodecConfig; @GuardedBy("mStateMachines") private BluetoothDevice mActiveDevice; Loading @@ -111,10 +114,8 @@ public class A2dpService extends ProfileService { // Upper limit of all A2DP devices: Bonded or Connected private static final int MAX_A2DP_STATE_MACHINES = 50; // Upper limit of all A2DP devices that are Connected or Connecting private int mMaxConnectedAudioDevices = 1; // A2DP Offload Enabled in platform boolean mA2dpOffloadEnabled = false; private final boolean mA2dpOffloadEnabled; private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback = new AudioManagerAudioDeviceCallback(); Loading @@ -133,62 +134,43 @@ public class A2dpService extends ProfileService { mCompanionDeviceManager = requireNonNull(getSystemService(CompanionDeviceManager.class)); mLooper = requireNonNull(looper); mHandler = new Handler(mLooper); } public static boolean isEnabled() { return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false); } ActiveDeviceManager getActiveDeviceManager() { return mAdapterService.getActiveDeviceManager(); } @Override protected IProfileServiceBinder initBinder() { return new BluetoothA2dpBinder(this); } @Override public void start() { Log.i(TAG, "start()"); if (sA2dpService != null) { throw new IllegalStateException("start() called twice"); } // Step 2: Get maximum number of connected audio devices mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices(); Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices); // Step 3: Start handler thread for state machines // Setup Handler. mStateMachines.clear(); if (!Flags.a2dpServiceLooper()) { mStateMachinesThread = new HandlerThread("A2dpService.StateMachines"); mStateMachinesThread.start(); } else { mStateMachinesThread = null; } // Step 4: Setup codec config mA2dpCodecConfig = new A2dpCodecConfig(this, mNativeInterface); // Step 5: Initialize native interface mNativeInterface.init( mMaxConnectedAudioDevices, mA2dpCodecConfig.codecConfigPriorities(), mA2dpCodecConfig.codecConfigOffloading()); // Step 6: Check if A2DP is in offload mode mA2dpOffloadEnabled = mAdapterService.isA2dpOffloadEnabled(); Log.d(TAG, "A2DP offload flag set to " + mA2dpOffloadEnabled); // Step 7: Register Audio Device callback mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler); // Step 8: Mark service as started setA2dpService(this); } // Step 9: Clear active device removeActiveDevice(false); public static boolean isEnabled() { return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false); } ActiveDeviceManager getActiveDeviceManager() { return mAdapterService.getActiveDeviceManager(); } @Override protected IProfileServiceBinder initBinder() { return new BluetoothA2dpBinder(this); } @Override Loading @@ -211,14 +193,10 @@ public class A2dpService extends ProfileService { // Step 6: Cleanup native interface mNativeInterface.cleanup(); // Step 5: Clear codec config mA2dpCodecConfig = null; // Step 4: Destroy state machines and stop handler thread synchronized (mStateMachines) { for (A2dpStateMachine sm : mStateMachines.values()) { sm.doQuit(); sm.cleanup(); } mStateMachines.clear(); } Loading @@ -227,21 +205,12 @@ public class A2dpService extends ProfileService { try { mStateMachinesThread.quitSafely(); mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS); mStateMachinesThread = null; } catch (InterruptedException e) { // Do not rethrow as we are shutting down anyway } } mHandler.removeCallbacksAndMessages(null); // Step 2: Reset maximum number of connected audio devices mMaxConnectedAudioDevices = 1; } @Override public void cleanup() { Log.i(TAG, "cleanup()"); } public static synchronized A2dpService getA2dpService() { Loading Loading @@ -301,7 +270,7 @@ public class A2dpService extends ProfileService { Log.e(TAG, "Cannot connect to " + device + " : no state machine"); return false; } smConnect.sendMessage(A2dpStateMachine.CONNECT); smConnect.sendMessage(A2dpStateMachine.MESSAGE_CONNECT); return true; } } Loading @@ -321,7 +290,7 @@ public class A2dpService extends ProfileService { Log.e(TAG, "Ignored disconnect request for " + device + " : no state machine"); return false; } sm.sendMessage(A2dpStateMachine.DISCONNECT); sm.sendMessage(A2dpStateMachine.MESSAGE_DISCONNECT); return true; } } Loading Loading @@ -938,8 +907,7 @@ public class A2dpService extends ProfileService { if (sm == null) { if (stackEvent.type == A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { switch (stackEvent.valueInt) { case A2dpStackEvent.CONNECTION_STATE_CONNECTED: case A2dpStackEvent.CONNECTION_STATE_CONNECTING: case STATE_CONNECTED, STATE_CONNECTING -> { // Create a new state machine only when connecting to a device if (!connectionAllowedCheckMaxDevices(device)) { Log.e( Loading @@ -950,9 +918,7 @@ public class A2dpService extends ProfileService { return; } sm = getOrCreateStateMachine(device); break; default: break; } } } } Loading @@ -960,7 +926,7 @@ public class A2dpService extends ProfileService { Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent); return; } sm.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent); sm.sendMessage(A2dpStateMachine.MESSAGE_STACK_EVENT, stackEvent); } } Loading Loading @@ -1045,10 +1011,11 @@ public class A2dpService extends ProfileService { } Log.d(TAG, "Creating a new state machine for " + device); sm = A2dpStateMachine.make( device, new A2dpStateMachine( this, device, mNativeInterface, mA2dpOffloadEnabled, Flags.a2dpServiceLooper() ? mLooper : mStateMachinesThread.getLooper()); mStateMachines.put(device, sm); return sm; Loading Loading @@ -1225,7 +1192,6 @@ public class A2dpService extends ProfileService { } Log.i(TAG, "removeStateMachine: removing state machine for device: " + device); sm.doQuit(); sm.cleanup(); mStateMachines.remove(device); } } Loading Loading @@ -1697,7 +1663,6 @@ public class A2dpService extends ProfileService { ProfileService.println(sb, "mActiveDevice: " + mActiveDevice); } ProfileService.println(sb, "mMaxConnectedAudioDevices: " + mMaxConnectedAudioDevices); if (mA2dpCodecConfig != null) { ProfileService.println(sb, "codecConfigPriorities:"); for (BluetoothCodecConfig codecConfig : mA2dpCodecConfig.codecConfigPriorities()) { ProfileService.println( Loading @@ -1714,9 +1679,6 @@ public class A2dpService extends ProfileService { ProfileService.println(sb, " " + codecConfig); } } } else { ProfileService.println(sb, "mA2dpCodecConfig: null"); } for (A2dpStateMachine sm : mStateMachines.values()) { sm.dump(sb); } Loading
android/app/src/com/android/bluetooth/a2dp/A2dpStackEvent.java +3 −19 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.bluetooth.a2dp; import static android.bluetooth.BluetoothProfile.getConnectionStateName; import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; Loading @@ -30,12 +32,6 @@ public class A2dpStackEvent { public static final int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; public static final int EVENT_TYPE_CODEC_CONFIG_CHANGED = 3; // Do not modify without updating the HAL bt_av.h files. // Match up with btav_connection_state_t enum of bt_av.h static final int CONNECTION_STATE_DISCONNECTED = 0; static final int CONNECTION_STATE_CONNECTING = 1; static final int CONNECTION_STATE_CONNECTED = 2; static final int CONNECTION_STATE_DISCONNECTING = 3; // Match up with btav_audio_state_t enum of bt_av.h static final int AUDIO_STATE_REMOTE_SUSPEND = 0; static final int AUDIO_STATE_STOPPED = 1; Loading Loading @@ -82,19 +78,7 @@ public class A2dpStackEvent { private static String eventTypeValueIntToString(int type, int value) { switch (type) { case EVENT_TYPE_CONNECTION_STATE_CHANGED: switch (value) { case CONNECTION_STATE_DISCONNECTED: return "DISCONNECTED"; case CONNECTION_STATE_CONNECTING: return "CONNECTING"; case CONNECTION_STATE_CONNECTED: return "CONNECTED"; case CONNECTION_STATE_DISCONNECTING: return "DISCONNECTING"; default: break; } break; return getConnectionStateName(value); case EVENT_TYPE_AUDIO_STATE_CHANGED: switch (value) { case AUDIO_STATE_REMOTE_SUSPEND: Loading