diff options
8 files changed, 175 insertions, 18 deletions
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java index 30144c8191..8a095bac75 100755 --- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java +++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java @@ -752,7 +752,14 @@ public class BassClientService extends ProfileService { BluetoothStatusCodes.ERROR_BAD_PARAMETERS); return; } + if (getConnectionState(sink) != BluetoothProfile.STATE_CONNECTED) { + log("addSource: device is not connected"); + mCallbacks.notifySourceAddFailed(sink, sourceMetadata, + BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR); + return; + } if (!hasRoomForBroadcastSourceAddition(sink)) { + log("addSource: device has no room"); mCallbacks.notifySourceAddFailed(sink, sourceMetadata, BluetoothStatusCodes.ERROR_REMOTE_NOT_ENOUGH_RESOURCES); return; @@ -788,6 +795,12 @@ public class BassClientService extends ProfileService { BluetoothStatusCodes.ERROR_BAD_PARAMETERS); return; } + if (getConnectionState(sink) != BluetoothProfile.STATE_CONNECTED) { + log("modifySource: device is not connected"); + mCallbacks.notifySourceModifyFailed(sink, sourceId, + BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR); + return; + } Message message = stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE); message.arg1 = sourceId; message.obj = updatedMetadata; @@ -807,11 +820,17 @@ public class BassClientService extends ProfileService { BassClientStateMachine stateMachine = getOrCreateStateMachine(sink); if (sourceId == BassConstants.INVALID_SOURCE_ID || stateMachine == null) { - log("Error bad parameters: sourceId = " + sourceId); + log("removeSource: Error bad parameters: sourceId = " + sourceId); mCallbacks.notifySourceRemoveFailed(sink, sourceId, BluetoothStatusCodes.ERROR_BAD_PARAMETERS); return; } + if (getConnectionState(sink) != BluetoothProfile.STATE_CONNECTED) { + log("removeSource: device is not connected"); + mCallbacks.notifySourceRemoveFailed(sink, sourceId, + BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR); + return; + } Message message = stateMachine.obtainMessage(BassClientStateMachine.REMOVE_BCAST_SOURCE); message.arg1 = sourceId; stateMachine.sendMessage(message); diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java index 4dd90fec32..b1b9237fb2 100755 --- a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java +++ b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java @@ -73,6 +73,7 @@ import android.bluetooth.le.PeriodicAdvertisingManager; import android.bluetooth.le.PeriodicAdvertisingReport; import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; +import android.os.Binder; import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; @@ -186,12 +187,14 @@ public class BassClientStateMachine extends StateMachine { if (mBluetoothAdapter != null) { mPeriodicAdvManager = mBluetoothAdapter.getPeriodicAdvertisingManager(); } + long token = Binder.clearCallingIdentity(); mIsAllowedList = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, "persist.vendor.service.bt.wl", true); mDefNoPAS = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, "persist.vendor.service.bt.defNoPAS", false); mForceSB = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, "persist.vendor.service.bt.forceSB", false); + Binder.restoreCallingIdentity(token); } static BassClientStateMachine make(BluetoothDevice device, diff --git a/framework/java/android/bluetooth/BluetoothLeAudioContentMetadata.java b/framework/java/android/bluetooth/BluetoothLeAudioContentMetadata.java index 4f02e89312..17d99ae72d 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudioContentMetadata.java +++ b/framework/java/android/bluetooth/BluetoothLeAudioContentMetadata.java @@ -25,7 +25,9 @@ import android.os.Parcelable; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Objects; /** * A class representing the media metadata information defined in the Basic Audio Profile. @@ -50,6 +52,22 @@ public final class BluetoothLeAudioContentMetadata implements Parcelable { mRawMetadata = rawMetadata; } + @Override + public boolean equals(@Nullable Object o) { + if (!(o instanceof BluetoothLeAudioContentMetadata)) { + return false; + } + final BluetoothLeAudioContentMetadata other = (BluetoothLeAudioContentMetadata) o; + return Objects.equals(mProgramInfo, other.getProgramInfo()) + && Objects.equals(mLanguage, other.getLanguage()) + && Arrays.equals(mRawMetadata, other.getRawMetadata()); + } + + @Override + public int hashCode() { + return Objects.hash(mProgramInfo, mLanguage, mRawMetadata); + } + /** * Get the title and/or summary of Audio Stream content in UTF-8 format. * diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java index 70955252ea..05716eb6b1 100755 --- a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java +++ b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java @@ -363,6 +363,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au @Override public @BluetoothProfile.BtProfileState int getConnectionState(@NonNull BluetoothDevice sink) { log("getConnectionState(" + sink + ")"); + Objects.requireNonNull(sink, "sink cannot be null"); final IBluetoothLeBroadcastAssistant service = getService(); final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; if (service == null) { @@ -392,6 +393,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates( @NonNull int[] states) { log("getDevicesMatchingConnectionStates()"); + Objects.requireNonNull(states, "states cannot be null"); final IBluetoothLeBroadcastAssistant service = getService(); final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); if (service == null) { @@ -445,6 +447,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au * @param device Paired bluetooth device * @param connectionPolicy is the connection policy to set to for this profile * @return true if connectionPolicy is set, false on error + * @throws NullPointerException if <var>device</var> is null * @hide */ @SystemApi @@ -456,6 +459,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { log("setConnectionPolicy()"); + Objects.requireNonNull(device, "device cannot be null"); final IBluetoothLeBroadcastAssistant service = getService(); final boolean defaultValue = false; if (service == null) { @@ -482,6 +486,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au * * @param device Bluetooth device * @return connection policy of the device + * @throws NullPointerException if <var>device</var> is null * @hide */ @SystemApi @@ -492,6 +497,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { log("getConnectionPolicy()"); + Objects.requireNonNull(device, "device cannot be null"); final IBluetoothLeBroadcastAssistant service = getService(); final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; if (service == null) { @@ -518,8 +524,8 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au * * @param executor an {@link Executor} to execute given callback * @param callback user implementation of the {@link Callback} - * @throws NullPointerException if a null executor, or callback is given, or - * IllegalArgumentException if the same <var>callback<var> is already registered. + * @throws NullPointerException if a null executor, or callback is given + * @throws IllegalArgumentException if the same <var>callback<var> is already registered * @hide */ @SystemApi @@ -554,8 +560,8 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au * <p>Callbacks are automatically unregistered when application process goes away. * * @param callback user implementation of the {@link Callback} - * @throws NullPointerException when callback is null or IllegalArgumentException when no - * callback is registered + * @throws NullPointerException when callback is null + * @throws IllegalArgumentException when the <var>callback</var> was not registered before * @hide */ @SystemApi @@ -572,9 +578,10 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); } else if (mBluetoothAdapter.isEnabled()) { - if (mCallback != null) { - mCallback.unregister(callback); + if (mCallback == null) { + throw new IllegalArgumentException("no callback was ever registered"); } + mCallback.unregister(callback); } } @@ -605,7 +612,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au * * @param filters {@link ScanFilter}s for finding exact Broadcast Source, if no filter is * needed, please provide an empty list instead - * @throws IllegalArgumentException when <var>filters</var> argument is null + * @throws NullPointerException when <var>filters</var> argument is null * @throws IllegalStateException when no callback is registered * @hide */ @@ -618,8 +625,12 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au }) public void startSearchingForSources(@NonNull List<ScanFilter> filters) { log("searchForBroadcastSources"); - if (filters == null) { - throw new IllegalArgumentException("filters can be empty, but not null"); + Objects.requireNonNull(filters, "filters can be empty, but not null"); + if (mCallback == null) { + throw new IllegalStateException("No callback was ever registered"); + } + if (!mCallback.isAtLeastOneCallbackRegistered()) { + throw new IllegalStateException("All callbacks are unregistered"); } final IBluetoothLeBroadcastAssistant service = getService(); if (service == null) { @@ -652,6 +663,12 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au }) public void stopSearchingForSources() { log("stopSearchingForSources:"); + if (mCallback == null) { + throw new IllegalStateException("No callback was ever registered"); + } + if (!mCallback.isAtLeastOneCallbackRegistered()) { + throw new IllegalStateException("All callbacks are unregistered"); + } final IBluetoothLeBroadcastAssistant service = getService(); if (service == null) { Log.w(TAG, "Proxy not attached to service"); @@ -751,7 +768,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au * @param isGroupOp {@code true} if Application wants to perform this operation for all * coordinated set members throughout this session. Otherwise, caller * would have to add, modify, and remove individual set members. - * @throws IllegalArgumentException if <var>sink</var> or <var>source</var> are null + * @throws NullPointerException if <var>sink</var> or <var>source</var> is null * @throws IllegalStateException if callback was not registered * @hide */ @@ -764,6 +781,14 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au public void addSource(@NonNull BluetoothDevice sink, @NonNull BluetoothLeBroadcastMetadata sourceMetadata, boolean isGroupOp) { log("addBroadcastSource: " + sourceMetadata + " on " + sink); + Objects.requireNonNull(sink, "sink cannot be null"); + Objects.requireNonNull(sourceMetadata, "sourceMetadata cannot be null"); + if (mCallback == null) { + throw new IllegalStateException("No callback was ever registered"); + } + if (!mCallback.isAtLeastOneCallbackRegistered()) { + throw new IllegalStateException("All callbacks are unregistered"); + } final IBluetoothLeBroadcastAssistant service = getService(); if (service == null) { Log.w(TAG, "Proxy not attached to service"); @@ -818,6 +843,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au * {@link Callback#onSourceAdded(BluetoothDevice, int, int)} * @param updatedMetadata updated Broadcast Source metadata to be updated on the Broadcast Sink * @throws IllegalStateException if callback was not registered + * @throws NullPointerException if <var>sink</var> or <var>updatedMetadata</var> is null * @hide */ @SystemApi @@ -829,6 +855,14 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au public void modifySource(@NonNull BluetoothDevice sink, int sourceId, @NonNull BluetoothLeBroadcastMetadata updatedMetadata) { log("updateBroadcastSource: " + updatedMetadata + " on " + sink); + Objects.requireNonNull(sink, "sink cannot be null"); + Objects.requireNonNull(updatedMetadata, "updatedMetadata cannot be null"); + if (mCallback == null) { + throw new IllegalStateException("No callback was ever registered"); + } + if (!mCallback.isAtLeastOneCallbackRegistered()) { + throw new IllegalStateException("All callbacks are unregistered"); + } final IBluetoothLeBroadcastAssistant service = getService(); if (service == null) { Log.w(TAG, "Proxy not attached to service"); @@ -862,7 +896,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au * @param sink Broadcast Sink from which a Broadcast Source should be removed * @param sourceId source ID as delivered in * {@link Callback#onSourceAdded(BluetoothDevice, int, int)} - * @throws IllegalArgumentException when the <var>sink</var> is null + * @throws NullPointerException when the <var>sink</var> is null * @throws IllegalStateException if callback was not registered * @hide */ @@ -874,6 +908,13 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au }) public void removeSource(@NonNull BluetoothDevice sink, int sourceId) { log("removeBroadcastSource: " + sourceId + " from " + sink); + Objects.requireNonNull(sink, "sink cannot be null"); + if (mCallback == null) { + throw new IllegalStateException("No callback was ever registered"); + } + if (!mCallback.isAtLeastOneCallbackRegistered()) { + throw new IllegalStateException("All callbacks are unregistered"); + } final IBluetoothLeBroadcastAssistant service = getService(); if (service == null) { Log.w(TAG, "Proxy not attached to service"); @@ -894,7 +935,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au * @param sink Broadcast Sink from which to get all Broadcast Sources * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState} * stored in the Broadcast Sink - * @throws IllegalArgumentException when <var>sink</var> is null + * @throws NullPointerException when <var>sink</var> is null * @hide */ @SystemApi @@ -906,6 +947,7 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au public @NonNull List<BluetoothLeBroadcastReceiveState> getAllSources( @NonNull BluetoothDevice sink) { log("getAllSources()"); + Objects.requireNonNull(sink, "sink cannot be null"); final IBluetoothLeBroadcastAssistant service = getService(); final List<BluetoothLeBroadcastReceiveState> defaultValue = new ArrayList<BluetoothLeBroadcastReceiveState>(); @@ -927,11 +969,12 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au * * @param sink Broadcast Sink device * @return maximum number of sources that can be added to this Broadcast Sink - * @throws IllegalArgumentException when <var>sink</var> is null + * @throws NullPointerException when <var>sink</var> is null * @hide */ @SystemApi public int getMaximumSourceCapacity(@NonNull BluetoothDevice sink) { + Objects.requireNonNull(sink, "sink cannot be null"); final IBluetoothLeBroadcastAssistant service = getService(); final int defaultValue = 0; if (service == null) { diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java index 96e04a9424..aa7ca8ea52 100755 --- a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java +++ b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java @@ -44,12 +44,13 @@ public class BluetoothLeBroadcastAssistantCallback * @hide * @param executor an {@link Executor} to execute given callback * @param callback user implementation of the {@link BluetoothLeBroadcastAssistant#Callback} + * @throws IllegalArgumentException if the same <var>callback<var> is already registered. */ public void register(@NonNull Executor executor, @NonNull BluetoothLeBroadcastAssistant.Callback callback) { synchronized (this) { if (mCallbackMap.containsKey(callback)) { - return; + throw new IllegalArgumentException("callback is already registered"); } mCallbackMap.put(callback, executor); @@ -58,7 +59,7 @@ public class BluetoothLeBroadcastAssistantCallback mAdapter.registerCallback(this); mIsRegistered = true; } catch (RemoteException e) { - Log.w(TAG, "Failed to register broaddcast assistant callback"); + Log.w(TAG, "Failed to register broadcast assistant callback"); Log.e(TAG, Log.getStackTraceString(new Throwable())); } } @@ -68,11 +69,12 @@ public class BluetoothLeBroadcastAssistantCallback /** * @hide * @param callback user implementation of the {@link BluetoothLeBroadcastAssistant#Callback} + * @throws IllegalArgumentException if <var>callback</var> was not registered before */ public void unregister(@NonNull BluetoothLeBroadcastAssistant.Callback callback) { synchronized (this) { if (!mCallbackMap.containsKey(callback)) { - return; + throw new IllegalArgumentException("callback was not registered before"); } mCallbackMap.remove(callback); if (mCallbackMap.isEmpty() && mIsRegistered) { @@ -80,13 +82,25 @@ public class BluetoothLeBroadcastAssistantCallback mAdapter.unregisterCallback(this); mIsRegistered = false; } catch (RemoteException e) { - Log.w(TAG, "Failed to unregister broaddcast assistant with service"); + Log.w(TAG, "Failed to unregister callback with service"); Log.e(TAG, Log.getStackTraceString(new Throwable())); } } } } + /** + * Check if at least one callback is registered from this App + * + * @return true if at least one callback is registered + * @hide + */ + public boolean isAtLeastOneCallbackRegistered() { + synchronized (this) { + return !mCallbackMap.isEmpty(); + } + } + @Override public void onSearchStarted(int reason) { synchronized (this) { diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastChannel.java b/framework/java/android/bluetooth/BluetoothLeBroadcastChannel.java index 05f7f4a56d..3040af7eb3 100644 --- a/framework/java/android/bluetooth/BluetoothLeBroadcastChannel.java +++ b/framework/java/android/bluetooth/BluetoothLeBroadcastChannel.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -44,6 +45,22 @@ public final class BluetoothLeBroadcastChannel implements Parcelable { mCodecMetadata = codecMetadata; } + @Override + public boolean equals(@Nullable Object o) { + if (!(o instanceof BluetoothLeBroadcastChannel)) { + return false; + } + final BluetoothLeBroadcastChannel other = (BluetoothLeBroadcastChannel) o; + return mIsSelected == other.isSelected() + && mChannelIndex == other.getChannelIndex() + && mCodecMetadata.equals(other.getCodecMetadata()); + } + + @Override + public int hashCode() { + return Objects.hash(mIsSelected, mChannelIndex, mCodecMetadata); + } + /** * Return true if the channel is selected by Broadcast Assistant for the Broadcast Sink. * diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java b/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java index c63061a893..b79f31e0bc 100644 --- a/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java +++ b/framework/java/android/bluetooth/BluetoothLeBroadcastMetadata.java @@ -24,6 +24,7 @@ import android.os.Parcel; import android.os.Parcelable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -76,6 +77,30 @@ public final class BluetoothLeBroadcastMetadata implements Parcelable { mSubgroups = subgroups; } + @Override + public boolean equals(@Nullable Object o) { + if (!(o instanceof BluetoothLeBroadcastMetadata)) { + return false; + } + final BluetoothLeBroadcastMetadata other = (BluetoothLeBroadcastMetadata) o; + return mSourceAddressType == other.getSourceAddressType() + && mSourceDevice.equals(other.getSourceDevice()) + && mSourceAdvertisingSid == other.getSourceAdvertisingSid() + && mBroadcastId == other.getBroadcastId() + && mPaSyncInterval == other.getPaSyncInterval() + && mIsEncrypted == other.isEncrypted() + && Arrays.equals(mBroadcastCode, other.getBroadcastCode()) + && mPresentationDelayMicros == other.getPresentationDelayMicros() + && mSubgroups.equals(other.getSubgroups()); + } + + @Override + public int hashCode() { + return Objects.hash(mSourceAddressType, mSourceDevice, mSourceAdvertisingSid, + mBroadcastId, mPaSyncInterval, mIsEncrypted, Arrays.hashCode(mBroadcastCode), + mPresentationDelayMicros, mSubgroups); + } + /** * Get the address type of the Broadcast Source. * diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastSubgroup.java b/framework/java/android/bluetooth/BluetoothLeBroadcastSubgroup.java index b2abe06551..38a747b5d5 100644 --- a/framework/java/android/bluetooth/BluetoothLeBroadcastSubgroup.java +++ b/framework/java/android/bluetooth/BluetoothLeBroadcastSubgroup.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -48,6 +49,23 @@ public final class BluetoothLeBroadcastSubgroup implements Parcelable { mChannels = channels; } + @Override + public boolean equals(@Nullable Object o) { + if (!(o instanceof BluetoothLeBroadcastSubgroup)) { + return false; + } + final BluetoothLeBroadcastSubgroup other = (BluetoothLeBroadcastSubgroup) o; + return mCodecId == other.getCodecId() + && mCodecSpecificConfig.equals(other.getCodecSpecificConfig()) + && mContentMetadata.equals(other.getContentMetadata()) + && mChannels.equals(other.getChannels()); + } + + @Override + public int hashCode() { + return Objects.hash(mCodecId, mCodecSpecificConfig, mContentMetadata, mChannels); + } + /** * Get the codec ID field as defined by the Basic Audio Profile. * |