diff options
8 files changed, 391 insertions, 120 deletions
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java index 166f3702c3..123ef0aca4 100644 --- a/android/app/src/com/android/bluetooth/hap/HapClientService.java +++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java @@ -1227,11 +1227,15 @@ public class HapClientService extends ProfileService { public void getConnectedDevices(AttributionSource source, SynchronousResultReceiver receiver) { try { + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); List<BluetoothDevice> defaultValue = new ArrayList<>(); HapClientService service = getService(source); - if (service != null) { - defaultValue = service.getConnectedDevices(); + if (service == null) { + throw new IllegalStateException("service is null"); } + enforceBluetoothPrivilegedPermission(service); + defaultValue = service.getConnectedDevices(); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); @@ -1242,11 +1246,15 @@ public class HapClientService extends ProfileService { public void getDevicesMatchingConnectionStates(int[] states, AttributionSource source, SynchronousResultReceiver receiver) { try { + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); List<BluetoothDevice> defaultValue = new ArrayList<>(); HapClientService service = getService(source); - if (service != null) { - defaultValue = service.getDevicesMatchingConnectionStates(states); + if (service == null) { + throw new IllegalStateException("service is null"); } + enforceBluetoothPrivilegedPermission(service); + defaultValue = service.getDevicesMatchingConnectionStates(states); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); @@ -1257,11 +1265,16 @@ public class HapClientService extends ProfileService { public void getConnectionState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { + Objects.requireNonNull(device, "device cannot be null"); + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); int defaultValue = BluetoothProfile.STATE_DISCONNECTED; HapClientService service = getService(source); - if (service != null) { - defaultValue = service.getConnectionState(device); + if (service == null) { + throw new IllegalStateException("service is null"); } + enforceBluetoothPrivilegedPermission(service); + defaultValue = service.getConnectionState(device); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); @@ -1272,11 +1285,16 @@ public class HapClientService extends ProfileService { public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver) { try { + Objects.requireNonNull(device, "device cannot be null"); + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); boolean defaultValue = false; HapClientService service = getService(source); - if (service != null) { - defaultValue = service.setConnectionPolicy(device, connectionPolicy); + if (service == null) { + throw new IllegalStateException("service is null"); } + enforceBluetoothPrivilegedPermission(service); + defaultValue = service.setConnectionPolicy(device, connectionPolicy); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); @@ -1287,11 +1305,16 @@ public class HapClientService extends ProfileService { public void getConnectionPolicy(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { + Objects.requireNonNull(device, "device cannot be null"); + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); int defaultValue = BluetoothProfile.CONNECTION_POLICY_UNKNOWN; HapClientService service = getService(source); - if (service != null) { - defaultValue = service.getConnectionPolicy(device); + if (service == null) { + throw new IllegalStateException("service is null"); } + enforceBluetoothPrivilegedPermission(service); + defaultValue = service.getConnectionPolicy(device); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); @@ -1302,11 +1325,16 @@ public class HapClientService extends ProfileService { public void getActivePresetIndex(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { + Objects.requireNonNull(device, "device cannot be null"); + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); int defaultValue = BluetoothHapClient.PRESET_INDEX_UNAVAILABLE; HapClientService service = getService(source); - if (service != null) { - defaultValue = service.getActivePresetIndex(device); + if (service == null) { + throw new IllegalStateException("service is null"); } + enforceBluetoothPrivilegedPermission(service); + defaultValue = service.getActivePresetIndex(device); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); @@ -1317,11 +1345,16 @@ public class HapClientService extends ProfileService { public void getActivePresetInfo(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { + Objects.requireNonNull(device, "device cannot be null"); + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); BluetoothHapPresetInfo defaultValue = null; HapClientService service = getService(source); - if (service != null) { - defaultValue = service.getActivePresetInfo(device); + if (service == null) { + throw new IllegalStateException("service is null"); } + enforceBluetoothPrivilegedPermission(service); + defaultValue = service.getActivePresetInfo(device); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); @@ -1332,11 +1365,16 @@ public class HapClientService extends ProfileService { public void getHapGroup(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { + Objects.requireNonNull(device, "device cannot be null"); + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); int defaultValue = BluetoothCsipSetCoordinator.GROUP_ID_INVALID; HapClientService service = getService(source); - if (service != null) { - defaultValue = service.getHapGroup(device); + if (service == null) { + throw new IllegalStateException("service is null"); } + enforceBluetoothPrivilegedPermission(service); + defaultValue = service.getHapGroup(device); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); @@ -1346,61 +1384,115 @@ public class HapClientService extends ProfileService { @Override public void selectPreset(BluetoothDevice device, int presetIndex, AttributionSource source) { + if (source == null) { + Log.w(TAG, "source cannot be null"); + return; + } + HapClientService service = getService(source); - if (service != null) { - service.selectPreset(device, presetIndex); + if (service == null) { + Log.w(TAG, "service is null"); + return; } + enforceBluetoothPrivilegedPermission(service); + service.selectPreset(device, presetIndex); } @Override public void selectPresetForGroup(int groupId, int presetIndex, AttributionSource source) { + if (source == null) { + Log.w(TAG, "source cannot be null"); + return; + } + HapClientService service = getService(source); - if (service != null) { - service.selectPresetForGroup(groupId, presetIndex); + if (service == null) { + Log.w(TAG, "service is null"); + return; } + enforceBluetoothPrivilegedPermission(service); + service.selectPresetForGroup(groupId, presetIndex); } @Override public void switchToNextPreset(BluetoothDevice device, AttributionSource source) { + if (source == null) { + Log.w(TAG, "source cannot be null"); + return; + } + HapClientService service = getService(source); - if (service != null) { - service.switchToNextPreset(device); + if (service == null) { + Log.w(TAG, "service is null"); + return; } + enforceBluetoothPrivilegedPermission(service); + service.switchToNextPreset(device); } @Override public void switchToNextPresetForGroup(int groupId, AttributionSource source) { + if (source == null) { + Log.w(TAG, "source cannot be null"); + return; + } + HapClientService service = getService(source); - if (service != null) { - service.switchToNextPresetForGroup(groupId); + if (service == null) { + Log.w(TAG, "service is null"); + return; } + enforceBluetoothPrivilegedPermission(service); + service.switchToNextPresetForGroup(groupId); } @Override public void switchToPreviousPreset(BluetoothDevice device, AttributionSource source) { + if (source == null) { + Log.w(TAG, "source cannot be null"); + return; + } + HapClientService service = getService(source); - if (service != null) { - service.switchToPreviousPreset(device); + if (service == null) { + Log.w(TAG, "service is null"); + return; } + enforceBluetoothPrivilegedPermission(service); + service.switchToPreviousPreset(device); } @Override public void switchToPreviousPresetForGroup(int groupId, AttributionSource source) { + if (source == null) { + Log.w(TAG, "source cannot be null"); + return; + } + HapClientService service = getService(source); - if (service != null) { - service.switchToPreviousPresetForGroup(groupId); + if (service == null) { + Log.w(TAG, "service is null"); + return; } + enforceBluetoothPrivilegedPermission(service); + service.switchToPreviousPresetForGroup(groupId); } @Override public void getPresetInfo(BluetoothDevice device, int presetIndex, AttributionSource source, SynchronousResultReceiver receiver) { + try { + Objects.requireNonNull(device, "device cannot be null"); + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); BluetoothHapPresetInfo defaultValue = null; HapClientService service = getService(source); - if (service != null) { - defaultValue = service.getPresetInfo(device, presetIndex); + if (service == null) { + throw new IllegalStateException("service is null"); } + enforceBluetoothPrivilegedPermission(service); + defaultValue = service.getPresetInfo(device, presetIndex); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); @@ -1411,11 +1503,16 @@ public class HapClientService extends ProfileService { public void getAllPresetInfo(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { + Objects.requireNonNull(device, "device cannot be null"); + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); List<BluetoothHapPresetInfo> defaultValue = new ArrayList<>(); HapClientService service = getService(source); - if (service != null) { - defaultValue = service.getAllPresetInfo(device); + if (service == null) { + throw new IllegalStateException("service is null"); } + enforceBluetoothPrivilegedPermission(service); + defaultValue = service.getAllPresetInfo(device); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); @@ -1426,11 +1523,16 @@ public class HapClientService extends ProfileService { public void getFeatures(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { + Objects.requireNonNull(device, "device cannot be null"); + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); int defaultValue = 0x00; HapClientService service = getService(source); - if (service != null) { - defaultValue = service.getFeatures(device); + if (service == null) { + throw new IllegalStateException("service is null"); } + enforceBluetoothPrivilegedPermission(service); + defaultValue = service.getFeatures(device); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); @@ -1440,31 +1542,60 @@ public class HapClientService extends ProfileService { @Override public void setPresetName(BluetoothDevice device, int presetIndex, String name, AttributionSource source) { + if (device == null) { + Log.w(TAG, "device cannot be null"); + return; + } + if (name == null) { + Log.w(TAG, "name cannot be null"); + return; + } + if (source == null) { + Log.w(TAG, "source cannot be null"); + return; + } + HapClientService service = getService(source); - if (service != null) { - service.setPresetName(device, presetIndex, name); + if (service == null) { + Log.w(TAG, "service is null"); + return; } + enforceBluetoothPrivilegedPermission(service); + service.setPresetName(device, presetIndex, name); } @Override public void setPresetNameForGroup(int groupId, int presetIndex, String name, AttributionSource source) { + if (name == null) { + Log.w(TAG, "name cannot be null"); + return; + } + if (source == null) { + Log.w(TAG, "source cannot be null"); + return; + } HapClientService service = getService(source); - if (service != null) { - service.setPresetNameForGroup(groupId, presetIndex, name); + if (service == null) { + Log.w(TAG, "service is null"); + return; } + enforceBluetoothPrivilegedPermission(service); + service.setPresetNameForGroup(groupId, presetIndex, name); } @Override public void registerCallback(IBluetoothHapClientCallback callback, AttributionSource source, SynchronousResultReceiver receiver) { - HapClientService service = getService(source); - if (service == null) { - throw new IllegalStateException("Service is unavailable"); - } - - enforceBluetoothPrivilegedPermission(service); try { + Objects.requireNonNull(callback, "callback cannot be null"); + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); + HapClientService service = getService(source); + if (service == null) { + throw new IllegalStateException("Service is unavailable"); + } + enforceBluetoothPrivilegedPermission(service); service.mCallbacks.register(callback); receiver.send(null); } catch (RuntimeException e) { @@ -1475,13 +1606,16 @@ public class HapClientService extends ProfileService { @Override public void unregisterCallback(IBluetoothHapClientCallback callback, AttributionSource source, SynchronousResultReceiver receiver) { - HapClientService service = getService(source); - if (service == null) { - throw new IllegalStateException("Service is unavailable"); - } - - enforceBluetoothPrivilegedPermission(service); try { + Objects.requireNonNull(callback, "callback cannot be null"); + Objects.requireNonNull(source, "source cannot be null"); + Objects.requireNonNull(receiver, "receiver cannot be null"); + + HapClientService service = getService(source); + if (service == null) { + throw new IllegalStateException("Service is unavailable"); + } + enforceBluetoothPrivilegedPermission(service); service.mCallbacks.unregister(callback); receiver.send(null); } catch (RuntimeException e) { diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index 027d64e333..21a3939f4b 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -500,7 +500,7 @@ package android.bluetooth { method @IntRange(from=0, to=16777215) public int getPresentationDelayMicros(); method public int getSourceAddressType(); method public int getSourceAdvertisingSid(); - method @Nullable public android.bluetooth.BluetoothDevice getSourceDevice(); + method @NonNull public android.bluetooth.BluetoothDevice getSourceDevice(); method @NonNull public java.util.List<android.bluetooth.BluetoothLeBroadcastSubgroup> getSubgroups(); method public boolean isEncrypted(); field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothLeBroadcastMetadata> CREATOR; @@ -519,7 +519,7 @@ package android.bluetooth { method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setPaSyncInterval(int); method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setPresentationDelayMicros(@IntRange(from=0, to=16777215) int); method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setSourceAdvertisingSid(int); - method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setSourceDevice(@Nullable android.bluetooth.BluetoothDevice, int); + method @NonNull public android.bluetooth.BluetoothLeBroadcastMetadata.Builder setSourceDevice(@NonNull android.bluetooth.BluetoothDevice, int); } public final class BluetoothLeBroadcastReceiveState implements android.os.Parcelable { @@ -551,7 +551,7 @@ package android.bluetooth { method public long getCodecId(); method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfigMetadata getCodecSpecificConfig(); method @NonNull public android.bluetooth.BluetoothLeAudioContentMetadata getContentMetadata(); - method public boolean isNoChannelPreference(); + method public boolean hasChannelPreference(); field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothLeBroadcastSubgroup> CREATOR; } @@ -564,7 +564,6 @@ package android.bluetooth { method @NonNull public android.bluetooth.BluetoothLeBroadcastSubgroup.Builder setCodecId(long); method @NonNull public android.bluetooth.BluetoothLeBroadcastSubgroup.Builder setCodecSpecificConfig(@NonNull android.bluetooth.BluetoothLeAudioCodecConfigMetadata); method @NonNull public android.bluetooth.BluetoothLeBroadcastSubgroup.Builder setContentMetadata(@NonNull android.bluetooth.BluetoothLeAudioContentMetadata); - method @NonNull public android.bluetooth.BluetoothLeBroadcastSubgroup.Builder setNoChannelPreference(boolean); } public final class BluetoothMap implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 146b627147..3561eeff90 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -119,6 +119,7 @@ public final class BluetoothClass implements Parcelable { private static final int BITMASK = 0xFFE000; public static final int LIMITED_DISCOVERABILITY = 0x002000; + /** Represent devices LE audio service */ public static final int LE_AUDIO = 0x004000; public static final int POSITIONING = 0x010000; public static final int NETWORKING = 0x020000; diff --git a/framework/java/android/bluetooth/BluetoothHapClient.java b/framework/java/android/bluetooth/BluetoothHapClient.java index 986c4adf14..dda803ef38 100644 --- a/framework/java/android/bluetooth/BluetoothHapClient.java +++ b/framework/java/android/bluetooth/BluetoothHapClient.java @@ -76,42 +76,66 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { + // needed for future release compatibility BluetoothStatusCodes.ERROR_UNKNOWN, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST, BluetoothStatusCodes.REASON_REMOTE_REQUEST, BluetoothStatusCodes.REASON_SYSTEM_POLICY, - BluetoothStatusCodes.ERROR_REMOTE_OPERATION_REJECTED, - BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED, - BluetoothStatusCodes.ERROR_HAP_PRESET_NAME_TOO_LONG, - BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX, - BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID, }) - @interface Status {} + @interface PresetSelectionReason {} /** * Invoked to inform about HA device's currently active preset. * * @param device remote device, * @param presetIndex the currently active preset index. - * @param statusCode reason for the selected preset change + * @param reason reason for the selected preset change * * @hide */ @SystemApi void onPresetSelected(@NonNull BluetoothDevice device, int presetIndex, - @Status int statusCode); + @PresetSelectionReason int reason); + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + // needed for future release compatibility + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST, + BluetoothStatusCodes.REASON_SYSTEM_POLICY, + BluetoothStatusCodes.ERROR_REMOTE_OPERATION_REJECTED, + BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED, + BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX, + }) + @interface PresetSelectionFailureReason {} /** * Invoked inform about the result of a failed preset change attempt. * * @param device remote device, - * @param statusCode failure reason. + * @param reason failure reason. * * @hide */ @SystemApi - void onPresetSelectionFailed(@NonNull BluetoothDevice device, @Status int statusCode); + void onPresetSelectionFailed(@NonNull BluetoothDevice device, + @PresetSelectionFailureReason int reason); + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + // needed for future release compatibility + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST, + BluetoothStatusCodes.REASON_SYSTEM_POLICY, + BluetoothStatusCodes.ERROR_REMOTE_OPERATION_REJECTED, + BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED, + BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX, + BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID, + }) + @interface GroupPresetSelectionFailureReason {} /** * Invoked to inform about the result of a failed preset change attempt. @@ -119,36 +143,79 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable * The implementation will try to restore the state for every device back to original * * @param hapGroupId valid HAP group ID, - * @param statusCode failure reason. + * @param reason failure reason. * * @hide */ @SystemApi - void onPresetSelectionForGroupFailed(int hapGroupId, @Status int statusCode); + void onPresetSelectionForGroupFailed(int hapGroupId, + @GroupPresetSelectionFailureReason int reason); + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + // needed for future release compatibility + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST, + BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST, + BluetoothStatusCodes.REASON_REMOTE_REQUEST, + BluetoothStatusCodes.REASON_SYSTEM_POLICY, + }) + @interface PresetInfoChangeReason {} /** * Invoked to inform about the preset list changes. * * @param device remote device, * @param presetInfoList a list of all preset information on the target device - * @param statusCode reason for the preset list change + * @param reason reason for the preset list change * * @hide */ @SystemApi void onPresetInfoChanged(@NonNull BluetoothDevice device, @NonNull List<BluetoothHapPresetInfo> presetInfoList, - @Status int statusCode); + @PresetInfoChangeReason int reason); + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + // needed for future release compatibility + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST, + BluetoothStatusCodes.REASON_SYSTEM_POLICY, + BluetoothStatusCodes.ERROR_REMOTE_OPERATION_REJECTED, + BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED, + BluetoothStatusCodes.ERROR_HAP_PRESET_NAME_TOO_LONG, + BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX, + }) + @interface PresetNameChangeFailureReason {} /** * Invoked to inform about the failed preset rename attempt. * * @param device remote device - * @param status Failure reason code. + * @param reason Failure reason code. * @hide */ @SystemApi - void onSetPresetNameFailed(@NonNull BluetoothDevice device, @Status int status); + void onSetPresetNameFailed(@NonNull BluetoothDevice device, + @PresetNameChangeFailureReason int reason); + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + // needed for future release compatibility + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST, + BluetoothStatusCodes.REASON_SYSTEM_POLICY, + BluetoothStatusCodes.ERROR_REMOTE_OPERATION_REJECTED, + BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED, + BluetoothStatusCodes.ERROR_HAP_PRESET_NAME_TOO_LONG, + BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX, + BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID, + }) + @interface GroupPresetNameChangeFailureReason {} /** * Invoked to inform about the failed preset rename attempt. @@ -156,11 +223,12 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable * The implementation will try to restore the state for every device back to original * * @param hapGroupId valid HAP group ID, - * @param status Failure reason code. + * @param reason Failure reason code. * @hide */ @SystemApi - void onSetPresetNameForGroupFailed(int hapGroupId, @Status int status); + void onSetPresetNameForGroupFailed(int hapGroupId, + @GroupPresetNameChangeFailureReason int reason); } @SuppressLint("AndroidFrameworkBluetoothPermission") diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastChannel.java b/framework/java/android/bluetooth/BluetoothLeBroadcastChannel.java index 676692ae4a..05f7f4a56d 100644 --- a/framework/java/android/bluetooth/BluetoothLeBroadcastChannel.java +++ b/framework/java/android/bluetooth/BluetoothLeBroadcastChannel.java @@ -21,6 +21,8 @@ import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import java.util.Objects; + /** * This class contains the Broadcast Isochronous Channel level information as defined in the BASE * structure of the Basic Audio Profile. @@ -168,11 +170,17 @@ public final class BluetoothLeBroadcastChannel implements Parcelable { /** * Set the Broadcast Isochronous Channel index of this Broadcast Channel. * - * @return Broadcast Isochronous Channel index + * @param channelIndex Broadcast Isochronous Channel index + * @throws IllegalArgumentException if the input argument is not valid + * @return this builder * @hide */ @SystemApi public @NonNull Builder setChannelIndex(int channelIndex) { + if (channelIndex == UNKNOWN_VALUE_PLACEHOLDER) { + throw new IllegalArgumentException("channelIndex cannot be " + + UNKNOWN_VALUE_PLACEHOLDER); + } mChannelIndex = channelIndex; return this; } @@ -181,12 +189,14 @@ public final class BluetoothLeBroadcastChannel implements Parcelable { * Set the codec specific configuration for this Broadcast Channel. * * @param codecMetadata codec specific configuration for this Broadcast Channel + * @throws NullPointerException if codecMetadata is null * @return this builder * @hide */ @SystemApi public @NonNull Builder setCodecMetadata( @NonNull BluetoothLeAudioCodecConfigMetadata codecMetadata) { + Objects.requireNonNull(codecMetadata, "codecMetadata cannot be null"); mCodecMetadata = codecMetadata; return this; } @@ -195,13 +205,16 @@ public final class BluetoothLeBroadcastChannel implements Parcelable { * Build {@link BluetoothLeBroadcastChannel}. * * @return constructed {@link BluetoothLeBroadcastChannel} + * @throws NullPointerException if {@link NonNull} items are null * @throws IllegalArgumentException if the object cannot be built * @hide */ @SystemApi public @NonNull BluetoothLeBroadcastChannel build() { - if (mCodecMetadata == null) { - throw new IllegalArgumentException("codec metadata cannot be null"); + Objects.requireNonNull(mCodecMetadata, "codec metadata cannot be null"); + if (mChannelIndex == UNKNOWN_VALUE_PLACEHOLDER) { + throw new IllegalArgumentException("mChannelIndex cannot be " + + UNKNOWN_VALUE_PLACEHOLDER); } return new BluetoothLeBroadcastChannel(mIsSelected, mChannelIndex, mCodecMetadata); } diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java b/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java index 2e748b9619..c63061a893 100644 --- a/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java +++ b/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java @@ -25,6 +25,7 @@ import android.os.Parcelable; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * This class represents a Broadcast Source group and the associated information that is needed @@ -97,7 +98,7 @@ public final class BluetoothLeBroadcastMetadata implements Parcelable { * @hide */ @SystemApi - public @Nullable BluetoothDevice getSourceDevice() { + public @NonNull BluetoothDevice getSourceDevice() { return mSourceDevice; } @@ -300,6 +301,7 @@ public final class BluetoothLeBroadcastMetadata implements Parcelable { /** * Create an empty builder. + * * @hide */ @SystemApi @@ -336,14 +338,26 @@ public final class BluetoothLeBroadcastMetadata implements Parcelable { * * @param sourceDevice source advertiser address * @param sourceAddressType source advertiser address type + * @throws IllegalArgumentException if sourceAddressType is invalid + * @throws NullPointerException if sourceDevice is null * @return this builder * @hide */ @SystemApi - public @NonNull Builder setSourceDevice(@Nullable BluetoothDevice sourceDevice, + public @NonNull Builder setSourceDevice(@NonNull BluetoothDevice sourceDevice, @BluetoothDevice.AddressType int sourceAddressType) { - mSourceDevice = sourceDevice; + if (sourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) { + throw new IllegalArgumentException( + "sourceAddressType cannot be ADDRESS_TYPE_UNKNOWN"); + } + if (sourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM + && sourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) { + throw new IllegalArgumentException("sourceAddressType " + sourceAddressType + + " is invalid"); + } + Objects.requireNonNull(sourceDevice, "sourceDevice cannot be null"); mSourceAddressType = sourceAddressType; + mSourceDevice = sourceDevice; return this; } @@ -433,12 +447,18 @@ public final class BluetoothLeBroadcastMetadata implements Parcelable { * * @param presentationDelayMicros presentation delay of this Broadcast Source in * microseconds + * @throws IllegalArgumentException if presentationDelayMicros does not fall in + * [0, 0xFFFFFF] * @return this builder * @hide */ @SystemApi public @NonNull Builder setPresentationDelayMicros( @IntRange(from = 0, to = 0xFFFFFF) int presentationDelayMicros) { + if (presentationDelayMicros < 0 || presentationDelayMicros >= 0xFFFFFF) { + throw new IllegalArgumentException("presentationDelayMicros " + + presentationDelayMicros + " does not fall in [0, 0xFFFFFF]"); + } mPresentationDelayMicros = presentationDelayMicros; return this; } @@ -447,11 +467,13 @@ public final class BluetoothLeBroadcastMetadata implements Parcelable { * Add a subgroup to this broadcast source. * * @param subgroup {@link BluetoothLeBroadcastSubgroup} that contains a subgroup's metadata + * @throws NullPointerException if subgroup is null * @return this builder * @hide */ @SystemApi public @NonNull Builder addSubgroup(@NonNull BluetoothLeBroadcastSubgroup subgroup) { + Objects.requireNonNull(subgroup, "subgroup cannot be null"); mSubgroups.add(subgroup); return this; } @@ -474,6 +496,7 @@ public final class BluetoothLeBroadcastMetadata implements Parcelable { * * @return {@link BluetoothLeBroadcastMetadata} * @throws IllegalArgumentException if the object cannot be built + * @throws NullPointerException if {@link NonNull} items are null * @hide */ @SystemApi @@ -481,9 +504,12 @@ public final class BluetoothLeBroadcastMetadata implements Parcelable { if (mSourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) { throw new IllegalArgumentException("SourceAddressTyp cannot be unknown"); } - if (mSourceDevice == null) { - throw new IllegalArgumentException("SourceDevice cannot be null"); + if (mSourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM + && mSourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) { + throw new IllegalArgumentException("sourceAddressType " + mSourceAddressType + + " is invalid"); } + Objects.requireNonNull(mSourceDevice, "mSourceDevice cannot be null"); if (mSubgroups.isEmpty()) { throw new IllegalArgumentException("Must contain at least one subgroup"); } diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastReceiveState.java b/framework/java/android/bluetooth/BluetoothLeBroadcastReceiveState.java index bab17ee797..70a2add80e 100644 --- a/framework/java/android/bluetooth/BluetoothLeBroadcastReceiveState.java +++ b/framework/java/android/bluetooth/BluetoothLeBroadcastReceiveState.java @@ -28,6 +28,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * The {@link BluetoothLeBroadcastReceiveState} is used by the BASS server to expose information @@ -187,13 +188,59 @@ public final class BluetoothLeBroadcastReceiveState implements Parcelable { /** * Constructor to create a read-only {@link BluetoothLeBroadcastReceiveState} instance. * + * @throws NullPointerException if sourceDevice, bisSyncState, or subgroupMetadata is null + * @throws IllegalArgumentException if sourceID is not [0, 0xFF] or if sourceAddressType + * is invalid or if bisSyncState.size() != numSubgroups or if subgroupMetadata.size() != + * numSubgroups or if paSyncState or bigEncryptionState is not recognized bye IntDef * @hide */ - public BluetoothLeBroadcastReceiveState(int sourceId, int sourceAddressType, - BluetoothDevice sourceDevice, int sourceAdvertisingSid, int broadcastId, - int paSyncState, int bigEncryptionState, byte[] badCode, int numSubgroups, - List<Long> bisSyncState, - List<BluetoothLeAudioContentMetadata> subgroupMetadata) { + public BluetoothLeBroadcastReceiveState(@IntRange(from = 0x00, to = 0xFF) int sourceId, + @BluetoothDevice.AddressType int sourceAddressType, + @NonNull BluetoothDevice sourceDevice, int sourceAdvertisingSid, int broadcastId, + @PaSyncState int paSyncState, @BigEncryptionState int bigEncryptionState, + byte[] badCode, @IntRange(from = 0x00) int numSubgroups, + @NonNull List<Long> bisSyncState, + @NonNull List<BluetoothLeAudioContentMetadata> subgroupMetadata) { + if (sourceId < 0x00 || sourceId > 0xFF) { + throw new IllegalArgumentException("sourceId " + sourceId + + " does not fall between 0x00 and 0xFF"); + } + Objects.requireNonNull(sourceDevice, "sourceDevice cannot be null"); + if (sourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) { + throw new IllegalArgumentException("sourceAddressType cannot be ADDRESS_TYPE_UNKNOWN"); + } + if (sourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM + && sourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) { + throw new IllegalArgumentException("sourceAddressType " + sourceAddressType + + " is invalid"); + } + Objects.requireNonNull(bisSyncState, "bisSyncState cannot be null"); + if (bisSyncState.size() != numSubgroups) { + throw new IllegalArgumentException("bisSyncState.size() " + bisSyncState.size() + + " must be equal to numSubgroups " + numSubgroups); + } + Objects.requireNonNull(subgroupMetadata, "subgroupMetadata cannot be null"); + if (subgroupMetadata.size() != numSubgroups) { + throw new IllegalArgumentException("subgroupMetadata.size() " + + subgroupMetadata.size() + " must be equal to numSubgroups " + numSubgroups); + } + if (paSyncState != PA_SYNC_STATE_IDLE && paSyncState != PA_SYNC_STATE_SYNCINFO_REQUEST + && paSyncState != PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE + && paSyncState != PA_SYNC_STATE_NO_PAST && paSyncState != PA_SYNC_STATE_INVALID) { + throw new IllegalArgumentException("unrecognized paSyncState " + paSyncState); + } + if (bigEncryptionState != BIG_ENCRYPTION_STATE_NOT_ENCRYPTED + && bigEncryptionState != BIG_ENCRYPTION_STATE_CODE_REQUIRED + && bigEncryptionState != BIG_ENCRYPTION_STATE_DECRYPTING + && bigEncryptionState != BIG_ENCRYPTION_STATE_BAD_CODE + && bigEncryptionState != BIG_ENCRYPTION_STATE_INVALID) { + throw new IllegalArgumentException("unrecognized bigEncryptionState " + + bigEncryptionState); + } + if (badCode != null && badCode.length != 16) { + throw new IllegalArgumentException("badCode must be 16 bytes long of null, but is " + + badCode.length + " + bytes long"); + } mSourceId = sourceId; mSourceAddressType = sourceAddressType; mSourceDevice = sourceDevice; diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastSubgroup.java b/framework/java/android/bluetooth/BluetoothLeBroadcastSubgroup.java index f8490c661d..e47c7d6a75 100644 --- a/framework/java/android/bluetooth/BluetoothLeBroadcastSubgroup.java +++ b/framework/java/android/bluetooth/BluetoothLeBroadcastSubgroup.java @@ -23,6 +23,7 @@ import android.os.Parcelable; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * This class contains the subgroup level information as defined in the BASE structure of Basic @@ -35,17 +36,15 @@ public final class BluetoothLeBroadcastSubgroup implements Parcelable { private final long mCodecId; private final BluetoothLeAudioCodecConfigMetadata mCodecSpecificConfig; private final BluetoothLeAudioContentMetadata mContentMetadata; - private final boolean mNoChannelPreference; private final List<BluetoothLeBroadcastChannel> mChannels; private BluetoothLeBroadcastSubgroup(long codecId, BluetoothLeAudioCodecConfigMetadata codecSpecificConfig, - BluetoothLeAudioContentMetadata contentMetadata, boolean noChannelPreference, + BluetoothLeAudioContentMetadata contentMetadata, List<BluetoothLeBroadcastChannel> channels) { mCodecId = codecId; mCodecSpecificConfig = codecSpecificConfig; mContentMetadata = contentMetadata; - mNoChannelPreference = noChannelPreference; mChannels = channels; } @@ -91,16 +90,17 @@ public final class BluetoothLeBroadcastSubgroup implements Parcelable { } /** - * Indicate if Broadcast Sink should have no Broadcast Channel (BIS) preference. + * Indicate if Broadcast Sink should have a preferred Broadcast Channel (BIS). * * Only used by Broadcast Assistant and Sink. Ignored by Broadcast Source * - * @return true if Broadcast Sink should have no Broadcast Channel (BIS) preference + * @return true if Broadcast Sink has at least one preferred Broadcast Channel (BIS) as + * indicated by {@link BluetoothLeBroadcastChannel#isSelected()} * @hide */ @SystemApi - public boolean isNoChannelPreference() { - return mNoChannelPreference; + public boolean hasChannelPreference() { + return mChannels.stream().anyMatch(BluetoothLeBroadcastChannel::isSelected); } /** @@ -136,7 +136,6 @@ public final class BluetoothLeBroadcastSubgroup implements Parcelable { out.writeLong(mCodecId); out.writeTypedObject(mCodecSpecificConfig, 0); out.writeTypedObject(mContentMetadata, 0); - out.writeBoolean(mNoChannelPreference); out.writeTypedList(mChannels); } @@ -152,7 +151,6 @@ public final class BluetoothLeBroadcastSubgroup implements Parcelable { builder.setCodecId(in.readLong()); builder.setCodecSpecificConfig(in.readTypedObject( BluetoothLeAudioCodecConfigMetadata.CREATOR)); - builder.setNoChannelPreference(in.readBoolean()); List<BluetoothLeBroadcastChannel> channels = new ArrayList<>(); in.readTypedList(channels, BluetoothLeBroadcastChannel.CREATOR); for (BluetoothLeBroadcastChannel channel : channels) { @@ -177,11 +175,11 @@ public final class BluetoothLeBroadcastSubgroup implements Parcelable { private long mCodecId = UNKNOWN_VALUE_PLACEHOLDER; private BluetoothLeAudioCodecConfigMetadata mCodecSpecificConfig = null; private BluetoothLeAudioContentMetadata mContentMetadata = null; - private boolean mNoChannelPreference = false; private List<BluetoothLeBroadcastChannel> mChannels = new ArrayList<>(); /** * Create an empty constructor. + * * @hide */ @SystemApi @@ -198,11 +196,9 @@ public final class BluetoothLeBroadcastSubgroup implements Parcelable { mCodecId = original.getCodecId(); mCodecSpecificConfig = original.getCodecSpecificConfig(); mContentMetadata = original.getContentMetadata(); - mNoChannelPreference = original.isNoChannelPreference(); mChannels = original.getChannels(); } - /** * Set the codec ID field as defined by the Basic Audio Profile. * @@ -227,12 +223,14 @@ public final class BluetoothLeBroadcastSubgroup implements Parcelable { * Set codec specific config metadata for this subgroup. * * @param codecSpecificConfig codec specific config metadata for this subgroup + * @throws {@link NullPointerException} if codecSpecificConfig is null * @return this builder * @hide */ @SystemApi public @NonNull Builder setCodecSpecificConfig( @NonNull BluetoothLeAudioCodecConfigMetadata codecSpecificConfig) { + Objects.requireNonNull(codecSpecificConfig, "codecSpecificConfig cannot be null"); mCodecSpecificConfig = codecSpecificConfig; return this; } @@ -241,33 +239,19 @@ public final class BluetoothLeBroadcastSubgroup implements Parcelable { * Set content metadata for this Broadcast Source subgroup. * * @param contentMetadata content metadata for this Broadcast Source subgroup + * @throws NullPointerException if contentMetadata is null * @return this builder * @hide */ @SystemApi public @NonNull Builder setContentMetadata( @NonNull BluetoothLeAudioContentMetadata contentMetadata) { + Objects.requireNonNull(contentMetadata, "contentMetadata cannot be null"); mContentMetadata = contentMetadata; return this; } /** - * Set if Broadcast Sink should have no Broadcast Channel (BIS) preference. - * - * Only used by Broadcast Assistant and Sink. Ignored by Broadcast Source - * - * @param isNoChannelPreference true if Broadcast Sink should have no Broadcast Channel - * (BIS) preference - * @return this builder - * @hide - */ - @SystemApi - public @NonNull Builder setNoChannelPreference(boolean isNoChannelPreference) { - mNoChannelPreference = isNoChannelPreference; - return this; - } - - /** * Add a Broadcast Channel to this Broadcast subgroup. * * Each Broadcast Channel represents a Broadcast Isochronous Stream (BIS) @@ -275,11 +259,13 @@ public final class BluetoothLeBroadcastSubgroup implements Parcelable { * A Broadcast subgroup should contain at least 1 Broadcast Channel * * @param channel a Broadcast Channel to be added to this Broadcast subgroup + * @throws NullPointerException if channel is null * @return this builder * @hide */ @SystemApi public @NonNull Builder addChannel(@NonNull BluetoothLeBroadcastChannel channel) { + Objects.requireNonNull(channel, "channel cannot be null"); mChannels.add(channel); return this; } @@ -301,22 +287,19 @@ public final class BluetoothLeBroadcastSubgroup implements Parcelable { * Build {@link BluetoothLeBroadcastSubgroup}. * * @return constructed {@link BluetoothLeBroadcastSubgroup} + * @throws NullPointerException if {@link NonNull} items are null * @throws IllegalArgumentException if the object cannot be built * @hide */ @SystemApi public @NonNull BluetoothLeBroadcastSubgroup build() { - if (mCodecSpecificConfig == null) { - throw new IllegalArgumentException("CodecSpecificConfig is null"); - } - if (mContentMetadata == null) { - throw new IllegalArgumentException("ContentMetadata is null"); - } + Objects.requireNonNull(mCodecSpecificConfig, "CodecSpecificConfig is null"); + Objects.requireNonNull(mContentMetadata, "ContentMetadata is null"); if (mChannels.isEmpty()) { throw new IllegalArgumentException("Must have at least one channel"); } return new BluetoothLeBroadcastSubgroup(mCodecId, mCodecSpecificConfig, - mContentMetadata, mNoChannelPreference, mChannels); + mContentMetadata, mChannels); } } } |