WidcommBluetoothClient.cs :  » Business-Application » 32feet.NET » InTheHand » Net » Bluetooth » Widcomm » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Business Application » 32feet.NET 
32feet.NET » InTheHand » Net » Bluetooth » Widcomm » WidcommBluetoothClient.cs
// 32feet.NET - Personal Area Networking for .NET
//
// InTheHand.Net.Sockets.WidcommBluetoothClient
// 
// Copyright (c) 2008-2010 In The Hand Ltd, All rights reserved.
// This source code is licensed under the In The Hand Community License - see License.txt

using System;
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;
using System.Diagnostics;
using System.Net.Sockets;
using System.Collections.Generic;
using List_IBluetoothDeviceInfoSystem.Collections.Generic.ListInTheHand.Net.Bluetooth.Factory.IBluetoothDeviceInfo;
using AsyncResultDDInTheHand.Net.AsyncResultSystem.Collections.Generic.ListInTheHand.Net.Bluetooth.Factory.IBluetoothDeviceInfoInTheHand.Net.Bluetooth.Factory.DiscoDevsParams;
using System.Threading;
using InTheHand.Net.Bluetooth.Factory;
using System.Diagnostics.CodeAnalysis;

namespace InTheHand.Net.Bluetooth.Widcomm{
    /// <summary>
    /// Provides client connections for Bluetooth network services with Widcomm stack.
    /// </summary>
    internal sealed class WidcommBluetoothClient : IBluetoothClient
    {
        [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Is used in the DEBUG build.")]
        bool m_disposed;
        readonly WidcommBluetoothFactoryBase m_factory;
        TimeSpan _inquiryLength = TimeSpan.FromSeconds(15);
        NetworkStream m_netStrm;
        readonly WidcommRfcommStream m_conn;
        readonly WidcommBtInterface m_btIf;
        //TEST_EARLY: public WidcommBtInterface m_btIf___HACK;
        AsyncResultNoResult m_arConnect;
        //
        string m_passcode;
        //BluetoothEndPoint m_localEP;
        int? _remotePort;  // Only known in the client case, not in the BtLsnr->BtCli case.
        private BluetoothEndPoint m_remoteEndPoint;

        //--------------
        internal WidcommBluetoothClient(WidcommBluetoothFactoryBase factory)
        {
            Debug.Assert(factory != null, "factory must not be null; is used by GetRemoteMachineName etc.");
            m_factory = factory;
            m_conn = factory.GetWidcommRfcommStream();
            m_btIf = factory.GetWidcommBtInterface();
            //TEST_EARLY: m_btIf___HACK = m_btIf;
        }

        internal WidcommBluetoothClient(BluetoothEndPoint localEP, WidcommBluetoothFactoryBase factory)
            : this(factory)
        {
            // All we could do is fail if the specified local address isn't the
            // (sole) Widcomm Radio's address.  We also can't tell Widcomm to
            // bind to a specified local port so all we could do is fail if the
            // specified port is not 'unspecified' (i.e. 0/-1).
            throw new NotSupportedException("Don't support binding to a particular local address/port.");
        }

        /// <summary>
        /// Used by WidcommBluetoothListener to return the newly accepted connection.
        /// </summary>
        /// -
        /// <param name="strm">The WidcommRfcommStream containing the newly connected 
        /// RfCommPort.
        /// </param>
        /// <param name="factory">Factory to use in GetRemoteMachineName etc.
        /// </param>
        internal WidcommBluetoothClient(WidcommRfcommStream strm, WidcommBluetoothFactoryBase factory)
        {
            Debug.Assert(factory != null, "factory must not be null; is used by GetRemoteMachineName etc.");
            m_factory = factory;
            m_conn = strm;
        }

        //--------------
        public System.Net.Sockets.NetworkStream GetStream()
        {
            GetStream2(); // Check validity (but a sub-type, so hard to use here).
            if (m_netStrm == null)
                m_netStrm = new WidcommDecoratorNetworkStream(m_conn);
            return m_netStrm;
        }

        public System.IO.Stream GetStream2()
        {
            //if (cleanedUp) {
            //    throw new ObjectDisposedException(base.GetType().FullName);
            //}
            if (!Connected) {
                throw new InvalidOperationException("The operation is not allowed on non-connected sockets.");
            }
            return m_conn;
        }

        public LingerOption LingerState
        {
            get { return m_conn.LingerState; }
            set { m_conn.LingerState = value; }
        }

        //--------------------------------------------------------------
        public bool Connected
        {
            get { return m_conn.Connected; }
        }

        public void Dispose()
        {
            m_disposed = true;
            m_conn.Close();
            //
            // Abort the Connect (usually the SDP lookup) if we're still in that phase
            Connect_SetAsCompleted_CompletedSyncFalse(null, new ObjectDisposedException("BluetoothClient"));
        }

        void Connect_SetAsCompleted_CompletedSyncFalse(AsyncResultNoResult arConnect_Debug, Exception ex)
        {
            // Read state to check not already completed (is null).  And set it to null always.
            AsyncResultNoResult arOrig = Interlocked.Exchange(ref m_arConnect, null);
            if (arOrig == null) {
                // We use m_arConnect being null as an indication that it is already SetAsCompleted.
                Debug.Assert(m_disposed, "arConnect is already IsCompleted but NOT m_cancelled");
                Debug.Assert(arConnect_Debug == null || arConnect_Debug.IsCompleted, "NOT arConnect.IsCompleted: How!? Different instances?");
            } else {
                Debug.Assert(arConnect_Debug == null || arConnect_Debug == arOrig, "arConnect != m_arConnect: should only be one instance!");
                // Set!
                var args = new RaiseConnectParams { arOrig = arOrig, ex = ex };
                ThreadPool.QueueUserWorkItem(RaiseConnect, args);
            }
        }

        class RaiseConnectParams
        {
            internal Exception ex { get; set; }
            internal AsyncResultNoResult arOrig { get; set; }
        }

        private static void RaiseConnect(object state)
        {
            var args = (RaiseConnectParams)state;
            args.arOrig.SetAsCompleted(args.ex, false);
        }


        public void Connect(BluetoothEndPoint remoteEP)
        {
            IAsyncResult ar = BeginConnect(remoteEP, null, null);
            EndConnect(ar);
        }

        public IAsyncResult BeginConnect(BluetoothEndPoint remoteEP, AsyncCallback requestCallback, object state)
        {
            // Just in case the user modifies the original endpoint or address!!!
            BluetoothEndPoint rep2 = (BluetoothEndPoint)remoteEP.Clone();
            //
            AsyncResultNoResult arConnect = new AsyncResultNoResult(requestCallback, state);
            AsyncResultNoResult origArConnect = System.Threading.Interlocked.CompareExchange(
                ref m_arConnect, arConnect, null);
            if (origArConnect != null)
                throw new InvalidOperationException("Another Connect operation is already in progress.");
            BeginFillInPort(rep2, Connect_FillInPortCallback,
                new BeginConnectState(rep2, arConnect));
            return arConnect;
        }

        public void EndConnect(IAsyncResult asyncResult)
        {
            AsyncResultNoResult ar2 = (AsyncResultNoResult)asyncResult;
            ar2.EndInvoke();
            Debug.Assert(m_arConnect == null, "NOT m_arConnect == null");
        }

        sealed class BeginConnectState
        {
            //Unused: internal BluetoothEndPoint inputEP;
            internal AsyncResultNoResult arCliConnect;

            public BeginConnectState(BluetoothEndPoint inputEP, AsyncResultNoResult arCliConnect)
            {
                //this.inputEP = inputEP;
                this.arCliConnect = arCliConnect;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "Exception is rethrown in EndXxxx.")]
        void Connect_FillInPortCallback(IAsyncResult ar)
        {
            BeginConnectState bcState = (BeginConnectState)ar.AsyncState;
            AsyncResultNoResult arConnect = bcState.arCliConnect;
            try {
                BluetoothEndPoint remoteEpWithPort = EndFillInPort(ar);
                if (arConnect.IsCompleted) {
                    // User called Close/Dispose when we were in (slow!) SDP lookup.
                    Debug.Assert(m_disposed, "arConnect.IsCompleted but NOT m_cancelled");
                    return;
                }
                _remotePort = remoteEpWithPort.Port;
                Debug.Assert(_remotePort != -1 && _remotePort != 0, "port is 'empty' is: " + _remotePort);
                m_conn.BeginConnect(remoteEpWithPort, m_passcode, Connect_ConnCallback, bcState);
            } catch (Exception ex) {
                Connect_SetAsCompleted_CompletedSyncFalse(arConnect, ex);
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "Exception is rethrown in EndXxxx.")]
        void Connect_ConnCallback(IAsyncResult ar)
        {
            BeginConnectState bsState = (BeginConnectState)ar.AsyncState;
            AsyncResultNoResult arCliConnect = bsState.arCliConnect;
            try {
                m_conn.EndConnect(ar);
                Connect_SetAsCompleted_CompletedSyncFalse(arCliConnect, null);
            } catch (Exception ex) {
                Connect_SetAsCompleted_CompletedSyncFalse(arCliConnect, ex);
            }
        }

        //--------------------------------------------------------------
        private IAsyncResult BeginFillInPort(BluetoothEndPoint bep, AsyncCallback asyncCallback, Object state)
        {
            WidcommUtils.Trace_WriteLine("BeginFillInPortState");
            AsyncResult<BluetoothEndPoint> arFIP = new AsyncResult<BluetoothEndPoint>(asyncCallback, state);
            if (bep.Port != 0 && bep.Port != -1) { // Thus, no modification required.
                WidcommUtils.Trace_WriteLine("BeginFillInPort, has port -> Completed Syncronously");
                Debug.Assert(bep.Port >= BluetoothEndPoint.MinScn && bep.Port <= BluetoothEndPoint.MaxScn, "!!Port=" + bep.Port);
                arFIP.SetAsCompleted(bep, true);
                return arFIP;
            }
            IAsyncResult ar2 = m_btIf.BeginServiceDiscovery(bep.Address, bep.Service, SdpSearchScope.ServiceClassOnly,
                FillInPort_ServiceDiscoveryCallback, new BeginFillInPortState(bep, arFIP));
            return arFIP;
        }

        private BluetoothEndPoint EndFillInPort(IAsyncResult ar)
        {
            AsyncResult<BluetoothEndPoint> arFIP = (AsyncResult<BluetoothEndPoint>)ar;
            return arFIP.EndInvoke();
        }

        sealed class BeginFillInPortState
        {
            internal BluetoothEndPoint inputEP;
            internal AsyncResult<BluetoothEndPoint> arFillInPort;

            public BeginFillInPortState(BluetoothEndPoint inputEP, AsyncResult<BluetoothEndPoint> arFillInPort)
            {
                this.inputEP = inputEP;
                this.arFillInPort = arFillInPort;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "Exception is rethrown in EndXxxx.")]
        void FillInPort_ServiceDiscoveryCallback(IAsyncResult ar)
        {
            BeginFillInPortState fipState = (BeginFillInPortState)ar.AsyncState;
            AsyncResult<BluetoothEndPoint> arFIP = fipState.arFillInPort;
            try {
                using (ISdpDiscoveryRecordsBuffer recBuf = m_btIf.EndServiceDiscovery(ar)) {
                    if (recBuf.RecordCount == 0) {
                        WidcommUtils.Trace_WriteLine("FillInPort_ServiceDiscoveryCallback, zero records!");
                        arFIP.SetAsCompleted(WidcommSocketExceptions.Create_NoResultCode(
                                WidcommSocketExceptions.SocketError_NoSuchService, "PortLookup_Zero"),
                            false);
                    } else {
                        int[] ports = recBuf.Hack_GetPorts();
                        WidcommUtils.Trace_WriteLine("FillInPort_ServiceDiscoveryCallback, got {0} records.", recBuf.RecordCount);
                        // Do this in reverse order, as Widcomm appears to keep old
                        // (out of date!!) service records around, so we want to 
                        // use the newests ones in preference.
                        Debug.Assert(ports.Length >= 1, "NOT ports.Length>=1, is: " + ports.Length);
                        for (int i = ports.Length - 1; i >= 0; --i) {
                            int cur = ports[i];
                            if (cur != -1) {
                                WidcommUtils.Trace_WriteLine("FillInPort_ServiceDiscoveryCallback, got port: {0}", cur);
                                BluetoothEndPoint epWithPort = new BluetoothEndPoint(
                                    fipState.inputEP.Address, fipState.inputEP.Service, (byte)cur);
                                arFIP.SetAsCompleted(epWithPort, false);
                                return;
                            }
                        }//for
                        WidcommUtils.Trace_WriteLine("FillInPort_ServiceDiscoveryCallback, no scn found");
                        // -> Error. No Rfcomm SCN!
                        arFIP.SetAsCompleted(WidcommSocketExceptions.Create_NoResultCode(
                                WidcommSocketExceptions.SocketError_ServiceNoneRfcommScn, "PortLookup_NoneRfcomm"),
                            false);
                    }
                }
            } catch (Exception ex) {
                arFIP.SetAsCompleted(ex, false);
            }
        }

        //--------------------------------------------------------------
        public int Available
        {
            get { return m_conn.AmountInReadBuffers; }
        }

        //--------------------------------------------------------------
#if NETCF
        // No support for CBtIf::GetRemoteDeviceInfo on my iPAQ.
        static bool s_useRegistryForGetKnownRemoteDevice = true;
#else
        // No way to lookup *all* known devices, need specific CoD value!
        static bool s_useRegistryForGetKnownRemoteDevice = true;
#endif

        /// <summary>
        /// ... Allow the tests to disable the Registry lookup.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
            Justification = "Used by unit-tests.")]
        internal static bool ReadKnownDeviceFromTheRegistry
        {
            set { s_useRegistryForGetKnownRemoteDevice = value; }
            get { return s_useRegistryForGetKnownRemoteDevice; }
        }

        public IBluetoothDeviceInfo[] DiscoverDevices(int maxDevices,
            bool authenticated, bool remembered, bool unknown, bool discoverableOnly)
        {
            IAsyncResult ar = BeginDiscoverDevices(maxDevices,
                authenticated, remembered, unknown, discoverableOnly,
                null, null);
            return EndDiscoverDevices(ar);
        }

        public IAsyncResult BeginDiscoverDevices(int maxDevices,
            bool authenticated, bool remembered, bool unknown, bool discoverableOnly,
            AsyncCallback callback, object state)
        {
            DateTime discoTime = DateTime.UtcNow;
            DiscoDevsParams args = new DiscoDevsParams(maxDevices, authenticated, remembered, unknown, discoverableOnly, discoTime);
            AsyncResultDD arDD = new AsyncResultDD(callback, state, args);
            //
            if (unknown || discoverableOnly) { // No need to do SLOW Inquiry when just want known remembered.
                IAsyncResult ar = m_btIf.BeginInquiry(maxDevices, authenticated, remembered, unknown,
                    _inquiryLength,
                    DiscoDevs_InquiryCallback, arDD);
            } else {
                arDD.SetAsCompleted(null, true);
            }
            return arDD;
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        void DiscoDevs_InquiryCallback(IAsyncResult ar)
        {
            AsyncResultDD arDD = (AsyncResultDD)ar.AsyncState;
            try {
                List_IBluetoothDeviceInfo discoverableDevices = m_btIf.EndInquiry(ar);
                arDD.SetAsCompleted(discoverableDevices, false);
            } catch (Exception ex) {
                arDD.SetAsCompleted(ex, false);
            }
        }

        public IBluetoothDeviceInfo[] EndDiscoverDevices(IAsyncResult asyncResult)
        {
            AsyncResultDD arDD = (AsyncResultDD)asyncResult;
            List_IBluetoothDeviceInfo discoverableDevices = arDD.EndInvoke();
            DiscoDevsParams args = arDD.BeginParameters;
            // DEBUG Iff 'known' devices only: we complete immediately (sync'ly), and with null result.
            if (args.unknown || args.discoverableOnly) {
                // Result from BeginInquiry callback expected
                Debug.Assert(discoverableDevices != null, "a1");
                Debug.Assert(!arDD.CompletedSynchronously, "a2"); // don't really care however
            } else {
                // Null result from BeginDD method expected.
                Debug.Assert(discoverableDevices == null, "b1");
                Debug.Assert(arDD.CompletedSynchronously, "b2");
            }
            //
            List_IBluetoothDeviceInfo knownDevices;
            if (s_useRegistryForGetKnownRemoteDevice)
                knownDevices = m_btIf.ReadKnownDevicesFromRegistry();
            else
                knownDevices = m_btIf.GetKnownRemoteDeviceEntries();
            //
            List_IBluetoothDeviceInfo mergedDevices = BluetoothClient.DiscoverDevicesMerge(
                args.authenticated, args.remembered, args.unknown, knownDevices, discoverableDevices,
                args.discoverableOnly, args.discoTime);
            return mergedDevices.ToArray();
        }

        public TimeSpan InquiryLength
        {
            get { return _inquiryLength; }
            set
            {
                if ((value.TotalSeconds > 0) && (value.TotalSeconds <= 60)) {
                    _inquiryLength = value;
                } else {
                    throw new ArgumentOutOfRangeException("value",
                        "QueryLength must be a positive timespan between 0 and 60 seconds.");
                }
            }
        }

        public int InquiryAccessCode
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        //--------------------------------------------------------------
        public System.Net.Sockets.Socket Client
        {
            get { throw new NotSupportedException("The Widcomm stack does not use Sockets."); }
            set { throw new NotSupportedException("The Widcomm stack does not use Sockets."); }
        }

        //--------------------------------------------------------------
        public bool Authenticate
        {
            get { return false; }
            set { throw new NotImplementedException("The method or operation is not implemented."); }
        }

        public bool Encrypt
        {
            get { return false; }
            set { throw new NotImplementedException("The method or operation is not implemented."); }
        }

        public BluetoothEndPoint RemoteEndPoint
        {
            get
            {
                if (m_remoteEndPoint == null) {
                    BluetoothAddress addr = m_conn.RemoteAddress;
                    Debug.Assert(addr != null, "port.IsConnected should have returned the remote address!");
                    // Don't know the remote port unfortunately so just use 0/-1.
                    if (_remotePort.HasValue)
                        m_remoteEndPoint = new BluetoothEndPoint(addr, BluetoothService.Empty, _remotePort.Value);
                    else
                        m_remoteEndPoint = new BluetoothEndPoint(addr, BluetoothService.Empty);
                }
                return m_remoteEndPoint;
            }
        }

        public string GetRemoteMachineName(BluetoothAddress device)
        {
            // This is what we do on Win32.  Good enough??
            // Is there a Widcomm function to do the name lookup?
            // Leave it internal to WidcommBluetoothDeviceInfo anyway!
            IBluetoothDeviceInfo bdi
                = WidcommBluetoothDeviceInfo.CreateFromGivenAddress(device, m_factory);
            return bdi.DeviceName;
        }

        public Guid LinkKey
        {
            get { throw new NotImplementedException("The method or operation is not implemented."); }
        }

        public LinkPolicy LinkPolicy
        {
            get { throw new NotImplementedException("The method or operation is not implemented."); }
        }

        public string RemoteMachineName
        {
            get { return GetRemoteMachineName(RemoteEndPoint.Address); }
        }

        public void SetPin(string pin)
        {
            m_passcode = pin;
        }

        public void SetPin(BluetoothAddress device, string pin)
        {
            throw new NotImplementedException("Use this.SetPin or BluetoothSecurity.PairRequest...");
        }

    }

}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.