|
1 /* |
|
2 * Copyright (c) 2005-2006 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: DRM Play Session |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include "DRMPlaySession.h" |
|
20 #include <e32test.h> |
|
21 #include <e32std.h> |
|
22 #include <e32svr.h> |
|
23 #include <mmf/common/mmfcontrollerframework.h> |
|
24 #ifdef RD_TSP_CLIENT_MAPPER |
|
25 #include <tspclientmapper.h> |
|
26 #endif // #ifdef RD_TSP_CLIENT_MAPPER |
|
27 |
|
28 #ifdef _DEBUG |
|
29 #define DEP_PRN0(str) RDebug::Print(str) |
|
30 #define DEP_PRN1(str, v1) RDebug::Print(str, v1) |
|
31 #define DEP_PRN2(str, v1, v2) RDebug::Print(str, v1, v2) |
|
32 #else |
|
33 #define DEP_PRN0(str) |
|
34 #define DEP_PRN1(str, v1) |
|
35 #define DEP_PRN2(str, v1, v2) |
|
36 #endif // _DEBUG |
|
37 |
|
38 // This is the UID defined by custom interface builder |
|
39 const TUid KUidCustomInterfaceBuilderImpl = {0x10207A8E}; |
|
40 |
|
41 // panic the client |
|
42 void CDRMPlayServerSession::PanicClient(const RMessage2 &aMessage, TInt aPanic) const |
|
43 { |
|
44 _LIT(KTxtServer,"DRM Play server"); |
|
45 aMessage.Panic(KTxtServer,aPanic); |
|
46 } |
|
47 |
|
48 CDRMPlayServerSession::CDRMPlayServerSession(CDRMPlayServer* aServer) |
|
49 :iMessage2(NULL) |
|
50 { |
|
51 iServer=aServer; |
|
52 } |
|
53 |
|
54 CDRMPlayServerSession* CDRMPlayServerSession::NewL(CDRMPlayServer* aServer, const RMessage2& aMessage ) |
|
55 { |
|
56 DEP_PRN0(_L("CDRMPlayServerSession::NewL")); |
|
57 |
|
58 CDRMPlayServerSession* self = new(ELeave) CDRMPlayServerSession(aServer); |
|
59 CleanupStack::PushL(self); |
|
60 self->ConstructL( aMessage ); |
|
61 CleanupStack::Pop(self); |
|
62 return self; |
|
63 } |
|
64 |
|
65 #ifdef RD_TSP_CLIENT_MAPPER |
|
66 void CDRMPlayServerSession::ConstructL( const RMessage2& aMessage ) |
|
67 #else |
|
68 void CDRMPlayServerSession::ConstructL( const RMessage2& /*aMessage*/ ) |
|
69 #endif //RD_TSP_CLIENT_MAPPER |
|
70 { |
|
71 #ifdef RD_TSP_CLIENT_MAPPER |
|
72 iTSPClientMapper = CTspClientMapper::NewL(); |
|
73 // Get client process ID |
|
74 RThread clientThread; |
|
75 RProcess clientProcess; |
|
76 // Open client thread and process handles |
|
77 User::LeaveIfError( aMessage.Client(clientThread) ); |
|
78 User::LeaveIfError( clientThread.Process(clientProcess) ); |
|
79 // Get process id |
|
80 iClientProcessId = clientProcess.Id(); |
|
81 // Close client thread and client process handles |
|
82 clientThread.Close(); |
|
83 clientProcess.Close(); |
|
84 |
|
85 iTSPClientMapper->SetTspTargetClient( |
|
86 CTspClientMapper::ERegisteredClients, |
|
87 iClientProcessId ); |
|
88 #endif // #ifdef RD_TSP_CLIENT_MAPPER |
|
89 |
|
90 // Increment number of sessions in server |
|
91 iServer->iClients++; |
|
92 |
|
93 DEP_PRN1(_L("CDRMPlayServerSession::iClients = %d"), iServer->iClients); |
|
94 } |
|
95 |
|
96 CDRMPlayServerSession::~CDRMPlayServerSession() |
|
97 { |
|
98 if(iMetaDataBuffer) |
|
99 { |
|
100 delete iMetaDataBuffer; |
|
101 iMetaDataBuffer = NULL; |
|
102 } |
|
103 // Delete any pending message objects |
|
104 // This will never happen, only happens when |
|
105 // Client closes the session in the middle of playback |
|
106 // but we cannot complete the message as RSession Handle is invalid |
|
107 /* if (iMessage2) |
|
108 { |
|
109 iMessage2->Complete(KErrCancel); |
|
110 } |
|
111 */ |
|
112 if(iMessage2) |
|
113 { |
|
114 delete iMessage2; |
|
115 iMessage2 = NULL; |
|
116 } |
|
117 |
|
118 // Delete play utility |
|
119 if (iPlayUtil) |
|
120 { |
|
121 delete iPlayUtil; |
|
122 iPlayUtil = NULL; |
|
123 } |
|
124 //delete iAsyncCallback; |
|
125 //iAsyncCallback = NULL; |
|
126 |
|
127 #ifdef RD_TSP_CLIENT_MAPPER |
|
128 if (iTSPClientMapper) |
|
129 { |
|
130 |
|
131 TInt status = iTSPClientMapper->RemoveTspTargetClient( |
|
132 CTspClientMapper::ERegisteredClients, |
|
133 iClientProcessId ); |
|
134 DEP_PRN2(_L("CDRMPlayServerSession::~CDRMPlayServerSession:Unreg PID[%x]Status[%d]"), iClientProcessId.Id(), status ); |
|
135 delete iTSPClientMapper; |
|
136 } |
|
137 #endif // #ifdef RD_TSP_CLIENT_MAPPER |
|
138 |
|
139 // Cancel any pending async messages. |
|
140 TInt index(0); |
|
141 CDRMCustomCommandAsyncAO* tempAO(NULL); |
|
142 for (;index < iActiveAsyncCustomCommands.Count(); index++) |
|
143 { |
|
144 tempAO = iActiveAsyncCustomCommands[0]; |
|
145 if ( iCDRMCustomCommandAsyncAO2Phase == tempAO ) |
|
146 { |
|
147 iCDRMCustomCommandAsyncAO2Phase = NULL; |
|
148 } |
|
149 delete tempAO; |
|
150 iActiveAsyncCustomCommands.Remove(0); |
|
151 } |
|
152 iActiveAsyncCustomCommands.Reset(); |
|
153 iActiveAsyncCustomCommands.Close(); |
|
154 |
|
155 delete iCDRMCustomCommandAsyncAO2Phase; |
|
156 |
|
157 iServer->iClients--; |
|
158 |
|
159 if(!iServer->iClients) |
|
160 { |
|
161 CActiveScheduler::Stop(); |
|
162 } |
|
163 } |
|
164 |
|
165 void CDRMPlayServerSession::ServiceL(const RMessage2& aMessage) |
|
166 { |
|
167 TInt ret = KErrNone; |
|
168 TRAPD(err,ret = DispatchMessageL(aMessage)); |
|
169 if (KErrNone != err) |
|
170 { |
|
171 ret = err; |
|
172 } |
|
173 if(iAsyncComplete == EFalse) |
|
174 { |
|
175 DEP_PRN1(_L("CDRMPlayServerSession::ServiceL - ret = %d"),ret); |
|
176 aMessage.Complete(ret); |
|
177 } |
|
178 } |
|
179 |
|
180 |
|
181 |
|
182 // service a client request; test the opcode and then do appropriate servicing |
|
183 TInt CDRMPlayServerSession::DispatchMessageL(const RMessage2 &aMessage) |
|
184 { |
|
185 TInt ret=KErrNone; |
|
186 iAsyncComplete= EFalse; |
|
187 DEP_PRN1(_L("CDRMPlayServerSession::DispatchMessageL:FnId[%d]"),aMessage.Function()); |
|
188 switch (aMessage.Function()) |
|
189 { |
|
190 case EDRMPlayServCreate: |
|
191 break; |
|
192 case EDRMPlayServNewFilePlayerL: |
|
193 iMessage = aMessage; |
|
194 NewFilePlayerL(aMessage); |
|
195 iAsyncComplete = ETrue; |
|
196 break; |
|
197 case EDRMPlayServNewDesPlayerL: |
|
198 iMessage = aMessage; |
|
199 NewDesPlayerL(aMessage); |
|
200 iAsyncComplete = ETrue; |
|
201 break; |
|
202 case EDRMPlayServNewDesPlayerReadOnlyL: |
|
203 iMessage = aMessage; |
|
204 NewDesPlayerReadOnlyL(aMessage); |
|
205 iAsyncComplete = ETrue; |
|
206 break; |
|
207 case EDRMPlayServNewPlayerL: |
|
208 NewPlayerL(aMessage); |
|
209 break; |
|
210 case EDRMPlayServOpenFile: |
|
211 iMessage = aMessage; |
|
212 OpenFileL(aMessage); |
|
213 iAsyncComplete = ETrue; |
|
214 break; |
|
215 case EDRMPlayServOpenFileByHandle: |
|
216 iMessage = aMessage; |
|
217 OpenFileByHandleL(aMessage); |
|
218 iAsyncComplete = ETrue; |
|
219 break; |
|
220 case EDRMPlayServOpenFileBySource: |
|
221 iMessage = aMessage; |
|
222 OpenFileBySourceL(aMessage); |
|
223 iAsyncComplete = ETrue; |
|
224 break; |
|
225 case EDRMPlayServOpenDes: |
|
226 iMessage = aMessage; |
|
227 OpenDesL(aMessage); |
|
228 iAsyncComplete = ETrue; |
|
229 break; |
|
230 case EDRMPlayServOpenUrl: |
|
231 iMessage = aMessage; |
|
232 OpenUrlL(aMessage); |
|
233 iAsyncComplete = ETrue; |
|
234 break; |
|
235 case EDRMPlayServPlay: |
|
236 iMessage = aMessage; |
|
237 if(iMessage2) |
|
238 { |
|
239 iMessage2->Complete(KErrCancel); |
|
240 delete iMessage2; |
|
241 iMessage2 = NULL; |
|
242 } |
|
243 iMessage2 = new (ELeave) RMessage2(aMessage); |
|
244 Play(); |
|
245 iAsyncComplete = ETrue; |
|
246 break; |
|
247 case EDRMPlayServStop: |
|
248 if(iState == EStopped) |
|
249 break; |
|
250 if(iMessage2) |
|
251 { |
|
252 iMessage2->Complete(KErrCancel); |
|
253 delete iMessage2; |
|
254 iMessage2 = NULL; |
|
255 } |
|
256 Stop(); |
|
257 break; |
|
258 case EDRMPlayServSetVolume: |
|
259 SetVolume(aMessage); |
|
260 break; |
|
261 case EDRMPlayServSetRepeats: |
|
262 SetRepeats(aMessage); |
|
263 break; |
|
264 case EDRMPlayServSetVolumeRamp: |
|
265 SetVolumeRamp(aMessage); |
|
266 break; |
|
267 case EDRMPlayServDuration: |
|
268 Duration(aMessage); |
|
269 break; |
|
270 case EDRMPlayServMaxVolume: |
|
271 ret = MaxVolume(); |
|
272 break; |
|
273 case EDRMPlayServPause: |
|
274 ret = Pause(); |
|
275 if(iMessage2) |
|
276 { |
|
277 iMessage2->Complete(KErrCancel); |
|
278 delete iMessage2; |
|
279 iMessage2 = NULL; |
|
280 } |
|
281 iAsyncComplete = EFalse; |
|
282 break; |
|
283 case EDRMPlayServClose: |
|
284 Close(); |
|
285 break; |
|
286 case EDRMPlayServGetPosition: |
|
287 ret = GetPosition(aMessage); |
|
288 break; |
|
289 case EDRMPlayServSetPosition: |
|
290 SetPosition(aMessage); |
|
291 break; |
|
292 case EDRMPlayServSetPriority: |
|
293 ret = SetPriority(aMessage); |
|
294 break; |
|
295 case EDRMPlayServGetVolume: |
|
296 ret = GetVolume(aMessage); |
|
297 break; |
|
298 case EDRMPlayServGetNumberOfMetaDataEntries: |
|
299 ret = GetNumberOfMetaDataEntries(aMessage); |
|
300 break; |
|
301 case EDRMPlayServGetMetaDataEntry: |
|
302 GetMetaDataEntryL(aMessage); |
|
303 break; |
|
304 case EDRMPlayServClearPlayWindow: |
|
305 ret = ClearPlayWindow(); |
|
306 break; |
|
307 case EDRMPlayServSetBalance: |
|
308 ret = SetBalance(aMessage); |
|
309 break; |
|
310 case EDRMPlayServGetBalance: |
|
311 ret = GetBalance(aMessage); |
|
312 break; |
|
313 case EDRMPlayServGetBitRate: |
|
314 ret = GetBitRate(aMessage); |
|
315 break; |
|
316 case EDRMPlayServRegisterForAudioLoadingNotification: |
|
317 iMessage = aMessage; |
|
318 RegisterForAudioLoadingNotification(aMessage); |
|
319 iAsyncComplete = ETrue; |
|
320 break; |
|
321 case EDRMPlayServGetAudioLoadingProgress: |
|
322 GetAudioLoadingProgressL(aMessage); |
|
323 break; |
|
324 case EDRMPlayServSetPlayWindow: |
|
325 SetPlayWindow(aMessage); |
|
326 break; |
|
327 case EDRMPlayServControllerImplementationInformation: |
|
328 ControllerImplementationInformationL(aMessage); |
|
329 break; |
|
330 case EDRMPlayServCustomCommandSyncWithReturn: |
|
331 case EDRMPlayServCustomCommandSyncWithoutReturn: |
|
332 CustomCommandSyncL(aMessage); |
|
333 break; |
|
334 case EDRMPlayServCustomCommandAsyncWithReturnStep1: |
|
335 case EDRMPlayServCustomCommandAsyncWithoutReturnStep1: |
|
336 CustomCommandAsyncStep1L(aMessage); |
|
337 // Setting this flag does not complete message in ServiceL, |
|
338 iAsyncComplete = ETrue; |
|
339 break; |
|
340 case EDRMPlayServCustomCommandAsyncWithReturnStep2: |
|
341 case EDRMPlayServCustomCommandAsyncWithoutReturnStep2: |
|
342 CustomCommandAsyncStep2L(aMessage); |
|
343 // Setting this flag does not complete message in ServiceL, |
|
344 iAsyncComplete = ETrue; |
|
345 break; |
|
346 case EDRMPlayServSetPriorityPreference: |
|
347 SetPriorityPreference(aMessage); |
|
348 break; |
|
349 |
|
350 default: |
|
351 PanicClient(aMessage, EBadRequest); |
|
352 break; |
|
353 } |
|
354 return ret; |
|
355 } |
|
356 |
|
357 void CDRMPlayServerSession::NewFilePlayerL(const RMessage2 aMessage) |
|
358 { |
|
359 TDataStructPckgBuf params; |
|
360 |
|
361 aMessage.Read(0,iCallbackState); |
|
362 aMessage.Read(1,params); |
|
363 aMessage.Read(2,iErrDurPckg); |
|
364 HBufC16* inputBuf = HBufC16::NewL(aMessage.GetDesLengthL(3)); |
|
365 TPtr16 ptr = inputBuf->Des(); |
|
366 aMessage.Read(3,ptr); |
|
367 const TDataStruct &theStruct = params(); |
|
368 delete iPlayUtil; |
|
369 iPlayUtil = NULL; |
|
370 iPlayUtil = CMdaAudioPlayerUtility::NewFilePlayerL( inputBuf->Des(), |
|
371 *this, |
|
372 iPriority, |
|
373 iPref); |
|
374 delete inputBuf; |
|
375 } |
|
376 |
|
377 void CDRMPlayServerSession::NewDesPlayerL(const RMessage2 aMessage) |
|
378 { |
|
379 TDataStructPckgBuf thePckg; |
|
380 aMessage.Read(0,iCallbackState); |
|
381 aMessage.Read(1,thePckg); |
|
382 aMessage.Read(2,iErrDurPckg); |
|
383 |
|
384 delete iPlayUtil; |
|
385 iPlayUtil = NULL; |
|
386 iPlayUtil = CMdaAudioPlayerUtility::NewDesPlayerL(thePckg().iData, |
|
387 *this, |
|
388 iPriority, |
|
389 iPref); |
|
390 |
|
391 } |
|
392 |
|
393 void CDRMPlayServerSession::NewDesPlayerReadOnlyL(const RMessage2 aMessage) |
|
394 { |
|
395 TDataStructPckgBuf thePckg; |
|
396 aMessage.Read(0,iCallbackState); |
|
397 aMessage.Read(1,thePckg); |
|
398 aMessage.Read(2,iErrDurPckg); |
|
399 |
|
400 delete iPlayUtil; |
|
401 iPlayUtil = NULL; |
|
402 iPlayUtil = CMdaAudioPlayerUtility::NewDesPlayerReadOnlyL(thePckg().iData, |
|
403 *this, |
|
404 iPriority, |
|
405 iPref); |
|
406 } |
|
407 |
|
408 void CDRMPlayServerSession::NewPlayerL(const RMessage2 aMessage) |
|
409 { |
|
410 |
|
411 TDataStructPckgBuf thePckg; |
|
412 aMessage.Read(0,iCallbackState); |
|
413 aMessage.Read(1,thePckg); |
|
414 aMessage.Read(2,iErrDurPckg); |
|
415 |
|
416 delete iPlayUtil; |
|
417 iPlayUtil = NULL; |
|
418 iPlayUtil = CMdaAudioPlayerUtility::NewL(*this, |
|
419 iPriority, |
|
420 iPref); |
|
421 } |
|
422 |
|
423 void CDRMPlayServerSession::OpenFileL(const RMessage2 &aMessage) |
|
424 { |
|
425 //call mmfclientaudioplayer open file |
|
426 iFileHandleUsed = EFalse; |
|
427 |
|
428 aMessage.Read(0,iCallbackState); |
|
429 // Get the file length |
|
430 TInt len = aMessage.GetDesLengthL(1); |
|
431 HBufC* fileName = HBufC::NewLC(len); |
|
432 TPtr fileNamePtr = fileName->Des(); |
|
433 aMessage.Read(1,fileNamePtr); |
|
434 aMessage.Read(2,iErrDurPckg); |
|
435 if(iPlayUtil) |
|
436 { |
|
437 iPlayUtil->OpenFileL(fileNamePtr); |
|
438 } |
|
439 CleanupStack::PopAndDestroy(fileName); |
|
440 } |
|
441 |
|
442 void CDRMPlayServerSession::OpenFileByHandleL(const RMessage2 &aMessage) |
|
443 { |
|
444 iFileHandleUsed = ETrue; |
|
445 TInt err = iFileHandle.AdoptFromClient(aMessage,1,3); |
|
446 |
|
447 aMessage.Read(0,iCallbackState); |
|
448 aMessage.Read(2,iErrDurPckg); |
|
449 |
|
450 if (err == KErrNone) |
|
451 { |
|
452 if(iPlayUtil) |
|
453 { |
|
454 iPlayUtil->OpenFileL(iFileHandle); |
|
455 } |
|
456 } |
|
457 else |
|
458 { |
|
459 User::Leave(err); |
|
460 } |
|
461 } |
|
462 |
|
463 void CDRMPlayServerSession::OpenFileBySourceL(const RMessage2 &/*aMessage*/) |
|
464 { |
|
465 |
|
466 } |
|
467 |
|
468 void CDRMPlayServerSession::OpenDesL(const RMessage2 &aMessage) |
|
469 { |
|
470 //call mmfclientaudioplayer OpenDesL |
|
471 TBuf8<50> aDes; |
|
472 |
|
473 aMessage.Read(0,iCallbackState); |
|
474 aMessage.Read(1,aDes); |
|
475 aMessage.Read(2,iErrDurPckg); |
|
476 |
|
477 if(iPlayUtil) |
|
478 { |
|
479 iPlayUtil->OpenDesL(aDes); |
|
480 } |
|
481 } |
|
482 |
|
483 void CDRMPlayServerSession::OpenUrlL(const RMessage2 &aMessage) |
|
484 { |
|
485 //call mmfclientaudioplayer OpenUrlL |
|
486 TUrlStructPckgBuf thePckg; |
|
487 |
|
488 aMessage.Read(0,iCallbackState); |
|
489 aMessage.Read(1,thePckg); |
|
490 aMessage.Read(2,iErrDurPckg); |
|
491 |
|
492 if(iPlayUtil) |
|
493 { |
|
494 iPlayUtil->OpenUrlL(thePckg().iUrl, |
|
495 thePckg().iIapId, |
|
496 thePckg().iMimeType); |
|
497 } |
|
498 } |
|
499 |
|
500 void CDRMPlayServerSession::Play() |
|
501 { |
|
502 //call mmfclientaudioplayer Play |
|
503 iState = EPlaying; |
|
504 if(iPlayUtil) |
|
505 { |
|
506 iPlayUtil->Play(); |
|
507 #ifdef RD_TSP_CLIENT_MAPPER |
|
508 TInt status = iTSPClientMapper->SetTspTargetClientToOtherType( |
|
509 CTspClientMapper::EPlayingClients, |
|
510 iClientProcessId ); |
|
511 DEP_PRN2(_L("CDRMPlayServerSession::Play:Change PID[%x]Status[%d]"), iClientProcessId.Id(), status ); |
|
512 #endif // #ifdef RD_TSP_CLIENT_MAPPER |
|
513 } |
|
514 } |
|
515 |
|
516 void CDRMPlayServerSession::Stop() |
|
517 { |
|
518 //call mmfclientaudioplayer Stop |
|
519 if(iPlayUtil) |
|
520 { |
|
521 iPlayUtil->Stop(); |
|
522 #ifdef RD_TSP_CLIENT_MAPPER |
|
523 TInt status = iTSPClientMapper->SetTspTargetClientToOtherType( |
|
524 CTspClientMapper::EStoppedClients, |
|
525 iClientProcessId ); |
|
526 DEP_PRN2(_L("CDRMPlayServerSession::Stop:Change PID[%x]Status[%d]"), iClientProcessId.Id(), status ); |
|
527 #endif // #ifdef RD_TSP_CLIENT_MAPPER |
|
528 } |
|
529 iState = EStopped; |
|
530 } |
|
531 |
|
532 void CDRMPlayServerSession::SetVolume(const RMessage2 & aMessage) |
|
533 { |
|
534 //call mmfclientaudioplayer SetVolume |
|
535 TInt theVolume = aMessage.Int0(); |
|
536 |
|
537 if(iPlayUtil) |
|
538 { |
|
539 iPlayUtil->SetVolume(theVolume); |
|
540 } |
|
541 } |
|
542 |
|
543 void CDRMPlayServerSession::SetRepeats(const RMessage2 &aMessage) |
|
544 { |
|
545 //call mmfclientaudioplayer SetRepeats |
|
546 TInt theRepeatNumberOfTimes = aMessage.Int0(); |
|
547 TTimeIntervalMicroSeconds theTrailingSilence; |
|
548 |
|
549 TPckgTTimeIntervalMicroSeconds theTrailingSilencePckg(theTrailingSilence); |
|
550 aMessage.Read(1,theTrailingSilencePckg); |
|
551 |
|
552 iPlayUtil->SetRepeats(theRepeatNumberOfTimes,theTrailingSilence); |
|
553 } |
|
554 |
|
555 void CDRMPlayServerSession::SetVolumeRamp(const RMessage2 &aMessage) |
|
556 { |
|
557 //call mmfclientaudioplayer SetVolumeRamp |
|
558 TTimeIntervalMicroSeconds theRampDuration; |
|
559 TPckgTTimeIntervalMicroSeconds theRampDurationPckg(theRampDuration); |
|
560 aMessage.Read(0,theRampDurationPckg); |
|
561 |
|
562 iPlayUtil->SetVolumeRamp(theRampDuration); |
|
563 } |
|
564 |
|
565 void CDRMPlayServerSession::Duration(const RMessage2& aMessage) |
|
566 { |
|
567 TTimeIntervalMicroSeconds theDuration; |
|
568 theDuration = iPlayUtil->Duration(); |
|
569 |
|
570 TPckgBufTTimeIntervalMicroSeconds theDurationPckg(theDuration); |
|
571 aMessage.Write(0,theDurationPckg); |
|
572 } |
|
573 |
|
574 TInt CDRMPlayServerSession::MaxVolume() |
|
575 { |
|
576 TInt theMaxVolume; |
|
577 theMaxVolume = iPlayUtil->MaxVolume(); |
|
578 |
|
579 return theMaxVolume; |
|
580 } |
|
581 |
|
582 // API Additions since version 7.0 |
|
583 TInt CDRMPlayServerSession::Pause() |
|
584 { |
|
585 TInt ret = KErrNone; |
|
586 if(iPlayUtil) |
|
587 { |
|
588 ret = iPlayUtil->Pause(); |
|
589 } |
|
590 |
|
591 // This message should not be completed here. It should be completed in ServiceL() |
|
592 /* if(iState == EPlaying) |
|
593 { |
|
594 iMessage.Complete(KErrNone); |
|
595 }*/ |
|
596 iState = EStopped; |
|
597 return ret; |
|
598 } |
|
599 |
|
600 void CDRMPlayServerSession::Close() |
|
601 { |
|
602 |
|
603 if(iFileHandleUsed) |
|
604 iFileHandle.Close(); |
|
605 |
|
606 iPlayUtil->Close(); |
|
607 } |
|
608 |
|
609 TInt CDRMPlayServerSession::GetPosition(const RMessage2 &aMessage) |
|
610 { |
|
611 TInt ret; |
|
612 TTimeIntervalMicroSeconds thePosition; |
|
613 |
|
614 ret = iPlayUtil->GetPosition(thePosition); |
|
615 |
|
616 |
|
617 TPckgTTimeIntervalMicroSeconds thePositionPckg(thePosition); |
|
618 aMessage.Write(0,thePositionPckg); |
|
619 |
|
620 return ret; |
|
621 } |
|
622 |
|
623 void CDRMPlayServerSession::SetPosition(const RMessage2 &aMessage) |
|
624 { |
|
625 TTimeIntervalMicroSeconds thePosition; |
|
626 TPckgTTimeIntervalMicroSeconds thePositionPckg(thePosition); |
|
627 aMessage.Read(0,thePositionPckg); |
|
628 iPlayUtil->SetPosition(thePosition); |
|
629 } |
|
630 |
|
631 TInt CDRMPlayServerSession::SetPriority(const RMessage2 &aMessage) |
|
632 { |
|
633 TInt thePriority = aMessage.Int0(); |
|
634 TMdaPriorityPreference thePref = (TMdaPriorityPreference)aMessage.Int1(); |
|
635 |
|
636 TInt ret = iPlayUtil->SetPriority(thePriority,thePref); |
|
637 |
|
638 return ret; |
|
639 } |
|
640 |
|
641 TInt CDRMPlayServerSession::GetVolume(const RMessage2 &aMessage) |
|
642 { |
|
643 TInt theVolume; |
|
644 TInt ret = iPlayUtil->GetVolume(theVolume); |
|
645 TPckgBufTInt pckg(theVolume); |
|
646 aMessage.Write(0,pckg); |
|
647 return ret; |
|
648 } |
|
649 |
|
650 TInt CDRMPlayServerSession::GetNumberOfMetaDataEntries(const RMessage2 &aMessage) |
|
651 { |
|
652 TInt ret; |
|
653 TInt theNumOfMetaDataEntries; |
|
654 |
|
655 ret = iPlayUtil->GetNumberOfMetaDataEntries(theNumOfMetaDataEntries); |
|
656 |
|
657 TPckgBufTInt pckg(theNumOfMetaDataEntries); |
|
658 aMessage.Write(0,pckg); |
|
659 |
|
660 return ret; |
|
661 } |
|
662 |
|
663 |
|
664 void CDRMPlayServerSession::GetMetaDataEntryL(const RMessage2 &aMessage) |
|
665 { |
|
666 //Get the index of the required entry |
|
667 TInt theIndex = aMessage.Int0(); |
|
668 CMMFMetaDataEntry* entry = iPlayUtil->GetMetaDataEntryL(theIndex); |
|
669 CleanupStack::PushL(entry); |
|
670 // Delete any existing buffer |
|
671 if (iMetaDataBuffer) |
|
672 { |
|
673 delete iMetaDataBuffer; |
|
674 iMetaDataBuffer = NULL; |
|
675 } |
|
676 |
|
677 // Create a buffer to hold the externalised entry |
|
678 iMetaDataBuffer = CBufFlat::NewL(32); |
|
679 RBufWriteStream s; |
|
680 s.Open(*iMetaDataBuffer); |
|
681 CleanupClosePushL(s); |
|
682 entry->ExternalizeL(s); |
|
683 CleanupStack::PopAndDestroy(2);//s, entry |
|
684 |
|
685 //Write the externalised data back to the client |
|
686 aMessage.Write(1,iMetaDataBuffer->Ptr(0)); |
|
687 } |
|
688 |
|
689 |
|
690 TInt CDRMPlayServerSession::SetPlayWindow(const RMessage2 &aMessage) |
|
691 { |
|
692 TPlayWindowStructBuf buf; |
|
693 aMessage.Read(0,buf); |
|
694 |
|
695 TTimeIntervalMicroSeconds playStart = buf().iPlayStart; |
|
696 TTimeIntervalMicroSeconds playEnd = buf().iPlayEnd; |
|
697 |
|
698 TInt ret = iPlayUtil->SetPlayWindow(playStart,playEnd); |
|
699 |
|
700 return ret; |
|
701 } |
|
702 |
|
703 TInt CDRMPlayServerSession::ClearPlayWindow() |
|
704 { |
|
705 return iPlayUtil->ClearPlayWindow(); |
|
706 } |
|
707 |
|
708 TInt CDRMPlayServerSession::SetBalance(const RMessage2 &aMessage) |
|
709 { |
|
710 TInt theBalance = aMessage.Int0(); |
|
711 return iPlayUtil->SetBalance(theBalance); |
|
712 } |
|
713 |
|
714 TInt CDRMPlayServerSession::GetBalance(const RMessage2 &aMessage) |
|
715 { |
|
716 TInt theBalance; |
|
717 TInt ret; |
|
718 |
|
719 ret = iPlayUtil->GetBalance(theBalance); |
|
720 |
|
721 TPckgBufTInt pckg(theBalance); |
|
722 aMessage.Write(0,pckg); |
|
723 |
|
724 return ret; |
|
725 } |
|
726 |
|
727 TInt CDRMPlayServerSession::GetBitRate(const RMessage2 &aMessage) |
|
728 { |
|
729 TUint theBitRate; |
|
730 TInt ret; |
|
731 |
|
732 ret = iPlayUtil->GetBitRate(theBitRate); |
|
733 |
|
734 TPckgBufTUint pckg(theBitRate); |
|
735 aMessage.Write(0,pckg); |
|
736 |
|
737 return ret; |
|
738 } |
|
739 |
|
740 void CDRMPlayServerSession::RegisterForAudioLoadingNotification(const RMessage2 &aMessage) |
|
741 { |
|
742 aMessage.Read(0,iCallbackState); |
|
743 |
|
744 if(iCallbackState()==ELoadingStarted) |
|
745 { |
|
746 iPlayUtil->RegisterForAudioLoadingNotification(*this); |
|
747 } |
|
748 } |
|
749 |
|
750 void CDRMPlayServerSession::GetAudioLoadingProgressL(const RMessage2 &aMessage) |
|
751 { |
|
752 TInt thePercentProgress; |
|
753 |
|
754 iPlayUtil->GetAudioLoadingProgressL(thePercentProgress); |
|
755 |
|
756 TPckgBufTInt pckg(thePercentProgress); |
|
757 |
|
758 aMessage.Write(0,pckg); |
|
759 } |
|
760 |
|
761 void CDRMPlayServerSession::ControllerImplementationInformationL(const RMessage2 &aMessage) |
|
762 { |
|
763 const CMMFControllerImplementationInformation& x= iPlayUtil->ControllerImplementationInformationL(); |
|
764 |
|
765 //for now just write Uid by to client |
|
766 typedef TPckg<TUid> TPckgTUid; |
|
767 |
|
768 TPckgTUid thePckg(x.Uid()); |
|
769 aMessage.Write(0,thePckg); |
|
770 } |
|
771 |
|
772 |
|
773 void CDRMPlayServerSession::CustomCommandSyncL(const RMessage2 &aMessage) |
|
774 { |
|
775 DEP_PRN0(_L("CDRMPlayServerSession::CustomCommandSyncL:Enter")); |
|
776 |
|
777 TPckgCustomCommand thePckgCustomCommand; |
|
778 User::LeaveIfError(aMessage.Read(0,thePckgCustomCommand)); |
|
779 |
|
780 HBufC8* dataTo1Buf(NULL); |
|
781 HBufC8* dataTo2Buf(NULL); |
|
782 HBufC8* dataFromBuf(NULL); |
|
783 TPtr8 ptr1(NULL, NULL); |
|
784 TPtr8 ptr2(NULL, NULL); |
|
785 TPtr8 ptr3(NULL, NULL); |
|
786 |
|
787 // Read data1 |
|
788 dataTo1Buf = HBufC8::NewL(aMessage.GetDesLengthL(1)); |
|
789 CleanupStack::PushL(dataTo1Buf); |
|
790 ptr1.Set(dataTo1Buf->Des()); |
|
791 User::LeaveIfError(aMessage.Read(1,ptr1)); |
|
792 |
|
793 // Check and filter the messages |
|
794 // Dont Allow Select Custom Commands |
|
795 if( !IsValidCustomCommandDestination(thePckgCustomCommand().iDestination().InterfaceId(), ptr1) ) |
|
796 User::Leave(KErrAccessDenied); |
|
797 |
|
798 // Read data2 |
|
799 dataTo2Buf = HBufC8::NewL(aMessage.GetDesLengthL(2)); |
|
800 CleanupStack::PushL(dataTo2Buf); |
|
801 ptr2.Set(dataTo2Buf->Des()); |
|
802 User::LeaveIfError(aMessage.Read(2,ptr2)); |
|
803 |
|
804 if ( aMessage.Function() == EDRMPlayServCustomCommandSyncWithReturn ) |
|
805 { |
|
806 // Read data3 |
|
807 dataFromBuf = HBufC8::NewL(aMessage.GetDesLengthL(3)); |
|
808 CleanupStack::PushL(dataFromBuf); |
|
809 ptr3.Set(dataFromBuf->Des()); |
|
810 User::LeaveIfError(aMessage.Read(3,ptr3)); |
|
811 /* |
|
812 RDebug::Print(_L("CDRMPlayServerSession::CustomCommandSync:IFId[%x]DestHndl[%x]Fn[%d]RetDesLen[%d]"), |
|
813 thePckgCustomCommand().iDestination().InterfaceId(), |
|
814 thePckgCustomCommand().iDestination().DestinationHandle(), |
|
815 thePckgCustomCommand().iFunction, |
|
816 ptr3.MaxLength()); |
|
817 */ |
|
818 // Call CustomCommandSync on play util |
|
819 User::LeaveIfError(iPlayUtil->CustomCommandSync( |
|
820 thePckgCustomCommand().iDestination, |
|
821 thePckgCustomCommand().iFunction, |
|
822 ptr1, |
|
823 ptr2, |
|
824 ptr3)); |
|
825 User::LeaveIfError(aMessage.Write(3,ptr3)); |
|
826 |
|
827 // Free heap allocated for data3 |
|
828 CleanupStack::PopAndDestroy(dataFromBuf); |
|
829 } |
|
830 else |
|
831 { |
|
832 /* |
|
833 RDebug::Print(_L("CDRMPlayServerSession::CustomCommandSync:IFId[%x]DestHndl[%x]Fn[%d]"), |
|
834 thePckgCustomCommand().iDestination().InterfaceId(), |
|
835 thePckgCustomCommand().iDestination().DestinationHandle(), |
|
836 thePckgCustomCommand().iFunction ); |
|
837 */ |
|
838 // Call CustomCommandSync on play util |
|
839 User::LeaveIfError(iPlayUtil->CustomCommandSync( |
|
840 thePckgCustomCommand().iDestination, |
|
841 thePckgCustomCommand().iFunction, |
|
842 ptr1, |
|
843 ptr2)); |
|
844 } |
|
845 |
|
846 // Free heap allocated for data2 |
|
847 CleanupStack::PopAndDestroy(dataTo2Buf); |
|
848 // Free heap allocated for data1 |
|
849 CleanupStack::PopAndDestroy(dataTo1Buf); |
|
850 |
|
851 DEP_PRN0(_L("CDRMPlayServerSession::CustomCommandSyncL:Exit")); |
|
852 } |
|
853 |
|
854 void CDRMPlayServerSession::CustomCommandAsyncStep1L( const RMessage2& aMessage ) |
|
855 { |
|
856 DEP_PRN0(_L("CDRMPlayServerSession::CustomCommandAsyncStep1L:Enter")); |
|
857 |
|
858 // If there is a pending phase1, then there is something wrong. |
|
859 if ( iCDRMCustomCommandAsyncAO2Phase ) |
|
860 { |
|
861 User::Leave(KErrAlreadyExists); |
|
862 } |
|
863 |
|
864 if ( aMessage.Function() == EDRMPlayServCustomCommandAsyncWithReturnStep1 ) |
|
865 { |
|
866 iCDRMCustomCommandAsyncAO2Phase = CDRMCustomCommandAsyncAO::NewL( |
|
867 aMessage, |
|
868 CDRMCustomCommandAsyncAO::ECustomCommandWithResult, |
|
869 *this ); |
|
870 } |
|
871 else /*if ( aMessage.Function() == EDRMPlayServCustomCommandAsyncWithoutReturnStep1 )*/ |
|
872 { |
|
873 iCDRMCustomCommandAsyncAO2Phase = CDRMCustomCommandAsyncAO::NewL( |
|
874 aMessage, |
|
875 CDRMCustomCommandAsyncAO::ECustomCommandWithoutResult, |
|
876 *this ); |
|
877 } |
|
878 // Get reference to data1 descriptor |
|
879 TPtr8 data1Ptr(iCDRMCustomCommandAsyncAO2Phase->GetData1FromClient()->Des()); |
|
880 |
|
881 // Check and filter the messages |
|
882 // Dont Allow Select Custom Commands |
|
883 if( !IsValidCustomCommandDestination((iCDRMCustomCommandAsyncAO2Phase->GetMMFMessageDestinationPckg())().InterfaceId(), data1Ptr) ) |
|
884 { |
|
885 User::Leave( KErrAccessDenied ); |
|
886 } |
|
887 |
|
888 aMessage.Complete( KErrNone ); |
|
889 DEP_PRN0(_L("CDRMPlayServerSession::CustomCommandAsyncStep1L:Exit")); |
|
890 } |
|
891 |
|
892 void CDRMPlayServerSession::CustomCommandAsyncStep2L( const RMessage2& aMessage ) |
|
893 { |
|
894 DEP_PRN0(_L("CDRMPlayServerSession::CustomCommandAsyncStep2L:Enter")); |
|
895 |
|
896 // Read info from client session |
|
897 TPtr8 data1Ptr(NULL,NULL); |
|
898 TPtr8 data2Ptr(NULL,NULL); |
|
899 TPtr8 dataToClientPtr(NULL,NULL); |
|
900 |
|
901 // If there is no pending phase1, then there is something wrong. |
|
902 if ( !iCDRMCustomCommandAsyncAO2Phase ) |
|
903 { |
|
904 User::Leave(KErrNotReady); |
|
905 } |
|
906 |
|
907 // Add custCmdAsyncAO object to array of async custom command objects |
|
908 iActiveAsyncCustomCommands.AppendL( iCDRMCustomCommandAsyncAO2Phase ); |
|
909 |
|
910 // aMessage ownership is transferred to custCmdAsyncAO. |
|
911 // After succesful transfer, aMessage should not be completed |
|
912 // anywhere outside of this object. |
|
913 iCDRMCustomCommandAsyncAO2Phase->TransferOwnershipL(aMessage); |
|
914 |
|
915 CDRMCustomCommandAsyncAO* drmCCAsyncAO = iCDRMCustomCommandAsyncAO2Phase; |
|
916 iCDRMCustomCommandAsyncAO2Phase = NULL; |
|
917 |
|
918 // Get reference to descriptors |
|
919 data1Ptr.Set(drmCCAsyncAO->GetData1FromClient()->Des()); |
|
920 data2Ptr.Set(drmCCAsyncAO->GetData2FromClient()->Des()); |
|
921 |
|
922 // Make call on controller |
|
923 drmCCAsyncAO->SetActive(); |
|
924 if ( aMessage.Function() == EDRMPlayServCustomCommandAsyncWithReturnStep2 ) |
|
925 { |
|
926 // If date needs to be returned, get reference to the descriptor |
|
927 dataToClientPtr.Set(drmCCAsyncAO->GetDataToClient()->Des()); |
|
928 /* |
|
929 RDebug::Print(_L("CDRMPlayServerSession::CustomCommandAsyncStep2L:IFId[%x]DestHndl[%x]Fn[%d]Data1[Len:%d:MaxLen:%d]Data2Len[Len:%d:MaxLen:%d]RetDesLen[Len:%d:MaxLen:%d]"), |
|
930 (drmCCAsyncAO->GetMMFMessageDestinationPckg())().InterfaceId(), |
|
931 (drmCCAsyncAO->GetMMFMessageDestinationPckg())().DestinationHandle(), |
|
932 drmCCAsyncAO->GetMMFMessageFunction(), |
|
933 data1Ptr.Length(), |
|
934 data1Ptr.MaxLength(), |
|
935 data2Ptr.Length(), |
|
936 data2Ptr.MaxLength(), |
|
937 dataToClientPtr.Length(), |
|
938 dataToClientPtr.MaxLength() ); |
|
939 */ |
|
940 // Call CustomCommandAsync on play util |
|
941 iPlayUtil->CustomCommandAsync( |
|
942 drmCCAsyncAO->GetMMFMessageDestinationPckg(), |
|
943 drmCCAsyncAO->GetMMFMessageFunction(), |
|
944 data1Ptr, |
|
945 data2Ptr, |
|
946 dataToClientPtr, |
|
947 drmCCAsyncAO->iStatus ); |
|
948 } |
|
949 else /*if ( aMessage.Function() == EDRMPlayServCustomCommandAsyncWithoutReturnStep2 )*/ |
|
950 { |
|
951 /* |
|
952 RDebug::Print(_L("CDRMPlayServerSession::CustomCommandAsyncStep2L:IFId[%x]DestHndl[%x]Fn[%d]Data1[Len:%d:MaxLen:%d]Data2Len[Len:%d:MaxLen:%d]"), |
|
953 (drmCCAsyncAO->GetMMFMessageDestinationPckg())().InterfaceId(), |
|
954 (drmCCAsyncAO->GetMMFMessageDestinationPckg())().DestinationHandle(), |
|
955 drmCCAsyncAO->GetMMFMessageFunction(), |
|
956 data1Ptr.Length(), |
|
957 data1Ptr.MaxLength(), |
|
958 data2Ptr.Length(), |
|
959 data2Ptr.MaxLength() ); |
|
960 */ |
|
961 // Call CustomCommandAsync on play util |
|
962 iPlayUtil->CustomCommandAsync( |
|
963 drmCCAsyncAO->GetMMFMessageDestinationPckg(), |
|
964 drmCCAsyncAO->GetMMFMessageFunction(), |
|
965 data1Ptr, |
|
966 data2Ptr, |
|
967 drmCCAsyncAO->iStatus ); |
|
968 } |
|
969 |
|
970 DEP_PRN0(_L("CDRMPlayServerSession::CustomCommandAsyncStep2L:Exit")); |
|
971 } |
|
972 |
|
973 void CDRMPlayServerSession::MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& aDuration) |
|
974 { |
|
975 |
|
976 DEP_PRN2(_L("CDRMPlayServerSession::MapcInitComplete :-> Status[%d] Duration[%d]"), aError, I64INT(aDuration.Int64())); |
|
977 //iMessage.Write aError & aDuration to client |
|
978 iCallbackState() = EInitComplete; |
|
979 |
|
980 iMessage.Write(0,iCallbackState); |
|
981 |
|
982 iErrDurPckg().iError = aError; |
|
983 iErrDurPckg().iDuration = aDuration; |
|
984 |
|
985 iMessage.Write(2,iErrDurPckg); |
|
986 iMessage.Complete(aError); |
|
987 iAsyncComplete= EFalse; |
|
988 } |
|
989 |
|
990 void CDRMPlayServerSession::MapcPlayComplete(TInt aError) |
|
991 { |
|
992 DEP_PRN1(_L("CDRMPlayServerSession::MapcPlayComplete :-> Error[%d]"), aError); |
|
993 |
|
994 #ifdef RD_TSP_CLIENT_MAPPER |
|
995 TInt status = iTSPClientMapper->SetTspTargetClientToOtherType( |
|
996 CTspClientMapper::EStoppedClients, |
|
997 iClientProcessId ); |
|
998 DEP_PRN2(_L("CDRMPlayServerSession::MapcPlayComplete:Change PID[%x]Status[%d]"), iClientProcessId.Id(), status ); |
|
999 #endif // #ifdef RD_TSP_CLIENT_MAPPER |
|
1000 |
|
1001 |
|
1002 iState = EStopped; |
|
1003 |
|
1004 iCallbackState()= EPlayComplete; |
|
1005 /* |
|
1006 iMessage.Write(0,iCallbackState); |
|
1007 iErrDurPckg().iError = aError; |
|
1008 iErrDurPckg().iDuration = 0; |
|
1009 iMessage.Write(2,iErrDurPckg); |
|
1010 iMessage.Complete(aError); |
|
1011 */ |
|
1012 if ( iMessage2 ) |
|
1013 { |
|
1014 //iMessage2. Write aError & aDuration to client |
|
1015 iCallbackState() = EPlayComplete; |
|
1016 |
|
1017 iMessage2->Write(0,iCallbackState); |
|
1018 |
|
1019 iErrDurPckg().iError = aError; |
|
1020 iErrDurPckg().iDuration = 0; |
|
1021 iMessage2->Write(2,iErrDurPckg); |
|
1022 iMessage2->Complete(aError); |
|
1023 delete iMessage2; |
|
1024 iMessage2 = NULL; |
|
1025 } |
|
1026 |
|
1027 iAsyncComplete= EFalse; |
|
1028 } |
|
1029 |
|
1030 |
|
1031 void CDRMPlayServerSession::MaloLoadingStarted() |
|
1032 { |
|
1033 //send event to client |
|
1034 iCallbackState() = ELoadingStarted; |
|
1035 iMessage.Write(0,iCallbackState); |
|
1036 iMessage.Complete(KErrNone); |
|
1037 iAsyncComplete= EFalse; |
|
1038 } |
|
1039 void CDRMPlayServerSession::MaloLoadingComplete() |
|
1040 { |
|
1041 //send event to client |
|
1042 iCallbackState() = ELoadingComplete; |
|
1043 iMessage.Write(0,iCallbackState); |
|
1044 iMessage.Complete(KErrNone); |
|
1045 iAsyncComplete= EFalse; |
|
1046 } |
|
1047 |
|
1048 void CDRMPlayServerSession::AsyncCustomCommandCompleted( CDRMCustomCommandAsyncAO* aObject) |
|
1049 { |
|
1050 TInt index = iActiveAsyncCustomCommands.Find( aObject ); |
|
1051 // This should never happen. Else there is something wrong.... |
|
1052 if ( index != KErrNotFound ) |
|
1053 { |
|
1054 iActiveAsyncCustomCommands.Remove(index); |
|
1055 } |
|
1056 delete aObject; |
|
1057 } |
|
1058 |
|
1059 void CDRMPlayServerSession::SetPriorityPreference(const RMessage2 &aMessage) |
|
1060 { |
|
1061 iPriority = aMessage.Int0(); |
|
1062 iPref = (TMdaPriorityPreference)aMessage.Int1(); |
|
1063 } |
|
1064 |
|
1065 TBool CDRMPlayServerSession::IsValidCustomCommandDestination(TUid aDestinationUid, TDesC8& aParam) |
|
1066 { |
|
1067 TBool retValue(ETrue); |
|
1068 if (aDestinationUid == KUidInterfaceMMFDRMControl) |
|
1069 { |
|
1070 retValue = EFalse; |
|
1071 } |
|
1072 else if ( aDestinationUid == KUidCustomInterfaceBuilderImpl ) |
|
1073 { |
|
1074 RDesReadStream stream(aParam); |
|
1075 CleanupClosePushL(stream); |
|
1076 TUid paramUid; |
|
1077 TRAPD(status, paramUid.iUid = stream.ReadInt32L()); |
|
1078 CleanupStack::PopAndDestroy(&stream); |
|
1079 if ( (status == KErrNone ) && (paramUid == KUidAudioOutput) ) |
|
1080 { |
|
1081 retValue = EFalse; |
|
1082 } |
|
1083 } |
|
1084 return retValue; |
|
1085 } |
|
1086 |
|
1087 // End of File |
|
1088 |