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