/* * 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.Collections.Generic; using System.Runtime.InteropServices; using System.ComponentModel; using System.Threading.Tasks; namespace Tizen.Network.Bluetooth { /// /// The BluetoothSocket provides functions for managing connections to other devices and exchanging data. /// /// 3 public class BluetoothServerSocket : IDisposable { private static event EventHandler _acceptStateChanged; private static event EventHandler _connectionRequested; private static Interop.Bluetooth.SocketConnectionStateChangedCallback _connectionStateChangedCallback; private static Interop.Bluetooth.SocketConnectionRequestedCallback _connectionRequestedCallback; private TaskCompletionSource _taskForAccept; internal int socketFd; private bool disposed = false; internal BluetoothServerSocket(int socketFd) { Log.Info (Globals.LogTag, "Constructing server socket"); StaticAcceptStateChanged += OnAcceptStateChanged; StaticConnectionRequested += OnConnectionRequested; this.socketFd = socketFd; } private void OnConnectionRequested(Object s, SocketConnectionRequestedEventArgs e) { if (e.SocketFd == socketFd) { ConnectionRequested?.Invoke(this, e); } } private void OnAcceptStateChanged(Object s, AcceptStateChangedEventArgs e) { if (e.Connection.ServerFd == socketFd) { if (_taskForAccept != null && !_taskForAccept.Task.IsCompleted) { if (e.State == BluetoothSocketState.Connected) { _taskForAccept.SetResult(e.Connection); } else { _taskForAccept.SetException(BluetoothErrorFactory.CreateBluetoothException((int)e.Result)); } _taskForAccept = null; } AcceptStateChanged?.Invoke(this, e); } } /// /// The AcceptStateChanged event is raised when the socket connection state is changed. /// /// Thrown when the Bluetooth is not enabled /// or when the register accpet state changed callback fails. /// 3 public event EventHandler AcceptStateChanged; private static event EventHandler StaticAcceptStateChanged { add { if (_acceptStateChanged == null) { RegisterAcceptStateChangedEvent(); } _acceptStateChanged += value; } remove { _acceptStateChanged -= value; if (_acceptStateChanged == null) { UnregisterAcceptStateChangedEvent(); } } } private static void RegisterAcceptStateChangedEvent() { _connectionStateChangedCallback = (int result, BluetoothSocketState connectionState, ref SocketConnectionStruct socketConnection, IntPtr userData) => { Log.Info(Globals.LogTag, "AcceptStateChanged cb is called"); BluetoothSocket socket = new BluetoothSocket(); socket.connectedSocket = socketConnection.SocketFd; socket.remoteAddress = socketConnection.Address; socket.serviceUuid = socketConnection.ServiceUuid; _acceptStateChanged?.Invoke(null, new AcceptStateChangedEventArgs((BluetoothError)result, connectionState, BluetoothUtils.ConvertStructToSocketConnection(socketConnection), socket)); }; int ret = Interop.Bluetooth.SetConnectionStateChangedCallback(_connectionStateChangedCallback, IntPtr.Zero); if (ret != (int)BluetoothError.None) { Log.Error(Globals.LogTag, "Failed to set accept state changed callback, Error - " + (BluetoothError)ret); BluetoothErrorFactory.ThrowBluetoothException(ret); } } private static void UnregisterAcceptStateChangedEvent() { int ret = Interop.Bluetooth.UnsetSocketConnectionStateChangedCallback(); if (ret != (int)BluetoothError.None) { Log.Error(Globals.LogTag, "Failed to unset accept state changed callback, Error - " + (BluetoothError)ret); BluetoothErrorFactory.ThrowBluetoothException(ret); } } /// /// Starts listening on the passed RFCOMM socket and accepts connection requests. /// /// /// The socket must be created with CreateServerSocket(). This API invokes the ConnectionStateChanged event. /// /// Thrown when the Bluetooth is not enabled /// or when the listen on socket procedure fails. /// 3 public void Listen() { int ret = Interop.Bluetooth.Listen(socketFd, 1); if (ret != (int)BluetoothError.None) { Log.Error(Globals.LogTag, "Failed to accept connection, Error - " + (BluetoothError)ret); BluetoothErrorFactory.ThrowBluetoothException(ret); } } /// /// Starts listening on the passed RFCOMM socket without accepting connection requests. /// /// 6 /// http://tizen.org/feature/network.bluetooth /// http://tizen.org/privilege/bluetooth.admin /// Thrown when the Bluetooth is not supported. /// Thrown when the method is failed with message. [EditorBrowsable(EditorBrowsableState.Never)] public void ListenWithoutAccept() { int ret = Interop.Bluetooth.ListenWithoutAccept(socketFd, 1); if (ret != (int)BluetoothError.None) { Log.Error(Globals.LogTag, "Failed to ListenWithoutAccept, Error - " + (BluetoothError)ret); BluetoothErrorFactory.ThrowBluetoothException(ret); } } /// /// Accepts a connection request asynchronously. /// /// 6 /// A task indicating whether the method is done or not. /// http://tizen.org/feature/network.bluetooth /// http://tizen.org/privilege/bluetooth.admin /// Thrown when the Bluetooth is not supported. /// Thrown when the method is failed with message. [EditorBrowsable(EditorBrowsableState.Never)] public Task AcceptAsync() { if (_taskForAccept != null && !_taskForAccept.Task.IsCompleted) { BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NowInProgress); } _taskForAccept = new TaskCompletionSource(); int ret = Interop.Bluetooth.Accept(socketFd); if (ret != (int)BluetoothError.None) { Log.Error(Globals.LogTag, "Failed to accept connection, Error - " + (BluetoothError)ret); BluetoothErrorFactory.ThrowBluetoothException(ret); } return _taskForAccept.Task; } /// /// Rejects a connection request. /// /// 6 /// http://tizen.org/feature/network.bluetooth /// http://tizen.org/privilege/bluetooth.admin /// Thrown when the Bluetooth is not supported. /// Thrown when the method is failed with message. [EditorBrowsable(EditorBrowsableState.Never)] public void Reject() { int ret = Interop.Bluetooth.Reject(socketFd); if (ret != (int)BluetoothError.None) { Log.Error(Globals.LogTag, "Failed to reject connection, Error - " + (BluetoothError)ret); BluetoothErrorFactory.ThrowBluetoothException(ret); } } /// /// Registers a callback function that will be invoked when a RFCOMM connection is requested. /// /// 6 /// http://tizen.org/feature/network.bluetooth [EditorBrowsable(EditorBrowsableState.Never)] public event EventHandler ConnectionRequested; private static event EventHandler StaticConnectionRequested { add { if (_connectionRequested == null) { RegisterConnectionRequestedEvent(); } _connectionRequested += value; } remove { _connectionRequested -= value; if (_connectionRequested == null) { UnregisterConnectionRequestedEvent(); } } } private static void RegisterConnectionRequestedEvent() { _connectionRequestedCallback = (int socketFd, string remoteAddress, IntPtr userData) => { Log.Info(Globals.LogTag, "SocketConnectionRequestedCallback is called"); _connectionRequested?.Invoke(null, new SocketConnectionRequestedEventArgs(socketFd, remoteAddress)); }; int ret = Interop.Bluetooth.SetSocketConnectionRequestedCallback(_connectionRequestedCallback, IntPtr.Zero); if (ret != (int)BluetoothError.None) { Log.Error(Globals.LogTag, "Failed to set connection requested callback, Error - " + (BluetoothError)ret); BluetoothErrorFactory.ThrowBluetoothException(ret); } } private static void UnregisterConnectionRequestedEvent() { int ret = Interop.Bluetooth.UnsetSocketConnectionRequestedCallback(); if (ret != (int)BluetoothError.None) { Log.Error(Globals.LogTag, "Failed to unset connection requested callback, Error - " + (BluetoothError)ret); BluetoothErrorFactory.ThrowBluetoothException(ret); } } /// /// BluetoothServerSocket destructor. /// ~BluetoothServerSocket() { Dispose(false); } /// /// Dispose /// /// 3 public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (disposed) return; int ret = Interop.Bluetooth.DestroyServerSocket(socketFd); if (ret != (int)BluetoothError.None) { Log.Error(Globals.LogTag, "Failed to destroy socket, Error - " + (BluetoothError)ret); } StaticAcceptStateChanged -= OnAcceptStateChanged; StaticConnectionRequested -= OnConnectionRequested; disposed = true; } } }