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

Commit 6d7970c9 authored by SongFerng Wang's avatar SongFerng Wang Committed by Android (Google) Code Review
Browse files

Merge "[LE_Broadcast] Add more APIs for the broadcast profile" into tm-dev

parents 18badb64 04a6e834
Loading
Loading
Loading
Loading
+258 −16
Original line number Diff line number Diff line
@@ -29,18 +29,23 @@ import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile.ServiceListener;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.android.settingslib.R;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executor;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;

/**
 * LocalBluetoothLeBroadcast provides an interface between the Settings app
@@ -55,13 +60,26 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
    static final String NAME = "LE_AUDIO_BROADCAST";
    // Order of this profile in device profiles list
    private static final int ORDINAL = 1;
    private static final String PREF_NAME = "LocalBluetoothLeBroadcast";
    private static final String PREF_PROGRAM_INFO = "PrefProgramInfo";
    private static final String PREF_BROADCAST_CODE = "PrefBroadcastCode";
    private static final String PREF_APP_SOURCE_NAME = "PrefAppSourceName";
    private static final String UNDERLINE = "_";
    private static final int DEFAULT_CODE_MIN = 1000;
    private static final int DEFAULT_CODE_MAX = 9999;

    private BluetoothLeBroadcast mService;
    private BluetoothLeAudioContentMetadata mBluetoothLeAudioContentMetadata;
    private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
    private BluetoothLeAudioContentMetadata.Builder mBuilder;
    private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
    private String mAppSourceName = "";
    private String mNewAppSourceName = "";
    private boolean mIsProfileReady;
    private String mProgramInfo;
    private byte[] mBroadcastCode;
    private SharedPreferences mSharedPref;
    private Executor mExecutor;

    private final ServiceListener mServiceListener = new ServiceListener() {
        @Override
@@ -71,41 +89,232 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
            }
            mService = (BluetoothLeBroadcast) proxy;
            mIsProfileReady = true;
            registerServiceCallBack(mExecutor, mBroadcastCallback);
        }

        @Override
        public void onServiceDisconnected(int profile) {
            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;
            unregisterServiceCallBack(mBroadcastCallback);
        }
    };

    private final BluetoothLeBroadcast.Callback mBroadcastCallback =
            new BluetoothLeBroadcast.Callback() {
                @Override
                public void onBroadcastStarted(int reason, int broadcastId) {
                    if (DEBUG) {
                        Log.d(TAG,
                                "onBroadcastStarted(), reason = " + reason + ", broadcastId = "
                                        + broadcastId);
                    }
                    setLatestBroadcastId(broadcastId);
                    setAppSourceName(mNewAppSourceName);
                }

                @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);
                    }
                    setLatestBluetoothLeBroadcastMetadata(metadata);
                }

                @Override
                public void onBroadcastStopped(int reason, int broadcastId) {
                    if (DEBUG) {
                        Log.d(TAG,
                                "onBroadcastStopped(), reason = " + reason + ", broadcastId = "
                                        + broadcastId);
                    }
                    resetCacheInfo();
                }

                @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);
                    }
                    setLatestBroadcastId(broadcastId);
                    setAppSourceName(mNewAppSourceName);
                }

                @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) {
                }
            };

    LocalBluetoothLeBroadcast(Context context) {
        mExecutor = Executors.newSingleThreadExecutor();
        BluetoothAdapter.getDefaultAdapter().
                getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST);
        mBuilder = new BluetoothLeAudioContentMetadata.Builder();
        mSharedPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        if (mSharedPref != null) {
            String programInfo = mSharedPref.getString(PREF_PROGRAM_INFO, "");
            if (programInfo.isEmpty()) {
                programInfo = getDefaultValueOfProgramInfo();
            }
            setProgramInfo(programInfo);

    public void startBroadcast(byte[] broadcastCode, String language,
            String programInfo) {
            String prefBroadcastCode = mSharedPref.getString(PREF_BROADCAST_CODE, "");
            byte[] broadcastCode;
            if (prefBroadcastCode.isEmpty()) {
                broadcastCode = getDefaultValueOfBroadcastCode();
            } else {
                broadcastCode = prefBroadcastCode.getBytes(StandardCharsets.UTF_8);
            }
            setBroadcastCode(broadcastCode);

            mAppSourceName = mSharedPref.getString(PREF_APP_SOURCE_NAME, "");
        }
    }

    /**
     * Start the LE Broadcast. If the system started the LE Broadcast, then the system calls the
     * corresponding callback {@link BluetoothLeBroadcast.Callback}.
     */
    public void startBroadcast(String appSourceName, String language) {
        mNewAppSourceName = appSourceName;
        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);
            Log.d(TAG,
                    "startBroadcast: language = " + language + " ,programInfo = " + mProgramInfo);
        }
        buildContentMetadata(language, mProgramInfo);
        mService.startBroadcast(mBluetoothLeAudioContentMetadata, mBroadcastCode);
    }

    public String getProgramInfo() {
        return mProgramInfo;
    }

    public void setProgramInfo(String programInfo) {
        if (programInfo == null || programInfo.isEmpty()) {
            Log.d(TAG, "setProgramInfo: programInfo is null or empty");
            return;
        }
        Log.d(TAG, "setProgramInfo: " + programInfo);
        mProgramInfo = programInfo;

        if (mSharedPref == null) {
            Log.d(TAG, "setProgramInfo: sharedPref is null");
            return;
        }
        SharedPreferences.Editor editor = mSharedPref.edit();
        editor.putString(PREF_PROGRAM_INFO, mProgramInfo);
        editor.apply();

    }

    public byte[] getBroadcastCode() {
        return mBroadcastCode;
    }

    public void setBroadcastCode(byte[] broadcastCode) {
        if (broadcastCode == null || broadcastCode.length == 0) {
            Log.d(TAG, "setBroadcastCode: broadcastCode is null or empty");
            return;
        }
        mBroadcastCode = broadcastCode;

        if (mSharedPref == null) {
            Log.d(TAG, "setBroadcastCode: sharedPref is null");
            return;
        }
        buildContentMetadata(language, programInfo);
        mService.startBroadcast(mBluetoothLeAudioContentMetadata, broadcastCode);
        SharedPreferences.Editor editor = mSharedPref.edit();
        editor.putString(PREF_BROADCAST_CODE, new String(broadcastCode, StandardCharsets.UTF_8));
        editor.apply();
    }

    public void stopBroadcast() {
    private void setLatestBroadcastId(int broadcastId) {
        mBroadcastId = broadcastId;
    }

    public int getLatestBroadcastId() {
        return mBroadcastId;
    }

    private void setAppSourceName(String appSourceName) {
        if (TextUtils.isEmpty(appSourceName)) {
            appSourceName = "";
        }
        mAppSourceName = appSourceName;
        mNewAppSourceName = "";
        if (mSharedPref == null) {
            Log.d(TAG, "setBroadcastCode: sharedPref is null");
            return;
        }
        SharedPreferences.Editor editor = mSharedPref.edit();
        editor.putString(PREF_APP_SOURCE_NAME, appSourceName);
        editor.apply();
    }

    public String getAppSourceName() {
        return mAppSourceName;
    }

    private void setLatestBluetoothLeBroadcastMetadata(
            BluetoothLeBroadcastMetadata bluetoothLeBroadcastMetadata) {
        if (bluetoothLeBroadcastMetadata != null
                && bluetoothLeBroadcastMetadata.getBroadcastId() == mBroadcastId) {
            mBluetoothLeBroadcastMetadata = bluetoothLeBroadcastMetadata;
        }
    }

    public BluetoothLeBroadcastMetadata getLatestBluetoothLeBroadcastMetadata() {
        return mBluetoothLeBroadcastMetadata;
    }

    /**
     * Stop the latest LE Broadcast. If the system stopped the LE Broadcast, then the system
     * calls the corresponding callback {@link BluetoothLeBroadcast.Callback}.
     */
    public void stopLatestBroadcast() {
        stopBroadcast(mBroadcastId);
    }

    /**
     * Stop the LE Broadcast. If the system stopped the LE Broadcast, then the system calls the
     * corresponding callback {@link BluetoothLeBroadcast.Callback}.
     */
    public void stopBroadcast(int broadcastId) {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcast is null when stopping the broadcast.");
            return;
@@ -113,19 +322,24 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
        if (DEBUG) {
            Log.d(TAG, "stopBroadcast()");
        }
        mService.stopBroadcast(mBroadcastId);
        mService.stopBroadcast(broadcastId);
    }

    public void updateBroadcast(String language, String programInfo) {
    /**
     * Update the LE Broadcast. If the system stopped the LE Broadcast, then the system calls the
     * corresponding callback {@link BluetoothLeBroadcast.Callback}.
     */
    public void updateBroadcast(String appSourceName, String language) {
        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);
                    "updateBroadcast: language = " + language + " ,programInfo = " + mProgramInfo);
        }
        mBluetoothLeAudioContentMetadata = mBuilder.setProgramInfo(programInfo).build();
        mNewAppSourceName = appSourceName;
        mBluetoothLeAudioContentMetadata = mBuilder.setProgramInfo(mProgramInfo).build();
        mService.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata);
    }

@@ -196,7 +410,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
        return mService.getConnectedDevices();
    }

    public @NonNull List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() {
    public @NonNull
    List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() {
        if (mService == null) {
            Log.d(TAG, "The BluetoothLeBroadcast is null.");
            return Collections.emptyList();
@@ -265,4 +480,31 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
            }
        }
    }

    private String getDefaultValueOfProgramInfo() {
        //set the default value;
        int postfix = ThreadLocalRandom.current().nextInt(DEFAULT_CODE_MIN, DEFAULT_CODE_MAX);
        return BluetoothAdapter.getDefaultAdapter().getName() + UNDERLINE + postfix;
    }

    private byte[] getDefaultValueOfBroadcastCode() {
        //set the default value;
        return generateRandomPassword().getBytes(StandardCharsets.UTF_8);
    }

    private void resetCacheInfo() {
        if (DEBUG) {
            Log.d(TAG, "resetCacheInfo:");
        }
        mNewAppSourceName = "";
        mAppSourceName = "";
        mBluetoothLeBroadcastMetadata = null;
        mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
    }

    private String generateRandomPassword() {
        String randomUUID = UUID.randomUUID().toString();
        //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
        return randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
    }
}