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

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17800406

Change-Id: I646e9c2c6d895f03a49bef63f8de7018761db62a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 0c652f6..bf9debf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -29,18 +29,23 @@
 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 @@
     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 @@
             }
             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);
+
+            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, "");
+        }
     }
 
-    public void startBroadcast(byte[] broadcastCode, String language,
-            String programInfo) {
+    /**
+     * 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, programInfo);
-        mService.startBroadcast(mBluetoothLeAudioContentMetadata, broadcastCode);
+        buildContentMetadata(language, mProgramInfo);
+        mService.startBroadcast(mBluetoothLeAudioContentMetadata, mBroadcastCode);
     }
 
-    public void stopBroadcast() {
+    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;
+        }
+        SharedPreferences.Editor editor = mSharedPref.edit();
+        editor.putString(PREF_BROADCAST_CODE, new String(broadcastCode, StandardCharsets.UTF_8));
+        editor.apply();
+    }
+
+    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 @@
         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 @@
         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 @@
             }
         }
     }
+
+    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);
+    }
 }