Disable AVRCP position change in silence mode

* Save the silence mode information in BtifAvPeer send from
  JAVA, which helps AVRCP devices to check whether device
  silence mode is enabled or not.
* Stop sending AVRCP position change event to remote when
  silence mode is enabled.

Bug: 112323989
Test: Manual
Change-Id: I99d88a31caea062790c4ff0dc9199ed82f058067
diff --git a/binder/android/bluetooth/IBluetooth.aidl b/binder/android/bluetooth/IBluetooth.aidl
index 40a2db4..cc4c9cf 100644
--- a/binder/android/bluetooth/IBluetooth.aidl
+++ b/binder/android/bluetooth/IBluetooth.aidl
@@ -94,6 +94,8 @@
     boolean setPairingConfirmation(in BluetoothDevice device, boolean accept);
 
     int getPhonebookAccessPermission(in BluetoothDevice device);
+    boolean setSilenceMode(in BluetoothDevice device, boolean silence);
+    boolean getSilenceMode(in BluetoothDevice device);
     boolean setPhonebookAccessPermission(in BluetoothDevice device, int value);
     int getMessageAccessPermission(in BluetoothDevice device);
     boolean setMessageAccessPermission(in BluetoothDevice device, int value);
diff --git a/btif/avrcp/avrcp_service.cc b/btif/avrcp/avrcp_service.cc
index bd09fa5..0603687 100644
--- a/btif/avrcp/avrcp_service.cc
+++ b/btif/avrcp/avrcp_service.cc
@@ -52,6 +52,10 @@
 
 class A2dpInterfaceImpl : public A2dpInterface {
   RawAddress active_peer() override { return btif_av_source_active_peer(); }
+
+  bool is_peer_in_silence_mode(const RawAddress& peer_address) override {
+    return btif_av_is_peer_silenced(peer_address);
+  }
 } a2dp_interface_;
 
 class AvrcpInterfaceImpl : public AvrcpInterface {
diff --git a/btif/include/btif_av.h b/btif/include/btif_av.h
index 370d5f1..e42f056 100644
--- a/btif/include/btif_av.h
+++ b/btif/include/btif_av.h
@@ -186,4 +186,13 @@
  *  @param  none
  */
 bool btif_av_is_a2dp_offload_enabled(void);
+
+/**
+ * Check whether peer device is silenced
+ *
+ * @param peer_address to check
+ *
+ */
+bool btif_av_is_peer_silenced(const RawAddress& peer_address);
+
 #endif /* BTIF_AV_H */
diff --git a/btif/src/btif_av.cc b/btif/src/btif_av.cc
index 818abcf..b004f44 100644
--- a/btif/src/btif_av.cc
+++ b/btif/src/btif_av.cc
@@ -276,6 +276,9 @@
 
   bool IsConnected() const;
   bool IsStreaming() const;
+  bool IsInSilenceMode() const { return is_silenced_; };
+
+  void SetSilence(bool silence) { is_silenced_ = silence; };
 
   /**
    * Check whether any of the flags specified by the bitlags mask is set.
@@ -324,6 +327,7 @@
   tBTA_AV_EDR edr_;
   uint8_t flags_;
   bool self_initiated_connection_;
+  bool is_silenced_;
 };
 
 class BtifAvSource {
@@ -385,6 +389,54 @@
   const RawAddress& ActivePeer() const { return active_peer_; }
 
   /**
+   * Check whether peer is silenced
+   *
+   * @param peer_address the peer to check
+   * @return true on silence mode enabled, otherwise false
+   */
+  bool IsPeerSilenced(const RawAddress& peer_address) {
+    if (peer_address.IsEmpty()) {
+      return false;
+    }
+    BtifAvPeer* peer = FindPeer(peer_address);
+    if (peer == nullptr) {
+      BTIF_TRACE_WARNING("%s: peer is null", __func__);
+      return false;
+    }
+    if (!peer->IsConnected()) {
+      BTIF_TRACE_WARNING("%s: peer is not connected", __func__);
+      return false;
+    }
+    return peer->IsInSilenceMode();
+  }
+
+  /**
+   * Set peer silence mode
+   *
+   * @param peer_address the peer to set
+   * @param silence true on enable silence mode, false on disable
+   * @return true on success, otherwise false
+   */
+  bool SetSilencePeer(const RawAddress& peer_address, const bool silence) {
+    if (peer_address.IsEmpty()) {
+      return false;
+    }
+    LOG_INFO(LOG_TAG, "%s: peer: %s", __PRETTY_FUNCTION__,
+             peer_address.ToString().c_str());
+    BtifAvPeer* peer = FindPeer(peer_address);
+    if (peer == nullptr) {
+      BTIF_TRACE_WARNING("%s: peer is null", __func__);
+      return false;
+    }
+    if (!peer->IsConnected()) {
+      BTIF_TRACE_WARNING("%s: peer is not connected", __func__);
+      return false;
+    }
+    peer->SetSilence(silence);
+    return true;
+  }
+
+  /**
    * Set the active peer.
    *
    * @param peer_address the active peer address or RawAddress::kEmpty to
@@ -463,6 +515,7 @@
   bool a2dp_offload_enabled_;
   int max_connected_peers_;
   std::map<RawAddress, BtifAvPeer*> peers_;
+  std::set<RawAddress> silenced_peers_;
   RawAddress active_peer_;
   std::map<uint8_t, tBTA_AV_HNDL> peer_id2bta_handle_;
 };
@@ -829,6 +882,7 @@
 bt_status_t BtifAvPeer::Init() {
   alarm_free(av_open_on_rc_timer_);
   av_open_on_rc_timer_ = alarm_new("btif_av_peer.av_open_on_rc_timer");
+  is_silenced_ = false;
 
   state_machine_.Start();
   return BT_STATUS_SUCCESS;
@@ -2586,6 +2640,16 @@
   return BT_STATUS_SUCCESS;
 }
 
+static void set_source_silence_peer_int(const RawAddress& peer_address,
+                                        bool silence) {
+  BTIF_TRACE_EVENT("%s: peer_address=%s, silence=%s", __func__,
+                   peer_address.ToString().c_str(), silence ? "true" : "false");
+  if (!btif_av_source.SetSilencePeer(peer_address, silence)) {
+    BTIF_TRACE_ERROR("%s: Error setting silence state to %s", __func__,
+                     peer_address.ToString().c_str());
+  }
+}
+
 // Set the active peer
 static void set_active_peer_int(uint8_t peer_sep,
                                 const RawAddress& peer_address) {
@@ -2672,6 +2736,18 @@
                             peer_address, kBtaHandleUnknown, btif_av_event));
 }
 
+static bt_status_t src_set_silence_sink(const RawAddress& peer_address,
+                                        bool silence) {
+  BTIF_TRACE_EVENT("%s: Peer %s", __func__, peer_address.ToString().c_str());
+  if (!btif_av_source.Enabled()) {
+    BTIF_TRACE_WARNING("%s: BTIF AV Source is not enabled", __func__);
+    return BT_STATUS_NOT_READY;
+  }
+
+  return do_in_main_thread(FROM_HERE, base::Bind(&set_source_silence_peer_int,
+                                                 peer_address, silence));
+}
+
 static bt_status_t src_set_active_sink(const RawAddress& peer_address) {
   BTIF_TRACE_EVENT("%s: Peer %s", __func__, peer_address.ToString().c_str());
 
@@ -2718,6 +2794,7 @@
     init_src,
     src_connect_sink,
     src_disconnect_sink,
+    src_set_silence_sink,
     src_set_active_sink,
     codec_config_src,
     cleanup_src,
@@ -3097,3 +3174,7 @@
 bool btif_av_is_a2dp_offload_enabled() {
   return btif_av_source.A2dpOffloadEnabled();
 }
+
+bool btif_av_is_peer_silenced(const RawAddress& peer_address) {
+  return btif_av_source.IsPeerSilenced(peer_address);
+}
diff --git a/include/hardware/bt_av.h b/include/hardware/bt_av.h
index 5cdf1c2..d38beaa 100644
--- a/include/hardware/bt_av.h
+++ b/include/hardware/bt_av.h
@@ -313,6 +313,9 @@
   /** dis-connect from headset */
   bt_status_t (*disconnect)(const RawAddress& bd_addr);
 
+  /** sets the connected device silence state */
+  bt_status_t (*set_silence_device)(const RawAddress& bd_addr, bool silence);
+
   /** sets the connected device as active */
   bt_status_t (*set_active_device)(const RawAddress& bd_addr);
 
diff --git a/profile/avrcp/avrcp_internal.h b/profile/avrcp/avrcp_internal.h
index c2e6549..4aeffdf 100644
--- a/profile/avrcp/avrcp_internal.h
+++ b/profile/avrcp/avrcp_internal.h
@@ -82,6 +82,7 @@
 class A2dpInterface {
  public:
   virtual RawAddress active_peer() = 0;
+  virtual bool is_peer_in_silence_mode(const RawAddress& peer_address) = 0;
 
   virtual ~A2dpInterface() = default;
-};
\ No newline at end of file
+};
diff --git a/profile/avrcp/device.cc b/profile/avrcp/device.cc
index c616d17..6956513 100644
--- a/profile/avrcp/device.cc
+++ b/profile/avrcp/device.cc
@@ -67,6 +67,10 @@
   return address_ == a2dp_interface_->active_peer();
 }
 
+bool Device::IsInSilenceMode() const {
+  return a2dp_interface_->is_peer_in_silence_mode(address_);
+}
+
 void Device::VendorPacketHandler(uint8_t label,
                                  std::shared_ptr<VendorPacket> pkt) {
   CHECK(media_interface_);
@@ -466,7 +470,7 @@
   // We still try to send updates while music is playing to the non active
   // device even though the device thinks the music is paused. This makes
   // the status bar on the remote device move.
-  if (status.state == PlayState::PLAYING) {
+  if (status.state == PlayState::PLAYING && !IsInSilenceMode()) {
     DEVICE_VLOG(0) << __func__ << ": Queue next play position update";
     play_pos_update_cb_.Reset(base::Bind(&Device::HandlePlayPosUpdate,
                                          weak_ptr_factory_.GetWeakPtr()));
@@ -1145,9 +1149,12 @@
 }
 
 void Device::SendMediaUpdate(bool metadata, bool play_status, bool queue) {
+  bool is_silence = IsInSilenceMode();
+
   CHECK(media_interface_);
   DEVICE_VLOG(4) << __func__ << ": Metadata=" << metadata
-                 << " : play_status= " << play_status << " : queue=" << queue;
+                 << " : play_status= " << play_status << " : queue=" << queue
+                 << " ; is_silence=" << is_silence;
 
   if (queue) {
     HandleNowPlayingUpdate();
@@ -1155,7 +1162,9 @@
 
   if (play_status) {
     HandlePlayStatusUpdate();
-    HandlePlayPosUpdate();
+    if (!is_silence) {
+      HandlePlayPosUpdate();
+    }
   }
 
   if (metadata) HandleTrackUpdate();
diff --git a/profile/avrcp/device.h b/profile/avrcp/device.h
index 363f68d..e789706 100644
--- a/profile/avrcp/device.h
+++ b/profile/avrcp/device.h
@@ -75,6 +75,11 @@
   bool Disconnect();
 
   /**
+   * Returns true if the current device is silenced.
+   */
+  bool IsInSilenceMode() const;
+
+  /**
    * Returns true if the current device is active.
    */
   bool IsActive() const;
diff --git a/profile/avrcp/tests/avrcp_test_helper.h b/profile/avrcp/tests/avrcp_test_helper.h
index 16fc31f..edf3592 100644
--- a/profile/avrcp/tests/avrcp_test_helper.h
+++ b/profile/avrcp/tests/avrcp_test_helper.h
@@ -76,6 +76,7 @@
   MOCK_METHOD1(event_open, void(const RawAddress&));
   MOCK_METHOD1(event_close, void(const RawAddress&));
   MOCK_METHOD0(active_peer, RawAddress());
+  MOCK_METHOD1(is_peer_in_silence_mode, bool(const RawAddress&));
 };
 
 class MockSdpInterface : public SdpInterface {