Add setDeviceVolume to VolumeControlProfile

Test: atest VolumeControlProfile
Bug: 305620450
Change-Id: I5607b1117212f5bedf2741a95a0e7c866bffbf5d
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
index f83e37b..3774b88 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
@@ -37,9 +37,7 @@
 import java.util.List;
 import java.util.concurrent.Executor;
 
-/**
- * VolumeControlProfile handles Bluetooth Volume Control Controller role
- */
+/** VolumeControlProfile handles Bluetooth Volume Control Controller role */
 public class VolumeControlProfile implements LocalBluetoothProfile {
     private static final String TAG = "VolumeControlProfile";
     private static boolean DEBUG = true;
@@ -77,8 +75,8 @@
                     }
                     device = mDeviceManager.addDevice(nextDevice);
                 }
-                device.onProfileStateChanged(VolumeControlProfile.this,
-                        BluetoothProfile.STATE_CONNECTED);
+                device.onProfileStateChanged(
+                        VolumeControlProfile.this, BluetoothProfile.STATE_CONNECTED);
                 device.refresh();
             }
 
@@ -95,32 +93,36 @@
         }
     }
 
-    VolumeControlProfile(Context context, CachedBluetoothDeviceManager deviceManager,
+    VolumeControlProfile(
+            Context context,
+            CachedBluetoothDeviceManager deviceManager,
             LocalBluetoothProfileManager profileManager) {
         mContext = context;
         mDeviceManager = deviceManager;
         mProfileManager = profileManager;
 
-        BluetoothAdapter.getDefaultAdapter().getProfileProxy(context,
-                new VolumeControlProfile.VolumeControlProfileServiceListener(),
-                BluetoothProfile.VOLUME_CONTROL);
+        BluetoothAdapter.getDefaultAdapter()
+                .getProfileProxy(
+                        context,
+                        new VolumeControlProfile.VolumeControlProfileServiceListener(),
+                        BluetoothProfile.VOLUME_CONTROL);
     }
 
-
     /**
-     * Registers a {@link BluetoothVolumeControl.Callback} that will be invoked during the
-     * operation of this profile.
+     * Registers a {@link BluetoothVolumeControl.Callback} that will be invoked during the operation
+     * of this profile.
      *
-     * Repeated registration of the same <var>callback</var> object will have no effect after
-     * the first call to this method, even when the <var>executor</var> is different. API caller
-     * would have to call {@link #unregisterCallback(BluetoothVolumeControl.Callback)} with
-     * the same callback object before registering it again.
+     * <p>Repeated registration of the same <var>callback</var> object will have no effect after the
+     * first call to this method, even when the <var>executor</var> is different. API caller would
+     * have to call {@link #unregisterCallback(BluetoothVolumeControl.Callback)} with the same
+     * callback object before registering it again.
      *
      * @param executor an {@link Executor} to execute given callback
      * @param callback user implementation of the {@link BluetoothVolumeControl.Callback}
      * @throws IllegalArgumentException if a null executor or callback is given
      */
-    public void registerCallback(@NonNull @CallbackExecutor Executor executor,
+    public void registerCallback(
+            @NonNull @CallbackExecutor Executor executor,
             @NonNull BluetoothVolumeControl.Callback callback) {
         if (mService == null) {
             Log.w(TAG, "Proxy not attached to service. Cannot register callback.");
@@ -131,8 +133,9 @@
 
     /**
      * Unregisters the specified {@link BluetoothVolumeControl.Callback}.
-     * <p>The same {@link BluetoothVolumeControl.Callback} object used when calling
-     * {@link #registerCallback(Executor, BluetoothVolumeControl.Callback)} must be used.
+     *
+     * <p>The same {@link BluetoothVolumeControl.Callback} object used when calling {@link
+     * #registerCallback(Executor, BluetoothVolumeControl.Callback)} must be used.
      *
      * <p>Callbacks are automatically unregistered when application process goes away
      *
@@ -153,8 +156,8 @@
      * @param device {@link BluetoothDevice} representing the remote device
      * @param volumeOffset volume offset to be set on the remote device
      */
-    public void setVolumeOffset(BluetoothDevice device,
-            @IntRange(from = -255, to = 255) int volumeOffset) {
+    public void setVolumeOffset(
+            BluetoothDevice device, @IntRange(from = -255, to = 255) int volumeOffset) {
         if (mService == null) {
             Log.w(TAG, "Proxy not attached to service. Cannot set volume offset.");
             return;
@@ -165,16 +168,13 @@
         }
         mService.setVolumeOffset(device, volumeOffset);
     }
-
     /**
-     * Provides information about the possibility to set volume offset on the remote device.
-     * If the remote device supports Volume Offset Control Service, it is automatically
-     * connected.
+     * Provides information about the possibility to set volume offset on the remote device. If the
+     * remote device supports Volume Offset Control Service, it is automatically connected.
      *
      * @param device {@link BluetoothDevice} representing the remote device
      * @return {@code true} if volume offset function is supported and available to use on the
-     *         remote device. When Bluetooth is off, the return value should always be
-     *         {@code false}.
+     *     remote device. When Bluetooth is off, the return value should always be {@code false}.
      */
     public boolean isVolumeOffsetAvailable(BluetoothDevice device) {
         if (mService == null) {
@@ -188,6 +188,28 @@
         return mService.isVolumeOffsetAvailable(device);
     }
 
+    /**
+     * Tells the remote device to set a volume.
+     *
+     * @param device {@link BluetoothDevice} representing the remote device
+     * @param volume volume to be set on the remote device
+     * @param isGroupOp whether to set the volume to remote devices within the same CSIP group
+     */
+    public void setDeviceVolume(
+            BluetoothDevice device,
+            @IntRange(from = 0, to = 255) int volume,
+            boolean isGroupOp) {
+        if (mService == null) {
+            Log.w(TAG, "Proxy not attached to service. Cannot set volume offset.");
+            return;
+        }
+        if (device == null) {
+            Log.w(TAG, "Device is null. Cannot set volume offset.");
+            return;
+        }
+        mService.setDeviceVolume(device, volume, isGroupOp);
+    }
+
     @Override
     public boolean accessProfileEnabled() {
         return false;
@@ -199,10 +221,9 @@
     }
 
     /**
-     * Gets VolumeControlProfile devices matching connection states{
-     * {@code BluetoothProfile.STATE_CONNECTED},
-     * {@code BluetoothProfile.STATE_CONNECTING},
-     * {@code BluetoothProfile.STATE_DISCONNECTING}}
+     * Gets VolumeControlProfile devices matching connection states{ {@code
+     * BluetoothProfile.STATE_CONNECTED}, {@code BluetoothProfile.STATE_CONNECTING}, {@code
+     * BluetoothProfile.STATE_DISCONNECTING}}
      *
      * @return Matching device list
      */
@@ -211,8 +232,11 @@
             return new ArrayList<BluetoothDevice>(0);
         }
         return mService.getDevicesMatchingConnectionStates(
-                new int[]{BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING,
-                        BluetoothProfile.STATE_DISCONNECTING});
+                new int[] {
+                    BluetoothProfile.STATE_CONNECTED,
+                    BluetoothProfile.STATE_CONNECTING,
+                    BluetoothProfile.STATE_DISCONNECTING
+                });
     }
 
     @Override
@@ -285,7 +309,7 @@
 
     @Override
     public int getSummaryResourceForDevice(BluetoothDevice device) {
-        return 0;   // VCP profile not displayed in UI
+        return 0; // VCP profile not displayed in UI
     }
 
     @Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java
index c560627..fe1529d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/VolumeControlProfileTest.java
@@ -54,18 +54,14 @@
 public class VolumeControlProfileTest {
 
     private static final int TEST_VOLUME_OFFSET = 10;
+    private static final int TEST_VOLUME_VALUE = 10;
 
-    @Rule
-    public final MockitoRule mockito = MockitoJUnit.rule();
+    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
 
-    @Mock
-    private CachedBluetoothDeviceManager mDeviceManager;
-    @Mock
-    private LocalBluetoothProfileManager mProfileManager;
-    @Mock
-    private BluetoothDevice mBluetoothDevice;
-    @Mock
-    private BluetoothVolumeControl mService;
+    @Mock private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock private LocalBluetoothProfileManager mProfileManager;
+    @Mock private BluetoothDevice mBluetoothDevice;
+    @Mock private BluetoothVolumeControl mService;
 
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private BluetoothProfile.ServiceListener mServiceListener;
@@ -177,14 +173,14 @@
     @Test
     public void getConnectedDevices_returnCorrectList() {
         mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
-        int[] connectedStates = new int[] {
-                BluetoothProfile.STATE_CONNECTED,
-                BluetoothProfile.STATE_CONNECTING,
-                BluetoothProfile.STATE_DISCONNECTING};
-        List<BluetoothDevice> connectedList = Arrays.asList(
-                mBluetoothDevice,
-                mBluetoothDevice,
-                mBluetoothDevice);
+        int[] connectedStates =
+                new int[] {
+                    BluetoothProfile.STATE_CONNECTED,
+                    BluetoothProfile.STATE_CONNECTING,
+                    BluetoothProfile.STATE_DISCONNECTING
+                };
+        List<BluetoothDevice> connectedList =
+                Arrays.asList(mBluetoothDevice, mBluetoothDevice, mBluetoothDevice);
         when(mService.getDevicesMatchingConnectionStates(connectedStates))
                 .thenReturn(connectedList);
 
@@ -222,6 +218,16 @@
     }
 
     @Test
+    public void setDeviceVolume_verifyIsCalled() {
+        mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
+
+        mProfile.setDeviceVolume(mBluetoothDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true);
+
+        verify(mService)
+                .setDeviceVolume(mBluetoothDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true);
+    }
+
+    @Test
     public void isVolumeOffsetAvailable_verifyIsCalledAndReturnTrue() {
         mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
         when(mService.isVolumeOffsetAvailable(mBluetoothDevice)).thenReturn(true);