diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java new file mode 100644 index 0000000000000000000000000000000000000000..bb47c5e107114ebca3ab66a06337eacde010c469 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.bluetooth; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothLeAudioContentMetadata; +import android.bluetooth.BluetoothLeBroadcast; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProfile.ServiceListener; +import android.content.Context; +import android.util.Log; + +/** + * LocalBluetoothLeBroadcast provides an interface between the Settings app + * and the functionality of the local {@link BluetoothLeBroadcast}. + */ +public class LocalBluetoothLeBroadcast implements BluetoothLeBroadcast.Callback { + + private static final String TAG = "LocalBluetoothLeBroadcast"; + private static final int UNKNOWN_VALUE_PLACEHOLDER = -1; + private static final boolean DEBUG = BluetoothUtils.D; + + private BluetoothLeBroadcast mBluetoothLeBroadcast; + private LocalBluetoothProfileManager mProfileManager; + private BluetoothLeAudioContentMetadata mBluetoothLeAudioContentMetadata; + private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata; + private BluetoothLeAudioContentMetadata.Builder mBuilder; + private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER; + private boolean mIsProfileReady; + + private final ServiceListener mServiceListener = new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) { + if (DEBUG) { + Log.d(TAG,"Bluetooth service connected"); + } + mBluetoothLeBroadcast = (BluetoothLeBroadcast) proxy; + mProfileManager.callServiceConnectedListeners(); + mIsProfileReady = true; + } + } + + @Override + public void onServiceDisconnected(int profile) { + if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) { + if (DEBUG) { + Log.d(TAG,"Bluetooth service disconnected"); + } + mIsProfileReady = false; + } + } + }; + + LocalBluetoothLeBroadcast(Context context, LocalBluetoothProfileManager profileManager) { + mProfileManager = profileManager; + BluetoothAdapter.getDefaultAdapter(). + getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST); + mBuilder = new BluetoothLeAudioContentMetadata.Builder(); + } + + public void startBroadcast(byte[] broadcastCode, String language, + String programInfo) { + if (DEBUG) { + if (mBluetoothLeBroadcast == null) { + Log.d(TAG, "The BluetoothLeBroadcast is null when starting the broadcast."); + return; + } + Log.d(TAG, "startBroadcast: language = " + language + " ,programInfo = " + programInfo); + } + buildContentMetadata(language, programInfo); + mBluetoothLeBroadcast.startBroadcast(mBluetoothLeAudioContentMetadata, broadcastCode); + } + + public void stopBroadcast() { + if (DEBUG) { + if (mBluetoothLeBroadcast == null) { + Log.d(TAG, "The BluetoothLeBroadcast is null when stopping the broadcast."); + return; + } + Log.d(TAG, "stopBroadcast()"); + } + mBluetoothLeBroadcast.stopBroadcast(mBroadcastId); + } + + public void updateBroadcast(String language, String programInfo) { + if (DEBUG) { + if (mBluetoothLeBroadcast == null) { + Log.d(TAG, "The BluetoothLeBroadcast is null when updating the broadcast."); + return; + } + Log.d(TAG, + "updateBroadcast: language = " + language + " ,programInfo = " + programInfo); + } + mBluetoothLeAudioContentMetadata = mBuilder.setProgramInfo(programInfo).build(); + mBluetoothLeBroadcast.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata); + } + + private void buildContentMetadata(String language, String programInfo) { + mBluetoothLeAudioContentMetadata = mBuilder.setLanguage(language).setProgramInfo( + programInfo).build(); + } + + public LocalBluetoothLeBroadcastMetadata getLocalBluetoothLeBroadcastMetaData() { + return new LocalBluetoothLeBroadcastMetadata(mBluetoothLeBroadcastMetadata); + } + + @Override + public void onBroadcastStarted(int reason, int broadcastId) { + if (DEBUG) { + Log.d(TAG, + "onBroadcastStarted(), reason = " + reason + ", broadcastId = " + broadcastId); + } + } + + @Override + public void onBroadcastStartFailed(int reason) { + if (DEBUG) { + Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason); + } + } + + @Override + public void onBroadcastMetadataChanged(int broadcastId, + @NonNull BluetoothLeBroadcastMetadata metadata) { + if (DEBUG) { + Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId); + } + mBluetoothLeBroadcastMetadata = metadata; + } + + @Override + public void onBroadcastStopped(int reason, int broadcastId) { + if (DEBUG) { + Log.d(TAG, + "onBroadcastStopped(), reason = " + reason + ", broadcastId = " + broadcastId); + } + } + + @Override + public void onBroadcastStopFailed(int reason) { + if (DEBUG) { + Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason); + } + } + + @Override + public void onBroadcastUpdated(int reason, int broadcastId) { + if (DEBUG) { + Log.d(TAG, + "onBroadcastUpdated(), reason = " + reason + ", broadcastId = " + broadcastId); + } + } + + @Override + public void onBroadcastUpdateFailed(int reason, int broadcastId) { + if (DEBUG) { + Log.d(TAG, + "onBroadcastUpdateFailed(), reason = " + reason + ", broadcastId = " + + broadcastId); + } + } + + @Override + public void onPlaybackStarted(int reason, int broadcastId) { + } + + @Override + public void onPlaybackStopped(int reason, int broadcastId) { + } + + public boolean isProfileReady() { + return mIsProfileReady; + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java new file mode 100644 index 0000000000000000000000000000000000000000..d904265efda9f070504c71532857f9d240b48d1c --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.bluetooth; + +import android.annotation.NonNull; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeBroadcast; +import android.bluetooth.BluetoothLeBroadcastAssistant; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProfile.ServiceListener; +import android.content.Context; +import android.util.Log; + +import java.util.List; + +/** + * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app + * and the functionality of the local {@link BluetoothLeBroadcastAssistant}. + */ +public class LocalBluetoothLeBroadcastAssistant implements + BluetoothLeBroadcastAssistant.Callback { + + private static final String TAG = "LocalBluetoothLeBroadcastAssistant"; + private static final int UNKNOWN_VALUE_PLACEHOLDER = -1; + private static final boolean DEBUG = BluetoothUtils.D; + + private LocalBluetoothProfileManager mProfileManager; + private BluetoothLeBroadcastAssistant mBluetoothLeBroadcastAssistant; + private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata; + private BluetoothLeBroadcastMetadata.Builder mBuilder; + private boolean mIsProfileReady; + + private final ServiceListener mServiceListener = new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) { + if (DEBUG) { + Log.d(TAG,"Bluetooth service connected"); + } + mBluetoothLeBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy; + mProfileManager.callServiceConnectedListeners(); + mIsProfileReady = true; + } + } + + @Override + public void onServiceDisconnected(int profile) { + if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) { + if (DEBUG) { + Log.d(TAG,"Bluetooth service disconnected"); + } + mIsProfileReady = false; + } + } + }; + + LocalBluetoothLeBroadcastAssistant(Context context, + LocalBluetoothProfileManager profileManager) { + mProfileManager = profileManager; + BluetoothAdapter.getDefaultAdapter(). + getProfileProxy(context, mServiceListener, + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); + mBuilder = new BluetoothLeBroadcastMetadata.Builder(); + } + + public void addSource(@NonNull BluetoothDevice sink, int sourceAddressType, + int presentationDelayMicros, int sourceAdvertisingSid, int broadcastId, + int paSyncInterval, boolean isEncrypted, byte[] broadcastCode, + BluetoothDevice sourceDevice, boolean isGroupOp) { + if (DEBUG) { + Log.d(TAG, "addSource()"); + } + if (mBluetoothLeBroadcastAssistant == null) { + Log.d(TAG, "The BluetoothLeBroadcastAssistant is null"); + return ; + } + buildMetadata(sourceAddressType, presentationDelayMicros, sourceAdvertisingSid, broadcastId, + paSyncInterval, isEncrypted, broadcastCode, sourceDevice); + mBluetoothLeBroadcastAssistant.addSource(sink, mBluetoothLeBroadcastMetadata, isGroupOp); + } + + private void buildMetadata(int sourceAddressType, int presentationDelayMicros, + int sourceAdvertisingSid, int broadcastId, int paSyncInterval, boolean isEncrypted, + byte[] broadcastCode, BluetoothDevice sourceDevice) { + mBluetoothLeBroadcastMetadata = + mBuilder.setSourceDevice(sourceDevice, sourceAddressType) + .setSourceAdvertisingSid(sourceAdvertisingSid) + .setBroadcastId(broadcastId) + .setPaSyncInterval(paSyncInterval) + .setEncrypted(isEncrypted) + .setBroadcastCode(broadcastCode) + .setPresentationDelayMicros(presentationDelayMicros) + .build(); + } + + public void removeSource(@NonNull BluetoothDevice sink, int sourceId) { + if (DEBUG) { + Log.d(TAG, "removeSource()"); + } + if (mBluetoothLeBroadcastAssistant == null) { + Log.d(TAG, "The BluetoothLeBroadcastAssistant is null"); + return ; + } + mBluetoothLeBroadcastAssistant.removeSource(sink, sourceId); + } + + public void startSearchingForSources(@NonNull List filters) { + if (DEBUG) { + Log.d(TAG, "startSearchingForSources()"); + } + if (mBluetoothLeBroadcastAssistant == null) { + Log.d(TAG, "The BluetoothLeBroadcastAssistant is null"); + return ; + } + mBluetoothLeBroadcastAssistant.startSearchingForSources(filters); + } + + @Override + public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, int reason) { + if (DEBUG) { + Log.d(TAG, "onSourceAdded(), reason = " + reason + " , sourceId = " + sourceId); + } + + } + + @Override + public void onSourceAddFailed(@NonNull BluetoothDevice sink, + @NonNull BluetoothLeBroadcastMetadata source, int reason) { + if (DEBUG) { + Log.d(TAG, "onSourceAddFailed(), reason = " + reason); + } + } + + @Override + public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId, int reason) { + if (DEBUG) { + Log.d(TAG, "onSourceRemoved(), reason = " + reason + " , sourceId = " + sourceId); + } + } + + @Override + public void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId, int reason) { + if (DEBUG) { + Log.d(TAG, "onSourceRemoveFailed(), reason = " + reason + " , sourceId = " + sourceId); + } + } + + @Override + public void onSearchStarted(int reason) { + if (DEBUG) { + Log.d(TAG, "onSearchStarted(), reason = " + reason); + } + } + + @Override + public void onSearchStartFailed(int reason) { + if (DEBUG) { + Log.d(TAG, "onSearchStartFailed(), reason = " + reason); + } + } + + @Override + public void onSearchStopped(int reason) { + if (DEBUG) { + Log.d(TAG, "onSearchStopped(), reason = " + reason); + } + } + + @Override + public void onSearchStopFailed(int reason) { + if (DEBUG) { + Log.d(TAG, "onSearchStopFailed(), reason = " + reason); + } + } + + @Override + public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) { + } + + @Override + public void onSourceModified(@NonNull BluetoothDevice sink, int sourceId, int reason) { + } + + @Override + public void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId, int reason) { + } + + @Override + public void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId, + @NonNull BluetoothLeBroadcastReceiveState state) { + } + + public boolean isProfileReady() { + return mIsProfileReady; + } + +}