|
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 |
|
17 |
|
18 /** |
|
19 @file |
|
20 @internalComponent |
|
21 @released |
|
22 */ |
|
23 |
|
24 #include <remconcoreapi.h> |
|
25 |
|
26 #include "avcpanel.h" |
|
27 #include "controlcommand.h" |
|
28 #include "avrcpoutgoingcommandhandler.h" |
|
29 #include "avrcplog.h" |
|
30 #include "avrcprouter.h" |
|
31 #include "avrcptimer.h" |
|
32 #include "avrcputils.h" |
|
33 #include "controlbearer.h" |
|
34 |
|
35 //--------------------------------------------------------------------- |
|
36 // Construction/Destruction |
|
37 //--------------------------------------------------------------------- |
|
38 |
|
39 /** Factory function. |
|
40 |
|
41 @param aBearer The CRemConBearerAvrcp this is to handle commands for. |
|
42 @param aObserver The observer of the bearer. Used to aquire converters. |
|
43 @param aRouter A CRcpRouter to use for communication with remote devices. |
|
44 @param aTimer CDeltaTimer to use for queuing timed events. |
|
45 @return A fully constructed CRcpOutgoingCommandHandler. |
|
46 @leave System wide error codes. |
|
47 */ |
|
48 CRcpOutgoingCommandHandler* CRcpOutgoingCommandHandler::NewL(CRemConBearerAvrcp& aBearer, |
|
49 MRemConBearerObserver& aObserver, |
|
50 CRcpRouter& aRouter, |
|
51 CDeltaTimer& aTimer) |
|
52 { |
|
53 LOG_STATIC_FUNC |
|
54 CRcpOutgoingCommandHandler* handler = new(ELeave)CRcpOutgoingCommandHandler(aBearer, aObserver, aRouter, aTimer); |
|
55 return handler; |
|
56 } |
|
57 |
|
58 /** Constructor. |
|
59 |
|
60 @param aBearer The CRemConBearerAvrcp this is to handle commands for. |
|
61 @param aObserver The observer of the bearer. Used to aquire converters. |
|
62 @param aRouter A CRcpRouter to use for communication with remote devices. |
|
63 @param aTimer CDeltaTimer to use for queuing timed events. |
|
64 @return A partially constructed CRcpIncomingCommandHandler. |
|
65 @leave System wide error codes. |
|
66 */ |
|
67 CRcpOutgoingCommandHandler::CRcpOutgoingCommandHandler(CRemConBearerAvrcp& aBearer, |
|
68 MRemConBearerObserver& aObserver, |
|
69 CRcpRouter& aRouter, |
|
70 CDeltaTimer& aTimer) : iCommandQueue(_FOFF(CControlCommand, iHandlingLink)), |
|
71 iNotifyCommandQueue(_FOFF(CControlCommand, iHandlingLink)), |
|
72 iBearer(aBearer), iObserver(aObserver), iRouter(aRouter), iTimer(aTimer) |
|
73 { |
|
74 LOG_FUNC |
|
75 } |
|
76 |
|
77 /** Destructor. |
|
78 */ |
|
79 CRcpOutgoingCommandHandler::~CRcpOutgoingCommandHandler() |
|
80 { |
|
81 LOG_FUNC |
|
82 |
|
83 ClearQueue(iCommandQueue); |
|
84 ClearQueue(iNotifyCommandQueue); |
|
85 } |
|
86 |
|
87 void CRcpOutgoingCommandHandler::ClearQueue(TDblQue<CControlCommand>& aQue) |
|
88 { |
|
89 while(!aQue.IsEmpty()) |
|
90 { |
|
91 CControlCommand* command = aQue.First(); |
|
92 command->CancelTimer(iTimer); |
|
93 command->iHandlingLink.Deque(); |
|
94 command->DecrementUsers(); |
|
95 } |
|
96 } |
|
97 //--------------------------------------------------------------------- |
|
98 // Called from the bearer |
|
99 //--------------------------------------------------------------------- |
|
100 |
|
101 /** Tell the handler to gracefully shutdown. |
|
102 |
|
103 @param aClearQueue Whether to clear the queue without handling the things |
|
104 on it. If this is true the commands will be deleted. |
|
105 If this is false then pending commands will have responses |
|
106 generated to RemCon. |
|
107 */ |
|
108 void CRcpOutgoingCommandHandler::Disconnect(TBool aClearQueue) |
|
109 { |
|
110 LOG_FUNC |
|
111 ProcessDisconnect(iCommandQueue, aClearQueue); |
|
112 ProcessDisconnect(iNotifyCommandQueue, aClearQueue); |
|
113 } |
|
114 |
|
115 void CRcpOutgoingCommandHandler::ProcessDisconnect(TDblQue<CControlCommand>& aQue, TBool aClearQueue) |
|
116 { |
|
117 while(!aQue.IsEmpty()) |
|
118 { |
|
119 CControlCommand* command = aQue.First(); |
|
120 iRouter.RemoveFromSendQueue(*command); |
|
121 command->CancelTimer(iTimer); |
|
122 |
|
123 if(aClearQueue) |
|
124 { |
|
125 GenerateFailureResult(*command, KErrDisconnected); |
|
126 } |
|
127 |
|
128 command->iHandlingLink.Deque(); |
|
129 command->DecrementUsers(); |
|
130 } |
|
131 } |
|
132 /** Sends a new command. |
|
133 |
|
134 @param aInterfaceUid The RemCon client interface this command is from. |
|
135 @param aCommand The operation id within aInterfaceUid. |
|
136 @param aId A unique identifier provided by RemCon. |
|
137 @param aCommandData Data associated with this command. |
|
138 @param aAddr Bluetooth address of device to send this command to. |
|
139 @leave KErrNoMemory or system wide error code. |
|
140 @leave Command parsing error. |
|
141 */ |
|
142 void CRcpOutgoingCommandHandler::SendCommandL(TUid aInterfaceUid, |
|
143 TUint aCommand, |
|
144 TUint aId, |
|
145 RBuf8& aCommandData, |
|
146 const TBTDevAddr& aAddr) |
|
147 { |
|
148 LOG_FUNC |
|
149 |
|
150 if(aInterfaceUid.iUid == KRemConCoreApiUid) |
|
151 { |
|
152 // Passthrough commands are stateful, so we need to examine the |
|
153 // history - we can't just blindly wham it on the queue. |
|
154 HandleCoreApiCommandL(aCommand, aId, aCommandData, aAddr); |
|
155 } |
|
156 else |
|
157 { |
|
158 SendCommandL(aInterfaceUid, aCommand, aId, aCommandData, EFalse, aAddr, ETrue, EFalse); |
|
159 } |
|
160 } |
|
161 |
|
162 /** Sends a new notify command. |
|
163 |
|
164 @param aInterfaceUid The RemCon client interface this command is from. |
|
165 @param aCommand The operation id within aInterfaceUid. |
|
166 @param aId A unique identifier provided by RemCon, the transaction ID. |
|
167 @param aCommandData Data associated with this command. |
|
168 @param aAddr Bluetooth address of device to send this command to. |
|
169 @leave KErrNoMemory or system wide error code. |
|
170 @leave Command parsing error. |
|
171 */ |
|
172 void CRcpOutgoingCommandHandler::SendNotifyCommandL(TUid aInterfaceUid, |
|
173 TUint aCommand, |
|
174 TUint aId, |
|
175 RBuf8& aCommandData, |
|
176 const TBTDevAddr& aAddr) |
|
177 { |
|
178 LOG_FUNC |
|
179 SendCommandL(aInterfaceUid, aCommand, aId, aCommandData, EFalse, aAddr, ETrue, ETrue); |
|
180 } |
|
181 |
|
182 //--------------------------------------------------------------------- |
|
183 // Data notifications from the router |
|
184 //--------------------------------------------------------------------- |
|
185 |
|
186 /** Called by the router to provide a new response. |
|
187 |
|
188 @param aFrame The AV/C frame for this response. Ownership is taken. |
|
189 @param aTransLabel The AVCTP transaction id of this response. This is used |
|
190 to match it with its command. |
|
191 @param aAddr The remote from which this response originated |
|
192 */ |
|
193 void CRcpOutgoingCommandHandler::ReceiveResponse(const TDesC8& aMessageInformation, |
|
194 SymbianAvctp::TTransactionLabel aTransLabel, |
|
195 TBool aIpidBitSet) |
|
196 { |
|
197 LOG_FUNC |
|
198 |
|
199 CAVCFrame* frame = NULL; |
|
200 TInt err = KErrNone; |
|
201 if(!aIpidBitSet) |
|
202 { |
|
203 TRAP(err, frame = CAVCFrame::NewL(aMessageInformation, AVC::EResponse)); |
|
204 } |
|
205 |
|
206 if(!err) |
|
207 { |
|
208 CControlCommand* command = NULL; |
|
209 command = FindInQueue(iCommandQueue, aTransLabel); |
|
210 if ( command != NULL ) |
|
211 { |
|
212 //Found, so it is a normal command response. |
|
213 ProcessReceiveResponse(frame, aIpidBitSet, command, EFalse); |
|
214 } |
|
215 else |
|
216 { |
|
217 //Try to find in the notify command queue. |
|
218 command = FindInQueue(iNotifyCommandQueue, aTransLabel); |
|
219 if( command != NULL ) |
|
220 { |
|
221 //Found, so it is a notify command response. |
|
222 ProcessReceiveResponse(frame, aIpidBitSet, command, ETrue); |
|
223 } |
|
224 } |
|
225 |
|
226 delete frame; |
|
227 } |
|
228 } |
|
229 |
|
230 CControlCommand* CRcpOutgoingCommandHandler::FindInQueue(TDblQue<CControlCommand>& aQue, |
|
231 SymbianAvctp::TTransactionLabel aTransLabel) |
|
232 { |
|
233 CControlCommand* command = NULL; |
|
234 TDblQueIter<CControlCommand> iter(aQue); |
|
235 while (iter) |
|
236 { |
|
237 command = iter++; |
|
238 if(command->TransactionLabel() == aTransLabel) |
|
239 { |
|
240 return command; |
|
241 } |
|
242 } |
|
243 |
|
244 return NULL; |
|
245 } |
|
246 |
|
247 void CRcpOutgoingCommandHandler::ProcessReceiveResponse(CAVCFrame* aFrame, |
|
248 TBool aIpidBitSet, |
|
249 CControlCommand* aCommand, |
|
250 TBool aNotify) |
|
251 { |
|
252 aCommand->CancelTimer(iTimer); |
|
253 |
|
254 TInt err = KErrNone; |
|
255 // Inform the bearer if this is something it knows about |
|
256 // ie not a click release |
|
257 if(aCommand->KnownToBearer()) |
|
258 { |
|
259 if(!aIpidBitSet) |
|
260 { |
|
261 if(aFrame->Data().Length() < KAVCFrameHeaderLength) |
|
262 { |
|
263 // Drop corrupt frames |
|
264 return; |
|
265 } |
|
266 |
|
267 err = aCommand->ParseIncomingResponse(iObserver, *aFrame); |
|
268 } |
|
269 else |
|
270 { |
|
271 // If aIpidBitSet is true that means AVRCP is not supported |
|
272 // by the remote end. We handle this in the same way as not |
|
273 // supported commands, passing them up to RemCon as not |
|
274 // supported, so just map the ctype here, rather than setting |
|
275 // up another path for ipid handling, but we need pass as the |
|
276 // frame the original because we don't get one from AVCTP if |
|
277 // ipid is set. |
|
278 aCommand->SetResponseType(KErrNotSupported); |
|
279 err = aCommand->ParseIncomingResponse(iObserver, aCommand->Frame()); |
|
280 } |
|
281 |
|
282 if ( aNotify ) |
|
283 {//This is a notify command |
|
284 iBearer.MrccciNewNotifyResponse(*aCommand); |
|
285 } |
|
286 else |
|
287 { |
|
288 iBearer.MrccciNewResponse(*aCommand); |
|
289 } |
|
290 } |
|
291 |
|
292 TBool doDeque = ETrue; |
|
293 if ( (!aIpidBitSet) && (err == KErrNone) && (aNotify) && (aFrame->Type() == AVC::EInterim)) |
|
294 { |
|
295 doDeque = EFalse; |
|
296 } |
|
297 |
|
298 // If this a passthrough press that hasn't yet been released, we need |
|
299 // to wait for a release before getting rid of this, otherwise we're done. |
|
300 if(aCommand == iUnreleasedCommand) |
|
301 { |
|
302 iUnreleasedHasResponse = ETrue; |
|
303 StartReleaseTimer(*iUnreleasedCommand); |
|
304 doDeque = EFalse; |
|
305 } |
|
306 |
|
307 if ( doDeque ) |
|
308 { |
|
309 aCommand->iHandlingLink.Deque(); |
|
310 aCommand->DecrementUsers(); |
|
311 } |
|
312 } |
|
313 /** Called by the router to complete a send. |
|
314 |
|
315 @param aCommand The command which has been sent. |
|
316 @param aSendResult The result of the send. KErrNone if successful. |
|
317 */ |
|
318 void CRcpOutgoingCommandHandler::MessageSent(CAvrcpCommand& aCommand, TInt aSendResult) |
|
319 { |
|
320 LOG_FUNC |
|
321 |
|
322 if(aSendResult == KErrNone) |
|
323 { |
|
324 // Set off response timer |
|
325 StartResponseTimer(static_cast<CControlCommand&>(aCommand)); |
|
326 } |
|
327 else |
|
328 { |
|
329 CControlCommand* command = FindInQueue(iNotifyCommandQueue, aCommand.TransactionLabel()); |
|
330 |
|
331 if(command) |
|
332 { |
|
333 command->SetNotifyVolumeChangeResult(command->Frame()); |
|
334 iBearer.MrccciNewNotifyResponse(*command); |
|
335 } |
|
336 else |
|
337 { |
|
338 command = FindInQueue(iCommandQueue, aCommand.TransactionLabel()); |
|
339 |
|
340 // Generate error response up to RemCon |
|
341 // if this is a core command we can set the result, |
|
342 // otherwise we just return it as we got it. |
|
343 if(command->Frame().Opcode() == AVC::EPassThrough) |
|
344 { |
|
345 // Need to insert before setting the button action so we have |
|
346 // long enough data |
|
347 if (!command->InsertCoreResult(aSendResult)) |
|
348 { |
|
349 if(command->Click()) |
|
350 { |
|
351 command->SetCoreButtonAction(ERemConCoreApiButtonClick, ETrue); |
|
352 } |
|
353 } |
|
354 } |
|
355 |
|
356 iBearer.MrccciNewResponse(*command); |
|
357 } |
|
358 |
|
359 command->iHandlingLink.Deque(); |
|
360 command->DecrementUsers(); |
|
361 } |
|
362 } |
|
363 |
|
364 //--------------------------------------------------------------------- |
|
365 // Internal Utility functions |
|
366 //--------------------------------------------------------------------- |
|
367 |
|
368 void CRcpOutgoingCommandHandler::CleanupUnreleased() |
|
369 { |
|
370 iUnreleasedCommand->CancelTimer(iTimer); |
|
371 iUnreleasedCommand->iHandlingLink.Deque(); |
|
372 iUnreleasedCommand->DecrementUsers(); |
|
373 iUnreleasedHasResponse = EFalse; |
|
374 } |
|
375 |
|
376 /** Handle a command that is part of the Core API. |
|
377 |
|
378 @param aCommand The operation id within aInterfaceUid. |
|
379 @param aId A unique identifier provided by RemCon. |
|
380 @param aCommandData Data associated with this command. |
|
381 @param aAddr Bluetooth address of device to send this command to. |
|
382 @leave KErrNoMemory or system wide error code. |
|
383 @leave Command parsing error. |
|
384 */ |
|
385 void CRcpOutgoingCommandHandler::HandleCoreApiCommandL(TUint aCommand, |
|
386 TUint aId, |
|
387 RBuf8& aCommandData, |
|
388 const TBTDevAddr& aAddr) |
|
389 { |
|
390 if(aCommandData.Length() < KRemConCoreApiButtonDataLength) |
|
391 { |
|
392 User::Leave(KErrCorrupt); |
|
393 } |
|
394 |
|
395 TInt buttonData; |
|
396 AvrcpUtils::ReadCommandDataToInt(aCommandData, |
|
397 KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, buttonData); |
|
398 |
|
399 // First check if there's anything we need to do before sending this command, |
|
400 // mainly releasing a previous press. |
|
401 if(iUnreleasedCommand) |
|
402 { |
|
403 TUint prevOpId = iUnreleasedCommand->RemConOperationId(); |
|
404 |
|
405 if(aCommand == prevOpId) |
|
406 { |
|
407 // Either we've received a release, or we've refreshed the press. |
|
408 // If the unreleased press has already been responded too we can |
|
409 // dispose of it now. |
|
410 // If the unreleased press has not been responded too we can |
|
411 // treat it like a normal command on reception of response, so just |
|
412 // set iUnreleased to NULL. |
|
413 if(iUnreleasedHasResponse) |
|
414 { |
|
415 CleanupUnreleased(); |
|
416 } |
|
417 |
|
418 iUnreleasedCommand = NULL; |
|
419 } |
|
420 else |
|
421 { |
|
422 // A new operation! |
|
423 if(buttonData != ERemConCoreApiButtonRelease) |
|
424 { |
|
425 // Try and generate the release for the previous command, if |
|
426 // if fails then the remote will just have to assume it. |
|
427 // There's no point leaving this to the release timer, because |
|
428 // we want to send another command now, so even if we send the |
|
429 // release later the remote should ignore it. |
|
430 TRAP_IGNORE(GenerateCommandL(*iUnreleasedCommand, ERemConCoreApiButtonRelease)); |
|
431 |
|
432 if(iUnreleasedHasResponse) |
|
433 { |
|
434 CleanupUnreleased(); |
|
435 } |
|
436 |
|
437 iUnreleasedCommand = NULL; |
|
438 } |
|
439 else |
|
440 { |
|
441 // A release for a command other than iUnreleased. We can't |
|
442 // send this now. |
|
443 User::Leave(KErrNotReady); |
|
444 } |
|
445 } |
|
446 } |
|
447 else if(buttonData == ERemConCoreApiButtonRelease) |
|
448 { |
|
449 // We don't have an unreleased command. We must have already |
|
450 // released this, either via the timer, or because we've sent |
|
451 // another command in the meantime. We can't send this now. |
|
452 // Leaving synchronously means we don't need to worry about generating |
|
453 // a fake response, which may mislead the application. |
|
454 User::Leave(KErrNotReady); |
|
455 } |
|
456 |
|
457 if(buttonData == ERemConCoreApiButtonClick) |
|
458 { |
|
459 // aCommandData is still owned by RemCon until we return successfully. |
|
460 // If we try the operations with the new data first we won't end up |
|
461 // in a situation where the new CControlCommand thinks that it owns |
|
462 // aCommandData, then the release operation leaves, so RemCon also |
|
463 // thinks it owns aCommandData. |
|
464 |
|
465 RBuf8 pressBuf; |
|
466 pressBuf.CreateL(aCommandData); |
|
467 CleanupClosePushL(pressBuf); |
|
468 |
|
469 AvrcpUtils::SetCommandDataFromInt(pressBuf, |
|
470 KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, ERemConCoreApiButtonPress); |
|
471 SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, pressBuf, ETrue, aAddr, ETrue, EFalse); |
|
472 |
|
473 // Data has been taken ownership of by SendCommandL, so can just let |
|
474 // pressbuf go out of scope. |
|
475 CleanupStack::Pop(&pressBuf); |
|
476 |
|
477 AvrcpUtils::SetCommandDataFromInt(aCommandData, |
|
478 KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, ERemConCoreApiButtonRelease); |
|
479 SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, aCommandData, ETrue, aAddr, EFalse, EFalse); |
|
480 } |
|
481 else if(buttonData == ERemConCoreApiButtonPress) |
|
482 { |
|
483 iUnreleasedCommand = &SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, aCommandData, EFalse, aAddr, ETrue, EFalse); |
|
484 iReleaseTimerExpiryCount = 0; |
|
485 } |
|
486 else |
|
487 { |
|
488 // Must be release |
|
489 __ASSERT_DEBUG(buttonData == ERemConCoreApiButtonRelease, AvrcpUtils::Panic(EAvrcpUnknownButtonAction)); |
|
490 SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, aCommandData, EFalse, aAddr, ETrue, EFalse); |
|
491 } |
|
492 } |
|
493 |
|
494 /** Creates a command from the RemCon data. |
|
495 |
|
496 This is an internal utility function. |
|
497 |
|
498 A CControlCommand will be created. Calling ProcessOutgoingCommandL on |
|
499 this creates a CAVCFrame from the provided data. If an AV/C frame |
|
500 can be created the command will be added to the outgoing queue to |
|
501 wait a response from the remote. Otherwise this function will |
|
502 leave. |
|
503 |
|
504 @param aInterfaceUid The RemCon client interface this command is from. |
|
505 @param aCommand The operation id within aInterfaceUid. |
|
506 @param aId A unique identifier provided by RemCon. |
|
507 @param aCommandData Data associated with this command. |
|
508 @param aIsClick Whether this is a button click. |
|
509 @param aAddr Bluetooth address of device to send this command to. |
|
510 @return The generated command. |
|
511 @leave KErrNoMemory or system wide error code. |
|
512 @leave Command parsing error. |
|
513 */ |
|
514 CControlCommand& CRcpOutgoingCommandHandler::SendCommandL(TUid aInterfaceUid, |
|
515 TUint aCommand, |
|
516 TUint aId, |
|
517 RBuf8& aCommandData, |
|
518 TBool aIsClick, |
|
519 const TBTDevAddr& aAddr, |
|
520 TBool aKnownToBearer, |
|
521 TBool aNotify) |
|
522 { |
|
523 LOG_FUNC |
|
524 // Create a command and wham it on our queue, so we can match it up with its response |
|
525 // CControlCommand::NewL takes ownership of the data in aCommandData then NULLs aCommandData |
|
526 // so a leave later in the function won't cause double deletion. |
|
527 CControlCommand* command = CControlCommand::NewL(aInterfaceUid, aCommand, aId, iCurrentTrans, |
|
528 aCommandData, aIsClick, aAddr, aKnownToBearer); |
|
529 CleanupStack::PushL(command); |
|
530 |
|
531 command->ProcessOutgoingCommandL(iObserver); |
|
532 CleanupStack::Pop(command); |
|
533 command->IncrementUsers(); |
|
534 |
|
535 if ( aNotify ) |
|
536 { |
|
537 iNotifyCommandQueue.AddLast(*command); |
|
538 } |
|
539 else |
|
540 { |
|
541 iCommandQueue.AddLast(*command); |
|
542 } |
|
543 |
|
544 // Increment our transaction id |
|
545 iCurrentTrans = (iCurrentTrans + 1) % SymbianAvctp::KMaxTransactionLabel; |
|
546 |
|
547 // Command stays on the queue till we've got the response |
|
548 iRouter.AddToSendQueue(*command); |
|
549 |
|
550 return *command; |
|
551 } |
|
552 |
|
553 /** Generate a failure response to RemCon. |
|
554 |
|
555 This sets the result for a passthrough command. |
|
556 It informs the bearer of the new response. |
|
557 |
|
558 @param aCommand The command to finish off. |
|
559 @param aResult The result (only valid for passthrough) |
|
560 */ |
|
561 void CRcpOutgoingCommandHandler::GenerateFailureResult(CControlCommand& aCommand, TInt aResult) |
|
562 { |
|
563 // Response is only necessary if the bearer knows about this command. |
|
564 if(aCommand.KnownToBearer() && (aCommand.Frame().Opcode() == AVC::EPassThrough)) |
|
565 { |
|
566 if (aCommand.InsertCoreResult(aResult) == KErrNone) |
|
567 { |
|
568 if(aCommand.Click()) |
|
569 { |
|
570 aCommand.SetCoreButtonAction(ERemConCoreApiButtonClick, ETrue); |
|
571 } |
|
572 else if(aCommand.ButtonAct() == AVCPanel::EButtonPress) |
|
573 { |
|
574 aCommand.SetCoreButtonAction(ERemConCoreApiButtonPress, ETrue); |
|
575 } |
|
576 else |
|
577 { |
|
578 aCommand.SetCoreButtonAction(ERemConCoreApiButtonRelease, ETrue); |
|
579 } |
|
580 |
|
581 iBearer.MrccciNewResponse(aCommand); |
|
582 } |
|
583 } |
|
584 } |
|
585 |
|
586 /** Generate a command to the remote. |
|
587 |
|
588 This is needed in situations where the application has not met the avrcp |
|
589 button refresh requirements so we need to internally generate something |
|
590 to stop the remote getting a bad impression of us. |
|
591 |
|
592 @param aCommand The command to be issue again. |
|
593 */ |
|
594 void CRcpOutgoingCommandHandler::GenerateCommandL(CControlCommand& aCommand, TRemConCoreApiButtonAction aButtonAct) |
|
595 { |
|
596 LOG_FUNC |
|
597 |
|
598 RBuf8 commandData; |
|
599 commandData.CreateMaxL(KRemConCoreApiButtonDataLength); |
|
600 |
|
601 AvrcpUtils::SetCommandDataFromInt(commandData, |
|
602 KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, aButtonAct); |
|
603 |
|
604 // This will not leave before taking ownership of commandData. |
|
605 SendCommandL(aCommand.RemConInterfaceUid(), aCommand.RemConOperationId(), aCommand.RemConCommandId(), commandData, EFalse, |
|
606 aCommand.RemoteAddress(), EFalse, EFalse); |
|
607 } |
|
608 |
|
609 //------------------------------------------------------------------------------------ |
|
610 // Timer functions |
|
611 //------------------------------------------------------------------------------------ |
|
612 |
|
613 /** Starts the response timer. |
|
614 |
|
615 AVRCP mandates a remote respond within 100ms of receiving a command. |
|
616 This is the timer for that, and is started when a command has |
|
617 successfully been sent. |
|
618 |
|
619 @param aCommand The command to start the timer for. |
|
620 */ |
|
621 void CRcpOutgoingCommandHandler::StartResponseTimer(CControlCommand& aCommand) |
|
622 { |
|
623 LOG_FUNC |
|
624 // These use placement new, so cannot fail |
|
625 TAvrcpTimerExpiryInfo* timerInfo = new(aCommand.TimerExpiryInfo())TAvrcpTimerExpiryInfo(this, aCommand); |
|
626 |
|
627 TCallBack callback(ResponseExpiry, timerInfo); |
|
628 TDeltaTimerEntry* timerEntry = new(aCommand.TimerEntry())TDeltaTimerEntry(callback); |
|
629 |
|
630 iTimer.Queue(KRcpResponseTimeOut, *timerEntry); |
|
631 } |
|
632 |
|
633 /** Callback when response timer expires. |
|
634 |
|
635 This is a static forwarding function. |
|
636 |
|
637 @param aExpiryInfo The information used by the real ResponseExpiry to |
|
638 deal with the timer expiry. |
|
639 */ |
|
640 TInt CRcpOutgoingCommandHandler::ResponseExpiry(TAny* aExpiryInfo) |
|
641 { |
|
642 LOG_STATIC_FUNC |
|
643 TAvrcpTimerExpiryInfo *timerInfo = reinterpret_cast<TAvrcpTimerExpiryInfo*>(aExpiryInfo); |
|
644 (reinterpret_cast<CRcpOutgoingCommandHandler*>(timerInfo->iHandler))->ResponseExpiry(timerInfo->iCommand); |
|
645 |
|
646 return KErrNone; |
|
647 } |
|
648 |
|
649 /** Deals with response timeout. |
|
650 |
|
651 This sends a timeout response to RemCon. |
|
652 |
|
653 @param aCommand The CControlCommand that has expired. |
|
654 */ |
|
655 void CRcpOutgoingCommandHandler::ResponseExpiry(CControlCommand& aCommand) |
|
656 { |
|
657 LOG_FUNC |
|
658 |
|
659 GenerateFailureResult(aCommand, KErrTimedOut); |
|
660 |
|
661 // Failed to get a response to this, don't bother about trying |
|
662 // to release it. |
|
663 if(iUnreleasedCommand == &aCommand) |
|
664 { |
|
665 iUnreleasedCommand = NULL; |
|
666 __ASSERT_DEBUG(!iUnreleasedHasResponse, AvrcpUtils::Panic(EAvrcpPressHasPhantomResponse)); |
|
667 } |
|
668 |
|
669 aCommand.iHandlingLink.Deque(); |
|
670 aCommand.DecrementUsers(); |
|
671 } |
|
672 |
|
673 /** Starts the release timer. |
|
674 |
|
675 AVRCP requires a press to be refreshed less than 2s after the first |
|
676 press, if it is not to be assumed to have been released. We pass |
|
677 this requirement on to RemCon clients as we don't know when they might |
|
678 go away and we don't want to keep buttons pressed forever. If the |
|
679 release timer expires we will assume a release, and generate it to |
|
680 the remote. |
|
681 |
|
682 @param aCommand The command to start the timer for. |
|
683 */ |
|
684 void CRcpOutgoingCommandHandler::StartReleaseTimer(CControlCommand& aCommand) |
|
685 { |
|
686 LOG_FUNC |
|
687 |
|
688 // These cannot fail as we use placement new |
|
689 TAvrcpTimerExpiryInfo* timerInfo = new(aCommand.TimerExpiryInfo())TAvrcpTimerExpiryInfo(this, aCommand); |
|
690 |
|
691 TCallBack callback(ReleaseExpiry, timerInfo); |
|
692 TDeltaTimerEntry* timerEntry = new(aCommand.TimerEntry())TDeltaTimerEntry(callback); |
|
693 |
|
694 iTimer.Queue(KRcpOutgoingButtonReleaseTimeout, *timerEntry); |
|
695 } |
|
696 |
|
697 /** Callback when release timer expires. |
|
698 |
|
699 This is a static forwarding function. |
|
700 |
|
701 @param aExpiryInfo The information used by the real ReleaseExpiry to |
|
702 deal with the timer expiry. |
|
703 */ |
|
704 TInt CRcpOutgoingCommandHandler::ReleaseExpiry(TAny* aExpiryInfo) |
|
705 { |
|
706 LOG_STATIC_FUNC |
|
707 TAvrcpTimerExpiryInfo *timerInfo = reinterpret_cast<TAvrcpTimerExpiryInfo*>(aExpiryInfo); |
|
708 (reinterpret_cast<CRcpOutgoingCommandHandler*>(timerInfo->iHandler))->ReleaseExpiry(timerInfo->iCommand); |
|
709 |
|
710 return KErrNone; |
|
711 } |
|
712 |
|
713 /** Deals with expiry of release timer. |
|
714 |
|
715 1) Generate release for this command. |
|
716 2) Send release to remote. |
|
717 |
|
718 @param aCommand The CControlCommand that has expired. |
|
719 */ |
|
720 void CRcpOutgoingCommandHandler::ReleaseExpiry(CControlCommand& aCommand) |
|
721 { |
|
722 LOG_FUNC |
|
723 __ASSERT_DEBUG((aCommand.ButtonAct() == AVCPanel::EButtonPress), AvrcpUtils::Panic(EAvrcpReleaseExpiryForRelease)); |
|
724 __ASSERT_DEBUG(&aCommand == iUnreleasedCommand, AvrcpUtils::Panic(EAvrcpReleaseExpiryForOldCommand)); |
|
725 __ASSERT_DEBUG(iUnreleasedHasResponse, AvrcpUtils::Panic(EAvrcpReleaseTimerStartedWithoutResponse)); |
|
726 |
|
727 iReleaseTimerExpiryCount++; |
|
728 |
|
729 TBool commandCompleted = ETrue; |
|
730 // If the client is not yet obliged to refresh this, then send another press. Otherwise generate |
|
731 // the release for them. |
|
732 if((iReleaseTimerExpiryCount * KRcpOutgoingButtonReleaseTimeout) < KRemConCoreApiPressRefreshInterval) |
|
733 { |
|
734 // This will try and generate a press that is identical to the original |
|
735 // aCommand, but with a new AVCTP transaction id. |
|
736 TRAPD(err, GenerateCommandL(aCommand, ERemConCoreApiButtonPress)); |
|
737 |
|
738 if(!err) |
|
739 { |
|
740 // Start the timer again on the original command |
|
741 StartReleaseTimer(aCommand); |
|
742 commandCompleted = EFalse; |
|
743 } |
|
744 } |
|
745 else |
|
746 { |
|
747 // Try an generate a release, but if it fails we just have to let the |
|
748 // remote assume it. |
|
749 TRAP_IGNORE(GenerateCommandL(aCommand, ERemConCoreApiButtonRelease)); |
|
750 } |
|
751 |
|
752 // This condition may be true because |
|
753 // - we have failed to generate a press, in which case the remote is entitled |
|
754 // to assume this is released, so we just give up on it. |
|
755 // or |
|
756 // - the client has not met its press refresh obligation (whether we've |
|
757 // successfully generated a release or not. |
|
758 // In either case we won't do anything more with this command. |
|
759 if(commandCompleted) |
|
760 { |
|
761 aCommand.iHandlingLink.Deque(); |
|
762 aCommand.DecrementUsers(); |
|
763 |
|
764 iUnreleasedCommand = NULL; |
|
765 iUnreleasedHasResponse = EFalse; |
|
766 iReleaseTimerExpiryCount = 0; |
|
767 } |
|
768 } |