[Bluetooth][TCSACR-323] Add new Avrcp Control API (#1666)
authorSudipto Bal <wootak.jung@samsung.com>
Tue, 30 Jun 2020 09:03:02 +0000 (18:03 +0900)
committerGitHub <noreply@github.com>
Tue, 30 Jun 2020 09:03:02 +0000 (18:03 +0900)
Signed-off by: Sudipto Bal <sudipto.bal@samsung.com>
Signed-off-by: Wootak Jung <wootak.jung@samsung.com>
src/Tizen.Network.Bluetooth/Interop/Interop.Bluetooth.cs
src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControl.cs [new file with mode: 0644]
src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControlImpl.cs [new file with mode: 0644]
src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothEnumerations.cs
src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothEventArgs.cs
src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothStructs.cs

index 1c9cfad..2a80d0f 100644 (file)
@@ -90,6 +90,15 @@ internal static partial class Interop
         internal delegate void ScanModeChangedCallback(int scan, IntPtr userData);
 
         [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void AvrcpControlConnectionChangedCallback(bool connected, string remoteAddress, IntPtr userData);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void PositionChangedCallback(uint position, IntPtr userData);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void PlayStatusChangedCallback(int play_status, IntPtr userData);
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void TrackInfoChangedCallback([MarshalAs(UnmanagedType.Struct)]ref TrackInfoStruct track, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
         internal delegate void ConnectionChangedCallback(int result, bool connected, string deviceAddress, IntPtr userData);
         [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
         internal delegate void ClientCharacteristicValueChangedCallback(IntPtr characteristicHandle, string value, int len, IntPtr userData);
@@ -512,6 +521,92 @@ internal static partial class Interop
         [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_unset_scan_mode_changed_cb")]
         internal static extern int UnsetScanModeChangedCallback();
 
+        // Bluetooth AVRCP Control
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_initialize")]
+        internal static extern int AvrcpControlInitialize(AvrcpControlConnectionChangedCallback callback, IntPtr userData);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_deinitialize")]
+        internal static extern int AvrcpControlDeinitialize();
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_connect")]
+        internal static extern int AvrcpControlConnect(string address);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_disconnect")]
+        internal static extern int AvrcpControlDisconnect(string address);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_set_equalizer_state")]
+        internal static extern int SetEqualizerState(EqualizerState state);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_equalizer_state")]
+        internal static extern int GetEqualizerState(out EqualizerState state);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_set_repeat_mode")]
+        internal static extern int SetRepeatMode(RepeatMode mode);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_repeat_mode")]
+        internal static extern int GetRepeatMode(out RepeatMode mode);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_set_shuffle_mode")]
+        internal static extern int SetShuffleMode(ShuffleMode mode);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_shuffle_mode")]
+        internal static extern int GetShuffleMode(out ShuffleMode mode);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_set_scan_mode")]
+        internal static extern int SetScanMode(ScanMode mode);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_scan_mode")]
+        internal static extern int GetScanMode(out ScanMode mode);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_position")]
+        internal static extern int GetPosition(out uint position);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_play_status")]
+        internal static extern int GetPlayStatus(out PlayerState state);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_track_info")]
+        internal static extern int GetTrackInfo(out IntPtr infoptr);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_free_track_info")]
+        internal static extern int FreeTrackInfo(IntPtr infoptr);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_send_player_command")]
+        internal static extern int SendPlayerCommand(PlayerCommand command);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_send_player_command_to")]
+        internal static extern int SendPlayerCommandTo(PlayerCommand command, string remoteAddress);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_set_absolute_volume")]
+        internal static extern int SetAbsoluteVolume(uint volume);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_increase_volume")]
+        internal static extern int IncreaseVolume();
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_decrease_volume")]
+        internal static extern int DecreaseVolume();
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_send_delay_report")]
+        internal static extern int SendDelayReport(uint value);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_set_position_changed_cb")]
+        internal static extern int SetPositionChangedCallback(PositionChangedCallback PositionChangedCb, IntPtr userData);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_unset_position_changed_cb")]
+        internal static extern int UnsetPositionChangedCallback();
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_set_play_status_changed_cb")]
+        internal static extern int SetPlayStatusChangedCallback(PlayStatusChangedCallback PlayStatusChangedCb, IntPtr userData);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_unset_play_status_changed_cb")]
+        internal static extern int UnsetPlayStatusChangedCallback();
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_set_track_info_changed_cb")]
+        internal static extern int SetTrackInfoChangedCallback(TrackInfoChangedCallback TrackInfoChangedCb, IntPtr userData);
+
+        [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_unset_track_info_changed_cb")]
+        internal static extern int UnsetTrackInfoChangedCallback();
+
         // Bluetooth GATT
 
         [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
diff --git a/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControl.cs b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControl.cs
new file mode 100644 (file)
index 0000000..d1f7df8
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+using System;
+using System.ComponentModel;
+using System.Threading.Tasks;
+
+namespace Tizen.Network.Bluetooth
+{
+    /// <summary>
+    /// This class is used to send commands from the control device (For example, headset) to the target device (For example, media player).
+    /// </summary>
+    /// <remarks>
+    /// This class can be obtained from BluetoothDevice.GetProfile method.
+    /// </remarks>
+    /// <privilege> http://tizen.org/privilege/bluetooth </privilege>
+    /// <feature> http://tizen.org/feature/network.bluetooth.audio.controller </feature>
+    /// <since_tizen> 8 </since_tizen>
+    public class BluetoothAvrcpControl : BluetoothProfile
+    {
+        private TaskCompletionSource<bool> _taskForConnection;
+        private TaskCompletionSource<bool> _taskForDisconnection;
+        private bool disposed = false;
+        private bool _isConnected = false;
+
+        internal BluetoothAvrcpControl()
+        {
+            BluetoothAvrcpControlImpl.Instance.ConnectionChanged += OnConnectionChanged;
+            BluetoothAvrcpControlImpl.Instance.PositionChanged += OnPositionChanged;
+            BluetoothAvrcpControlImpl.Instance.PlayStateChanged += OnPlayStateChanged;
+            BluetoothAvrcpControlImpl.Instance.TrackInfoChanged += OnTrackInfoChanged;
+        }
+
+        private void OnConnectionChanged(object s, AvrcpControlConnectionChangedEventArgs e)
+        {
+            if (e.RemoteAddress != RemoteAddress)
+            {
+                return;
+            }
+
+            _isConnected = e.IsConnected;
+
+            if (_taskForConnection != null && !_taskForConnection.Task.IsCompleted)
+            {
+                if (e.IsConnected == true)
+                {
+                    _taskForConnection.SetResult(true);
+                    ConnectionStateChanged?.Invoke(this, e);
+                }
+                else
+                {
+                    _taskForConnection.SetException(BluetoothErrorFactory.CreateBluetoothException((int)BluetoothError.OperationFailed));
+                }
+                _taskForConnection = null;
+                return;
+            }
+            if (_taskForDisconnection != null && !_taskForDisconnection.Task.IsCompleted)
+            {
+                if (e.IsConnected == false)
+                {
+                    _taskForDisconnection.SetResult(true);
+                    ConnectionStateChanged?.Invoke(this, e);
+                }
+                else
+                {
+                    _taskForDisconnection.SetException(BluetoothErrorFactory.CreateBluetoothException((int)BluetoothError.OperationFailed));
+                }
+                _taskForDisconnection = null;
+            }
+        }
+
+        private void OnPositionChanged(object s, PositionChangedEventArgs e)
+        {
+            PositionChanged?.Invoke(this, e);
+        }
+
+        private void OnPlayStateChanged(object s, PlayStateChangedEventArgs e)
+        {
+            PlayStateChanged?.Invoke(this, e);
+        }
+
+        private void OnTrackInfoChanged(object s, TrackInfoChangedEventArgs e)
+        {
+            TrackInfoChanged?.Invoke(this, e);
+        }
+
+        /// <summary>
+        /// The AvrcpControlConnectionChangedEventArgs event is invoked when the connection status of device is changed.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public event EventHandler<AvrcpControlConnectionChangedEventArgs> ConnectionStateChanged;
+
+        /// <summary>
+        /// The PositionChangedEventArgs event is invoked when the play position of a track is changed.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public event EventHandler<PositionChangedEventArgs> PositionChanged;
+
+        /// <summary>
+        /// The PlayStateChangedEventArgs event is invoked when the play state of a track gets changed.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public event EventHandler<PlayStateChangedEventArgs> PlayStateChanged;
+
+        /// <summary>
+        /// The TrackInfoChangedEventArgs event is invoked when info of a track gets changed.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public event EventHandler<TrackInfoChangedEventArgs> TrackInfoChanged;
+
+        /// <summary>
+        /// Asynchronously connects the remote device
+        /// </summary>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the method fails</exception>
+        /// <since_tizen> 8 </since_tizen>
+        public Task ConnectAsync()
+        {
+            if (_taskForConnection != null && !_taskForConnection.Task.IsCompleted)
+            {
+                BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NowInProgress);
+            }
+
+            _taskForConnection = new TaskCompletionSource<bool>();
+            BluetoothAvrcpControlImpl.Instance.Connect(RemoteAddress);
+            return _taskForConnection.Task;
+        }
+
+        /// <summary>
+        /// Asynchronously disconnects the remote device
+        /// </summary>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the method fails</exception>
+        /// <since_tizen> 8 </since_tizen>
+        public Task DisconnectAsync()
+        {
+            if (_taskForDisconnection != null && !_taskForDisconnection.Task.IsCompleted)
+            {
+                BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NowInProgress);
+            }
+            _taskForDisconnection = new TaskCompletionSource<bool>();
+            BluetoothAvrcpControlImpl.Instance.Disconnect(RemoteAddress);
+            return _taskForDisconnection.Task;
+        }
+
+        /// <summary>
+        /// A property for the equalizer mode of target device.
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected.
+        /// </remarks>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or when notifying the repeat mode state to the remote device fails.
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        public EqualizerState EqualizerState
+        {
+            get
+            {
+                if (_isConnected == true)
+                {
+                    return BluetoothAvrcpControlImpl.Instance.GetEqualizerState();
+                }
+                else
+                {
+                    return EqualizerState.Off;
+                }
+            }
+            set
+            {
+                if (_isConnected == true)
+                {
+                    BluetoothAvrcpControlImpl.Instance.SetEqualizerState(value);
+                }
+            }
+        }
+
+        /// <summary>
+        /// A property for the repeat mode of target device.
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected.
+        /// </remarks>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or when notifying the repeat mode state to the remote device fails.
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        public RepeatMode RepeatMode
+        {
+            get
+            {
+                if (_isConnected == true)
+                {
+                    return BluetoothAvrcpControlImpl.Instance.GetRepeatMode();
+                }
+                else
+                {
+                    return RepeatMode.Off;
+                }
+            }
+            set
+            {
+                if (_isConnected == true)
+                {
+                    BluetoothAvrcpControlImpl.Instance.SetRepeatMode(value);
+                }
+            }
+        }
+
+        /// <summary>
+        /// A property for the suffle mode of target device.
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected.
+        /// </remarks>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or if retrieving scan mode of the remote device fails.
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        public ShuffleMode ShuffleMode
+        {
+            get
+            {
+                if (_isConnected == true)
+                {
+                    return BluetoothAvrcpControlImpl.Instance.GetShuffleMode();
+                }
+                else
+                {
+                    return ShuffleMode.Off;
+                }
+            }
+            set
+            {
+                if (_isConnected == true)
+                {
+                    BluetoothAvrcpControlImpl.Instance.SetShuffleMode(value);
+                }
+            }
+        }
+
+        /// <summary>
+        /// A property for the scan mode of target device.
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected.
+        /// </remarks>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or if retrieving scan mode of the remote device fails.
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        public ScanMode ScanMode
+        {
+            get
+            {
+                if (_isConnected == true)
+                {
+                    return BluetoothAvrcpControlImpl.Instance.GetScanMode();
+                }
+                else
+                {
+                    return ScanMode.Off;
+                }
+            }
+            set
+            {
+                if (_isConnected == true)
+                {
+                    BluetoothAvrcpControlImpl.Instance.SetScanMode(value);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets position of the track being played on the target device.
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected.
+        /// </remarks>
+        /// <returns> Play position of the track being played on the target device </returns>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or if there is an error retrieving the position of the track that is currently being played.
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        public uint GetPosition()
+        {
+            if (_isConnected == true)
+            {
+                return BluetoothAvrcpControlImpl.Instance.GetPosition();
+            }
+            else
+            {
+                BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+                return 0;
+            }
+        }
+
+        /// <summary>
+        /// Gets player state of the target device.
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected.
+        /// </remarks>
+        /// <returns> Play status of the target device </returns>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or if there happens to be an error while retrieving the player state of the target device.
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        public PlayerState GetPlayStatus()
+        {
+            if (_isConnected == true)
+            {
+                return BluetoothAvrcpControlImpl.Instance.GetPlayStatus();
+            }
+            else
+            {
+                BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+                return PlayerState.Stopped;
+            }
+        }
+
+        /// <summary>
+        /// Gets info of the track being played on the target device.
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected.
+        /// </remarks>
+        /// <returns> Info of the track being played on the target device </returns>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or if there happens to be an error while retrieving info of the track being played on the target.
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        public Track GetTrackInfo()
+        {
+            if (_isConnected == true)
+            {
+                return BluetoothAvrcpControlImpl.Instance.GetTrackInfo();
+            }
+            else
+            {
+                BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+                return null;
+            }
+        }
+
+        /// <summary>
+        /// Sends a particular play command to the target device
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected.
+        /// </remarks>
+        /// <param name="command">Command to be sent</param>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or when sending command to the target device fails.
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        public void SendPlayerCommand(PlayerCommand command)
+        {
+            if (_isConnected == true)
+            {
+                BluetoothAvrcpControlImpl.Instance.SendPlayerCommand(command);
+            }
+            else
+            {
+                BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+            }
+        }
+
+        /// <summary>
+        /// Sends a play command to a particular target device
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected.
+        /// </remarks>
+        /// <param name="command">Command to be sent</param>
+        /// <param name="remoteAddress">Address of the device to send command</param>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or when sending command to the target device fails.
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SendPlayerCommandTo(PlayerCommand command, string remoteAddress)
+        {
+            if (_isConnected == true)
+            {
+                BluetoothAvrcpControlImpl.Instance.SendPlayerCommandTo(command, remoteAddress);
+            }
+            else
+            {
+                BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+            }
+        }
+
+        /// <summary>
+        /// Sets absolute volume of target device
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected
+        /// </remarks>
+        /// <param name="volume">The volume level to be set</param>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or when setting absolute volume of the target device fails
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SetAbsoluteVolume(uint volume)
+        {
+            if (_isConnected == true)
+            {
+                BluetoothAvrcpControlImpl.Instance.SetAbsoluteVolume(volume);
+            }
+            else
+            {
+                BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+            }
+        }
+
+        /// <summary>
+        /// Increases volume of target device
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected
+        /// </remarks>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or when increasing volume of the target device fails
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void IncreaseVolume()
+        {
+            if (_isConnected == true)
+            {
+                BluetoothAvrcpControlImpl.Instance.IncreaseVolume();
+            }
+            else
+            {
+                BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+            }
+        }
+
+        /// <summary>
+        /// Decreases volume of target device
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected
+        /// </remarks>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or when decreasing volume of the target device fails
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void DecreaseVolume()
+        {
+            if (_isConnected == true)
+            {
+                BluetoothAvrcpControlImpl.Instance.DecreaseVolume();
+            }
+            else
+            {
+                BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+            }
+        }
+
+        /// <summary>
+        /// Sends delay report to the target device
+        /// </summary>
+        /// <remarks>
+        /// The remote device must be connected
+        /// </remarks>
+        /// <param name="delay">Delay to be sent to target</param>
+        /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported</exception>
+        /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+        /// or when sending delay to the target device fails
+        /// </exception>
+        /// <since_tizen> 8 </since_tizen>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SendDelayReport(uint delay)
+        {
+            if (_isConnected == true)
+            {
+                BluetoothAvrcpControlImpl.Instance.SendDelayReport(delay);
+            }
+            else
+            {
+                BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+            }
+        }
+
+        ~BluetoothAvrcpControl()
+        {
+            Dispose(false);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        private void Dispose(bool disposing)
+        {
+            if (disposed)
+                return;
+
+            if (disposing)
+            {
+                // Free managed objects.
+                BluetoothAvrcpControlImpl.Instance.ConnectionChanged -= OnConnectionChanged;
+                BluetoothAvrcpControlImpl.Instance.PositionChanged -= OnPositionChanged;
+                BluetoothAvrcpControlImpl.Instance.PlayStateChanged -= OnPlayStateChanged;
+                BluetoothAvrcpControlImpl.Instance.TrackInfoChanged -= OnTrackInfoChanged;
+            }
+            //Free unmanaged objects.
+            disposed = true;
+        }
+    }
+}
diff --git a/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControlImpl.cs b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControlImpl.cs
new file mode 100644 (file)
index 0000000..3606378
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Tizen.Network.Bluetooth
+{
+    internal class BluetoothAvrcpControlImpl :IDisposable
+    {
+        private event EventHandler<PositionChangedEventArgs> _positionChanged;
+        private event EventHandler<PlayStateChangedEventArgs> _playStateChanged;
+        private event EventHandler<TrackInfoChangedEventArgs> _trackInfoChanged;
+
+        private Interop.Bluetooth.PositionChangedCallback _positionChangedCallback;
+        private Interop.Bluetooth.PlayStatusChangedCallback _playStateChangedCallback;
+        private Interop.Bluetooth.TrackInfoChangedCallback _trackInfoChangedCallback;
+        private Interop.Bluetooth.AvrcpControlConnectionChangedCallback _connStateChangedCallback;
+
+        private static BluetoothAvrcpControlImpl _instance = new BluetoothAvrcpControlImpl();
+        private bool disposed = false;
+
+        internal event EventHandler<AvrcpControlConnectionChangedEventArgs> ConnectionChanged;
+
+        internal event EventHandler<PositionChangedEventArgs> PositionChanged
+        {
+            add
+            {
+                if (_positionChanged == null)
+                {
+                    RegisterPositionChangedEvent();
+                }
+                _positionChanged += value;
+            }
+            remove
+            {
+                _positionChanged -= value;
+                if (_positionChanged == null)
+                {
+                    UnregisterPositionChangedEvent();
+                }
+            }
+        }
+
+        internal event EventHandler<PlayStateChangedEventArgs> PlayStateChanged
+        {
+            add
+            {
+                if (_playStateChanged == null)
+                {
+                    RegisterPlayStateChangedEvent();
+                }
+                _playStateChanged += value;
+            }
+            remove
+            {
+                _playStateChanged -= value;
+                if (_playStateChanged == null)
+                {
+                    UnregisterPlayStateChangedEvent();
+                }
+            }
+        }
+
+        internal event EventHandler<TrackInfoChangedEventArgs> TrackInfoChanged
+        {
+            add
+            {
+                if (_trackInfoChanged == null)
+                {
+                    RegisterTrackInfoChangedEvent();
+                }
+                _trackInfoChanged += value;
+            }
+            remove
+            {
+                _trackInfoChanged -= value;
+                if (_trackInfoChanged == null)
+                {
+                    UnregisterTrackInfoChangedEvent();
+                }
+            }
+        }
+
+        private void RegisterPositionChangedEvent()
+        {
+            _positionChangedCallback = (uint position, IntPtr userData) =>
+            {
+                _positionChanged?.Invoke(null, new PositionChangedEventArgs(position));
+            };
+            int ret = Interop.Bluetooth.SetPositionChangedCallback(_positionChangedCallback, IntPtr.Zero);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to set position changed callback, Error - " + (BluetoothError)ret);
+            }
+        }
+
+        private void UnregisterPositionChangedEvent()
+        {
+            int ret = Interop.Bluetooth.UnsetPositionChangedCallback();
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to unset position changed callback, Error - " + (BluetoothError)ret);
+            }
+        }
+
+        private void RegisterPlayStateChangedEvent()
+        {
+            _playStateChangedCallback = (int state, IntPtr userdata) =>
+            {
+                _playStateChanged?.Invoke(null, new PlayStateChangedEventArgs((PlayerState)state));
+            };
+            int ret = Interop.Bluetooth.SetPlayStatusChangedCallback(_playStateChangedCallback, IntPtr.Zero);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to set play status changed callback, Error - " + (BluetoothError)ret);
+            }
+        }
+
+        private void UnregisterPlayStateChangedEvent()
+        {
+            int ret = Interop.Bluetooth.UnsetPlayStatusChangedCallback();
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to unset play status changed callback, Error - " + (BluetoothError)ret);
+            }
+        }
+
+        private void RegisterTrackInfoChangedEvent()
+        {
+            _trackInfoChangedCallback = (ref TrackInfoStruct track_info, IntPtr userdata) =>
+            {
+                _trackInfoChanged?.Invoke(null, new TrackInfoChangedEventArgs(new Track()
+                {
+                    Album = track_info.Album,
+                    Artist = track_info.Artist,
+                    Genre = track_info.Genre,
+                    Title = track_info.Title,
+                    TotalTracks = track_info.total_tracks,
+                    TrackNum = track_info.number,
+                    Duration = track_info.duration
+                }));
+                int ret = Interop.Bluetooth.SetTrackInfoChangedCallback(_trackInfoChangedCallback, IntPtr.Zero);
+                if (ret != (int)BluetoothError.None)
+                {
+                    Log.Error(Globals.LogTag, "Failed to set track info changed callback, Error - " + (BluetoothError)ret);
+                }
+            };
+
+        }
+
+        private void UnregisterTrackInfoChangedEvent()
+        {
+            int ret = Interop.Bluetooth.UnsetTrackInfoChangedCallback();
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to unset track info changed callback, Error - " + (BluetoothError)ret);
+            }
+        }
+
+        internal void Connect(string address)
+        {
+            int ret = Interop.Bluetooth.AvrcpControlConnect(address);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to connect " + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+
+        internal void Disconnect(string address)
+        {
+            int ret = Interop.Bluetooth.AvrcpControlDisconnect(address);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to disconnect " + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+
+        internal EqualizerState GetEqualizerState()
+        {
+            EqualizerState state;
+            int ret = Interop.Bluetooth.GetEqualizerState(out state);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to get equalizer state " + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+            return state;
+        }
+
+        internal void SetEqualizerState(EqualizerState state)
+        {
+            int ret = Interop.Bluetooth.SetEqualizerState(state);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to set equalizer state to " + state + " - " + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+
+        internal RepeatMode GetRepeatMode()
+        {
+            RepeatMode mode;
+            int ret = Interop.Bluetooth.GetRepeatMode(out mode);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to get repeat mode" + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+            return mode;
+        }
+
+        internal void SetRepeatMode(RepeatMode mode)
+        {
+            int ret = Interop.Bluetooth.SetRepeatMode(mode);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to set repeat mode to " + mode + " - " + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+
+        internal ShuffleMode GetShuffleMode()
+        {
+            ShuffleMode mode;
+            int ret = Interop.Bluetooth.GetShuffleMode(out mode);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to get shuffle mode" + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+            return mode;
+        }
+
+        internal void SetShuffleMode(ShuffleMode mode)
+        {
+            int ret = Interop.Bluetooth.SetShuffleMode(mode);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to set shuffle mode to " + mode + " - " + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+
+        internal ScanMode GetScanMode()
+        {
+            ScanMode mode;
+            int ret = Interop.Bluetooth.GetScanMode(out mode);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to get scan mode" + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+            return mode;
+        }
+
+        internal void SetScanMode(ScanMode mode)
+        {
+            int ret = Interop.Bluetooth.SetScanMode(mode);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to set scan mode to " + mode + " - " + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+
+        internal uint GetPosition()
+        {
+            uint position;
+            int ret = Interop.Bluetooth.GetPosition(out position);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to get play position" + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+            return position;
+        }
+
+        internal PlayerState GetPlayStatus()
+        {
+            PlayerState state;
+            int ret = Interop.Bluetooth.GetPlayStatus(out state);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to get play status" + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+            return state;
+        }
+
+        internal Track GetTrackInfo()
+        {
+            Track trackdata = new Track();
+            TrackInfoStruct trackinfo;
+            IntPtr infoptr;
+
+            int ret = Interop.Bluetooth.GetTrackInfo(out infoptr);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to get track data" + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+
+            trackinfo = (TrackInfoStruct)Marshal.PtrToStructure(infoptr, typeof(TrackInfoStruct));
+            trackdata.Album = trackinfo.Album;
+            trackdata.Artist = trackinfo.Artist;
+            trackdata.Genre = trackinfo.Genre;
+            trackdata.Title = trackinfo.Title;
+            trackdata.TotalTracks = trackinfo.total_tracks;
+            trackdata.TrackNum = trackinfo.number;
+            trackdata.Duration = trackinfo.duration;
+
+            ret = Interop.Bluetooth.FreeTrackInfo(infoptr);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to free track data" + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+
+            return trackdata;
+        }
+
+        internal void SendPlayerCommand(PlayerCommand command)
+        {
+            int ret = Interop.Bluetooth.SendPlayerCommand(command);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to send player command " + command + " - " + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+        internal void SendPlayerCommandTo(PlayerCommand command, string remoteAddress)
+        {
+            int ret = Interop.Bluetooth.SendPlayerCommandTo(command, remoteAddress);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to send player command " + command + " to remote address " + remoteAddress + " - " + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+
+        internal void SetAbsoluteVolume(uint volume)
+        {
+            int ret = Interop.Bluetooth.SetAbsoluteVolume(volume);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to set absolute volume to level " + volume + " - " + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+
+        internal void IncreaseVolume()
+        {
+            int ret = Interop.Bluetooth.IncreaseVolume();
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to increase volume" + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+
+        internal void DecreaseVolume()
+        {
+            int ret = Interop.Bluetooth.DecreaseVolume();
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to decrease volume" + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+
+        internal void SendDelayReport(uint delay)
+        {
+            int ret = Interop.Bluetooth.SendDelayReport(delay);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to send delay report" + (BluetoothError)ret);
+                BluetoothErrorFactory.ThrowBluetoothException(ret);
+            }
+        }
+
+        internal static BluetoothAvrcpControlImpl Instance
+        {
+            get
+            {
+                return _instance;
+            }
+        }
+
+        private BluetoothAvrcpControlImpl ()
+        {
+            ControlInitialize();
+        }
+
+        private void ControlInitialize()
+        {
+            _connStateChangedCallback = (bool connected, string remoteAddress, IntPtr userData) =>
+            {
+                ConnectionChanged?.Invoke(null, new AvrcpControlConnectionChangedEventArgs(connected, remoteAddress));
+            };
+            int ret = Interop.Bluetooth.AvrcpControlInitialize(_connStateChangedCallback, IntPtr.Zero);
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to initialize AVRCP Control, Error - " + (BluetoothError)ret);
+            }
+        }
+
+        private void ControlDeinitialize()
+        {
+            int ret = Interop.Bluetooth.AvrcpControlDeinitialize();
+            if (ret != (int)BluetoothError.None)
+            {
+                Log.Error(Globals.LogTag, "Failed to deinitialize AVRCP Control, Error - " + (BluetoothError)ret);
+            }
+        }
+
+        private void UnregisterAllEvents()
+        {
+            if (_playStateChanged == null)
+            {
+                UnregisterPlayStateChangedEvent();
+            }
+            if (_positionChanged == null)
+            {
+                UnregisterPositionChangedEvent();
+            }
+            if (_trackInfoChanged == null)
+            {
+                UnregisterTrackInfoChangedEvent();
+            }
+        }
+
+        ~BluetoothAvrcpControlImpl()
+        {
+            Dispose(false);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        private void Dispose(bool disposing)
+        {
+            if (disposed)
+                return;
+
+            if (disposing)
+            {
+                // Free managed objects.
+            }
+            //Free unmanaged objects
+            ControlDeinitialize();
+            UnregisterAllEvents();
+            disposed = true;
+        }
+    }
+}
index a57f495..cf3c788 100644 (file)
@@ -977,6 +977,42 @@ namespace Tizen.Network.Bluetooth
     }
 
     /// <summary>
+    /// Enumeration for the player command.
+    /// </summary>
+    /// <since_tizen> 8 </since_tizen>
+    public enum PlayerCommand
+    {
+        /// <summary>
+        /// Play current track
+        /// </summary>
+        Play = 1,
+        /// <summary>
+        /// Pause current track
+        /// </summary>
+        Pause,
+        /// <summary>
+        /// Stop playing track
+        /// </summary>
+        Stop,
+        /// <summary>
+        /// Go to the next track
+        /// </summary>
+        Next,
+        /// <summary>
+        /// Go to the previous track
+        /// </summary>
+        Previous,
+        /// <summary>
+        /// Fast-forward current track
+        /// </summary>
+        FastForward,
+        /// <summary>
+        /// Rewind current track
+        /// </summary>
+        Rewind
+    }
+
+    /// <summary>
     /// Enumeration for the Bluetooth LE device address types.
     /// </summary>
     /// <since_tizen> 3 </since_tizen>
index 114de7b..2db5636 100644 (file)
@@ -392,13 +392,7 @@ namespace Tizen.Network.Bluetooth
         /// A value indicating whether the device is connected.
         /// </summary>
         /// <since_tizen> 3 </since_tizen>
-        public bool IsConnected
-        {
-            get
-            {
-                return _isConnected;
-            }
-        }
+        public bool IsConnected => _isConnected;
 
         /// <summary>
         /// The device connection data.
@@ -956,6 +950,121 @@ namespace Tizen.Network.Bluetooth
     }
 
     /// <summary>
+    /// An extended EventArgs class contains the connection state and the remote device address.
+    /// </summary>
+    /// <since_tizen> 8 </since_tizen>
+    public class AvrcpControlConnectionChangedEventArgs : EventArgs
+    {
+        private bool _isConnected;
+        string _remoteAddress;
+        // Setting Values when Event is invoked
+        internal AvrcpControlConnectionChangedEventArgs(bool conn, string address)
+        {
+            _isConnected = conn;
+            _remoteAddress = address;
+        }
+
+        /// <summary>
+        /// A value indicating whether this instance is connected.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public bool IsConnected
+        {
+            get
+            {
+                return _isConnected;
+            }
+        }
+
+        /// <summary>
+        /// The remote address.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public string RemoteAddress
+        {
+            get
+            {
+                return _remoteAddress;
+            }
+        }
+    }
+
+    /// <summary>
+    /// An extended EventArgs class contains the position.
+    /// </summary>
+    /// <since_tizen> 8 </since_tizen>
+    public class PositionChangedEventArgs : EventArgs
+    {
+        private uint _pos;
+        internal PositionChangedEventArgs(uint pos)
+        {
+            _pos = pos;
+        }
+
+        /// <summary>
+        /// The current position in milliseconds.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public uint Position
+        {
+            get
+            {
+                return _pos;
+            }
+        }
+    }
+
+    /// <summary>
+    /// An extended EventArgs class contains the play state.
+    /// </summary>
+    /// <since_tizen> 8 </since_tizen>
+    public class PlayStateChangedEventArgs : EventArgs
+    {
+        private PlayerState _playState;
+        internal PlayStateChangedEventArgs(PlayerState playState)
+        {
+            _playState = playState;
+        }
+
+        /// <summary>
+        /// The current play state.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public PlayerState PlayState
+        {
+            get
+            {
+                return _playState;
+            }
+        }
+    }
+
+    /// <summary>
+    /// An extended EventArgs class contains the play state.
+    /// </summary>
+    /// <since_tizen> 8 </since_tizen>
+    public class TrackInfoChangedEventArgs : EventArgs
+    {
+        private Track _track = new Track();
+        internal TrackInfoChangedEventArgs(Track Data)
+        {
+            _track = Data;
+        }
+
+        /// <summary>
+        /// The current track data
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public Track TrackData
+        {
+            get
+            {
+                return _track;
+            }
+        }
+    }
+
+    /// <summary>
     /// An extended EventArgs class contains the changed Bluetooth LE advertising state changed information.
     /// </summary>
     /// <since_tizen> 3 </since_tizen>
index ad5f49d..1370707 100644 (file)
@@ -225,6 +225,21 @@ namespace Tizen.Network.Bluetooth
         internal IntPtr data;
     }
 
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct TrackInfoStruct
+    {
+        [MarshalAsAttribute(UnmanagedType.LPStr)]
+        internal string Title;
+        [MarshalAsAttribute(UnmanagedType.LPStr)]
+        internal string Artist;
+        [MarshalAsAttribute(UnmanagedType.LPStr)]
+        internal string Album;
+        [MarshalAsAttribute(UnmanagedType.LPStr)]
+        internal string Genre;
+        internal uint total_tracks;
+        internal uint number;
+        internal uint duration;
+    }
     internal static class BluetoothUtils
     {
         internal static BluetoothDevice ConvertStructToDeviceClass(BluetoothDeviceStruct device)