1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <IceBT/AcceptorI.h>
6 #include <IceBT/Engine.h>
7 #include <IceBT/EndpointI.h>
8 #include <IceBT/Instance.h>
9 #include <IceBT/TransceiverI.h>
10 #include <IceBT/Util.h>
11 
12 #include <Ice/Communicator.h>
13 #include <Ice/Exception.h>
14 #include <Ice/LocalException.h>
15 #include <Ice/Network.h>
16 #include <Ice/Properties.h>
17 #include <Ice/StreamSocket.h>
18 #include <IceUtil/StringUtil.h>
19 
20 using namespace std;
21 using namespace Ice;
22 using namespace IceBT;
23 
upCast(AcceptorI * p)24 IceUtil::Shared* IceBT::upCast(AcceptorI* p) { return p; }
25 
26 namespace
27 {
28 
29 class ProfileCallbackI : public ProfileCallback
30 {
31 public:
32 
ProfileCallbackI(const AcceptorIPtr & acceptor)33     ProfileCallbackI(const AcceptorIPtr& acceptor) :
34         _acceptor(acceptor)
35     {
36     }
37 
newConnection(int fd)38     virtual void newConnection(int fd)
39     {
40         _acceptor->newConnection(fd);
41     }
42 
43 private:
44 
45     AcceptorIPtr _acceptor;
46 };
47 
48 }
49 
50 IceInternal::NativeInfoPtr
getNativeInfo()51 IceBT::AcceptorI::getNativeInfo()
52 {
53     return this;
54 }
55 
56 void
close()57 IceBT::AcceptorI::close()
58 {
59     if(!_path.empty())
60     {
61         try
62         {
63             _instance->engine()->unregisterProfile(_path);
64         }
65         catch(...)
66         {
67         }
68     }
69 }
70 
71 IceInternal::EndpointIPtr
listen()72 IceBT::AcceptorI::listen()
73 {
74     assert(!_uuid.empty());
75 
76     //
77     // The Bluetooth daemon will select an available channel if _channel == 0.
78     //
79 
80     try
81     {
82         ProfileCallbackPtr cb = ICE_MAKE_SHARED(ProfileCallbackI, this);
83         _path = _instance->engine()->registerProfile(_uuid, _name, _channel, cb);
84     }
85     catch(const BluetoothException& ex)
86     {
87         ostringstream os;
88         os << "unable to register Bluetooth profile";
89         if(!ex.reason.empty())
90         {
91             os << "\n" << ex.reason;
92         }
93         throw InitializationException(__FILE__, __LINE__, os.str());
94     }
95 
96     _endpoint = _endpoint->endpoint(this);
97     return _endpoint;
98 }
99 
100 IceInternal::TransceiverPtr
accept()101 IceBT::AcceptorI::accept()
102 {
103     //
104     // The plug-in may not be initialized.
105     //
106     if(!_instance->initialized())
107     {
108         throw PluginInitializationException(__FILE__, __LINE__, "IceBT: plug-in is not initialized");
109     }
110 
111     IceInternal::TransceiverPtr t;
112 
113     {
114         IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
115 
116         //
117         // The thread pool should only call accept() when we've notified it that we have a
118         // new transceiver ready to be accepted.
119         //
120         assert(!_transceivers.empty());
121         t = _transceivers.top();
122         _transceivers.pop();
123 
124         //
125         // Update our status with the thread pool.
126         //
127         ready(IceInternal::SocketOperationRead, !_transceivers.empty());
128     }
129 
130     return t;
131 }
132 
133 string
protocol() const134 IceBT::AcceptorI::protocol() const
135 {
136     return _instance->protocol();
137 }
138 
139 string
toString() const140 IceBT::AcceptorI::toString() const
141 {
142     return addrToString(_addr, _channel);
143 }
144 
145 string
toDetailedString() const146 IceBT::AcceptorI::toDetailedString() const
147 {
148     ostringstream os;
149     os << "local address = " << toString();
150     if(!_name.empty())
151     {
152         os << "\nservice name = '" << _name << "'";
153     }
154     if(!_uuid.empty())
155     {
156         os << "\nservice uuid = " << _uuid;
157     }
158     return os.str();
159 }
160 
161 int
effectiveChannel() const162 IceBT::AcceptorI::effectiveChannel() const
163 {
164     //
165     // If no channel was specified in the endpoint (_channel == 0), the Bluetooth daemon will select
166     // an available channel for us. Unfortunately, there's no way to discover what that channel is
167     // (aside from waiting for the first incoming connection and inspecting the socket endpoint).
168     //
169 
170     return _channel;
171 }
172 
173 void
newConnection(int fd)174 IceBT::AcceptorI::newConnection(int fd)
175 {
176     IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_lock);
177 
178     _transceivers.push(new TransceiverI(_instance, new StreamSocket(_instance, fd), 0, _uuid));
179 
180     //
181     // Notify the thread pool that we are ready to "read". The thread pool will invoke accept()
182     // and we can return the new transceiver.
183     //
184     ready(IceInternal::SocketOperationRead, true);
185 }
186 
AcceptorI(const EndpointIPtr & endpoint,const InstancePtr & instance,const string & adapterName,const string & addr,const string & uuid,const string & name,int channel)187 IceBT::AcceptorI::AcceptorI(const EndpointIPtr& endpoint, const InstancePtr& instance, const string& adapterName,
188                             const string& addr, const string& uuid, const string& name, int channel) :
189     _endpoint(endpoint),
190     _instance(instance),
191     _adapterName(adapterName),
192     _addr(addr),
193     _uuid(uuid),
194     _name(name),
195     _channel(channel)
196 {
197     string s = IceUtilInternal::trim(_addr);
198     if(s.empty())
199     {
200         //
201         // If no address was specified, we use the first available BT adapter.
202         //
203         s = _instance->engine()->getDefaultAdapterAddress();
204     }
205 
206     s = IceUtilInternal::toUpper(s);
207 
208     DeviceAddress da;
209     if(!parseDeviceAddress(s, da))
210     {
211         throw EndpointParseException(__FILE__, __LINE__, "invalid address value `" + s + "' in endpoint " +
212                                      endpoint->toString());
213     }
214     if(!_instance->engine()->adapterExists(s))
215     {
216         throw EndpointParseException(__FILE__, __LINE__, "no device found for `" + s + "' in endpoint " +
217                                      endpoint->toString());
218     }
219 
220     const_cast<string&>(_addr) = s;
221 }
222 
~AcceptorI()223 IceBT::AcceptorI::~AcceptorI()
224 {
225 }
226