[Multimedia] Fixed issues of Radio. 60/154660/2
authorcoderhyme <jhyo.kim@samsung.com>
Wed, 11 Oct 2017 03:39:14 +0000 (12:39 +0900)
committerJungHo Kim <jhyo.kim@samsung.com>
Wed, 11 Oct 2017 03:47:40 +0000 (03:47 +0000)
- The issue that a crash happens in operations related with callbacksk when GC is involved.
- Wrong implentations that exceptions are not thrown when the native apis return errors.

Change-Id: I4bbda573a09e791f49335f57639f1df4e722de26
Signed-off-by: coderhyme <jhyo.kim@samsung.com>
src/Tizen.Multimedia.Radio/Radio/Radio.cs

index 20d70fd..a21450a 100755 (executable)
@@ -16,6 +16,7 @@
 
 using System;
 using System.Linq;
+using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 using Tizen.System;
 using static Tizen.Multimedia.Interop.Radio;
@@ -43,8 +44,13 @@ namespace Tizen.Multimedia
 
             try
             {
-                SetScanCompletedCb(_handle, ScanCompleteCallback).ThrowIfFailed("Failed to initialize radio");
-                SetInterruptedCb(_handle, InterruptedCallback).ThrowIfFailed("Failed to initialize radio");
+                _scanCompletedCallback = _ => ScanCompleted?.Invoke(this, EventArgs.Empty);
+                _interruptedCallback = (reason, _) => Interrupted?.Invoke(this, new RadioInterruptedEventArgs(reason));
+                _scanUpdatedCallback = (frequency, _) => ScanUpdated?.Invoke(this, new ScanUpdatedEventArgs(frequency));
+                _scanStoppedCallback = _ => ScanStopped?.Invoke(this, EventArgs.Empty);
+
+                SetScanCompletedCb(_handle, _scanCompletedCallback).ThrowIfFailed("Failed to initialize radio");
+                SetInterruptedCb(_handle, _interruptedCallback).ThrowIfFailed("Failed to initialize radio");
             }
             catch (Exception)
             {
@@ -65,6 +71,14 @@ namespace Tizen.Multimedia
             }
         }
 
+        private ScanUpdatedCallback _scanUpdatedCallback;
+
+        private ScanStoppedCallback _scanStoppedCallback;
+
+        private ScanCompletedCallback _scanCompletedCallback;
+
+        private InterruptedCallback _interruptedCallback;
+
         /// <summary>
         /// Occurs when the radio scanning information is updated.
         /// </summary>
@@ -252,7 +266,7 @@ namespace Tizen.Multimedia
         {
             ValidateRadioState(RadioState.Ready, RadioState.Playing);
 
-            ScanStart(Handle, ScanUpdatedCallback);
+            ScanStart(Handle, _scanUpdatedCallback).ThrowIfFailed("Failed to start scanning");
         }
 
         /// <summary>
@@ -265,7 +279,7 @@ namespace Tizen.Multimedia
         {
             ValidateRadioState(RadioState.Scanning);
 
-            ScanStop(Handle, ScanStoppedCallback);
+            ScanStop(Handle, _scanStoppedCallback).ThrowIfFailed("Failed to stop scanning");
         }
 
         /// <summary>
@@ -277,19 +291,14 @@ namespace Tizen.Multimedia
         /// It can be -1 if the seeking operation has failed.
         /// </returns>
         /// <remarks>The radio must be in the <see cref="RadioState.Playing"/> state.</remarks>
-        /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
-        public async Task<int> SeekUpAsync()
+        /// <exception cref="InvalidOperationException">
+        ///     The radio is not in the valid state.\n
+        ///     -or-\n
+        ///     Seeking is in progress.
+        /// </exception>
+        public Task<int> SeekUpAsync()
         {
-            ValidateRadioState(RadioState.Playing);
-
-            TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
-            SeekCompletedCallback callback = (currentFrequency, _) =>
-            {
-                tcs.TrySetResult(currentFrequency);
-            };
-
-            SeekUp(Handle, callback);
-            return await tcs.Task;
+            return SeekAsync(SeekUp);
         }
 
         /// <summary>
@@ -301,53 +310,46 @@ namespace Tizen.Multimedia
         /// It can be -1 if the seeking operation has failed.
         /// </returns>
         /// <remarks>The radio must be in the <see cref="RadioState.Playing"/> state.</remarks>
-        /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
-        public async Task<int> SeekDownAsync()
+        /// <exception cref="InvalidOperationException">
+        ///     The radio is not in the valid state.\n
+        ///     -or-\n
+        ///     Seeking is in progress.
+        /// </exception>
+        public Task<int> SeekDownAsync()
+        {
+            return SeekAsync(SeekDown);
+        }
+
+        private async Task<int> SeekAsync(Func<Interop.RadioHandle, SeekCompletedCallback, IntPtr, RadioError> func)
         {
             ValidateRadioState(RadioState.Playing);
 
-            TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
-            SeekCompletedCallback callback = (currentFrequency, _) =>
+            var tcs = new TaskCompletionSource<int>();
+            SeekCompletedCallback callback = (currentFrequency, _) => tcs.TrySetResult(currentFrequency);
+
+            GCHandle gcHandle;
+            try
             {
-                tcs.TrySetResult(currentFrequency);
-            };
+                gcHandle = GCHandle.Alloc(callback);
 
-            SeekDown(Handle, callback);
-            return await tcs.Task;
+                func(Handle, callback, IntPtr.Zero).ThrowIfFailed("Failed to seek");
+                return await tcs.Task;
+            }
+            finally
+            {
+                gcHandle.Free();
+            }
         }
 
         private void ValidateFeatureSupported(string featurePath)
         {
-            bool supported = false;
-            Information.TryGetValue(featurePath, out supported);
-
-            if (supported == false)
+            if (Information.TryGetValue(featurePath, out bool supported) == false || supported == false)
             {
                 throw new NotSupportedException($"The feature({featurePath}) is not supported.");
             }
 
         }
 
-        private void ScanUpdatedCallback(int frequency, IntPtr data)
-        {
-            ScanUpdated?.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 InterruptedCallback(RadioInterruptedReason reason, IntPtr data)
-        {
-            Interrupted?.Invoke(this, new RadioInterruptedEventArgs(reason));
-        }
-
         private void ValidateRadioState(params RadioState[] required)
         {
             RadioState curState = State;