Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a02966ce authored by Zoey Chen's avatar Zoey Chen Committed by Automerger Merge Worker
Browse files

Merge "LE_Broadcast New LeBroadcast and Assistant at ProfileManager" into tm-dev am: 641107b2

parents 252fb5a1 641107b2
Loading
Loading
Loading
Loading
+157 −77
Original line number Diff line number Diff line
@@ -16,29 +16,47 @@

package com.android.settingslib.bluetooth;

import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;

import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
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.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.android.settingslib.R;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;


/**
 * LocalBluetoothLeBroadcast provides an interface between the Settings app
 * and the functionality of the local {@link BluetoothLeBroadcast}.
 * Use the {@link BluetoothLeBroadcast.Callback} to get the result callback.
 */
public class LocalBluetoothLeBroadcast implements BluetoothLeBroadcast.Callback {

public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
    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;
    static final String NAME = "LE_AUDIO_BROADCAST";
    // Order of this profile in device profiles list
    private static final int ORDINAL = 1;

    private BluetoothLeBroadcast mService;
    private BluetoothLeAudioContentMetadata mBluetoothLeAudioContentMetadata;
    private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
    private BluetoothLeAudioContentMetadata.Builder mBuilder;
@@ -48,29 +66,31 @@ public class LocalBluetoothLeBroadcast implements BluetoothLeBroadcast.Callback
    private final ServiceListener mServiceListener = new ServiceListener() {
        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) {
            if (profile != BluetoothProfile.LE_AUDIO_BROADCAST) {
                Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST");
                return;
            }
            if (DEBUG) {
                Log.d(TAG, "Bluetooth service connected");
            }
                mBluetoothLeBroadcast = (BluetoothLeBroadcast) proxy;
                mProfileManager.callServiceConnectedListeners();
            mService = (BluetoothLeBroadcast) proxy;
            mIsProfileReady = true;
        }
        }

        @Override
        public void onServiceDisconnected(int profile) {
            if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) {
            if (profile != BluetoothProfile.LE_AUDIO_BROADCAST) {
                Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST");
                return;
            }
            if (DEBUG) {
                Log.d(TAG, "Bluetooth service disconnected");
            }
            mIsProfileReady = false;
        }
        }
    };

    LocalBluetoothLeBroadcast(Context context, LocalBluetoothProfileManager profileManager) {
        mProfileManager = profileManager;
    LocalBluetoothLeBroadcast(Context context) {
        BluetoothAdapter.getDefaultAdapter().
                getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST);
        mBuilder = new BluetoothLeAudioContentMetadata.Builder();
@@ -78,39 +98,58 @@ public class LocalBluetoothLeBroadcast implements BluetoothLeBroadcast.Callback

    public void startBroadcast(byte[] broadcastCode, String language,
            String programInfo) {
        if (DEBUG) {
            if (mBluetoothLeBroadcast == null) {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcast is null when starting the broadcast.");
            return;
        }
        if (DEBUG) {
            Log.d(TAG, "startBroadcast: language = " + language + " ,programInfo = " + programInfo);
        }
        buildContentMetadata(language, programInfo);
        mBluetoothLeBroadcast.startBroadcast(mBluetoothLeAudioContentMetadata, broadcastCode);
        mService.startBroadcast(mBluetoothLeAudioContentMetadata, broadcastCode);
    }

    public void stopBroadcast() {
        if (DEBUG) {
            if (mBluetoothLeBroadcast == null) {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcast is null when stopping the broadcast.");
            return;
        }
        if (DEBUG) {
            Log.d(TAG, "stopBroadcast()");
        }
        mBluetoothLeBroadcast.stopBroadcast(mBroadcastId);
        mService.stopBroadcast(mBroadcastId);
    }

    public void updateBroadcast(String language, String programInfo) {
        if (DEBUG) {
            if (mBluetoothLeBroadcast == null) {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcast is null when updating the broadcast.");
            return;
        }
        if (DEBUG) {
            Log.d(TAG,
                    "updateBroadcast: language = " + language + " ,programInfo = " + programInfo);
        }
        mBluetoothLeAudioContentMetadata = mBuilder.setProgramInfo(programInfo).build();
        mBluetoothLeBroadcast.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata);
        mService.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata);
    }

    public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor,
            @NonNull BluetoothLeBroadcast.Callback callback) {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcast is null.");
            return;
        }

        mService.registerCallback(executor, callback);
    }

    public void unregisterServiceCallBack(@NonNull BluetoothLeBroadcast.Callback callback) {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcast is null.");
            return;
        }

        mService.unregisterCallback(callback);
    }

    private void buildContentMetadata(String language, String programInfo) {
@@ -122,71 +161,112 @@ public class LocalBluetoothLeBroadcast implements BluetoothLeBroadcast.Callback
        return new LocalBluetoothLeBroadcastMetadata(mBluetoothLeBroadcastMetadata);
    }

    public boolean isProfileReady() {
        return mIsProfileReady;
    }

    @Override
    public void onBroadcastStarted(int reason, int broadcastId) {
        if (DEBUG) {
            Log.d(TAG,
                    "onBroadcastStarted(), reason = " + reason + ", broadcastId = " + broadcastId);
    public int getProfileId() {
        return BluetoothProfile.LE_AUDIO_BROADCAST;
    }

    public boolean accessProfileEnabled() {
        return false;
    }

    @Override
    public void onBroadcastStartFailed(int reason) {
        if (DEBUG) {
            Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
    public boolean isAutoConnectable() {
        return true;
    }

    /**
     * Not supported since LE Audio Broadcasts do not establish a connection.
     */
    public int getConnectionStatus(BluetoothDevice device) {
        if (mService == null) {
            return BluetoothProfile.STATE_DISCONNECTED;
        }
        // LE Audio Broadcasts are not connection-oriented.
        return mService.getConnectionState(device);
    }

    @Override
    public void onBroadcastMetadataChanged(int broadcastId,
            @NonNull BluetoothLeBroadcastMetadata metadata) {
        if (DEBUG) {
            Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId);
    /**
     * Not supported since LE Audio Broadcasts do not establish a connection.
     */
    public List<BluetoothDevice> getConnectedDevices() {
        if (mService == null) {
            return new ArrayList<BluetoothDevice>(0);
        }
        mBluetoothLeBroadcastMetadata = metadata;
        // LE Audio Broadcasts are not connection-oriented.
        return mService.getConnectedDevices();
    }

    @Override
    public void onBroadcastStopped(int reason, int broadcastId) {
        if (DEBUG) {
            Log.d(TAG,
                    "onBroadcastStopped(), reason = " + reason + ", broadcastId = " + broadcastId);
    public @NonNull List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcast is null.");
            return Collections.emptyList();
        }

        return mService.getAllBroadcastMetadata();
    }

    @Override
    public void onBroadcastStopFailed(int reason) {
        if (DEBUG) {
            Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason);
    public boolean isEnabled(BluetoothDevice device) {
        if (mService == null) {
            return false;
        }

        return !mService.getAllBroadcastMetadata().isEmpty();
    }

    @Override
    public void onBroadcastUpdated(int reason, int broadcastId) {
        if (DEBUG) {
            Log.d(TAG,
                    "onBroadcastUpdated(), reason = " + reason + ", broadcastId = " + broadcastId);
    /**
     * Service does not provide method to get/set policy.
     */
    public int getConnectionPolicy(BluetoothDevice device) {
        return CONNECTION_POLICY_FORBIDDEN;
    }

    /**
     * Service does not provide "setEnabled" method. Please use {@link #startBroadcast},
     * {@link #stopBroadcast()} or {@link #updateBroadcast(String, String)}
     */
    public boolean setEnabled(BluetoothDevice device, boolean enabled) {
        return false;
    }

    @Override
    public void onBroadcastUpdateFailed(int reason, int broadcastId) {
        if (DEBUG) {
            Log.d(TAG,
                    "onBroadcastUpdateFailed(), reason = " + reason + ", broadcastId = "
                            + broadcastId);
    public String toString() {
        return NAME;
    }

    public int getOrdinal() {
        return ORDINAL;
    }

    @Override
    public void onPlaybackStarted(int reason, int broadcastId) {
    public int getNameResource(BluetoothDevice device) {
        return R.string.summary_empty;
    }

    @Override
    public void onPlaybackStopped(int reason, int broadcastId) {
    public int getSummaryResourceForDevice(BluetoothDevice device) {
        int state = getConnectionStatus(device);
        return BluetoothUtils.getConnectionStateSummary(state);
    }

    public boolean isProfileReady() {
        return mIsProfileReady;
    public int getDrawableResource(BluetoothClass btClass) {
        return 0;
    }

    @RequiresApi(Build.VERSION_CODES.S)
    protected void finalize() {
        if (DEBUG) {
            Log.d(TAG, "finalize()");
        }
        if (mService != null) {
            try {
                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
                        BluetoothProfile.LE_AUDIO_BROADCAST,
                        mService);
                mService = null;
            } catch (Throwable t) {
                Log.w(TAG, "Error cleaning up LeAudio proxy", t);
            }
        }
    }
}
+156 −73
Original line number Diff line number Diff line
@@ -16,32 +16,48 @@

package com.android.settingslib.bluetooth;

import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;

import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
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.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.android.settingslib.R;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;


/**
 * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app
 * and the functionality of the local {@link BluetoothLeBroadcastAssistant}.
 * Use the {@link BluetoothLeBroadcastAssistant.Callback} to get the result callback.
 */
public class LocalBluetoothLeBroadcastAssistant implements
        BluetoothLeBroadcastAssistant.Callback {

public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile {
    private static final String TAG = "LocalBluetoothLeBroadcastAssistant";
    private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
    private static final boolean DEBUG = BluetoothUtils.D;

    static final String NAME = "LE_AUDIO_BROADCAST_ASSISTANT";
    // Order of this profile in device profiles list
    private static final int ORDINAL = 1;

    private LocalBluetoothProfileManager mProfileManager;
    private BluetoothLeBroadcastAssistant mBluetoothLeBroadcastAssistant;
    private BluetoothLeBroadcastAssistant mService;
    private final CachedBluetoothDeviceManager mDeviceManager;
    private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
    private BluetoothLeBroadcastMetadata.Builder mBuilder;
    private boolean mIsProfileReady;
@@ -49,30 +65,55 @@ public class LocalBluetoothLeBroadcastAssistant implements
    private final ServiceListener mServiceListener = new ServiceListener() {
        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) {
            if (profile != BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) {
                Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST_ASSISTANT");
                return;
            }
            if (DEBUG) {
                Log.d(TAG, "Bluetooth service connected");
            }
                mBluetoothLeBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy;
            mService = (BluetoothLeBroadcastAssistant) proxy;
            // We just bound to the service, so refresh the UI for any connected LeAudio devices.
            List<BluetoothDevice> deviceList = mService.getConnectedDevices();
            while (!deviceList.isEmpty()) {
                BluetoothDevice nextDevice = deviceList.remove(0);
                CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
                // we may add a new device here, but generally this should not happen
                if (device == null) {
                    if (DEBUG) {
                        Log.d(TAG, "LocalBluetoothLeBroadcastAssistant found new device: "
                                + nextDevice);
                    }
                    device = mDeviceManager.addDevice(nextDevice);
                }
                device.onProfileStateChanged(LocalBluetoothLeBroadcastAssistant.this,
                        BluetoothProfile.STATE_CONNECTED);
                device.refresh();
            }

            mProfileManager.callServiceConnectedListeners();
            mIsProfileReady = true;
        }
        }

        @Override
        public void onServiceDisconnected(int profile) {
            if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) {
            if (profile != BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) {
                Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST_ASSISTANT");
                return;
            }
            if (DEBUG) {
                Log.d(TAG, "Bluetooth service disconnected");
            }
            mProfileManager.callServiceDisconnectedListeners();
            mIsProfileReady = false;
        }
        }
    };

    public LocalBluetoothLeBroadcastAssistant(Context context,
            CachedBluetoothDeviceManager deviceManager,
            LocalBluetoothProfileManager profileManager) {
        mProfileManager = profileManager;
        mDeviceManager = deviceManager;
        BluetoothAdapter.getDefaultAdapter().
                getProfileProxy(context, mServiceListener,
                        BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
@@ -90,7 +131,11 @@ public class LocalBluetoothLeBroadcastAssistant implements
     */
    public void addSource(BluetoothDevice sink, BluetoothLeBroadcastMetadata metadata,
            boolean isGroupOp) {
        mBluetoothLeBroadcastAssistant.addSource(sink, metadata, isGroupOp);
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcastAssistant is null");
            return;
        }
        mService.addSource(sink, metadata, isGroupOp);
    }

    /**
@@ -120,13 +165,9 @@ public class LocalBluetoothLeBroadcastAssistant implements
        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);
        addSource(sink, mBluetoothLeBroadcastMetadata, isGroupOp);
    }

    private void buildMetadata(int sourceAddressType, int presentationDelayMicros,
@@ -147,101 +188,143 @@ public class LocalBluetoothLeBroadcastAssistant implements
        if (DEBUG) {
            Log.d(TAG, "removeSource()");
        }
        if (mBluetoothLeBroadcastAssistant == null) {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcastAssistant is null");
            return;
        }
        mBluetoothLeBroadcastAssistant.removeSource(sink, sourceId);
        mService.removeSource(sink, sourceId);
    }

    public void startSearchingForSources(@NonNull List<android.bluetooth.le.ScanFilter> filters) {
        if (DEBUG) {
            Log.d(TAG, "startSearchingForSources()");
        }
        if (mBluetoothLeBroadcastAssistant == null) {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcastAssistant is null");
            return;
        }
        mBluetoothLeBroadcastAssistant.startSearchingForSources(filters);
        mService.startSearchingForSources(filters);
    }

    @Override
    public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, int reason) {
        if (DEBUG) {
            Log.d(TAG, "onSourceAdded(), reason = " + reason + " , sourceId = " + sourceId);
    public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor,
            @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcast is null.");
            return;
        }

        mService.registerCallback(executor, callback);
    }

    @Override
    public void onSourceAddFailed(@NonNull BluetoothDevice sink,
            @NonNull BluetoothLeBroadcastMetadata source, int reason) {
        if (DEBUG) {
            Log.d(TAG, "onSourceAddFailed(), reason = " + reason);
    public void unregisterServiceCallBack(
            @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcast is null.");
            return;
        }

        mService.unregisterCallback(callback);
    }

    @Override
    public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId, int reason) {
        if (DEBUG) {
            Log.d(TAG, "onSourceRemoved(), reason = " + reason + " , sourceId = " + sourceId);
    public boolean isProfileReady() {
        return mIsProfileReady;
    }

    public int getProfileId() {
        return BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT;
    }

    @Override
    public void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId, int reason) {
        if (DEBUG) {
            Log.d(TAG, "onSourceRemoveFailed(), reason = " + reason + " , sourceId = " + sourceId);
    public boolean accessProfileEnabled() {
        return false;
    }

    public boolean isAutoConnectable() {
        return true;
    }

    @Override
    public void onSearchStarted(int reason) {
        if (DEBUG) {
            Log.d(TAG, "onSearchStarted(), reason = " + reason);
    public int getConnectionStatus(BluetoothDevice device) {
        if (mService == null) {
            return BluetoothProfile.STATE_DISCONNECTED;
        }
        // LE Audio Broadcasts are not connection-oriented.
        return mService.getConnectionState(device);
    }

    @Override
    public void onSearchStartFailed(int reason) {
        if (DEBUG) {
            Log.d(TAG, "onSearchStartFailed(), reason = " + reason);
    public List<BluetoothDevice> getConnectedDevices() {
        if (mService == null) {
            return new ArrayList<BluetoothDevice>(0);
        }
        return mService.getDevicesMatchingConnectionStates(
                new int[]{BluetoothProfile.STATE_CONNECTED,
                        BluetoothProfile.STATE_CONNECTING,
                        BluetoothProfile.STATE_DISCONNECTING});
    }

    @Override
    public void onSearchStopped(int reason) {
        if (DEBUG) {
            Log.d(TAG, "onSearchStopped(), reason = " + reason);
    public boolean isEnabled(BluetoothDevice device) {
        if (mService == null || device == null) {
            return false;
        }
        return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
    }

    @Override
    public void onSearchStopFailed(int reason) {
        if (DEBUG) {
            Log.d(TAG, "onSearchStopFailed(), reason = " + reason);
    public int getConnectionPolicy(BluetoothDevice device) {
        if (mService == null || device == null) {
            return CONNECTION_POLICY_FORBIDDEN;
        }
        return mService.getConnectionPolicy(device);
    }

    @Override
    public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {
    public boolean setEnabled(BluetoothDevice device, boolean enabled) {
        boolean isEnabled = false;
        if (mService == null || device == null) {
            return false;
        }
        if (enabled) {
            if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
            }
        } else {
            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
        }

    @Override
    public void onSourceModified(@NonNull BluetoothDevice sink, int sourceId, int reason) {
        return isEnabled;
    }

    @Override
    public void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId, int reason) {
    public String toString() {
        return NAME;
    }

    @Override
    public void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId,
            @NonNull BluetoothLeBroadcastReceiveState state) {
    public int getOrdinal() {
        return ORDINAL;
    }

    public boolean isProfileReady() {
        return mIsProfileReady;
    public int getNameResource(BluetoothDevice device) {
        return R.string.summary_empty;
    }

    public int getSummaryResourceForDevice(BluetoothDevice device) {
        int state = getConnectionStatus(device);
        return BluetoothUtils.getConnectionStateSummary(state);
    }

    public int getDrawableResource(BluetoothClass btClass) {
        return 0;
    }

    @RequiresApi(Build.VERSION_CODES.S)
    protected void finalize() {
        if (DEBUG) {
            Log.d(TAG, "finalize()");
        }
        if (mService != null) {
            try {
                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
                        BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT,
                        mService);
                mService = null;
            } catch (Throwable t) {
                Log.w(TAG, "Error cleaning up LeAudio proxy", t);
            }
        }
    }
}
+30 −1

File changed.

Preview size limit exceeded, changes collapsed.