[MM-Radio] Add base code for radio
authorDinesh Dwivedi <dinesh.d@samsung.com>
Wed, 22 Feb 2017 10:19:52 +0000 (15:49 +0530)
committerDinesh Dwivedi <dinesh.d@samsung.com>
Wed, 22 Feb 2017 10:19:52 +0000 (15:49 +0530)
Change-Id: I2991e51abc09037c0c8df7d2fac9302f08a8d104
Signed-off-by: Dinesh Dwivedi <dinesh.d@samsung.com>
src/Tizen.Multimedia/Interop/Interop.ErrorCode.cs [new file with mode: 0755]
src/Tizen.Multimedia/Interop/Interop.Libraries.cs
src/Tizen.Multimedia/Interop/Interop.Radio.cs [new file with mode: 0755]
src/Tizen.Multimedia/Interop/Interop.SafeMultimediaHandle.cs [new file with mode: 0755]
src/Tizen.Multimedia/Radio/Radio.cs [new file with mode: 0755]
src/Tizen.Multimedia/Radio/RadioInterruptedEventArgs.cs [new file with mode: 0755]
src/Tizen.Multimedia/Radio/RadioInterruptedReason.cs [new file with mode: 0755]
src/Tizen.Multimedia/Radio/RadioState.cs [new file with mode: 0755]
src/Tizen.Multimedia/Radio/ScanUpdatedEventArgs.cs [new file with mode: 0755]

diff --git a/src/Tizen.Multimedia/Interop/Interop.ErrorCode.cs b/src/Tizen.Multimedia/Interop/Interop.ErrorCode.cs
new file mode 100755 (executable)
index 0000000..ec477d6
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2016 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.CompilerServices;
+using Tizen;
+
+internal static partial class Interop
+{
+    internal enum ErrorCode
+    {
+        None = Tizen.Internals.Errors.ErrorCode.None,
+        OutOfMemory = Tizen.Internals.Errors.ErrorCode.OutOfMemory,
+        InvalidParameter = Tizen.Internals.Errors.ErrorCode.InvalidParameter,
+        InvalidOperation = Tizen.Internals.Errors.ErrorCode.InvalidOperation,
+        PermissionDenied = Tizen.Internals.Errors.ErrorCode.PermissionDenied,
+        NotSupported = Tizen.Internals.Errors.ErrorCode.NotSupported,
+
+        InvalidState = -0x019A0000 | 0x01, // RADIO_ERROR_INVALID_STATE
+        SoundPolicy = -0x019A0000 | 0x02, // RADIO_ERROR_SOUND_POLICY
+        NoAntenna = -0x019A0000 | 0x03, // RADIO_ERROR_NO_ANTENNA
+    }
+}
+
+internal static class ErrorCodeExtensions
+{
+    private const string LogTag = "Tizen.Multimedia";
+
+    internal static bool IsSuccess(this Interop.ErrorCode err)
+    {
+        return err == Interop.ErrorCode.None;
+    }
+
+    internal static bool IsFailed(this Interop.ErrorCode err)
+    {
+        return !err.IsSuccess();
+    }
+
+    /// <summary>
+    /// Utility method to check for error, returns false if failed and print warning messages
+    /// </summary>
+    /// <returns>true in case of no error, false otherwise</returns>
+    internal static bool WarnIfFailed(this Interop.ErrorCode err, string msg, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+    {
+        if (err.IsFailed())
+        {
+            Log.Debug(LogTag, $"{msg}, err: {err.ToString()}", file, func, line);
+            return false;
+        }
+        return true;
+    }
+
+    /// <summary>
+    /// Utility method to check for error, returns false if failed and throw exception
+    /// </summary>
+    /// <returns>true in case of no error</returns>
+    internal static bool ThrowIfFailed(this Interop.ErrorCode err, string msg, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+    {
+        if (err.IsFailed())
+        {
+            Log.Error(LogTag, $"{msg}, err: {err.ToString()}", file, func, line);
+            throw err.GetException(msg);
+        }
+        return true;
+    }
+
+    internal static Exception GetException(this Interop.ErrorCode err, string message)
+    {
+        string errMessage = $"{message}, err: {err.ToString()}";
+        switch (err)
+        {
+            //case ErrorCode.None:
+            case Interop.ErrorCode.PermissionDenied: return new UnauthorizedAccessException(errMessage);
+            case Interop.ErrorCode.InvalidParameter: return new ArgumentException(errMessage);
+            case Interop.ErrorCode.OutOfMemory: return new OutOfMemoryException(errMessage);
+            case Interop.ErrorCode.NotSupported: return new NotSupportedException(errMessage);
+            case Interop.ErrorCode.NoAntenna: return new NotSupportedException(errMessage);
+            case Interop.ErrorCode.InvalidOperation:
+            case Interop.ErrorCode.InvalidState:
+            case Interop.ErrorCode.SoundPolicy:
+            default: return new InvalidOperationException(errMessage);
+        }
+    }
+}
\ No newline at end of file
index e3c3a4e..54a147b 100755 (executable)
@@ -31,6 +31,7 @@ internal static partial class Interop
         public const string Libc = "libc.so.6";
         public const string Camera = "libcapi-media-camera.so.0";
         public const string StreamRecorder = "libcapi-media-streamrecorder.so.0";
+        public const string Radio = "libcapi-media-radio.so.0";
         public const string ThumbnailExtractor = "libcapi-media-thumbnail-util.so";
         public const string WavPlayer = "libcapi-media-wav-player.so.0";
         public const string TonePlayer = "libcapi-media-tone-player.so.0";
diff --git a/src/Tizen.Multimedia/Interop/Interop.Radio.cs b/src/Tizen.Multimedia/Interop/Interop.Radio.cs
new file mode 100755 (executable)
index 0000000..fc53c11
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2016 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;
+
+internal static partial class Interop
+{
+    internal enum RadioState
+    {
+        Ready, // RADIO_STATE_READY
+        Playing, // RADIO_STATE_PLAYING
+        Scanning, // RADIO_STATE_SCANNING
+    }
+
+    internal enum RadioInterruptedReason
+    {
+        Completed, // RADIO_INTERRUPTED_COMPLETED
+        Media, // RADIO_INTERRUPTED_BY_MEDIA
+        Call, // RADIO_INTERRUPTED_BY_CALL
+        EarjackUnplug, // RADIO_INTERRUPTED_BY_EARJACK_UNPLUG
+        ResourceConflict, // RADIO_INTERRUPTED_BY_RESOURCE_CONFLICT
+        Alarm, // RADIO_INTERRUPTED_BY_ALARM
+        Emergency, // RADIO_INTERRUPTED_BY_EMERGENCY
+        ResumableMedia, // RADIO_INTERRUPTED_BY_RESUMABLE_MEDIA
+        Notification, // RADIO_INTERRUPTED_BY_NOTIFICATION
+    }
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_get_state")]
+    internal static extern ErrorCode GetState(this RadioHandle /* radio_h */ radio, out RadioState /* radio_state_e */ state);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_start")]
+    internal static extern ErrorCode Start(this RadioHandle /* radio_h */ radio);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_stop")]
+    internal static extern ErrorCode Stop(this RadioHandle /* radio_h */ radio);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_seek_up")]
+    internal static extern ErrorCode SeekUp(this RadioHandle /* radio_h */ radio, RadioHandle.SeekCompletedCallback callback, IntPtr /* void */ userData);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_seek_down")]
+    internal static extern ErrorCode SeekDown(this RadioHandle /* radio_h */ radio, RadioHandle.SeekCompletedCallback callback, IntPtr /* void */ userData);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_set_frequency")]
+    internal static extern ErrorCode SetFrequency(this RadioHandle /* radio_h */ radio, int frequency);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_get_frequency")]
+    internal static extern ErrorCode GetFrequency(this RadioHandle /* radio_h */ radio, out int frequency);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_get_signal_strength")]
+    internal static extern ErrorCode GetSignalStrength(this RadioHandle /* radio_h */ radio, out int strength);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_scan_start")]
+    internal static extern ErrorCode ScanStart(this RadioHandle /* radio_h */ radio, RadioHandle.ScanUpdatedCallback callback, IntPtr /* void */ userData);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_scan_stop")]
+    internal static extern ErrorCode ScanStop(this RadioHandle /* radio_h */ radio, RadioHandle.ScanStoppedCallback callback, IntPtr /* void */ userData);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_set_mute")]
+    internal static extern ErrorCode SetMute(this RadioHandle /* radio_h */ radio, bool muted);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_is_muted")]
+    internal static extern ErrorCode GetMuted(this RadioHandle /* radio_h */ radio, out bool muted);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_set_scan_completed_cb")]
+    internal static extern ErrorCode SetScanCompletedCb(this RadioHandle /* radio_h */ radio, RadioHandle.ScanCompletedCallback callback, IntPtr /* void */ userData);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_unset_scan_completed_cb")]
+    internal static extern ErrorCode UnsetScanCompletedCb(this RadioHandle /* radio_h */ radio);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_set_interrupted_cb")]
+    internal static extern ErrorCode SetInterruptedCb(this RadioHandle /* radio_h */ radio, RadioHandle.InterruptedCallback callback, IntPtr /* void */ userData);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_unset_interrupted_cb")]
+    internal static extern ErrorCode UnsetInterruptedCb(this RadioHandle /* radio_h */ radio);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_get_frequency_range")]
+    internal static extern ErrorCode GetFrequencyRange(this RadioHandle /* radio_h */ radio, out int minFreq, out int maxFreq);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_get_channel_spacing")]
+    internal static extern ErrorCode GetChannelSpacing(this RadioHandle /* radio_h */ radio, out int channelSpacing);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_set_volume")]
+    internal static extern ErrorCode SetVolume(this RadioHandle /* radio_h */ radio, float volume);
+
+    [DllImport(Libraries.Radio, EntryPoint = "radio_get_volume")]
+    internal static extern ErrorCode GetVolume(this RadioHandle /* radio_h */ radio, out float volume);
+
+    internal struct RadioFrequencyRange
+    {
+        public int minFreq;
+        public int maxFreq;
+    }
+
+    internal class RadioHandle : SafeMultimediaHandle
+    {
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void SeekCompletedCallback(int frequency, IntPtr /* void */ userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void ScanUpdatedCallback(int frequency, IntPtr /* void */ userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void ScanStoppedCallback(IntPtr /* void */ userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void ScanCompletedCallback(IntPtr /* void */ userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void InterruptedCallback(RadioInterruptedReason /* radio_interrupted_code_e */ code, IntPtr /* void */ userData);
+
+        [DllImport(Libraries.Radio, EntryPoint = "radio_create")]
+        internal static extern ErrorCode Create(out IntPtr /* radio_h */ radio);
+
+        [DllImport(Libraries.Radio, EntryPoint = "radio_destroy")]
+        internal static extern ErrorCode Destroy(IntPtr /* radio_h */ radio);
+
+        internal RadioHandle(IntPtr handle, bool needToRelease) : base(handle, needToRelease)
+        {
+        }
+
+        internal RadioHandle() : this(CreateNativeHandle(), true)
+        {
+        }
+
+        internal static IntPtr CreateNativeHandle()
+        {
+            IntPtr handle;
+            Create(out handle).ThrowIfFailed("Failed to create native handle");
+            return handle;
+        }
+
+        internal override ErrorCode DisposeNativeHandle()
+        {
+            ScanCompleteCb = null;
+            InteruptedCb = null;
+            return Destroy(handle);
+        }
+
+        internal RadioState State
+        {
+            get { return NativeGet<RadioState>(this.GetState); }
+        }
+
+        internal int Frequency
+        {
+            get { return NativeGet<int>(this.GetFrequency); }
+            set { NativeSet(this.SetFrequency, value); }
+        }
+
+        internal int SignalStrength
+        {
+            get { return NativeGet<int>(this.GetSignalStrength); }
+        }
+
+        internal bool IsMuted
+        {
+            get { return NativeGet<bool>(this.GetMuted); }
+            set { NativeSet(this.SetMute, value); }
+        }
+
+        internal int ChannelSpacing
+        {
+            get { return NativeGet<int>(this.GetChannelSpacing); }
+        }
+
+        internal float Volume
+        {
+            get { return NativeGet<float>(this.GetVolume); }
+            set { NativeSet(this.SetVolume, value); }
+        }
+
+        internal int MinimumFrequency
+        {
+            get
+            {
+                RadioFrequencyRange range;
+                this.GetFrequencyRange(out range.minFreq, out range.maxFreq);
+                return range.minFreq;
+            }
+        }
+
+        internal int MaximumFrequency
+        {
+            get
+            {
+                RadioFrequencyRange range;
+                this.GetFrequencyRange(out range.minFreq, out range.maxFreq);
+                return range.maxFreq;
+            }
+        }
+
+        internal ScanCompletedCallback ScanCompleteCb
+        {
+            set
+            {
+                var err = (value != null) ? this.SetScanCompletedCb(value, IntPtr.Zero) : this.UnsetScanCompletedCb();
+                err.ThrowIfFailed("Failed to set/ unset scan complete callback");
+            }
+        }
+
+        internal InterruptedCallback InteruptedCb
+        {
+            set
+            {
+                var err = (value != null) ? this.SetInterruptedCb(value, IntPtr.Zero) : this.UnsetInterruptedCb();
+                err.ThrowIfFailed("Failed to set/ unset interrupted callback");
+            }
+        }
+
+        internal void StartPlayback()
+        {
+            this.Start().ThrowIfFailed("Failed to start radio");
+        }
+
+        internal void StopPlayback()
+        {
+            this.Stop().ThrowIfFailed("Failed to stop radio");
+        }
+
+        internal void StartScan(ScanUpdatedCallback callback)
+        {
+            this.ScanStart(callback, IntPtr.Zero).ThrowIfFailed("Failed to start radio");
+        }
+
+        internal void StopScan(ScanStoppedCallback callback)
+        {
+            this.ScanStop(callback, IntPtr.Zero).ThrowIfFailed("Failed to stop radio");
+        }
+
+        internal void SeekUp(SeekCompletedCallback callback)
+        {
+            this.SeekUp(callback, IntPtr.Zero).ThrowIfFailed("Failed to start radio");
+        }
+
+        internal void SeekDown(SeekCompletedCallback callback)
+        {
+            this.SeekDown(callback, IntPtr.Zero).ThrowIfFailed("Failed to stop radio");
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia/Interop/Interop.SafeMultimediaHandle.cs b/src/Tizen.Multimedia/Interop/Interop.SafeMultimediaHandle.cs
new file mode 100755 (executable)
index 0000000..ced1e62
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016 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.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+internal static partial class Interop
+{
+    internal static Task<T> PinnedTask<T>(TaskCompletionSource<T> tcs)
+    {
+        var gch = GCHandle.Alloc(tcs);
+        return tcs.Task.ContinueWith(
+            t => { gch.Free(); return t; },
+            TaskContinuationOptions.ExecuteSynchronously).Unwrap();
+    }
+
+    internal abstract class SafeMultimediaHandle : SafeHandle
+    {
+        internal delegate ErrorCode GetterMethod<TProp>(out TProp value);
+        internal delegate ErrorCode SetterMethod<TProp>(TProp value);
+
+        protected SafeMultimediaHandle(IntPtr handle, bool needToRelease) : base(handle, true)
+        {
+            Debug.Assert(handle != IntPtr.Zero);
+            HasOwnership = needToRelease;
+        }
+
+        internal bool HasOwnership { get; set; }
+        public override bool IsInvalid { get { return handle == IntPtr.Zero; } }
+
+        internal abstract ErrorCode DisposeNativeHandle();
+
+        internal TProp NativeGet<TProp>(GetterMethod<TProp> getter, [CallerMemberName] string propertyName = "")
+        {
+            TProp value; getter(out value).ThrowIfFailed($"Failed to get {propertyName}");
+            return value;
+        }
+
+        internal string NativeGet(GetterMethod<string> getter, [CallerMemberName] string propertyName = "")
+        {
+            string value; getter(out value).ThrowIfFailed($"Failed to get {propertyName}");
+            return value;
+        }
+
+        internal void NativeSet<TProp>(SetterMethod<TProp> setter, TProp value, [CallerMemberName] string propertyName = "")
+        {
+            setter(value).ThrowIfFailed($"Failed to set {propertyName}");
+        }
+
+        protected override bool ReleaseHandle()
+        {
+            var err = ErrorCode.None;
+            if (HasOwnership)
+            {
+                err = DisposeNativeHandle();
+                err.WarnIfFailed($"Failed to delete native {GetType()} handle");
+            }
+
+            SetHandle(IntPtr.Zero);
+            return err.IsSuccess();
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia/Radio/Radio.cs b/src/Tizen.Multimedia/Radio/Radio.cs
new file mode 100755 (executable)
index 0000000..c61cce6
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2016 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.CompilerServices;
+using System.Threading.Tasks;
+
+namespace Tizen.Multimedia
+{
+    /// <summary>
+    /// Radio class, provides support for using the Radio feature
+    /// </summary>
+    public class Radio : IDisposable
+    {
+        internal Interop.RadioHandle _handle;
+
+        /// <summary>
+        /// Radio constructor
+        /// </summary>
+        /// <exception cref="OutOfMemoryException"></exception>
+        /// <exception cref="NotSupportedException">This is thrown if Radio feature is not supported</exception>
+        /// <exception cref="InvalidOperationException"></exception>
+        public Radio()
+        {
+            _handle = new Interop.RadioHandle();
+            _handle.ScanCompleteCb = ScanCompleteCallback;
+            _handle.InteruptedCb = PlaybackIntruptedCallback;
+        }
+
+        /// <summary>
+        /// Scan update event, to be triggered when radio scan information is updated
+        /// </summary>
+        public event EventHandler<ScanUpdatedEventArgs> ScanInformationUpdated;
+
+        /// <summary>
+        /// Scan stopped event, to be triggered when radio scanning stops
+        /// </summary>
+        public event EventHandler ScanStopped;
+
+        /// <summary>
+        /// Scan complete event, to be triggered when radio scan is completed
+        /// </summary>
+        public event EventHandler ScanCompleted;
+
+        /// <summary>
+        /// Playback interrupted event, to be triggered when radio playback is interrupted
+        /// </summary>
+        public event EventHandler<RadioInterruptedEventArgs> PlaybackInterrupted;
+
+        /// <summary>
+        /// Current state for the radio
+        /// </summary>
+        public RadioState State
+        {
+            get
+            {
+                ValidateObjectNotDisposed();
+                return (RadioState)_handle.State;
+            }
+        }
+
+        /// <summary>
+        /// Current radio frequency, in [87500 ~ 108000] (kHz) range
+        /// </summary>
+        /// <exception cref="ArgumentOutOfRangeException">This is thrown if value passed to setter in not in valid range</exception>
+        public int Frequency
+        {
+            get
+            {
+                ValidateObjectNotDisposed();
+                return _handle.Frequency;
+            }
+            set
+            {
+                ValidateObjectNotDisposed();
+                ValidateInputRangeForPropertySetter(value, 87500, 108000);
+                _handle.Frequency = value;
+            }
+        }
+
+        /// <summary>
+        /// Current signal strength, in  [-128 ~ 128] (dBm) range
+        /// </summary>
+        public int SignalStrength
+        {
+            get
+            {
+                ValidateObjectNotDisposed();
+                return _handle.SignalStrength;
+            }
+        }
+
+        /// <summary>
+        /// Indicates if radio is muted
+        /// </summary>
+        public bool IsMuted
+        {
+            get
+            {
+                ValidateObjectNotDisposed();
+                return _handle.IsMuted;
+            }
+            set
+            {
+                ValidateObjectNotDisposed();
+                _handle.IsMuted = value;
+            }
+        }
+
+        /// <summary>
+        /// Channel spacing for current region
+        /// </summary>
+        public int ChannelSpacing
+        {
+            get
+            {
+                ValidateObjectNotDisposed();
+                return _handle.ChannelSpacing;
+            }
+        }
+
+        /// <summary>
+        /// Current radio volume level, in [0.0 ~ 1.0](1.0 = 100%) range
+        /// </summary>
+        /// <exception cref="ArgumentOutOfRangeException">This is thrown if value passed to setter in not in valid range</exception>
+        public float Volume
+        {
+            get
+            {
+                ValidateObjectNotDisposed();
+                return _handle.Volume;
+            }
+            set
+            {
+                ValidateObjectNotDisposed();
+                ValidateInputRangeForPropertySetter(value, 0.0, 1.0);
+                _handle.Volume = value;
+            }
+        }
+
+        /// <summary>
+        /// Minimum frequency for the region, in [87500 ~ 108000] (kHz) range
+        /// </summary>
+        public int MinimumFrequency
+        {
+            get
+            {
+                ValidateObjectNotDisposed();
+                return _handle.MinimumFrequency;
+            }
+        }
+
+        /// <summary>
+        /// Maximum frequency for the region, in [87500 ~ 108000] (kHz) range
+        /// </summary>
+        public int MaximumFrequency
+        {
+            get
+            {
+                ValidateObjectNotDisposed();
+                return _handle.MaximumFrequency;
+            }
+        }
+
+        /// <summary>
+        /// Starts radio playback
+        /// </summary>
+        /// <remarks>This method can be called if Radio is in Ready state. This method will move Radio to Playing state</remarks>
+        /// <exception cref="InvalidOperationException">This is thrown if Radio is not in Ready state</exception>
+        public void StartPlayback()
+        {
+            ValidateObjectNotDisposed();
+            ValidateRadioState(() => State == RadioState.Ready);
+            _handle.StartPlayback();
+        }
+
+        /// <summary>
+        /// Stops radio playback
+        /// </summary>
+        /// <remarks>This method can be called if Radio is in Playing state. This method will move Radio to Ready state</remarks>
+        /// <exception cref="InvalidOperationException">This is thrown if Radio is not in Playing state</exception>
+        public void StopPlayback()
+        {
+            ValidateObjectNotDisposed();
+            ValidateRadioState(() => State == RadioState.Playing);
+            _handle.StopPlayback();
+        }
+
+        /// <summary>
+        /// Starts radio scan, will trigger ScanInformationUpdated event, when scan information is updated
+        /// </summary>
+        /// <remarks>This method should not be called if Radio is in Scanning state. This method will move Radio to Scanning state</remarks>
+        /// <exception cref="InvalidOperationException">This is thrown if Radio is already in Scanning state</exception>
+        public void StartScan()
+        {
+            ValidateObjectNotDisposed();
+            ValidateRadioState(() => State != RadioState.Scanning);
+            _handle.StartScan(ScanUpdateCallback);
+        }
+
+        /// <summary>
+        /// Stops radio scan, will trigger ScanStopped event, once complete
+        /// </summary>
+        /// <remarks>This method should be called only if Radio is in Scanning state</remarks>
+        /// <exception cref="InvalidOperationException">This is thrown if Radio is not in Scanning state</exception>
+        public void StopScan()
+        {
+            ValidateObjectNotDisposed();
+            ValidateRadioState(() => State == RadioState.Scanning);
+            _handle.StopScan(ScanStoppedCallback);
+        }
+
+        /// <summary>
+        /// Seeks up the effective frequency of the radio
+        /// </summary>
+        /// <returns>Current frequency, in range [87500 ~ 108000] (kHz)</returns>
+        /// <remarks>Radio must be in Playing state to use this API</remarks>
+        /// <exception cref="InvalidOperationException">This is thrown if Radio is not in Playing state</exception>
+        public Task<int> SeekUpAsync()
+        {
+            ValidateObjectNotDisposed();
+            ValidateRadioState(() => State == RadioState.Playing);
+
+            TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
+            Interop.RadioHandle.SeekCompletedCallback callback = (currentFrequency, userData) =>
+            {
+                tcs.TrySetResult(currentFrequency);
+            };
+
+            _handle.SeekUp(callback);
+            return Interop.PinnedTask(tcs);
+        }
+
+        /// <summary>
+        /// Seeks down the effective frequency of the radio
+        /// </summary>
+        /// <returns>Current frequency, in range [87500 ~ 108000] (kHz)</returns>
+        /// <remarks>Radio must be in Playing state to use this API</remarks>
+        /// <exception cref="InvalidOperationException">This is thrown if Radio is not in Playing state</exception>
+        public Task<int> SeekDownAsync()
+        {
+            ValidateObjectNotDisposed();
+            ValidateRadioState(() => State == RadioState.Playing);
+
+            TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
+            Interop.RadioHandle.SeekCompletedCallback callback = (currentFrequency, userData) =>
+            {
+                tcs.TrySetResult(currentFrequency);
+            };
+
+            _handle.SeekDown(callback);
+            return Interop.PinnedTask(tcs);
+        }
+
+        private void ScanUpdateCallback(int frequency, IntPtr data)
+        {
+            ScanInformationUpdated?.Invoke(this, new ScanUpdatedEventArgs(frequency));
+        }
+
+        private void ScanStoppedCallback(IntPtr data)
+        {
+            ScanStopped?.Invoke(this, EventArgs.Empty);
+        }
+
+        private void ScanCompleteCallback(IntPtr data)
+        {
+            ScanCompleted?.Invoke(this, EventArgs.Empty);
+        }
+
+        private void PlaybackIntruptedCallback(Interop.RadioInterruptedReason reason, IntPtr data)
+        {
+            PlaybackInterrupted?.Invoke(this, new RadioInterruptedEventArgs((RadioInterruptedReason)reason));
+        }
+
+        private void ValidateInputRangeForPropertySetter<T>(T input, T min, T max, [CallerMemberName] string member = "") where T : IComparable<T>
+        {
+            if (min.CompareTo(input) == 1 || max.CompareTo(input) == -1)
+            {
+                throw new ArgumentOutOfRangeException(member, input, $"Valid Range [{min} - {max}]");
+            }
+        }
+
+        private void ValidateRadioState(Func<bool> stateVerifier, [CallerMemberName] string member = "")
+        {
+            if (stateVerifier() == false)
+            {
+                throw new InvalidOperationException($"{State} is not valid state to call {member}. Please check API documentation");
+            }
+        }
+
+        private void ValidateObjectNotDisposed()
+        {
+            if (_disposedValue)
+            {
+                throw new ObjectDisposedException(GetType().Name);
+            }
+        }
+
+        #region IDisposable Support
+        private bool _disposedValue = false;
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (!_disposedValue)
+            {
+                _handle.Dispose();
+                _disposedValue = true;
+            }
+        }
+
+        ~Radio()
+        {
+            Dispose(false);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+        #endregion
+
+
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Multimedia/Radio/RadioInterruptedEventArgs.cs b/src/Tizen.Multimedia/Radio/RadioInterruptedEventArgs.cs
new file mode 100755 (executable)
index 0000000..c77e837
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 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;
+
+namespace Tizen.Multimedia
+{
+    /// <summary>
+    /// Arguments for radio Playback Interrupted events
+    /// </summary>
+    public class RadioInterruptedEventArgs : EventArgs
+    {
+        internal RadioInterruptedEventArgs(RadioInterruptedReason reason)
+        {
+            Reason = reason;
+        }
+
+        /// <summary>
+        /// Tuned radio frequency, in range [87500 ~ 108000] (kHz)
+        /// </summary>
+        public RadioInterruptedReason Reason { get; }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Multimedia/Radio/RadioInterruptedReason.cs b/src/Tizen.Multimedia/Radio/RadioInterruptedReason.cs
new file mode 100755 (executable)
index 0000000..45d6941
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016 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.
+ */
+
+namespace Tizen.Multimedia
+{
+    /// <summary>
+    /// Radio Interrupted Reason
+    /// </summary>
+    public enum RadioInterruptedReason
+    {
+        /// <summary>
+        /// Playback interrupted by a resource conflict
+        /// </summary>
+        ResourceConflict = Interop.RadioInterruptedReason.ResourceConflict,
+
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Multimedia/Radio/RadioState.cs b/src/Tizen.Multimedia/Radio/RadioState.cs
new file mode 100755 (executable)
index 0000000..a877536
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 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.
+ */
+
+namespace Tizen.Multimedia
+{
+    /// <summary>
+    /// Radio State
+    /// </summary>
+    public enum RadioState
+    {
+        /// <summary>
+        /// Ready to play or scan
+        /// </summary>
+        Ready = Interop.RadioState.Ready,
+        /// <summary>
+        /// Playing audio from the tuner
+        /// </summary>
+        Playing = Interop.RadioState.Playing,
+        /// <summary>
+        /// Scanning/ searching for the next station for signal
+        /// </summary>
+        Scanning = Interop.RadioState.Scanning,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Multimedia/Radio/ScanUpdatedEventArgs.cs b/src/Tizen.Multimedia/Radio/ScanUpdatedEventArgs.cs
new file mode 100755 (executable)
index 0000000..7e82f1e
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 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;
+
+namespace Tizen.Multimedia
+{
+    /// <summary>
+    /// Arguments for radio scan update events
+    /// </summary>
+    public class ScanUpdatedEventArgs : EventArgs
+    {
+        internal ScanUpdatedEventArgs(int tunedFrequency)
+        {
+            Frequency = tunedFrequency;
+        }
+
+        /// <summary>
+        /// Tuned radio frequency, in range [87500 ~ 108000] (kHz)
+        /// </summary>
+        public int Frequency { get; }
+    }
+}
\ No newline at end of file