|
1 // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include <bt_sock.h> |
|
17 #include <e32base.h> |
|
18 #include <e32msgqueue.h> |
|
19 #include <remconaddress.h> |
|
20 #include <remconbeareravrcp.h> |
|
21 #include "avrcplog.h" |
|
22 #include "avrcputils.h" |
|
23 |
|
24 /** |
|
25 @file |
|
26 @internalComponent |
|
27 @released |
|
28 */ |
|
29 |
|
30 /** Utility AVRCP panic function. |
|
31 |
|
32 @param aPanic The panic number. |
|
33 */ |
|
34 void AvrcpUtils::Panic(TAvrcpPanic aPanic) |
|
35 { |
|
36 User::Panic(KAvrcpPanicName, aPanic); |
|
37 } |
|
38 |
|
39 |
|
40 /** Set the command data. This overwrites the current |
|
41 contents of the data buffer. |
|
42 |
|
43 @param aCommandData The buffer in which to set the data. |
|
44 @param aOffset The offset within aCommandData to set the data. |
|
45 @param aLength The length of data to replace. |
|
46 @param aValue The new value for the replaced data. |
|
47 */ |
|
48 void AvrcpUtils::SetCommandDataFromInt(RBuf8& aCommandData, |
|
49 TInt aOffset, TInt aLength, TInt aValue) |
|
50 { |
|
51 LOG_STATIC_FUNC |
|
52 __ASSERT_DEBUG(aLength <= 4, Panic(EAvrcpCommandDataTooLong)); |
|
53 |
|
54 for(TInt i = 0; i < aLength; i++) |
|
55 { |
|
56 aCommandData[aOffset+i] = aValue >> (8*i); |
|
57 } |
|
58 } |
|
59 |
|
60 /** Reads command data from the buffer to an int. |
|
61 |
|
62 @param aCommandData The buffer from which to read the data. |
|
63 @param aOffset The offset within aCommandData read from. |
|
64 @param aLength The length of data to read. This must not be |
|
65 more than 4. |
|
66 @param aValue On return, the value of the specified data section. |
|
67 */ |
|
68 void AvrcpUtils::ReadCommandDataToInt(const RBuf8& aCommandData, |
|
69 TInt aOffset, TInt aLength, TInt& aValue) |
|
70 { |
|
71 LOG_STATIC_FUNC |
|
72 __ASSERT_DEBUG(aLength <= 4, Panic(EAvrcpCommandDataTooLong)); |
|
73 |
|
74 aValue = 0; |
|
75 |
|
76 for(TInt i = 0 ; i < aLength; i++) |
|
77 { |
|
78 aValue |= aCommandData[aOffset+i]<<(8*i); |
|
79 } |
|
80 } |
|
81 |
|
82 /** Convert from a RemCon address to a bluetooth device address. |
|
83 |
|
84 @param aRemoteAddress The RemCon format address to convert. |
|
85 @param aBTAddr On return, the bluetooth device address. |
|
86 @return Whether the conversion could be performed successfully. |
|
87 */ |
|
88 TInt AvrcpUtils::RemConToBTAddr(const TRemConAddress& aRemoteAddress, TBTDevAddr& aBTAddr) |
|
89 { |
|
90 LOG_STATIC_FUNC |
|
91 TInt err = KErrArgument; |
|
92 |
|
93 // Check client has provided us a valid address |
|
94 if(aRemoteAddress.Addr().Length() == KBTDevAddrSize) |
|
95 { |
|
96 aBTAddr = TBTDevAddr(aRemoteAddress.Addr()); |
|
97 err = KErrNone; |
|
98 } |
|
99 else |
|
100 { |
|
101 __ASSERT_DEBUG(EFalse, AvrcpUtils::Panic(EAvrcpBadBTAddr)); |
|
102 } |
|
103 |
|
104 return err; |
|
105 } |
|
106 |
|
107 /** Convert from a bluetooth device address to a RemCon address. |
|
108 |
|
109 We assume this cannot fail, as bluetooth addresses are generated |
|
110 internally rather than by a client, so they should always be |
|
111 valid, and so convertible. |
|
112 |
|
113 @param aBTAddr The bluetooth device address to convert. |
|
114 @param aRemoteAddress On return, the RemCon format address. |
|
115 */ |
|
116 void AvrcpUtils::BTToRemConAddr(const TBTDevAddr& aBTAddr, TRemConAddress& aRemConAddress) |
|
117 { |
|
118 LOG_STATIC_FUNC |
|
119 aRemConAddress.Addr() = aBTAddr.Des(); |
|
120 aRemConAddress.BearerUid() = TUid::Uid(KRemConBearerAvrcpImplementationUid); |
|
121 } |
|
122 |
|
123 NONSHARABLE_CLASS(CSpecificThreadCallBackBody) |
|
124 : public CActive |
|
125 { |
|
126 public: |
|
127 static CSpecificThreadCallBackBody* NewL(const TCallBack& aCallBack, TInt aPriority); |
|
128 ~CSpecificThreadCallBackBody(); |
|
129 |
|
130 TInt Start(); |
|
131 TInt CallBack(); |
|
132 void HandleCancel(); |
|
133 |
|
134 private: |
|
135 CSpecificThreadCallBackBody(const TCallBack& aCallBack, TInt aPriority); |
|
136 void ConstructL(); |
|
137 |
|
138 TInt AsyncMessage(TInt aParam); |
|
139 |
|
140 private: // from CActive |
|
141 void RunL(); |
|
142 void DoCancel(); |
|
143 |
|
144 private: |
|
145 TCallBack iCallBack; |
|
146 |
|
147 RThread iLocalThread; |
|
148 |
|
149 RMsgQueue<TInt> iInbound; |
|
150 RMsgQueue<TInt> iOutbound; |
|
151 }; |
|
152 |
|
153 RSpecificThreadCallBack::RSpecificThreadCallBack() |
|
154 : iBody(NULL) |
|
155 { |
|
156 LOG_FUNC |
|
157 } |
|
158 |
|
159 TInt RSpecificThreadCallBack::Create(const TCallBack& aCallBack, TInt aPriority) |
|
160 { |
|
161 TRAPD(err, iBody = CSpecificThreadCallBackBody::NewL(aCallBack, aPriority)); |
|
162 return err; |
|
163 } |
|
164 |
|
165 void RSpecificThreadCallBack::Close() |
|
166 { |
|
167 LOG_FUNC |
|
168 delete iBody; |
|
169 iBody = NULL; |
|
170 } |
|
171 |
|
172 TInt RSpecificThreadCallBack::Start() |
|
173 { |
|
174 return iBody->Start(); |
|
175 } |
|
176 |
|
177 TInt RSpecificThreadCallBack::CallBack() |
|
178 { |
|
179 return iBody->CallBack(); |
|
180 } |
|
181 |
|
182 void RSpecificThreadCallBack::Cancel() |
|
183 { |
|
184 return iBody->HandleCancel(); |
|
185 } |
|
186 |
|
187 CSpecificThreadCallBackBody* CSpecificThreadCallBackBody::NewL(const TCallBack& aCallBack, TInt aPriority) |
|
188 { |
|
189 CSpecificThreadCallBackBody* self = new(ELeave) CSpecificThreadCallBackBody(aCallBack, aPriority); |
|
190 CleanupStack::PushL(self); |
|
191 self->ConstructL(); |
|
192 CleanupStack::Pop(self); |
|
193 return self; |
|
194 } |
|
195 |
|
196 CSpecificThreadCallBackBody::CSpecificThreadCallBackBody(const TCallBack& aCallBack, TInt aPriority) |
|
197 : CActive(aPriority) |
|
198 , iCallBack(aCallBack) |
|
199 { |
|
200 LOG_FUNC |
|
201 } |
|
202 |
|
203 void CSpecificThreadCallBackBody::ConstructL() |
|
204 { |
|
205 User::LeaveIfError(iInbound.CreateLocal(1)); |
|
206 User::LeaveIfError(iOutbound.CreateLocal(1)); |
|
207 } |
|
208 |
|
209 CSpecificThreadCallBackBody::~CSpecificThreadCallBackBody() |
|
210 { |
|
211 LOG_FUNC |
|
212 HandleCancel(); |
|
213 iInbound.Close(); |
|
214 iOutbound.Close(); |
|
215 iLocalThread.Close(); |
|
216 } |
|
217 |
|
218 TInt CSpecificThreadCallBackBody::Start() |
|
219 { |
|
220 TInt err = KErrNone; |
|
221 if(!IsAdded()) |
|
222 { |
|
223 err = iLocalThread.Duplicate(RThread()); |
|
224 if(err == KErrNone) |
|
225 { |
|
226 CActiveScheduler::Add(this); |
|
227 iInbound.NotifyDataAvailable(iStatus); |
|
228 SetActive(); |
|
229 } |
|
230 } |
|
231 return err; |
|
232 } |
|
233 |
|
234 TInt CSpecificThreadCallBackBody::CallBack() |
|
235 { |
|
236 TInt err = KErrUnknown; |
|
237 if(iLocalThread.Id() == RThread().Id()) |
|
238 { |
|
239 // Simple synchronous case. |
|
240 err = iCallBack.CallBack(); |
|
241 } |
|
242 else |
|
243 { |
|
244 RThread thisThread; |
|
245 err = thisThread.Duplicate(RThread()); |
|
246 if(err == KErrNone) |
|
247 { |
|
248 err = AsyncMessage(thisThread.Handle()); |
|
249 } |
|
250 } |
|
251 return err; |
|
252 } |
|
253 |
|
254 TInt CSpecificThreadCallBackBody::AsyncMessage(TInt aParam) |
|
255 { |
|
256 TInt err = KErrNone; |
|
257 TRequestStatus logonStatus; |
|
258 iLocalThread.Logon(logonStatus); |
|
259 if(logonStatus == KErrNoMemory) |
|
260 { |
|
261 // This seems kludgy, but I think it is the most reliable way. |
|
262 User::WaitForRequest(logonStatus); // Ensure the all requests are correct... |
|
263 err = KErrNoMemory; |
|
264 } |
|
265 else |
|
266 { |
|
267 iInbound.SendBlocking(aParam); |
|
268 TRequestStatus status; |
|
269 iOutbound.NotifyDataAvailable(status); |
|
270 User::WaitForRequest(status, logonStatus); |
|
271 if(status == KRequestPending) |
|
272 { |
|
273 // Remote thread is dead |
|
274 iOutbound.CancelDataAvailable(); |
|
275 User::WaitForRequest(status); |
|
276 err = KErrDied; |
|
277 } |
|
278 else |
|
279 { |
|
280 // Success (the thread may have subsequently died, but we are only concerned with this call). |
|
281 iLocalThread.LogonCancel(logonStatus); |
|
282 User::WaitForRequest(logonStatus); |
|
283 err = status.Int(); |
|
284 if(err == KErrNone) |
|
285 { |
|
286 iOutbound.ReceiveBlocking(err); |
|
287 } |
|
288 } |
|
289 } |
|
290 return err; |
|
291 } |
|
292 |
|
293 |
|
294 void CSpecificThreadCallBackBody::RunL() |
|
295 { |
|
296 TInt threadHandle; |
|
297 iInbound.ReceiveBlocking(threadHandle); |
|
298 if(threadHandle == 0) |
|
299 { |
|
300 // 0 is a cancel message |
|
301 // therefore don't do anything |
|
302 iOutbound.SendBlocking(KErrNone); |
|
303 } |
|
304 else |
|
305 { |
|
306 RThread remoteThread; |
|
307 remoteThread.SetHandleNC(threadHandle); |
|
308 |
|
309 TInt result = iCallBack.CallBack(); |
|
310 |
|
311 // There doesn't seem to be a safe way of handling when the other thread |
|
312 // dies...... |
|
313 iOutbound.SendBlocking(result); |
|
314 |
|
315 remoteThread.Close(); |
|
316 |
|
317 iInbound.NotifyDataAvailable(iStatus); |
|
318 SetActive(); |
|
319 } |
|
320 } |
|
321 |
|
322 void CSpecificThreadCallBackBody::DoCancel() |
|
323 { |
|
324 if(RThread().Id() == iLocalThread.Id()) |
|
325 { |
|
326 iInbound.CancelDataAvailable(); |
|
327 } |
|
328 else |
|
329 { |
|
330 // other thread cancelling - so just complete the |
|
331 // request |
|
332 TRequestStatus* status = &iStatus; |
|
333 User::RequestComplete(status, KErrCancel); |
|
334 } |
|
335 } |
|
336 |
|
337 void CSpecificThreadCallBackBody::HandleCancel() |
|
338 { |
|
339 if(IsAdded()) |
|
340 { |
|
341 if(RThread().Id() == iLocalThread.Id()) |
|
342 { |
|
343 Cancel(); // synchronous cancel is fine in same thread... |
|
344 } |
|
345 else |
|
346 { |
|
347 // In a different thread - this is more interesting... |
|
348 TInt err = AsyncMessage(0); // 0 is special as it means cancel. |
|
349 if(err == KErrDied && IsActive()) |
|
350 { |
|
351 // Remote thread has already died so we need to tidy up the |
|
352 // active object ourselves. |
|
353 Cancel(); |
|
354 } |
|
355 } |
|
356 } |
|
357 // else shouldn't be active... |
|
358 } |
|
359 |
|
360 |