Loading android/app/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -108,7 +108,7 @@ android_app { static_libs: [ "android.hardware.radio-V1.0-java", "androidx.core_core", "androidx.legacy_legacy-support-v4", "androidx.media_media", "androidx.lifecycle_lifecycle-livedata", "androidx.room_room-runtime", "androidx.annotation_annotation", Loading android/app/src/com/android/bluetooth/BluetoothMethodProxy.java +9 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; import android.provider.Telephony; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; Loading @@ -36,6 +37,7 @@ import com.android.obex.HeaderSet; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Set; /** * Proxy class for method calls to help with unit testing Loading Loading @@ -171,4 +173,11 @@ public class BluetoothMethodProxy { public <T> T getSystemService(Context context, Class<T> serviceClass) { return context.getSystemService(serviceClass); } /** * Proxies {@link Telephony.Threads#getOrCreateThreadId(Context, Set <String>)}. */ public long telephonyGetOrCreateThreadId(Context context, Set<String> recipients) { return Telephony.Threads.getOrCreateThreadId(context, recipients); } } No newline at end of file android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java +6 −40 Original line number Diff line number Diff line Loading @@ -152,7 +152,8 @@ public class BassClientStateMachine extends StateMachine { private final Connecting mConnecting = new Connecting(); private final Disconnecting mDisconnecting = new Disconnecting(); private final ConnectedProcessing mConnectedProcessing = new ConnectedProcessing(); private final List<BluetoothGattCharacteristic> mBroadcastCharacteristics = @VisibleForTesting final List<BluetoothGattCharacteristic> mBroadcastCharacteristics = new ArrayList<BluetoothGattCharacteristic>(); @VisibleForTesting BluetoothDevice mDevice; Loading Loading @@ -182,7 +183,8 @@ public class BassClientStateMachine extends StateMachine { private PeriodicAdvertisingManager mPeriodicAdvManager; private boolean mAutoAssist = false; private boolean mAutoTriggered = false; private boolean mNoStopScanOffload = false; @VisibleForTesting boolean mNoStopScanOffload = false; private boolean mDefNoPAS = false; private boolean mForceSB = false; private int mBroadcastSourceIdLength = 3; Loading Loading @@ -560,7 +562,8 @@ public class BassClientStateMachine extends StateMachine { mService.getCallbacks().notifyReceiveStateChanged(mDevice, sourceId, state); } private static boolean isEmpty(final byte[] data) { @VisibleForTesting static boolean isEmpty(final byte[] data) { return IntStream.range(0, data.length).parallel().allMatch(i -> data[i] == 0); } Loading Loading @@ -1236,18 +1239,6 @@ public class BassClientStateMachine extends StateMachine { } } private byte[] bluetoothAddressToBytes(String s) { log("BluetoothAddressToBytes: input string:" + s); String[] splits = s.split(":"); byte[] addressBytes = new byte[6]; for (int i = 0; i < 6; i++) { int hexValue = Integer.parseInt(splits[i], 16); log("hexValue:" + hexValue); addressBytes[i] = (byte) hexValue; } return addressBytes; } private static int getBisSyncFromChannelPreference( List<BluetoothLeBroadcastChannel> channels) { int bisSync = 0; Loading Loading @@ -1397,15 +1388,6 @@ public class BassClientStateMachine extends StateMachine { return res; } private byte[] convertAsciitoValues(byte[] val) { byte[] ret = new byte[val.length]; for (int i = 0; i < val.length; i++) { ret[i] = (byte) (val[i] - (byte) '0'); } log("convertAsciitoValues: returns:" + Arrays.toString(val)); return ret; } private byte[] convertRecvStateToSetBroadcastCodeByteArray( BluetoothLeBroadcastReceiveState recvState) { byte[] res = new byte[BassConstants.PIN_CODE_CMD_LEN]; Loading Loading @@ -1995,22 +1977,6 @@ public class BassClientStateMachine extends StateMachine { return Integer.toString(what); } private static String profileStateToString(int state) { switch (state) { case BluetoothProfile.STATE_DISCONNECTED: return "DISCONNECTED"; case BluetoothProfile.STATE_CONNECTING: return "CONNECTING"; case BluetoothProfile.STATE_CONNECTED: return "CONNECTED"; case BluetoothProfile.STATE_DISCONNECTING: return "DISCONNECTING"; default: break; } return Integer.toString(state); } /** * Dump info */ Loading android/app/src/com/android/bluetooth/gatt/ScanManager.java +3 −2 Original line number Diff line number Diff line Loading @@ -1337,11 +1337,12 @@ public class ScanManager { private void initFilterIndexStack() { int maxFiltersSupported = AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported(); // Start from index 3 as: // Start from index 4 as: // index 0 is reserved for ALL_PASS filter in Settings app. // index 1 is reserved for ALL_PASS filter for regular scan apps. // index 2 is reserved for ALL_PASS filter for batch scan apps. for (int i = 3; i < maxFiltersSupported; ++i) { // index 3 is reserved for BAP/CAP Announcements for (int i = 4; i < maxFiltersSupported; ++i) { mFilterIndexStack.add(i); } } Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +73 −12 Original line number Diff line number Diff line Loading @@ -115,17 +115,21 @@ public class LeAudioService extends ProfileService { private volatile BluetoothDevice mActiveAudioOutDevice; private volatile BluetoothDevice mActiveAudioInDevice; private LeAudioCodecConfig mLeAudioCodecConfig; private Object mGroupLock = new Object(); private final Object mGroupLock = new Object(); ServiceFactory mServiceFactory = new ServiceFactory(); LeAudioNativeInterface mLeAudioNativeInterface; boolean mLeAudioNativeIsInitialized = false; boolean mBluetoothEnabled = false; BluetoothDevice mHfpHandoverDevice = null; LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface = null; @VisibleForTesting AudioManager mAudioManager; LeAudioTmapGattServer mTmapGattServer; @VisibleForTesting McpService mMcpService; @VisibleForTesting VolumeControlService mVolumeControlService; Loading Loading @@ -330,6 +334,7 @@ public class LeAudioService extends ProfileService { mLeAudioNativeInterface.cleanup(); mLeAudioNativeInterface = null; mLeAudioNativeIsInitialized = false; mBluetoothEnabled = false; mHfpHandoverDevice = null; mActiveAudioOutDevice = null; Loading Loading @@ -391,6 +396,7 @@ public class LeAudioService extends ProfileService { mAdapterService = null; mAudioManager = null; mMcpService = null; mVolumeControlService = null; return true; Loading Loading @@ -895,7 +901,13 @@ public class LeAudioService extends ProfileService { return false; } private void notifyActiveDeviceChanged() { /** * Send broadcast intent about LeAudio active device. * This is called when AudioManager confirms, LeAudio device * is added or removed. */ @VisibleForTesting void notifyActiveDeviceChanged() { Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mActiveAudioOutDevice != null ? mActiveAudioOutDevice : mActiveAudioInDevice); Loading Loading @@ -1683,11 +1695,6 @@ public class LeAudioService extends ProfileService { } else { Log.e(TAG, "no descriptors for group: " + myGroupId); } McpService mcpService = mServiceFactory.getMcpService(); if (mcpService != null) { mcpService.setDeviceAuthorized(device, true); } } // Check if the device is disconnected - if unbond, remove the state machine if (toState == BluetoothProfile.STATE_DISCONNECTED) { Loading @@ -1699,11 +1706,6 @@ public class LeAudioService extends ProfileService { removeStateMachine(device); } McpService mcpService = mServiceFactory.getMcpService(); if (mcpService != null) { mcpService.setDeviceAuthorized(device, false); } int myGroupId = getGroupId(device); LeAudioGroupDescriptor descriptor = getGroupDescriptor(myGroupId); if (descriptor == null) { Loading Loading @@ -1971,6 +1973,50 @@ public class LeAudioService extends ProfileService { } } McpService getMcpService() { if (mMcpService != null) { return mMcpService; } mMcpService = mServiceFactory.getMcpService(); return mMcpService; } /** * This function is called when the framework registers * a callback with the service for this first time. * This is used as an indication that Bluetooth has been enabled. * * It is used to authorize all known LeAudio devices in the services * which requires that e.g. GMCS */ @VisibleForTesting void handleBluetoothEnabled() { if (DBG) { Log.d(TAG, "handleBluetoothEnabled "); } mBluetoothEnabled = true; synchronized (mGroupLock) { if (mDeviceGroupIdMap.isEmpty()) { return; } } McpService mcpService = getMcpService(); if (mcpService == null) { Log.e(TAG, "mcpService not available "); return; } synchronized (mGroupLock) { for (Map.Entry<BluetoothDevice, Integer> entry : mDeviceGroupIdMap.entrySet()) { mcpService.setDeviceAuthorized(entry.getKey(), true); } } } private LeAudioGroupDescriptor getGroupDescriptor(int groupId) { synchronized (mGroupLock) { return mGroupDescriptors.get(groupId); Loading @@ -1990,6 +2036,13 @@ public class LeAudioService extends ProfileService { } notifyGroupNodeAdded(device, groupId); } if (mBluetoothEnabled) { McpService mcpService = getMcpService(); if (mcpService != null) { mcpService.setDeviceAuthorized(device, true); } } } private void notifyGroupNodeAdded(BluetoothDevice device, int groupId) { Loading Loading @@ -2033,6 +2086,11 @@ public class LeAudioService extends ProfileService { } notifyGroupNodeRemoved(device, groupId); } McpService mcpService = getMcpService(); if (mcpService != null) { mcpService.setDeviceAuthorized(device, false); } } private void notifyGroupNodeRemoved(BluetoothDevice device, int groupId) { Loading Loading @@ -2668,6 +2726,9 @@ public class LeAudioService extends ProfileService { enforceBluetoothPrivilegedPermission(service); service.mLeAudioCallbacks.register(callback); if (!service.mBluetoothEnabled) { service.handleBluetoothEnabled(); } receiver.send(null); } catch (RuntimeException e) { receiver.propagateException(e); Loading Loading
android/app/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -108,7 +108,7 @@ android_app { static_libs: [ "android.hardware.radio-V1.0-java", "androidx.core_core", "androidx.legacy_legacy-support-v4", "androidx.media_media", "androidx.lifecycle_lifecycle-livedata", "androidx.room_room-runtime", "androidx.annotation_annotation", Loading
android/app/src/com/android/bluetooth/BluetoothMethodProxy.java +9 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; import android.provider.Telephony; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; Loading @@ -36,6 +37,7 @@ import com.android.obex.HeaderSet; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Set; /** * Proxy class for method calls to help with unit testing Loading Loading @@ -171,4 +173,11 @@ public class BluetoothMethodProxy { public <T> T getSystemService(Context context, Class<T> serviceClass) { return context.getSystemService(serviceClass); } /** * Proxies {@link Telephony.Threads#getOrCreateThreadId(Context, Set <String>)}. */ public long telephonyGetOrCreateThreadId(Context context, Set<String> recipients) { return Telephony.Threads.getOrCreateThreadId(context, recipients); } } No newline at end of file
android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java +6 −40 Original line number Diff line number Diff line Loading @@ -152,7 +152,8 @@ public class BassClientStateMachine extends StateMachine { private final Connecting mConnecting = new Connecting(); private final Disconnecting mDisconnecting = new Disconnecting(); private final ConnectedProcessing mConnectedProcessing = new ConnectedProcessing(); private final List<BluetoothGattCharacteristic> mBroadcastCharacteristics = @VisibleForTesting final List<BluetoothGattCharacteristic> mBroadcastCharacteristics = new ArrayList<BluetoothGattCharacteristic>(); @VisibleForTesting BluetoothDevice mDevice; Loading Loading @@ -182,7 +183,8 @@ public class BassClientStateMachine extends StateMachine { private PeriodicAdvertisingManager mPeriodicAdvManager; private boolean mAutoAssist = false; private boolean mAutoTriggered = false; private boolean mNoStopScanOffload = false; @VisibleForTesting boolean mNoStopScanOffload = false; private boolean mDefNoPAS = false; private boolean mForceSB = false; private int mBroadcastSourceIdLength = 3; Loading Loading @@ -560,7 +562,8 @@ public class BassClientStateMachine extends StateMachine { mService.getCallbacks().notifyReceiveStateChanged(mDevice, sourceId, state); } private static boolean isEmpty(final byte[] data) { @VisibleForTesting static boolean isEmpty(final byte[] data) { return IntStream.range(0, data.length).parallel().allMatch(i -> data[i] == 0); } Loading Loading @@ -1236,18 +1239,6 @@ public class BassClientStateMachine extends StateMachine { } } private byte[] bluetoothAddressToBytes(String s) { log("BluetoothAddressToBytes: input string:" + s); String[] splits = s.split(":"); byte[] addressBytes = new byte[6]; for (int i = 0; i < 6; i++) { int hexValue = Integer.parseInt(splits[i], 16); log("hexValue:" + hexValue); addressBytes[i] = (byte) hexValue; } return addressBytes; } private static int getBisSyncFromChannelPreference( List<BluetoothLeBroadcastChannel> channels) { int bisSync = 0; Loading Loading @@ -1397,15 +1388,6 @@ public class BassClientStateMachine extends StateMachine { return res; } private byte[] convertAsciitoValues(byte[] val) { byte[] ret = new byte[val.length]; for (int i = 0; i < val.length; i++) { ret[i] = (byte) (val[i] - (byte) '0'); } log("convertAsciitoValues: returns:" + Arrays.toString(val)); return ret; } private byte[] convertRecvStateToSetBroadcastCodeByteArray( BluetoothLeBroadcastReceiveState recvState) { byte[] res = new byte[BassConstants.PIN_CODE_CMD_LEN]; Loading Loading @@ -1995,22 +1977,6 @@ public class BassClientStateMachine extends StateMachine { return Integer.toString(what); } private static String profileStateToString(int state) { switch (state) { case BluetoothProfile.STATE_DISCONNECTED: return "DISCONNECTED"; case BluetoothProfile.STATE_CONNECTING: return "CONNECTING"; case BluetoothProfile.STATE_CONNECTED: return "CONNECTED"; case BluetoothProfile.STATE_DISCONNECTING: return "DISCONNECTING"; default: break; } return Integer.toString(state); } /** * Dump info */ Loading
android/app/src/com/android/bluetooth/gatt/ScanManager.java +3 −2 Original line number Diff line number Diff line Loading @@ -1337,11 +1337,12 @@ public class ScanManager { private void initFilterIndexStack() { int maxFiltersSupported = AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported(); // Start from index 3 as: // Start from index 4 as: // index 0 is reserved for ALL_PASS filter in Settings app. // index 1 is reserved for ALL_PASS filter for regular scan apps. // index 2 is reserved for ALL_PASS filter for batch scan apps. for (int i = 3; i < maxFiltersSupported; ++i) { // index 3 is reserved for BAP/CAP Announcements for (int i = 4; i < maxFiltersSupported; ++i) { mFilterIndexStack.add(i); } } Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +73 −12 Original line number Diff line number Diff line Loading @@ -115,17 +115,21 @@ public class LeAudioService extends ProfileService { private volatile BluetoothDevice mActiveAudioOutDevice; private volatile BluetoothDevice mActiveAudioInDevice; private LeAudioCodecConfig mLeAudioCodecConfig; private Object mGroupLock = new Object(); private final Object mGroupLock = new Object(); ServiceFactory mServiceFactory = new ServiceFactory(); LeAudioNativeInterface mLeAudioNativeInterface; boolean mLeAudioNativeIsInitialized = false; boolean mBluetoothEnabled = false; BluetoothDevice mHfpHandoverDevice = null; LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface = null; @VisibleForTesting AudioManager mAudioManager; LeAudioTmapGattServer mTmapGattServer; @VisibleForTesting McpService mMcpService; @VisibleForTesting VolumeControlService mVolumeControlService; Loading Loading @@ -330,6 +334,7 @@ public class LeAudioService extends ProfileService { mLeAudioNativeInterface.cleanup(); mLeAudioNativeInterface = null; mLeAudioNativeIsInitialized = false; mBluetoothEnabled = false; mHfpHandoverDevice = null; mActiveAudioOutDevice = null; Loading Loading @@ -391,6 +396,7 @@ public class LeAudioService extends ProfileService { mAdapterService = null; mAudioManager = null; mMcpService = null; mVolumeControlService = null; return true; Loading Loading @@ -895,7 +901,13 @@ public class LeAudioService extends ProfileService { return false; } private void notifyActiveDeviceChanged() { /** * Send broadcast intent about LeAudio active device. * This is called when AudioManager confirms, LeAudio device * is added or removed. */ @VisibleForTesting void notifyActiveDeviceChanged() { Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mActiveAudioOutDevice != null ? mActiveAudioOutDevice : mActiveAudioInDevice); Loading Loading @@ -1683,11 +1695,6 @@ public class LeAudioService extends ProfileService { } else { Log.e(TAG, "no descriptors for group: " + myGroupId); } McpService mcpService = mServiceFactory.getMcpService(); if (mcpService != null) { mcpService.setDeviceAuthorized(device, true); } } // Check if the device is disconnected - if unbond, remove the state machine if (toState == BluetoothProfile.STATE_DISCONNECTED) { Loading @@ -1699,11 +1706,6 @@ public class LeAudioService extends ProfileService { removeStateMachine(device); } McpService mcpService = mServiceFactory.getMcpService(); if (mcpService != null) { mcpService.setDeviceAuthorized(device, false); } int myGroupId = getGroupId(device); LeAudioGroupDescriptor descriptor = getGroupDescriptor(myGroupId); if (descriptor == null) { Loading Loading @@ -1971,6 +1973,50 @@ public class LeAudioService extends ProfileService { } } McpService getMcpService() { if (mMcpService != null) { return mMcpService; } mMcpService = mServiceFactory.getMcpService(); return mMcpService; } /** * This function is called when the framework registers * a callback with the service for this first time. * This is used as an indication that Bluetooth has been enabled. * * It is used to authorize all known LeAudio devices in the services * which requires that e.g. GMCS */ @VisibleForTesting void handleBluetoothEnabled() { if (DBG) { Log.d(TAG, "handleBluetoothEnabled "); } mBluetoothEnabled = true; synchronized (mGroupLock) { if (mDeviceGroupIdMap.isEmpty()) { return; } } McpService mcpService = getMcpService(); if (mcpService == null) { Log.e(TAG, "mcpService not available "); return; } synchronized (mGroupLock) { for (Map.Entry<BluetoothDevice, Integer> entry : mDeviceGroupIdMap.entrySet()) { mcpService.setDeviceAuthorized(entry.getKey(), true); } } } private LeAudioGroupDescriptor getGroupDescriptor(int groupId) { synchronized (mGroupLock) { return mGroupDescriptors.get(groupId); Loading @@ -1990,6 +2036,13 @@ public class LeAudioService extends ProfileService { } notifyGroupNodeAdded(device, groupId); } if (mBluetoothEnabled) { McpService mcpService = getMcpService(); if (mcpService != null) { mcpService.setDeviceAuthorized(device, true); } } } private void notifyGroupNodeAdded(BluetoothDevice device, int groupId) { Loading Loading @@ -2033,6 +2086,11 @@ public class LeAudioService extends ProfileService { } notifyGroupNodeRemoved(device, groupId); } McpService mcpService = getMcpService(); if (mcpService != null) { mcpService.setDeviceAuthorized(device, false); } } private void notifyGroupNodeRemoved(BluetoothDevice device, int groupId) { Loading Loading @@ -2668,6 +2726,9 @@ public class LeAudioService extends ProfileService { enforceBluetoothPrivilegedPermission(service); service.mLeAudioCallbacks.register(callback); if (!service.mBluetoothEnabled) { service.handleBluetoothEnabled(); } receiver.send(null); } catch (RuntimeException e) { receiver.propagateException(e); Loading