[AudioManager] Add properties for the preferred device (#987)
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia / AudioManager / AudioStreamPolicy.cs
old mode 100755 (executable)
new mode 100644 (file)
index b914c9c..5a50f73
@@ -1,4 +1,4 @@
- /*
+/*
  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
  *
  * Licensed under the Apache License, Version 2.0 (the License);
  */
 
 using System;
+using System.Diagnostics;
 
 namespace Tizen.Multimedia
 {
-    internal static class AudioStreamPolicyLog
-    {
-        internal const string Tag = "Tizen.Multimedia.AudioStreamPolicy";
-    }
-
     /// <summary>
-    /// The Stream Policy API provides functions to control a sound stream.
+    /// Provides the ability to control the sound stream.
     /// </summary>
+    /// <since_tizen> 3 </since_tizen>
     public class AudioStreamPolicy : IDisposable
     {
-        private static int _focusStateWatchCounter = 0;
-        private static EventHandler<FocusStateChangedEventArgs> _focusStateWatchForPlayback;
-        private static EventHandler<FocusStateChangedEventArgs> _focusStateWatchForRecording;
-        private static Interop.SoundStreamFocusStateWatchCallback _focusStateWatchCallback;
-        private static int _focusWatchCbId;
-
-        private IntPtr _streamInfo;
-        private AudioStreamType _streamType;
+        private AudioStreamPolicyHandle _handle;
         private bool _disposed = false;
-        private EventHandler<StreamFocusStateChangedEventArgs> _focusStateChanged;
-        private Interop.SoundStreamFocusStateChangedCallback _focusStateChangedCallback;
+        private Interop.AudioStreamPolicy.FocusStateChangedCallback _focusStateChangedCallback;
+        private static AudioDevice _inputDevice = null;
+        private static AudioDevice _outputDevice = null;
+        private const string Tag = "Tizen.Multimedia.AudioStreamPolicy";
 
         /// <summary>
-        /// Creates and returns an AudioStreamPolicy object
+        /// Initializes a new instance of the <see cref="AudioStreamPolicy"/> class with <see cref="AudioStreamType"/>.
         /// </summary>
         /// <remarks>
-        /// To apply the stream policy according to this stream information, this object should be passed to other APIs
-        /// related to playback or recording. (e.g., player, wav-player, audio-io, etc.)
+        /// To apply the stream policy according to this stream information, the AudioStreamPolicy should
+        /// be passed to other APIs related to playback or recording. (For example., <see cref="T:Tizen.Multimedia.Player"/>,
+        /// <see cref="T:Tizen.Multimedia.WavPlayer"/> , etc.)
         /// </remarks>
-        /// <param name="streamType">Type of sound stream for which policy needs to be created</param>
-        /// <returns>StreamPolicy object</returns>
+        /// <param name="streamType">The type of the sound stream for which the policy needs to be created.</param>
+        /// <exception cref="ArgumentException"><paramref name="streamType"/> is invalid.</exception>
+        /// <since_tizen> 3 </since_tizen>
         public AudioStreamPolicy(AudioStreamType streamType)
         {
-            _streamType = streamType;
+            ValidationUtil.ValidateEnum(typeof(AudioStreamType), streamType, nameof(streamType));
 
-            _focusStateChangedCallback = (IntPtr streamInfo, AudioStreamFocusOptions focusMask, AudioStreamFocusState focusState, int reason, int audioStreamBehavior, string extraInfo, IntPtr userData) => {
-                StreamFocusStateChangedEventArgs eventArgs = new StreamFocusStateChangedEventArgs((AudioStreamFocusChangedReason)reason, extraInfo);
-                _focusStateChanged?.Invoke(this, eventArgs);
+            _focusStateChangedCallback = (IntPtr streamInfo, AudioStreamFocusOptions focusMask,
+                AudioStreamFocusState state, AudioStreamFocusChangedReason reason, AudioStreamBehaviors behaviors,
+                string extraInfo, IntPtr _) =>
+            {
+                FocusStateChanged?.Invoke(this,
+                    new AudioStreamPolicyFocusStateChangedEventArgs(focusMask, state, reason, behaviors, extraInfo));
             };
-            int ret = Interop.AudioStreamPolicy.CreateStreamInformation((int)streamType, _focusStateChangedCallback, IntPtr.Zero, out _streamInfo);
-            AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to create stream information");
-        }
 
-        ~AudioStreamPolicy()
-        {
-            Dispose(false);
+            Interop.AudioStreamPolicy.Create(streamType, _focusStateChangedCallback,
+                IntPtr.Zero, out _handle).ThrowIfError("Unable to create stream information");
+
+            Debug.Assert(_handle != null);
         }
 
         /// <summary>
-        /// Registers the watch function to be invoked when the focus state for each sound stream type is changed regardless of the process.
+        /// Occurs when the state of focus that belongs to the current AudioStreamPolicy is changed.
+        /// </summary>
         /// <remarks>
-        /// Remarks: You can set this only once per process.
+        /// The event is raised in the internal thread.
         /// </remarks>
-        /// </summary>
-        public static event EventHandler<FocusStateChangedEventArgs> PlaybackFocusStateWatch {
-            add {
-                Tizen.Log.Info(AudioStreamPolicyLog.Tag, "############# _focusStateWatchCounter" + _focusStateWatchCounter);
-                if(_focusStateWatchCounter == 0) {
-                    RegisterFocusStateWatchEvent();
-                }
-                _focusStateWatchCounter++;
-                _focusStateWatchForPlayback += value;
-            }
-            remove {
-                Tizen.Log.Info(AudioStreamPolicyLog.Tag, "############# _focusStateWatchCounter" + _focusStateWatchCounter);
-                _focusStateWatchForPlayback -= value;
-                _focusStateWatchCounter--;
-                if(_focusStateWatchCounter == 0) {
-                    UnregisterFocusStateWatch();
-                }
-            }
-        }
+        /// <since_tizen> 4 </since_tizen>
+        public event EventHandler<AudioStreamPolicyFocusStateChangedEventArgs> FocusStateChanged;
 
         /// <summary>
-        /// Registers the watch function to be invoked when the focus state for each sound stream type is changed regardless of the process.
+        /// Gets the <see cref="AudioVolumeType"/>.
+        /// </summary>
         /// <remarks>
-        /// Remarks: You can set this only once per process.
+        /// If the <see cref="AudioStreamType"/> of the current AudioStreamPolicy is <see cref="AudioStreamType.Emergency"/>,
+        /// it returns <see cref="AudioVolumeType.None"/>.
         /// </remarks>
-        /// </summary>
-        public static event EventHandler<FocusStateChangedEventArgs> RecordingFocusStateWatch {
-            add {
-                if(_focusStateWatchCounter == 0) {
-                    RegisterFocusStateWatchEvent();
-                }
-                _focusStateWatchCounter++;
-                _focusStateWatchForRecording += value;
-            }
-            remove {
-                _focusStateWatchForRecording -= value;
-                _focusStateWatchCounter--;
-                if(_focusStateWatchCounter == 0) {
-                    UnregisterFocusStateWatch();
+        /// <value>The <see cref="AudioVolumeType"/> of the policy instance.</value>
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <since_tizen> 3 </since_tizen>
+        public AudioVolumeType VolumeType
+        {
+            get
+            {
+                var ret = Interop.AudioStreamPolicy.GetSoundType(Handle, out var type);
+                if (ret == AudioManagerError.NoData)
+                {
+                    return AudioVolumeType.None;
                 }
+
+                ret.ThrowIfError("Failed to get volume type");
+
+                return type;
             }
         }
 
+        private AudioStreamFocusState GetFocusState(bool playback)
+        {
+            int ret = Interop.AudioStreamPolicy.GetFocusState(Handle, out var stateForPlayback, out var stateForRecording);
+            MultimediaDebug.AssertNoError(ret);
+
+            return playback ? stateForPlayback : stateForRecording;
+        }
+
+        /// <summary>
+        /// Gets the state of focus for the playback.
+        /// </summary>
+        /// <value>The state of focus for playback.</value>
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <since_tizen> 3 </since_tizen>
+        public AudioStreamFocusState PlaybackFocusState => GetFocusState(true);
+
         /// <summary>
-        /// Registers function to be called when the state of focus that belongs to the current 
-        /// streamInfo is changed.
+        /// Gets the state of focus for the recording.
         /// </summary>
+        /// <value>The state of focus for recording.</value>
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <since_tizen> 3 </since_tizen>
+        public AudioStreamFocusState RecordingFocusState => GetFocusState(false);
+
+        /// <summary>
+        /// Gets or sets the auto focus reacquisition.
+        /// </summary>
+        /// <value>
+        /// true if the auto focus reacquisition is enabled; otherwise, false.<br/>
+        /// The default is true.
+        /// </value>
         /// <remarks>
-        /// Remarks: This function is issued in the internal thread of the sound manager. Therefore it is recommended not to call UI update function in this function.
-        /// Postcondition : Check PlaybackFocusState and RecordingFocusState in the registered event handler to figure out how the focus state of the StreamInfo has been changed.
+        /// If you don't want to reacquire the focus you've lost automatically,
+        /// disable the focus reacquisition.
         /// </remarks>
-        public event EventHandler<StreamFocusStateChangedEventArgs> StreamFocusStateChanged {
-            add {
-                _focusStateChanged += value;
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <since_tizen> 3 </since_tizen>
+        public bool FocusReacquisitionEnabled
+        {
+            get
+            {
+                Interop.AudioStreamPolicy.GetFocusReacquisition(Handle, out var enabled).
+                    ThrowIfError("Failed to get focus reacquisition state");
+
+                return enabled;
             }
-            remove {
-                _focusStateChanged -= value;
+            set
+            {
+                Interop.AudioStreamPolicy.SetFocusReacquisition(Handle, value).
+                    ThrowIfError("Failed to set focus reacquisition");
             }
         }
 
-        /// <summary>
-        ///  The sound type of the stream information.
-        /// </summary>
-        public AudioVolumeType VolumeType {
-            get {
-                AudioVolumeType soundType;
-                int ret = Interop.AudioStreamPolicy.GetSoundType(_streamInfo, out soundType);
-                if(ret != 0) {
-                    Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Unable to get sound type:" + (AudioManagerError)ret);
-                    return AudioVolumeType.None;
+        internal AudioStreamPolicyHandle Handle
+        {
+            get
+            {
+                if (_disposed)
+                {
+                    throw new ObjectDisposedException(nameof(AudioStreamPolicy));
                 }
-                return soundType;
+                return _handle;
             }
         }
 
         /// <summary>
-        /// The state of focus for playback.
+        /// Acquires the stream focus.
         /// </summary>
-        public AudioStreamFocusState PlaybackFocusState {
-            get {
-                AudioStreamFocusState stateForPlayback;
-                AudioStreamFocusState stateForRecording;
-                int ret = Interop.AudioStreamPolicy.GetFocusState(_streamInfo, out stateForPlayback, out stateForRecording);
-                if(ret != 0) {
-                    Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Unable to get focus state" + (AudioManagerError)ret);
-                    return AudioStreamFocusState.Released;
-                }
-                return stateForPlayback;
+        /// <param name="options">The focuses that you want to acquire.</param>
+        /// <param name="behaviors">The requesting behaviors.</param>
+        /// <param name="extraInfo">The extra information for this request. This value can be null.</param>
+        /// <exception cref="ArgumentException"><paramref name="options"/> is zero.</exception>
+        /// <exception cref="ArgumentOutOfRangeException">
+        ///     <paramref name="options"/> contain a invalid bit.<br/>
+        ///     -or-<br/>
+        ///     <paramref name="behaviors"/> contain a invalid bit.
+        /// </exception>
+        /// <exception cref="InvalidOperationException">The focus has already been acquired.</exception>
+        /// <exception cref="AudioPolicyException">Called in <see cref="FocusStateChanged"/> raised by releasing focus.</exception>
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <since_tizen> 3 </since_tizen>
+        public void AcquireFocus(AudioStreamFocusOptions options, AudioStreamBehaviors behaviors, string extraInfo)
+        {
+            if (options == 0)
+            {
+                throw new ArgumentException("options can't be zero.", nameof(options));
+            }
+
+            if (options.IsValid() == false)
+            {
+                throw new ArgumentOutOfRangeException(nameof(options), options, "options contains a invalid bit.");
             }
+
+            if (behaviors.IsValid() == false)
+            {
+                throw new ArgumentOutOfRangeException(nameof(behaviors), behaviors, "behaviors contains a invalid bit.");
+            }
+
+            Interop.AudioStreamPolicy.AcquireFocus(Handle, options, behaviors, extraInfo).
+                ThrowIfError("Failed to acquire focus");
         }
 
         /// <summary>
-        /// The state of focus for recording.
+        /// Releases the acquired focus.
         /// </summary>
-        public AudioStreamFocusState RecordingFocusState {
-            get {
-                AudioStreamFocusState stateForPlayback;
-                AudioStreamFocusState stateForRecording;
-                int ret = Interop.AudioStreamPolicy.GetFocusState(_streamInfo, out stateForPlayback, out stateForRecording);
-                if(ret != 0) {
-                    Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Unable to get focus state" + (AudioManagerError)ret);
-                    return AudioStreamFocusState.Released;
-                }
-                return stateForRecording;
+        /// <param name="options">The focus mask that you want to release.</param>
+        /// <param name="behaviors">The requesting behaviors.</param>
+        /// <param name="extraInfo">The extra information for this request. This value can be null.</param>
+        /// <exception cref="ArgumentException"><paramref name="options"/> is zero.</exception>
+        /// <exception cref="ArgumentOutOfRangeException">
+        ///     <paramref name="options"/> contain a invalid bit.<br/>
+        ///     -or-<br/>
+        ///     <paramref name="behaviors"/> contain a invalid bit.
+        /// </exception>
+        /// <exception cref="InvalidOperationException">The focus has not been acquired.</exception>
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <since_tizen> 3 </since_tizen>
+        public void ReleaseFocus(AudioStreamFocusOptions options, AudioStreamBehaviors behaviors, string extraInfo)
+        {
+            if (options == 0)
+            {
+                throw new ArgumentException("options can't be zero.", nameof(options));
+            }
+
+            if (options.IsValid() == false)
+            {
+                throw new ArgumentOutOfRangeException(nameof(options), options, "options contains a invalid bit.");
+            }
+
+            if (behaviors.IsValid() == false)
+            {
+                throw new ArgumentOutOfRangeException(nameof(behaviors), behaviors, "behaviors contains a invalid bit.");
             }
+
+            Interop.AudioStreamPolicy.ReleaseFocus(Handle, options, behaviors, extraInfo).
+                ThrowIfError("Failed to release focus");
         }
 
         /// <summary>
-        /// Auto focus reacquisition property
+        /// Applies the stream routing.
         /// </summary>
         /// <remarks>
-        /// The focus reacquistion is set as default. If you don't want to reacquire the focus you've lost automatically, disable the focus reacqusition setting by using this API and vice versa.
+        /// If the stream has not been made yet, this will be applied when the stream starts to play.
         /// </remarks>
-        public bool FocusReacquisitionEnabled {
-            get {
-                bool enabled;
-                int ret = Interop.AudioStreamPolicy.GetFocusReacquisition(_streamInfo, out enabled);
-                if(ret != 0) {
-                    Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Unable to get focus reacquisition" + (AudioManagerError)ret);
-                    return true;
-                }
-                return enabled;
-            }
-            set {
-                int ret = Interop.AudioStreamPolicy.SetFocusReacquisition(_streamInfo, value);
-                AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to set focus reacquisition");
-            }
-        }
-
-        public IntPtr Handle {
-            get {
-                return _streamInfo;
-            }
+        /// <seealso cref="AddDeviceForStreamRouting(AudioDevice)"/>
+        /// <seealso cref="RemoveDeviceForStreamRouting(AudioDevice)"/>
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <since_tizen> 3 </since_tizen>
+        public void ApplyStreamRouting()
+        {
+            Interop.AudioStreamPolicy.ApplyStreamRouting(Handle).ThrowIfError("Failed to apply stream routing");
         }
 
         /// <summary>
-        /// Acquires the stream focus.
+        /// Adds a device for the stream routing.
         /// </summary>
-        /// <param name="options">The focus mask that user wants to acquire</param>
-        /// <param name="audioStreamBehavior">The required action for releaser</param>
-        /// <param name="extraInformation">The Extra information for this request (optional, this can be null)</param>
+        /// <param name="device">The device to add.</param>
         /// <remarks>
-        /// Do not call this API within event handlers of FocuStateChanged and StreamFocusStateWatch else it will throw and exception
+        /// The available <see cref="AudioStreamType"/> is <see cref="AudioStreamType.Voip"/> and <see cref="AudioStreamType.MediaExternalOnly"/>.
         /// </remarks>
-        public void AcquireFocus(AudioStreamFocusOptions options, AudioStreamBehavior audioStreamBehavior, string extraInformation)
+        /// <exception cref="InvalidOperationException">
+        ///     The device is not connected.<br/>
+        ///     -or-<br/>
+        ///     An internal error occurs.
+        /// </exception>
+        /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
+        /// <exception cref="AudioPolicyException"><see cref="AudioStreamType"/> of <paramref name="device"/> is unavailable for this.</exception>
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <seealso cref="AudioManager.GetConnectedDevices()"/>
+        /// <seealso cref="ApplyStreamRouting"/>
+        /// <since_tizen> 3 </since_tizen>
+        public void AddDeviceForStreamRouting(AudioDevice device)
         {
-            int ret = Interop.AudioStreamPolicy.AcquireFocus(_streamInfo, options, (int)audioStreamBehavior, extraInformation);
-            Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Acquire focus return: " + ret);
-            AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to acquire focus");
+            if (device == null)
+            {
+                throw new ArgumentNullException(nameof(device));
+            }
+
+            var ret = Interop.AudioStreamPolicy.AddDeviceForStreamRouting(Handle, device.Id);
+
+            if (ret == AudioManagerError.NoData)
+            {
+                throw new InvalidOperationException("The device seems not connected.");
+            }
+
+            ret.ThrowIfError("Failed to add device for stream routing");
         }
 
         /// <summary>
-        /// Releases the acquired focus.
+        /// Removes the device for the stream routing.
         /// </summary>
-        /// <param name="options">The focus mask that user wants to release</param>
-        /// <param name="audioStreamBehavior">The required action for acquirer</param>
-        /// <param name="extraInformation">he Extra information for this request (optional, this can be null)</param>
+        /// <param name="device">The device to remove.</param>
         /// <remarks>
-        /// Do not call this API within event handlers of FocuStateChanged and StreamFocusStateWatch else it will throw and exception
+        /// The available <see cref="AudioStreamType"/> is <see cref="AudioStreamType.Voip"/> and <see cref="AudioStreamType.MediaExternalOnly"/>.
         /// </remarks>
-        public void ReleaseFocus(AudioStreamFocusOptions options, AudioStreamBehavior audioStreamBehavior, string extraInformation)
+        /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <seealso cref="AudioManager.GetConnectedDevices()"/>
+        /// <since_tizen> 3 </since_tizen>
+        public void RemoveDeviceForStreamRouting(AudioDevice device)
         {
-            int ret = Interop.AudioStreamPolicy.ReleaseFocus(_streamInfo, options, (int)audioStreamBehavior, extraInformation);
-            Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Release focus return: " + ret);
-            AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to release focus");
+            if (device == null)
+            {
+                throw new ArgumentNullException(nameof(device));
+            }
+
+            Interop.AudioStreamPolicy.RemoveDeviceForStreamRouting(Handle, device.Id).
+                ThrowIfError("Failed to remove device for stream routing");
         }
 
         /// <summary>
-        /// Applies the stream routing.
+        /// Gets or sets the preferred input device.
         /// </summary>
+        /// <value>
+        /// The <see cref="AudioDevice"/> instance.<br/>
+        /// The default is null which means any device is not set on this property.
+        /// </value>
         /// <remarks>
-        /// If the stream has not been made yet, this setting will be applied when the stream starts to play.
-        /// Precondition: Call AddDeviceForStreamRouting() before calling this function.
+        /// This property is to set a specific built-in device when the system has multiple devices of the same built-in device type.
+        /// When there's only one device for a built-in device type in the system, nothing will happen even if this property is set successfully.
         /// </remarks>
-        public void ApplyStreamRouting()
+        /// <exception cref="ArgumentException">A device is not for input.</exception>
+        /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
+        /// <exception cref="AudioPolicyException">A device is not supported by this <see cref="AudioStreamPolicy"/> instance.</exception>
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <seealso cref="AudioManager.GetConnectedDevices()"/>
+        /// <since_tizen> 6 </since_tizen>
+        public AudioDevice PreferredInputDevice
         {
-            int ret = Interop.AudioStreamPolicy.ApplyStreamRouting(_streamInfo);
-            Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Apply Routing: " + (AudioManagerError)ret);
-            AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to apply stream routing");
+            get
+            {
+                /* This P/Invoke intends to validate if the core audio system
+                 * is normal. Otherwise, it'll throw an error here. */
+                Interop.AudioStreamPolicy.GetPreferredDevice(Handle, out var inDeviceId, out _).
+                    ThrowIfError("Failed to get preferred input device");
+
+                Log.Debug(Tag, $"preferred input device id:{inDeviceId}");
+
+                return _inputDevice;
+            }
+            set
+            {
+                Interop.AudioStreamPolicy.SetPreferredDevice(Handle, AudioDeviceIoDirection.Input, value?.Id ?? 0).
+                    ThrowIfError("Failed to set preferred input device");
+
+                _inputDevice = value;
+            }
         }
 
         /// <summary>
-        /// Adds the device to the stream information for the stream routing.
+        /// Gets or sets the preferred output device.
         /// </summary>
+        /// <value>
+        /// The <see cref="AudioDevice"/> instance.<br/>
+        /// The default is null which means any device is not set on this property.
+        /// </value>
         /// <remarks>
-        /// Remarks: Use SoundManager.GetCurrentDeviceList() to get the device.
-        /// The available types of the StreamInfo for this API are SoundStreamTypeVoip and SoundStreamTypeMediaExternalOnly.
-        /// Postcondition: You can apply this setting by calling ApplyStreamRouting().
+        /// This property is to set a specific built-in device when the system has multiple devices of the same built-in device type.
+        /// When there's only one device for a built-in device type in the system, nothing will happen even if this property is set successfully.
         /// </remarks>
-        /// <param name="soundDevice">The device item from the current sound devices list.</param>
-        public void AddDeviceForStreamRouting(AudioDevice soundDevice)
+        /// <exception cref="ArgumentException">A device is not for output.</exception>
+        /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
+        /// <exception cref="AudioPolicyException">A device is not supported by this <see cref="AudioStreamPolicy"/> instance.</exception>
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <seealso cref="AudioManager.GetConnectedDevices()"/>
+        /// <since_tizen> 6 </since_tizen>
+        public AudioDevice PreferredOutputDevice
         {
-            int ret = Interop.AudioStreamPolicy.AddDeviceForStreamRouting(_streamInfo, soundDevice.Handle);
-            Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Add stream routing: " + (AudioManagerError)ret);
+            get
+            {
+                /* This P/Invoke intends to validate if the core audio system
+                 * is normal. Otherwise, it'll throw an error here. */
+                Interop.AudioStreamPolicy.GetPreferredDevice(Handle, out _, out var outDeviceId).
+                    ThrowIfError("Failed to get preferred output device");
 
-            AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to add device for stream routing");
+                Log.Debug(Tag, $"preferred output device id:{outDeviceId}");
+
+                return _outputDevice;
+            }
+            set
+            {
+                Interop.AudioStreamPolicy.SetPreferredDevice(Handle, AudioDeviceIoDirection.Output, value?.Id ?? 0).
+                    ThrowIfError("Failed to set preferred output device");
+
+                _outputDevice = value;
+            }
         }
 
         /// <summary>
-        /// Removes the device to the stream information for the stream routing.
+        /// Checks if any stream from the current AudioStreamPolicy is using the device.
         /// </summary>
+        /// <returns>true if any audio stream from the current AudioStreamPolicy is using the device; otherwise, false.</returns>
+        /// <param name="device">The device to be checked.</param>
         /// <remarks>
-        /// Remarks: Use SoundManager.GetCurrentDeviceList() to get the device.
-        /// The available types of the StreamInfo for this API are SoundStreamTypeVoip and SoundStreamTypeMediaExternalOnly.
-        /// Postcondition: You can apply this setting by calling ApplyStreamRouting().
+        /// The AudioStreamPolicy can be applied to each playback or recording stream via other API set.
+        /// (For example., <see cref="T:Tizen.Multimedia.Player"/>, <see cref="T:Tizen.Multimedia.WavPlayer"/>,
+        /// <see cref="T:Tizen.Multimedia.AudioPlayback"/>, <see cref="T:Tizen.Multimedia.AudioCapture"/>, etc.)
+        /// This method returns true only when the device is used for the stream which meets to the two conditions.
+        /// One is that the current AudioStreamPolicy sets a audio route path to the device and the other is that the playback
+        /// or recording stream from other API set should have already started to prepare or to play.(It depends on the API set.)
         /// </remarks>
-        /// <param name="soundDevice">The device item from the current sound devices list.</param>
-        public void RemoveDeviceForStreamRouting(AudioDevice soundDevice)
+        /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
+        /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
+        /// <exception cref="ObjectDisposedException">The <see cref="AudioStreamPolicy"/> has already been disposed of.</exception>
+        /// <seealso cref="AudioManager.GetConnectedDevices()"/>
+        /// <since_tizen> 6 </since_tizen>
+        public bool HasStreamOnDevice(AudioDevice device)
         {
-            int ret = Interop.AudioStreamPolicy.RemoveDeviceForStreamRouting(_streamInfo, soundDevice.Handle);
-            Tizen.Log.Info(AudioStreamPolicyLog.Tag, "Remove stream routing: " + (AudioManagerError)ret);
-            AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to remove device for stream routing");
+            if (device == null)
+            {
+                throw new ArgumentNullException(nameof(device));
+            }
+
+            var ret = Interop.AudioStreamPolicy.IsStreamOnDevice(Handle, device.Id, out var isOn);
+            ret.ThrowIfError("Failed to check stream on device");
+
+            return isOn;
         }
 
+        /// <summary>
+        /// Releases all resources used by the <see cref="AudioStreamPolicy"/>.
+        /// </summary>
+        /// <since_tizen> 3 </since_tizen>
         public void Dispose()
         {
             Dispose(true);
             GC.SuppressFinalize(this);
         }
 
+        /// <summary>
+        /// Releases the unmanaged resources used by the <see cref="AudioStreamPolicy"/>.
+        /// </summary>
+        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
+        /// <since_tizen> 3 </since_tizen>
         protected virtual void Dispose(bool disposing)
         {
-            if(!_disposed) {
-                if(disposing) {
-                    // to be used if there are any other disposable objects
-                }
-                if(_streamInfo != IntPtr.Zero) {
-                    Interop.AudioStreamPolicy.DestroyStreamInformation(_streamInfo);  // Destroy the handle
-                    _streamInfo = IntPtr.Zero;
+            if (_disposed)
+            {
+                return;
+            }
+
+            if (disposing)
+            {
+                if (_handle != null)
+                {
+                    _handle.Dispose();
                 }
                 _disposed = true;
             }
         }
 
-        private static void RegisterFocusStateWatchEvent()
+        #region Static events
+
+        private static bool _isWatchCallbackRegistered;
+        private static EventHandler<StreamFocusStateChangedEventArgs> _streamFocusStateChanged;
+        private static Interop.AudioStreamPolicy.FocusStateWatchCallback _focusStateWatchCallback;
+        private static readonly object _streamFocusEventLock = new object();
+
+        /// <summary>
+        /// Occurs when the focus state for stream types is changed regardless of the process.
+        /// </summary>
+        /// <since_tizen> 3 </since_tizen>
+        public static event EventHandler<StreamFocusStateChangedEventArgs> StreamFocusStateChanged
         {
-            _focusStateWatchCallback = (int id, AudioStreamFocusOptions options, AudioStreamFocusState focusState, AudioStreamFocusChangedReason reason, string extraInfo, IntPtr userData) => {
-                Tizen.Log.Info(AudioStreamPolicyLog.Tag, "############# _Inside _focusStateWatchCallback : id = " + id + "options = " + options);
-                FocusStateChangedEventArgs eventArgs = new FocusStateChangedEventArgs(focusState, reason, extraInfo);
-                if(options == AudioStreamFocusOptions.Playback) {
-                    Tizen.Log.Info(AudioStreamPolicyLog.Tag, "############# _eventArgs =  " + eventArgs);
-                    _focusStateWatchForPlayback?.Invoke(null, eventArgs);
-                } else if(options == AudioStreamFocusOptions.Recording) {
-                    _focusStateWatchForRecording?.Invoke(null, eventArgs);
-                } else if(options == (AudioStreamFocusOptions.Playback | AudioStreamFocusOptions.Recording)) {
-                    _focusStateWatchForPlayback?.Invoke(null, eventArgs);
-                    _focusStateWatchForRecording?.Invoke(null, eventArgs);
+            add
+            {
+                lock (_streamFocusEventLock)
+                {
+                    if (_isWatchCallbackRegistered == false)
+                    {
+                        RegisterFocusStateWatch();
+                        _isWatchCallbackRegistered = true;
+                    }
+                    _streamFocusStateChanged += value;
                 }
-            };
-            int ret = Interop.AudioStreamPolicy.AddFocusStateWatchCallback(AudioStreamFocusOptions.Playback | AudioStreamFocusOptions.Recording, _focusStateWatchCallback, IntPtr.Zero, out _focusWatchCbId);
-            Tizen.Log.Info(AudioStreamPolicyLog.Tag, "############# _AddFocusStateWatchCallback : ret  =  " + ret + " ID = " + _focusWatchCbId);
-            AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to set focus state watch callback");
+            }
+            remove
+            {
+                lock (_streamFocusEventLock)
+                {
+                    _streamFocusStateChanged -= value;
+                }
+            }
         }
 
-        private static void UnregisterFocusStateWatch()
+        private static void RegisterFocusStateWatch()
         {
-            int ret = Interop.AudioStreamPolicy.RemoveFocusStateWatchCallback(_focusWatchCbId);
-            AudioManagerErrorFactory.CheckAndThrowException(ret, "Unable to unset focus state watch callback");
+            _focusStateWatchCallback = (id, options, focusState, reason, extraInfo, _) =>
+            {
+                _streamFocusStateChanged?.Invoke(null,
+                    new StreamFocusStateChangedEventArgs(options, focusState, reason, extraInfo));
+            };
+
+            Interop.AudioStreamPolicy.AddFocusStateWatchCallback(
+                AudioStreamFocusOptions.Playback | AudioStreamFocusOptions.Recording,
+                _focusStateWatchCallback, IntPtr.Zero, out var cbId).
+                ThrowIfError("Failed to initialize focus state event");
         }
+        #endregion
     }
 }