brillo: Add volume control to client library.

BUG=28246583
TEST=unittests

Change-Id: Ia78f608c8f01aaae5f284d23e34f673efb300aa3
diff --git a/brillo/audio/audioservice/audio_service_callback.cpp b/brillo/audio/audioservice/audio_service_callback.cpp
index 8100f0b..eecc1ce 100644
--- a/brillo/audio/audioservice/audio_service_callback.cpp
+++ b/brillo/audio/audioservice/audio_service_callback.cpp
@@ -20,6 +20,7 @@
 #include <base/bind.h>
 #include <base/logging.h>
 
+#include "brillo_audio_client_helpers.h"
 #include "brillo_audio_device_info_def.h"
 
 using android::binder::Status;
@@ -30,6 +31,7 @@
                                            void* user_data) {
   connected_callback_ = base::Bind(callback->OnAudioDeviceAdded);
   disconnected_callback_ = base::Bind(callback->OnAudioDeviceRemoved);
+  volume_callback_ = base::Bind(callback->OnVolumeChanged);
   user_data_ = user_data;
 }
 
@@ -55,9 +57,19 @@
   return Status::ok();
 }
 
+Status AudioServiceCallback::OnVolumeChanged(int stream,
+                                             int previous_index,
+                                             int current_index) {
+  auto usage = BrilloAudioClientHelpers::GetBAudioUsage(
+      static_cast<audio_stream_type_t>(stream));
+  volume_callback_.Run(usage, previous_index, current_index, user_data_);
+  return Status::ok();
+}
+
 bool AudioServiceCallback::Equals(android::sp<AudioServiceCallback> callback) {
   if (callback->connected_callback_.Equals(connected_callback_) &&
       callback->disconnected_callback_.Equals(disconnected_callback_) &&
+      callback->volume_callback_.Equals(volume_callback_) &&
       callback->user_data_ == user_data_)
     return true;
   return false;
diff --git a/brillo/audio/audioservice/audio_service_callback.h b/brillo/audio/audioservice/audio_service_callback.h
index 49a0231..5c72924 100644
--- a/brillo/audio/audioservice/audio_service_callback.h
+++ b/brillo/audio/audioservice/audio_service_callback.h
@@ -44,11 +44,18 @@
   // |devices| is a vector of audio_devices_t.
   Status OnAudioDevicesConnected(const std::vector<int>& devices);
 
-  // Callback function triggered with a device is disconnected.
+  // Callback function triggered when a device is disconnected.
   //
   // |devices| is a vector of audio_devices_t.
   Status OnAudioDevicesDisconnected(const std::vector<int>& devices);
 
+  // Callback function triggered when volume is changed.
+  //
+  // |stream| is an int representing the stream.
+  // |previous_index| is the volume index before the key press.
+  // |current_index| is the volume index after the key press.
+  Status OnVolumeChanged(int stream, int previous_index, int current_index);
+
   // Method to compare two AudioServiceCallback objects.
   //
   // |callback| is a ref counted pointer to a AudioServiceCallback object to be
@@ -62,6 +69,8 @@
   base::Callback<void(const BAudioDeviceInfo*, void*)> connected_callback_;
   // Callback when devices are disconnected.
   base::Callback<void(const BAudioDeviceInfo*, void*)> disconnected_callback_;
+  // Callback when the volume button is pressed.
+  base::Callback<void(BAudioUsage, int, int, void*)> volume_callback_;
   // User data passed to the callbacks.
   void* user_data_;
 };
diff --git a/brillo/audio/audioservice/brillo_audio_client.cpp b/brillo/audio/audioservice/brillo_audio_client.cpp
index 2984067..4f162be 100644
--- a/brillo/audio/audioservice/brillo_audio_client.cpp
+++ b/brillo/audio/audioservice/brillo_audio_client.cpp
@@ -21,6 +21,7 @@
 #include <binder/Status.h>
 #include <binderwrapper/binder_wrapper.h>
 
+#include "brillo_audio_client_helpers.h"
 #include "brillo_audio_device_info_def.h"
 #include "brillo_audio_device_info_internal.h"
 
@@ -98,6 +99,90 @@
   return status.serviceSpecificErrorCode();
 }
 
+int BrilloAudioClient::GetMaxVolumeSteps(BAudioUsage usage, int* max_steps) {
+  if (!brillo_audio_service_.get()) {
+    OnBASDisconnect();
+    return ECONNABORTED;
+  }
+  auto status = brillo_audio_service_->GetMaxVolumeSteps(
+      BrilloAudioClientHelpers::GetStreamType(usage), max_steps);
+  return status.serviceSpecificErrorCode();
+}
+
+int BrilloAudioClient::SetMaxVolumeSteps(BAudioUsage usage, int max_steps) {
+  if (!brillo_audio_service_.get()) {
+    OnBASDisconnect();
+    return ECONNABORTED;
+  }
+  auto status = brillo_audio_service_->SetMaxVolumeSteps(
+      BrilloAudioClientHelpers::GetStreamType(usage), max_steps);
+  return status.serviceSpecificErrorCode();
+}
+
+int BrilloAudioClient::SetVolumeIndex(BAudioUsage usage,
+                                      audio_devices_t device,
+                                      int index) {
+  if (!brillo_audio_service_.get()) {
+    OnBASDisconnect();
+    return ECONNABORTED;
+  }
+  auto status = brillo_audio_service_->SetVolumeIndex(
+      BrilloAudioClientHelpers::GetStreamType(usage), device, index);
+  return status.serviceSpecificErrorCode();
+}
+
+int BrilloAudioClient::GetVolumeIndex(BAudioUsage usage,
+                                      audio_devices_t device,
+                                      int* index) {
+  if (!brillo_audio_service_.get()) {
+    OnBASDisconnect();
+    return ECONNABORTED;
+  }
+  auto status = brillo_audio_service_->GetVolumeIndex(
+      BrilloAudioClientHelpers::GetStreamType(usage), device, index);
+  return status.serviceSpecificErrorCode();
+}
+
+int BrilloAudioClient::GetVolumeControlStream(BAudioUsage* usage) {
+  if (!brillo_audio_service_.get()) {
+    OnBASDisconnect();
+    return ECONNABORTED;
+  }
+  int stream;
+  auto status = brillo_audio_service_->GetVolumeControlStream(&stream);
+  *usage = BrilloAudioClientHelpers::GetBAudioUsage(
+      static_cast<audio_stream_type_t>(stream));
+  return status.serviceSpecificErrorCode();
+}
+
+int BrilloAudioClient::SetVolumeControlStream(BAudioUsage usage) {
+  if (!brillo_audio_service_.get()) {
+    OnBASDisconnect();
+    return ECONNABORTED;
+  }
+  auto status = brillo_audio_service_->SetVolumeControlStream(
+      BrilloAudioClientHelpers::GetStreamType(usage));
+  return status.serviceSpecificErrorCode();
+}
+
+int BrilloAudioClient::IncrementVolume() {
+  if (!brillo_audio_service_.get()) {
+    OnBASDisconnect();
+    return ECONNABORTED;
+  }
+  auto status = brillo_audio_service_->IncrementVolume();
+  return status.serviceSpecificErrorCode();
+}
+
+int BrilloAudioClient::DecrementVolume() {
+  if (!brillo_audio_service_.get()) {
+    OnBASDisconnect();
+    return ECONNABORTED;
+  }
+  auto status = brillo_audio_service_->DecrementVolume();
+  return status.serviceSpecificErrorCode();
+}
+
 int BrilloAudioClient::RegisterAudioCallback(
     android::sp<AudioServiceCallback> callback, int* callback_id) {
   if (!brillo_audio_service_.get()) {
diff --git a/brillo/audio/audioservice/brillo_audio_client.h b/brillo/audio/audioservice/brillo_audio_client.h
index e5de4a0..3b65c4c 100644
--- a/brillo/audio/audioservice/brillo_audio_client.h
+++ b/brillo/audio/audioservice/brillo_audio_client.h
@@ -78,6 +78,64 @@
   int SetDevice(audio_policy_force_use_t usage,
                 audio_policy_forced_cfg_t config);
 
+  // Get the maximum number of steps for a given BAudioUsage.
+  //
+  // |usage| is an enum of type BAudioUsage.
+  // |max_steps| is a pointer to the maximum number of steps.
+  //
+  // Returns 0 on success and errno on failure.
+  int GetMaxVolumeSteps(BAudioUsage usage, int* max_steps);
+
+  // Set the maximum number of steps to use for a given BAudioUsage.
+  //
+  // |usage| is an enum of type BAudioUsage.
+  // |max_steps| is an int between 0 and 100.
+  //
+  // Returns 0 on success and errno on failure.
+  int SetMaxVolumeSteps(BAudioUsage usage, int max_steps);
+
+  // Set the volume index for a given BAudioUsage and device.
+  //
+  // |usage| is an enum of type BAudioUsage.
+  // |device| is of type audio_devices_t.
+  // |index| is an int representing the current index.
+  //
+  // Returns 0 on success and errno on failure.
+  int SetVolumeIndex(BAudioUsage usage, audio_devices_t device, int index);
+
+  // Get the volume index for a given BAudioUsage and device.
+  //
+  // |usage| is an enum of type BAudioUsage.
+  // |device| is of type audio_devices_t.
+  // |index| is a pointer to an int representing the current index.
+  //
+  // Returns 0 on success and errno on failure.
+  int GetVolumeIndex(BAudioUsage usage, audio_devices_t device, int* index);
+
+  // Get default stream to use for volume buttons.
+  //
+  // |usage| is a pointer to a BAudioUsage.
+  //
+  // Returns 0 on success and errno on failure.
+  int GetVolumeControlStream(BAudioUsage* usage);
+
+  // Set default stream to use for volume buttons.
+  //
+  // |usage| is an enum of type BAudioUsage.
+  //
+  // Returns 0 on success and errno on failure.
+  int SetVolumeControlStream(BAudioUsage usage);
+
+  // Increment the volume.
+  //
+  // Returns 0 on success and errno on failure.
+  int IncrementVolume();
+
+  // Decrement the volume.
+  //
+  // Returns 0 on success and errno on failure.
+  int DecrementVolume();
+
  protected:
   BrilloAudioClient() = default;
 
diff --git a/brillo/audio/audioservice/brillo_audio_client_helpers.cpp b/brillo/audio/audioservice/brillo_audio_client_helpers.cpp
index 9decfa5..871c7a9 100644
--- a/brillo/audio/audioservice/brillo_audio_client_helpers.cpp
+++ b/brillo/audio/audioservice/brillo_audio_client_helpers.cpp
@@ -25,4 +25,35 @@
     return AUDIO_POLICY_FORCE_FOR_SYSTEM;
 }
 
+audio_stream_type_t BrilloAudioClientHelpers::GetStreamType(BAudioUsage usage) {
+  switch (usage) {
+    case kUsageAlarm:
+      return AUDIO_STREAM_ALARM;
+    case kUsageMedia:
+      return AUDIO_STREAM_MUSIC;
+    case kUsageNotifications:
+      return AUDIO_STREAM_NOTIFICATION;
+    case kUsageSystem:
+      return AUDIO_STREAM_SYSTEM;
+    default:
+      return AUDIO_STREAM_DEFAULT;
+  }
+}
+
+BAudioUsage BrilloAudioClientHelpers::GetBAudioUsage(
+    audio_stream_type_t stream) {
+  switch (stream) {
+    case AUDIO_STREAM_ALARM:
+      return kUsageAlarm;
+    case AUDIO_STREAM_MUSIC:
+      return kUsageMedia;
+    case AUDIO_STREAM_NOTIFICATION:
+      return kUsageNotifications;
+    case AUDIO_STREAM_SYSTEM:
+      return kUsageSystem;
+    default:
+      return kUsageInvalid;
+  }
+}
+
 }  // namespace brillo
diff --git a/brillo/audio/audioservice/brillo_audio_client_helpers.h b/brillo/audio/audioservice/brillo_audio_client_helpers.h
index 657b60d..a5bb7ba 100644
--- a/brillo/audio/audioservice/brillo_audio_client_helpers.h
+++ b/brillo/audio/audioservice/brillo_audio_client_helpers.h
@@ -29,6 +29,8 @@
 class BrilloAudioClientHelpers {
  public:
   static audio_policy_force_use_t GetForceUse(BAudioUsage usage);
+  static audio_stream_type_t GetStreamType(BAudioUsage usage);
+  static BAudioUsage GetBAudioUsage(audio_stream_type_t stream);
 };
 
 }  // namespace brillo
diff --git a/brillo/audio/audioservice/brillo_audio_device_info_internal.cpp b/brillo/audio/audioservice/brillo_audio_device_info_internal.cpp
index 9049520..215da21 100644
--- a/brillo/audio/audioservice/brillo_audio_device_info_internal.cpp
+++ b/brillo/audio/audioservice/brillo_audio_device_info_internal.cpp
@@ -48,6 +48,23 @@
   }
 }
 
+audio_devices_t BAudioDeviceInfoInternal::GetAudioDevicesT() {
+  switch (device_id_) {
+    case TYPE_BUILTIN_SPEAKER:
+      return AUDIO_DEVICE_OUT_SPEAKER;
+    case TYPE_WIRED_HEADSET:
+      return AUDIO_DEVICE_OUT_WIRED_HEADSET;
+    case TYPE_WIRED_HEADSET_MIC:
+      return AUDIO_DEVICE_IN_WIRED_HEADSET;
+    case TYPE_WIRED_HEADPHONES:
+      return AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
+    case TYPE_BUILTIN_MIC:
+      return AUDIO_DEVICE_IN_BUILTIN_MIC;
+    default:
+      return AUDIO_DEVICE_NONE;
+  }
+}
+
 BAudioDeviceInfoInternal* BAudioDeviceInfoInternal::CreateFromAudioDevicesT(
     unsigned int device) {
   int device_id = TYPE_UNKNOWN;
diff --git a/brillo/audio/audioservice/brillo_audio_device_info_internal.h b/brillo/audio/audioservice/brillo_audio_device_info_internal.h
index 29689aa..0eaaab4 100644
--- a/brillo/audio/audioservice/brillo_audio_device_info_internal.h
+++ b/brillo/audio/audioservice/brillo_audio_device_info_internal.h
@@ -54,6 +54,11 @@
   // Returns an int which is the device_id.
   int GetDeviceId();
 
+  // Get audio_devices_t that corresponds to device_id;
+  //
+  // Returns an audio_devices_t.
+  audio_devices_t GetAudioDevicesT();
+
  private:
   FRIEND_TEST(BrilloAudioDeviceInfoInternalTest, InWiredHeadset);
   FRIEND_TEST(BrilloAudioDeviceInfoInternalTest, OutWiredHeadset);
diff --git a/brillo/audio/audioservice/brillo_audio_manager.cpp b/brillo/audio/audioservice/brillo_audio_manager.cpp
index 37883f7..4c09824 100644
--- a/brillo/audio/audioservice/brillo_audio_manager.cpp
+++ b/brillo/audio/audioservice/brillo_audio_manager.cpp
@@ -95,6 +95,104 @@
                            device->internal_->GetConfig());
 }
 
+int BAudioManager_getMaxVolumeSteps(const BAudioManager* brillo_audio_manager,
+                                    BAudioUsage usage,
+                                    int* max_steps) {
+  if (!brillo_audio_manager || !max_steps)
+    return EINVAL;
+  auto client = brillo_audio_manager->client_.lock();
+  if (!client)
+    return ECONNABORTED;
+  return client->GetMaxVolumeSteps(usage, max_steps);
+}
+
+int BAudioManager_setMaxVolumeSteps(const BAudioManager* brillo_audio_manager,
+                                    BAudioUsage usage,
+                                    int max_steps) {
+  if (!brillo_audio_manager || max_steps < 0 || max_steps > 100)
+    return EINVAL;
+  auto client = brillo_audio_manager->client_.lock();
+  if (!client)
+    return ECONNABORTED;
+  return client->SetMaxVolumeSteps(usage, max_steps);
+}
+
+int BAudioManager_setVolumeIndex(const BAudioManager* brillo_audio_manager,
+                                 BAudioUsage usage,
+                                 const BAudioDeviceInfo* device,
+                                 int index) {
+  if (!brillo_audio_manager || !device) {
+    return EINVAL;
+  }
+  auto client = brillo_audio_manager->client_.lock();
+  if (!client) {
+    return ECONNABORTED;
+  }
+  return client->SetVolumeIndex(
+      usage, device->internal_->GetAudioDevicesT(), index);
+}
+
+int BAudioManager_getVolumeIndex(const BAudioManager* brillo_audio_manager,
+                                 BAudioUsage usage,
+                                 const BAudioDeviceInfo* device,
+                                 int* index) {
+  if (!brillo_audio_manager || !device || !index) {
+    return EINVAL;
+  }
+  auto client = brillo_audio_manager->client_.lock();
+  if (!client) {
+    return ECONNABORTED;
+  }
+  return client->GetVolumeIndex(
+      usage, device->internal_->GetAudioDevicesT(), index);
+}
+
+int BAudioManager_getVolumeControlUsage(
+    const BAudioManager* brillo_audio_manager, BAudioUsage* usage) {
+  if (!brillo_audio_manager || !usage) {
+    return EINVAL;
+  }
+  auto client = brillo_audio_manager->client_.lock();
+  if (!client) {
+    return ECONNABORTED;
+  }
+  return client->GetVolumeControlStream(usage);
+}
+
+int BAudioManager_setVolumeControlUsage(
+    const BAudioManager* brillo_audio_manager, BAudioUsage usage) {
+  if (!brillo_audio_manager) {
+    return EINVAL;
+  }
+  auto client = brillo_audio_manager->client_.lock();
+  if (!client) {
+    return ECONNABORTED;
+  }
+  return client->SetVolumeControlStream(usage);
+}
+
+int BAudioManager_incrementVolume(const BAudioManager* brillo_audio_manager) {
+  if (!brillo_audio_manager) {
+    return EINVAL;
+  }
+  auto client = brillo_audio_manager->client_.lock();
+  if (!client) {
+    return ECONNABORTED;
+  }
+  return client->IncrementVolume();
+}
+
+int BAudioManager_decrementVolume(const BAudioManager* brillo_audio_manager) {
+  if (!brillo_audio_manager) {
+    return EINVAL;
+  }
+  auto client = brillo_audio_manager->client_.lock();
+  if (!client) {
+    return ECONNABORTED;
+  }
+  return client->DecrementVolume();
+}
+
 int BAudioManager_registerAudioCallback(
     const BAudioManager* brillo_audio_manager, const BAudioCallback* callback,
     void* user_data, int* callback_id) {
diff --git a/brillo/audio/audioservice/include/brillo_audio_manager.h b/brillo/audio/audioservice/include/brillo_audio_manager.h
index 30063bd..ff80daa 100644
--- a/brillo/audio/audioservice/include/brillo_audio_manager.h
+++ b/brillo/audio/audioservice/include/brillo_audio_manager.h
@@ -72,7 +72,8 @@
   kUsageAlarm,
   kUsageMedia,
   kUsageNotifications,
-  kUsageSystem
+  kUsageSystem,
+  kUsageInvalid
 };
 
 // Select the output device to be used for playback.
@@ -88,6 +89,112 @@
     const BAudioManager* brillo_audio_manager, const BAudioDeviceInfo* device,
     BAudioUsage usage);
 
+// Get the number of steps for a given stream type.
+//
+// Args:
+//   brillo_audio_manager: A pointer to a BAudioManager object.
+//   usage: A BAudioUsage representing the audio stream.
+//   max_steps: A pointer to an int representing the number of steps for a given
+//              usage.
+//
+// Returns 0 on success and errno on failure.
+int BAudioManager_getMaxVolumeSteps(const BAudioManager* brillo_audio_manager,
+                                    BAudioUsage usage,
+                                    int* max_steps);
+
+// Set the number of steps for a given stream type.
+//
+// Args:
+//   brillo_audio_manager: A pointer to a BAudioManager object.
+//   usage: A BAudioUsage representing the audio stream.
+//   max_steps: An int representing the number of steps to use for a given
+//              usage.
+//
+// Returns 0 on success and errno on failure.
+int BAudioManager_setMaxVolumeSteps(const BAudioManager* brillo_audio_manager,
+                                    BAudioUsage usage,
+                                    int max_steps);
+
+// Set the volume for a given stream type.
+//
+// Args:
+//   brillo_audio_manager: A pointer to a BAudioManager object.
+//   usage: A BAudioUsage representing the audio stream.
+//   device: A pointer to a BAudioDeviceInfo object.
+//   value: An int representing the index to set the volume to. The index must
+//           be less than max_steps if BAudioManager_setMaxVolumeSteps was
+//           called or 100 otherwise.
+//
+// Returns 0 on success and errno on failure.
+int BAudioManager_setVolumeIndex(const BAudioManager* brillo_audio_manager,
+                                 BAudioUsage usage,
+                                 const BAudioDeviceInfo* device,
+                                 int index);
+
+// Get the volume for a given stream type.
+//
+// Args:
+//   brillo_audio_manager: A pointer to a BAudioManager object.
+//   usage: A BAudioUsage representing the audio stream.
+//   device: A pointer to a BAudioDeviceInfo object.
+//   value: A pointer to int. This will be set to an int representing the volume
+//          index for |usage|.
+//
+// Returns 0 on success and errno on failure.
+int BAudioManager_getVolumeIndex(const BAudioManager* brillo_audio_manager,
+                                 BAudioUsage usage,
+                                 const BAudioDeviceInfo* device,
+                                 int* index);
+
+// Get the default stream for volume buttons. If
+// BAudioManager_setVolumeControlUsage has not been called, this will return
+// kInvalidUsage.
+//
+// Args:
+//  brillo_audio_manager: A pointer to a BAudioManager object.
+//  usage: A pointer to a BAudioUsage representing the audio stream.
+//
+// Returns 0 on success and errno on failure.
+int BAudioManager_getVolumeControlUsage(
+    const BAudioManager* brillo_audio_manager, BAudioUsage* usage);
+
+// Set the default stream to use for volume buttons. By default, streams will be
+// ordered by priority:
+//   1. kUsageAlarm
+//   2. kUsageNotifications
+//   3. kUsageSystem
+//   4. kUsageMedia
+//
+// Calling BAudioMananager_setVolumeControlUsage with kInvalidUsage will reset
+// the volume control stream to its default priorities and undo the effects of
+// previous calls to BAudioManager_setVolumeControlUsage.
+//
+// Args:
+//  brillo_audio_manager: A pointer to a BAudioManager object.
+//  usage: A BAudioUsage representing the audio stream.
+//
+// Returns 0 on success and errno on failure.
+int BAudioManager_setVolumeControlUsage(
+    const BAudioManager* brillo_audio_manager, BAudioUsage usage);
+
+// Increment the volume of active streams or stream selected using
+// BAudioManager_setVolumeControlUsage.
+//
+// Args:
+//   brillo_audio_manager: A pointer to a BAudioManager object.
+//
+// Returns 0 on success and errno on failure.
+int BAudioManager_incrementVolume(const BAudioManager* brillo_audio_manager);
+
+// Decrement the volume of active streams or stream selected using
+// BAudioManager_setVolumeControlUsage.
+//
+// Args:
+//   brillo_audio_manager: A pointer to a BAudioManager object.
+//
+// Returns 0 on success and errno on failure.
+int BAudioManager_decrementVolume(const BAudioManager* brillo_audio_manager);
+
 // Object used for callbacks.
 struct BAudioCallback {
   // Function to be called when an audio device is added. If multiple audio
@@ -101,6 +208,12 @@
   // user is not responsible for freeing removed_device.
   void (*OnAudioDeviceRemoved)(const BAudioDeviceInfo* removed_device,
                                void* user_data);
+
+  // Function to be called when the volume button is pressed.
+  void (*OnVolumeChanged)(BAudioUsage usage,
+                          int old_volume_index,
+                          int new_volume_index,
+                          void* user_data);
 };
 
 typedef struct BAudioCallback BAudioCallback;
diff --git a/brillo/audio/audioservice/test/brillo_audio_client_test.cpp b/brillo/audio/audioservice/test/brillo_audio_client_test.cpp
index ad5ca49..3616c7b 100644
--- a/brillo/audio/audioservice/test/brillo_audio_client_test.cpp
+++ b/brillo/audio/audioservice/test/brillo_audio_client_test.cpp
@@ -102,8 +102,6 @@
 TEST_F(BrilloAudioClientTest, RegisterCallbackWithBAS) {
   EXPECT_TRUE(ConnectClientToBAS());
   BAudioCallback bcallback;
-  bcallback.OnAudioDeviceAdded = nullptr;
-  bcallback.OnAudioDeviceRemoved = nullptr;
   AudioServiceCallback* callback =
       new AudioServiceCallback(&bcallback, nullptr);
   int id = 0;
@@ -117,8 +115,6 @@
 TEST_F(BrilloAudioClientTest, RegisterSameCallbackTwiceWithBAS) {
   EXPECT_TRUE(ConnectClientToBAS());
   BAudioCallback bcallback;
-  bcallback.OnAudioDeviceAdded = nullptr;
-  bcallback.OnAudioDeviceRemoved = nullptr;
   AudioServiceCallback* callback =
       new AudioServiceCallback(&bcallback, nullptr);
   int id = -1;
@@ -136,8 +132,6 @@
 TEST_F(BrilloAudioClientTest, UnregisterAudioCallbackValidWithBAS) {
   EXPECT_TRUE(ConnectClientToBAS());
   BAudioCallback bcallback;
-  bcallback.OnAudioDeviceAdded = nullptr;
-  bcallback.OnAudioDeviceRemoved = nullptr;
   AudioServiceCallback* callback =
       new AudioServiceCallback(&bcallback, nullptr);
   int id = 0;
@@ -160,10 +154,6 @@
 TEST_F(BrilloAudioClientTest, RegisterAndUnregisterAudioTwoCallbacks) {
   EXPECT_TRUE(ConnectClientToBAS());
   BAudioCallback bcallback1, bcallback2;
-  bcallback1.OnAudioDeviceAdded = nullptr;
-  bcallback1.OnAudioDeviceRemoved = nullptr;
-  bcallback2.OnAudioDeviceAdded = nullptr;
-  bcallback2.OnAudioDeviceRemoved = nullptr;
   AudioServiceCallback* callback1 =
       new AudioServiceCallback(&bcallback1, nullptr);
   AudioServiceCallback* callback2 =
@@ -181,4 +171,117 @@
   EXPECT_EQ(client_.UnregisterAudioCallback(id2), 0);
 }
 
+TEST_F(BrilloAudioClientTest, GetMaxVolStepsNoService) {
+  EXPECT_CALL(client_, OnBASDisconnect());
+  int foo;
+  EXPECT_EQ(client_.GetMaxVolumeSteps(BAudioUsage::kUsageInvalid, &foo),
+            ECONNABORTED);
+}
+
+TEST_F(BrilloAudioClientTest, GetMaxVolStepsWithBAS) {
+  EXPECT_TRUE(ConnectClientToBAS());
+  int foo;
+  EXPECT_CALL(*bas_.get(), GetMaxVolumeSteps(AUDIO_STREAM_MUSIC, &foo))
+      .WillOnce(Return(Status::ok()));
+  EXPECT_EQ(client_.GetMaxVolumeSteps(BAudioUsage::kUsageMedia, &foo), 0);
+}
+
+TEST_F(BrilloAudioClientTest, SetMaxVolStepsNoService) {
+  EXPECT_CALL(client_, OnBASDisconnect());
+  EXPECT_EQ(client_.SetMaxVolumeSteps(BAudioUsage::kUsageInvalid, 100),
+            ECONNABORTED);
+}
+
+TEST_F(BrilloAudioClientTest, SetMaxVolStepsWithBAS) {
+  EXPECT_TRUE(ConnectClientToBAS());
+  EXPECT_CALL(*bas_.get(), SetMaxVolumeSteps(AUDIO_STREAM_MUSIC, 100))
+      .WillOnce(Return(Status::ok()));
+  EXPECT_EQ(client_.SetMaxVolumeSteps(BAudioUsage::kUsageMedia, 100), 0);
+}
+
+TEST_F(BrilloAudioClientTest, SetVolIndexNoService) {
+  EXPECT_CALL(client_, OnBASDisconnect());
+  EXPECT_EQ(client_.SetVolumeIndex(
+                BAudioUsage::kUsageInvalid, AUDIO_DEVICE_NONE, 100),
+            ECONNABORTED);
+}
+
+TEST_F(BrilloAudioClientTest, SetVolIndexWithBAS) {
+  EXPECT_TRUE(ConnectClientToBAS());
+  EXPECT_CALL(*bas_.get(),
+              SetVolumeIndex(AUDIO_STREAM_MUSIC, AUDIO_DEVICE_OUT_SPEAKER, 100))
+      .WillOnce(Return(Status::ok()));
+  EXPECT_EQ(client_.SetVolumeIndex(
+                BAudioUsage::kUsageMedia, AUDIO_DEVICE_OUT_SPEAKER, 100),
+            0);
+}
+
+TEST_F(BrilloAudioClientTest, GetVolIndexNoService) {
+  EXPECT_CALL(client_, OnBASDisconnect());
+  int foo;
+  EXPECT_EQ(client_.GetVolumeIndex(
+                BAudioUsage::kUsageInvalid, AUDIO_DEVICE_NONE, &foo),
+            ECONNABORTED);
+}
+
+TEST_F(BrilloAudioClientTest, GetVolIndexWithBAS) {
+  EXPECT_TRUE(ConnectClientToBAS());
+  int foo;
+  EXPECT_CALL(
+      *bas_.get(),
+      GetVolumeIndex(AUDIO_STREAM_MUSIC, AUDIO_DEVICE_OUT_SPEAKER, &foo))
+      .WillOnce(Return(Status::ok()));
+  EXPECT_EQ(client_.GetVolumeIndex(
+                BAudioUsage::kUsageMedia, AUDIO_DEVICE_OUT_SPEAKER, &foo),
+            0);
+}
+
+TEST_F(BrilloAudioClientTest, GetVolumeControlStreamNoService) {
+  EXPECT_CALL(client_, OnBASDisconnect());
+  BAudioUsage foo;
+  EXPECT_EQ(client_.GetVolumeControlStream(&foo), ECONNABORTED);
+}
+
+TEST_F(BrilloAudioClientTest, GetVolumeControlStreamWithBAS) {
+  EXPECT_TRUE(ConnectClientToBAS());
+  EXPECT_CALL(*bas_.get(), GetVolumeControlStream(_))
+      .WillOnce(Return(Status::ok()));
+  BAudioUsage foo;
+  EXPECT_EQ(client_.GetVolumeControlStream(&foo), 0);
+}
+
+TEST_F(BrilloAudioClientTest, SetVolumeControlStreamNoService) {
+  EXPECT_CALL(client_, OnBASDisconnect());
+  EXPECT_EQ(client_.SetVolumeControlStream(kUsageMedia), ECONNABORTED);
+}
+
+TEST_F(BrilloAudioClientTest, SetVolumeControlStreamWithBAS) {
+  EXPECT_TRUE(ConnectClientToBAS());
+  EXPECT_CALL(*bas_.get(), SetVolumeControlStream(AUDIO_STREAM_MUSIC))
+      .WillOnce(Return(Status::ok()));
+  EXPECT_EQ(client_.SetVolumeControlStream(kUsageMedia), 0);
+}
+
+TEST_F(BrilloAudioClientTest, IncrementVolNoService) {
+  EXPECT_CALL(client_, OnBASDisconnect());
+  EXPECT_EQ(client_.IncrementVolume(), ECONNABORTED);
+}
+
+TEST_F(BrilloAudioClientTest, IncrementVolWithBAS) {
+  EXPECT_TRUE(ConnectClientToBAS());
+  EXPECT_CALL(*bas_.get(), IncrementVolume()).WillOnce(Return(Status::ok()));
+  EXPECT_EQ(client_.IncrementVolume(), 0);
+}
+
+TEST_F(BrilloAudioClientTest, DecrementVolNoService) {
+  EXPECT_CALL(client_, OnBASDisconnect());
+  EXPECT_EQ(client_.DecrementVolume(), ECONNABORTED);
+}
+
+TEST_F(BrilloAudioClientTest, DecrementVolWithBAS) {
+  EXPECT_TRUE(ConnectClientToBAS());
+  EXPECT_CALL(*bas_.get(), DecrementVolume()).WillOnce(Return(Status::ok()));
+  EXPECT_EQ(client_.DecrementVolume(), 0);
+}
+
 }  // namespace brillo
diff --git a/brillo/audio/audioservice/test/brillo_audio_manager_test.cpp b/brillo/audio/audioservice/test/brillo_audio_manager_test.cpp
index a102c6c..6c4b1ff 100644
--- a/brillo/audio/audioservice/test/brillo_audio_manager_test.cpp
+++ b/brillo/audio/audioservice/test/brillo_audio_manager_test.cpp
@@ -292,4 +292,206 @@
             ECONNABORTED);
 }
 
+TEST_F(BrilloAudioManagerTest, GetMaxVolumeStepsInvalidParams) {
+  auto bam = GetValidManager();
+  int foo;
+  EXPECT_EQ(BAudioManager_getMaxVolumeSteps(
+                nullptr, BAudioUsage::kUsageMedia, nullptr),
+            EINVAL);
+  EXPECT_EQ(
+      BAudioManager_getMaxVolumeSteps(nullptr, BAudioUsage::kUsageMedia, &foo),
+      EINVAL);
+  EXPECT_EQ(
+      BAudioManager_getMaxVolumeSteps(bam, BAudioUsage::kUsageMedia, nullptr),
+      EINVAL);
+}
+
+TEST_F(BrilloAudioManagerTest, GetMaxVolStepsWithBAS) {
+  auto bam = GetValidManager();
+  int foo;
+  EXPECT_CALL(*bas_.get(), GetMaxVolumeSteps(AUDIO_STREAM_MUSIC, &foo))
+      .WillOnce(Return(Status::ok()));
+  EXPECT_EQ(
+      BAudioManager_getMaxVolumeSteps(bam, BAudioUsage::kUsageMedia, &foo), 0);
+}
+
+TEST_F(BrilloAudioManagerTest, GetMaxVolStepsBASDies) {
+  auto bam = GetValidManager();
+  int foo;
+  binder_wrapper()->NotifyAboutBinderDeath(bas_);
+  EXPECT_EQ(
+      BAudioManager_getMaxVolumeSteps(bam, BAudioUsage::kUsageMedia, &foo),
+      ECONNABORTED);
+}
+
+TEST_F(BrilloAudioManagerTest, SetMaxVolumeStepsInvalidParams) {
+  EXPECT_EQ(
+      BAudioManager_setMaxVolumeSteps(nullptr, BAudioUsage::kUsageMedia, 100),
+      EINVAL);
+}
+
+TEST_F(BrilloAudioManagerTest, SetMaxVolStepsWithBAS) {
+  auto bam = GetValidManager();
+  EXPECT_CALL(*bas_.get(), SetMaxVolumeSteps(AUDIO_STREAM_MUSIC, 100))
+      .WillOnce(Return(Status::ok()));
+  EXPECT_EQ(BAudioManager_setMaxVolumeSteps(bam, BAudioUsage::kUsageMedia, 100),
+            0);
+}
+
+TEST_F(BrilloAudioManagerTest, SetMaxVolStepsBASDies) {
+  auto bam = GetValidManager();
+  binder_wrapper()->NotifyAboutBinderDeath(bas_);
+  EXPECT_EQ(BAudioManager_setMaxVolumeSteps(bam, BAudioUsage::kUsageMedia, 100),
+            ECONNABORTED);
+}
+
+TEST_F(BrilloAudioManagerTest, SetVolIndexInvalidParams) {
+  auto bam = GetValidManager();
+  EXPECT_EQ(BAudioManager_setVolumeIndex(
+                nullptr, BAudioUsage::kUsageMedia, nullptr, 100),
+            EINVAL);
+  EXPECT_EQ(
+      BAudioManager_setVolumeIndex(bam, BAudioUsage::kUsageMedia, nullptr, 100),
+      EINVAL);
+}
+
+TEST_F(BrilloAudioManagerTest, SetVolIndexWithBAS) {
+  auto bam = GetValidManager();
+  auto device = BAudioDeviceInfo_new(TYPE_WIRED_HEADPHONES);
+  EXPECT_CALL(
+      *bas_.get(),
+      SetVolumeIndex(AUDIO_STREAM_MUSIC, AUDIO_DEVICE_OUT_WIRED_HEADPHONE, 100))
+      .WillOnce(Return(Status::ok()));
+  EXPECT_EQ(
+      BAudioManager_setVolumeIndex(bam, BAudioUsage::kUsageMedia, device, 100),
+      0);
+  BAudioDeviceInfo_delete(device);
+}
+
+TEST_F(BrilloAudioManagerTest, SetVolIndexBASDies) {
+  auto bam = GetValidManager();
+  auto device = BAudioDeviceInfo_new(TYPE_WIRED_HEADPHONES);
+  binder_wrapper()->NotifyAboutBinderDeath(bas_);
+  EXPECT_EQ(
+      BAudioManager_setVolumeIndex(bam, BAudioUsage::kUsageMedia, device, 100),
+      ECONNABORTED);
+  BAudioDeviceInfo_delete(device);
+}
+
+TEST_F(BrilloAudioManagerTest, GetVolIndexInvalidParams) {
+  auto bam = GetValidManager();
+  int foo;
+  EXPECT_EQ(BAudioManager_getVolumeIndex(
+                nullptr, BAudioUsage::kUsageMedia, nullptr, nullptr),
+            EINVAL);
+  auto device = BAudioDeviceInfo_new(TYPE_WIRED_HEADPHONES);
+  EXPECT_EQ(BAudioManager_getVolumeIndex(
+                bam, BAudioUsage::kUsageMedia, device, nullptr),
+            EINVAL);
+  EXPECT_EQ(BAudioManager_getVolumeIndex(
+                nullptr, BAudioUsage::kUsageMedia, device, &foo),
+            EINVAL);
+  EXPECT_EQ(BAudioManager_getVolumeIndex(
+                bam, BAudioUsage::kUsageMedia, nullptr, &foo),
+            EINVAL);
+}
+
+TEST_F(BrilloAudioManagerTest, GetVolIndexWithBAS) {
+  auto bam = GetValidManager();
+  auto device = BAudioDeviceInfo_new(TYPE_WIRED_HEADPHONES);
+  int foo;
+  EXPECT_CALL(*bas_.get(),
+              GetVolumeIndex(
+                  AUDIO_STREAM_MUSIC, AUDIO_DEVICE_OUT_WIRED_HEADPHONE, &foo))
+      .WillOnce(Return(Status::ok()));
+  EXPECT_EQ(
+      BAudioManager_getVolumeIndex(bam, BAudioUsage::kUsageMedia, device, &foo),
+      0);
+  BAudioDeviceInfo_delete(device);
+}
+
+TEST_F(BrilloAudioManagerTest, GetVolIndexBASDies) {
+  auto bam = GetValidManager();
+  auto device = BAudioDeviceInfo_new(TYPE_WIRED_HEADPHONES);
+  int foo;
+  binder_wrapper()->NotifyAboutBinderDeath(bas_);
+  EXPECT_EQ(
+      BAudioManager_getVolumeIndex(bam, BAudioUsage::kUsageMedia, device, &foo),
+      ECONNABORTED);
+  BAudioDeviceInfo_delete(device);
+}
+
+TEST_F(BrilloAudioManagerTest, GetVolumeControlUsageInvalidParams) {
+  auto bam = GetValidManager();
+  BAudioUsage foo;
+  EXPECT_EQ(BAudioManager_getVolumeControlUsage(nullptr, nullptr), EINVAL);
+  EXPECT_EQ(BAudioManager_getVolumeControlUsage(nullptr, &foo), EINVAL);
+  EXPECT_EQ(BAudioManager_getVolumeControlUsage(bam, nullptr), EINVAL);
+}
+
+TEST_F(BrilloAudioManagerTest, GetVolumeControlStreamWithBAS) {
+  auto bam = GetValidManager();
+  BAudioUsage foo;
+  EXPECT_CALL(*bas_.get(), GetVolumeControlStream(_))
+      .WillOnce(Return(Status::ok()));
+  EXPECT_EQ(BAudioManager_getVolumeControlUsage(bam, &foo), 0);
+}
+
+TEST_F(BrilloAudioManagerTest, GetVolumeControlStreamBASDies) {
+  auto bam = GetValidManager();
+  BAudioUsage foo;
+  binder_wrapper()->NotifyAboutBinderDeath(bas_);
+  EXPECT_EQ(BAudioManager_getVolumeControlUsage(bam, &foo), ECONNABORTED);
+}
+
+TEST_F(BrilloAudioManagerTest, SetVolumeControlUsageInvalidParams) {
+  EXPECT_EQ(
+      BAudioManager_setVolumeControlUsage(nullptr, BAudioUsage::kUsageMedia),
+      EINVAL);
+}
+
+TEST_F(BrilloAudioManagerTest, SetVolumeControlStreamWithBAS) {
+  auto bam = GetValidManager();
+  EXPECT_CALL(*bas_.get(), SetVolumeControlStream(AUDIO_STREAM_MUSIC))
+      .WillOnce(Return(Status::ok()));
+  EXPECT_EQ(BAudioManager_setVolumeControlUsage(bam, BAudioUsage::kUsageMedia),
+            0);
+}
+
+TEST_F(BrilloAudioManagerTest, SetVolumeControlStreamBASDies) {
+  auto bam = GetValidManager();
+  binder_wrapper()->NotifyAboutBinderDeath(bas_);
+  EXPECT_EQ(BAudioManager_setVolumeControlUsage(bam, BAudioUsage::kUsageMedia),
+            ECONNABORTED);
+}
+
+TEST_F(BrilloAudioManagerTest, DecIncInvalidParams) {
+  EXPECT_EQ(BAudioManager_decrementVolume(nullptr), EINVAL);
+  EXPECT_EQ(BAudioManager_incrementVolume(nullptr), EINVAL);
+}
+
+TEST_F(BrilloAudioManagerTest, IncVolWithBAS) {
+  auto bam = GetValidManager();
+  EXPECT_CALL(*bas_.get(), IncrementVolume()).WillOnce(Return(Status::ok()));
+  EXPECT_EQ(BAudioManager_incrementVolume(bam), 0);
+}
+
+TEST_F(BrilloAudioManagerTest, IncVolBASDies) {
+  auto bam = GetValidManager();
+  binder_wrapper()->NotifyAboutBinderDeath(bas_);
+  EXPECT_EQ(BAudioManager_incrementVolume(bam), ECONNABORTED);
+}
+
+TEST_F(BrilloAudioManagerTest, DecVolWithBAS) {
+  auto bam = GetValidManager();
+  EXPECT_CALL(*bas_.get(), DecrementVolume()).WillOnce(Return(Status::ok()));
+  EXPECT_EQ(BAudioManager_decrementVolume(bam), 0);
+}
+
+TEST_F(BrilloAudioManagerTest, DecVolBASDies) {
+  auto bam = GetValidManager();
+  binder_wrapper()->NotifyAboutBinderDeath(bas_);
+  EXPECT_EQ(BAudioManager_decrementVolume(bam), ECONNABORTED);
+}
+
 }  // namespace brillo
diff --git a/brillo/audio/audioservice/test/brillo_audio_service_mock.h b/brillo/audio/audioservice/test/brillo_audio_service_mock.h
index fa9312b..4b52ef1 100644
--- a/brillo/audio/audioservice/test/brillo_audio_service_mock.h
+++ b/brillo/audio/audioservice/test/brillo_audio_service_mock.h
@@ -32,14 +32,25 @@
 
   MOCK_METHOD2(GetDevices, Status(int flag, std::vector<int>* _aidl_return));
   MOCK_METHOD2(SetDevice, Status(int usage, int config));
+  MOCK_METHOD2(GetMaxVolumeSteps, Status(int stream, int* _aidl_return));
+  MOCK_METHOD2(SetMaxVolumeSteps, Status(int stream, int max_steps));
+  MOCK_METHOD3(SetVolumeIndex, Status(int stream, int device, int index));
+  MOCK_METHOD3(GetVolumeIndex,
+               Status(int stream, int device, int* _aidl_return));
+  MOCK_METHOD1(GetVolumeControlStream, Status(int* _aidl_return));
+  MOCK_METHOD1(SetVolumeControlStream, Status(int stream));
+  MOCK_METHOD0(IncrementVolume, Status());
+  MOCK_METHOD0(DecrementVolume, Status());
   MOCK_METHOD1(RegisterServiceCallback,
                Status(const android::sp<IAudioServiceCallback>& callback));
   MOCK_METHOD1(UnregisterServiceCallback,
                Status(const android::sp<IAudioServiceCallback>& callback));
 
-  void RegisterDeviceHandler(std::weak_ptr<AudioDeviceHandler>){};
+  void RegisterHandlers(std::weak_ptr<AudioDeviceHandler>,
+                        std::weak_ptr<AudioVolumeHandler>){};
   void OnDevicesConnected(const std::vector<int>&) {}
   void OnDevicesDisconnected(const std::vector<int>&) {}
+  void OnVolumeChanged(audio_stream_type_t, int, int){};
 };
 
 }  // namespace brillo