|
1 /* |
|
2 * Copyright (c) 2005-2009 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: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include <e32base.h> |
|
20 #include <ecom/ecomresolverparams.h> |
|
21 #include <ecom/ecom.h> |
|
22 |
|
23 #include "DataDealer.h" |
|
24 #include "DataDealerParams.h" |
|
25 |
|
26 // We cannot use UIDs that are reserved from Symbian (neither protected |
|
27 // nor unprotected) since Publish&Subscribe properties require that |
|
28 // the running process they are defined in has the secure id that matches |
|
29 // the id of the P&S property (secure id check is not made for legacy UIDs |
|
30 // which are < 0x10273357). |
|
31 // So we use a UID from development range: 0x00000000 - 0x0FFFFFFF |
|
32 const TUid KPropertyCategory = { 0x077A5355 }; |
|
33 //const TUid KPropertyCategory = { 0xA00023E0 }; |
|
34 |
|
35 // Time to wait before re-checking property values |
|
36 const TInt KWaitForPropertyTime( 100000 ); |
|
37 |
|
38 |
|
39 const TUid KDataDealerImplUID1 = {0xA00023E2}; |
|
40 |
|
41 /** |
|
42 * @param aProducer Set to ETrue if this is a producer instance. |
|
43 * @param aKey The Publish&Subscribe key that is used for data passing. |
|
44 */ |
|
45 CDataDealer* CDataDealer::NewL( const TBool aProducer, const TUint aKey ) |
|
46 { |
|
47 TDataDealerParams params( aProducer, aKey ); |
|
48 |
|
49 TAny* implementation = |
|
50 REComSession::CreateImplementationL( KDataDealerImplUID1, |
|
51 _FOFF( CDataDealer, iDtorID ), |
|
52 ¶ms ); |
|
53 |
|
54 |
|
55 CDataDealer* self = new (ELeave) CDataDealer( params.iProducer, params.iKey ); |
|
56 CleanupStack::PushL(self); |
|
57 self->ConstructL(); |
|
58 CleanupStack::Pop(self); |
|
59 |
|
60 return self; |
|
61 } |
|
62 |
|
63 CDataDealer::~CDataDealer() |
|
64 { |
|
65 // wait that the property is empty (= data consumed) |
|
66 TRAPD( err, WaitForConsumerL() ); |
|
67 if( err != KErrNone ) |
|
68 { |
|
69 // WaitForConsumerL error! |
|
70 } |
|
71 |
|
72 // The producer is responsible of creating and destroying the property |
|
73 if( iProducer ) |
|
74 { |
|
75 // CDataDealer: This is a producer instance, delete the P&S property |
|
76 TInt err = iProperty.Delete( KPropertyCategory, iKey ); |
|
77 if( err != KErrNone ) |
|
78 { |
|
79 // CDataDealer: Could not delete property |
|
80 } |
|
81 iProperty.Close(); |
|
82 } |
|
83 |
|
84 |
|
85 // Notify the ECom server that we released one implementation of some |
|
86 // DLL. When the counter hits zero ECom server may unload the DLL. |
|
87 REComSession::DestroyedImplementation( iDtorID ); |
|
88 } |
|
89 |
|
90 void CDataDealer::ConstructL() |
|
91 { |
|
92 // The producer is responsible of creating and destroying the property |
|
93 if( iProducer ) |
|
94 { |
|
95 // CDataDealer: This is a producer instance, create the P&S property |
|
96 TInt err = iProperty.Define( KPropertyCategory, |
|
97 iKey, |
|
98 RProperty::EByteArray, |
|
99 iPropertySize ); |
|
100 if( err == KErrAlreadyExists ) |
|
101 { |
|
102 // Property already exists. Clear contents. |
|
103 User::LeaveIfError( iProperty.Set( KPropertyCategory, iKey, KNullDesC8 ) ); |
|
104 } |
|
105 else if( err != KErrNone ) |
|
106 { |
|
107 // Could not create pub&sub property |
|
108 User::Leave( err ); |
|
109 } |
|
110 } |
|
111 // Consumer waits that the property is created by producer |
|
112 else |
|
113 { |
|
114 WaitForPropertyToBeCreatedL(); |
|
115 } |
|
116 } |
|
117 |
|
118 |
|
119 /** |
|
120 * Consumer uses this to wait for the property to be created by producer |
|
121 * when DataDealer is constructed. |
|
122 */ |
|
123 void CDataDealer::WaitForPropertyToBeCreatedL() |
|
124 { |
|
125 const TInt KTempPropertyValueLength(2); |
|
126 TBuf8<KTempPropertyValueLength> tempValue; |
|
127 |
|
128 TInt err = iProperty.Get( KPropertyCategory, iKey, tempValue ); |
|
129 while( err == KErrNotFound ) |
|
130 { |
|
131 // Property did not created yet, wait |
|
132 const TInt KWaitForProperty( 500000 ); |
|
133 // User::After here is not a critical error. It is used here to wait for the RProperty instance |
|
134 // to be created. The best solution would be to use RProperty notifications to do that but it is |
|
135 // currently too heavy considering how DataDealer is used: in testing context without any UI |
|
136 User::After( KWaitForProperty ); |
|
137 err = iProperty.Get( KPropertyCategory, iKey, tempValue ); |
|
138 } |
|
139 if( err != KErrNone && err != KErrOverflow ) |
|
140 { |
|
141 // Unexpected error! |
|
142 User::Leave( err ); |
|
143 } |
|
144 } |
|
145 |
|
146 CDataDealer::CDataDealer( const TBool aProducer, const TUint aKey ) |
|
147 : CBase(), |
|
148 // Use KMaxPropertySize as default size to ensure "real time" setting of a value |
|
149 iPropertySize( RProperty::KMaxPropertySize ), |
|
150 iProducer( aProducer ), |
|
151 iKey( aKey ), |
|
152 iDtorID() |
|
153 { |
|
154 } |
|
155 |
|
156 /** |
|
157 * Produce data to the data storage defined in the constructor. |
|
158 */ |
|
159 void CDataDealer::ProduceDataL( const TDesC8& aData ) |
|
160 { |
|
161 // "CDataDealer::ProduceDataL: %S", &aData |
|
162 |
|
163 // Check for misuse |
|
164 if( !iProducer ) User::Leave( KErrNotSupported ); |
|
165 |
|
166 for( TInt i(0); i < aData.Length(); i += iPropertySize ) |
|
167 { |
|
168 // wait that the property is empty (= data consumed) |
|
169 WaitForConsumerL(); |
|
170 |
|
171 // Then write data block-by-block |
|
172 TPtrC8 dataBlock( KNullDesC8 ); |
|
173 if( ( i + iPropertySize ) < aData.Length() ) |
|
174 dataBlock.Set( aData.Mid( i, iPropertySize ) ); |
|
175 else |
|
176 dataBlock.Set( aData.Mid( i, aData.Length() - i ) ); |
|
177 // Write single block |
|
178 User::LeaveIfError( iProperty.Set( KPropertyCategory, iKey, dataBlock ) ); |
|
179 } |
|
180 } |
|
181 |
|
182 /** |
|
183 * Wait synchronously until the consumer has deleted the data we have produced. |
|
184 * Used by data dealer that is producing data. |
|
185 */ |
|
186 void CDataDealer::WaitForConsumerL() |
|
187 { |
|
188 const TInt KTempPropertyValueLength(2); |
|
189 TBuf8<KTempPropertyValueLength> tempValue; |
|
190 |
|
191 TInt err = iProperty.Get( KPropertyCategory, iKey, tempValue ); |
|
192 |
|
193 if( err != KErrOverflow && err != KErrNone ) |
|
194 { |
|
195 // un-expected error |
|
196 // CDataDealer: Could not get property value. |
|
197 User::Leave( err ); |
|
198 } |
|
199 |
|
200 while( err == KErrOverflow || tempValue != KNullDesC8 ) |
|
201 { |
|
202 // Not empty yet, wait |
|
203 |
|
204 // User::After here is not a critical error. It is used here to wait for the RProperty instance |
|
205 // to be created. The best solution would be to use RProperty notifications to do that but it is |
|
206 // currently too heavy considering how DataDealer is used: in testing context without any UI |
|
207 User::After( KWaitForPropertyTime ); // CSI: 92 # |
|
208 err = iProperty.Get( KPropertyCategory, iKey, tempValue ); |
|
209 } |
|
210 |
|
211 } |
|
212 |
|
213 /** |
|
214 * @param aData On return contains a pointer to heap descriptor containing the received data. |
|
215 */ |
|
216 void CDataDealer::ConsumeDataL( HBufC8*& aData ) |
|
217 { |
|
218 // Check for misuse |
|
219 if( iProducer ) User::Leave( KErrNotSupported ); |
|
220 |
|
221 TInt err( KErrNone ); |
|
222 HBufC8* data = HBufC8::NewLC( iPropertySize ); |
|
223 TPtr8 dataPtr = data->Des(); |
|
224 const TInt KMinHBufCLength(1); |
|
225 HBufC8* wholeData = HBufC8::NewLC( KMinHBufCLength ); |
|
226 |
|
227 // Loop until the property is destroyed by the producer |
|
228 for( TInt i(0); err != KErrNotFound; ) |
|
229 { |
|
230 // check if the property exists and has data |
|
231 err = iProperty.Get( KPropertyCategory, iKey, dataPtr ); |
|
232 TBool empty = ( dataPtr == KNullDesC8 ); |
|
233 if( err == KErrNone ) |
|
234 { |
|
235 if( empty ) |
|
236 { |
|
237 // Property empty, cannot consume, wait |
|
238 // If the property exists but is empty, wait for data |
|
239 // User::After here is not a critical error. It is used here to wait for the RProperty instance |
|
240 // to be created. The best solution would be to use RProperty notifications to do that but it is |
|
241 // currently too heavy considering how DataDealer is used: in testing context without any UI |
|
242 User::After( KWaitForPropertyTime ); // CSI: 92 # |
|
243 } |
|
244 else |
|
245 { |
|
246 // Clear the property... |
|
247 User::LeaveIfError( iProperty.Set( KPropertyCategory, iKey, KNullDesC8 ) ); |
|
248 |
|
249 // Append the retrieved data to the buffer that will be returned |
|
250 // CDataDealer: Data ready, consumed |
|
251 HBufC8* newWholeData = wholeData->ReAllocL( wholeData->Length() + iPropertySize ); |
|
252 CleanupStack::Pop( wholeData ); |
|
253 wholeData = newWholeData; |
|
254 CleanupStack::PushL( wholeData ); |
|
255 wholeData->Des().Append( *data ); |
|
256 // Clear the buffer |
|
257 dataPtr.Zero(); |
|
258 |
|
259 // ..and set new storage index |
|
260 i += iPropertySize; |
|
261 } |
|
262 } |
|
263 // If the property is not found, it means that there is (no more) data to be consumed available |
|
264 else if( err != KErrNotFound ) |
|
265 { |
|
266 // CDataDealer: Could not open the property. |
|
267 User::Leave( err ); |
|
268 } |
|
269 } |
|
270 |
|
271 // "CDataDealer::ConsumeDataL: %S", wholeData |
|
272 // Ownership transferred |
|
273 CleanupStack::Pop( wholeData ); |
|
274 aData = wholeData; |
|
275 CleanupStack::PopAndDestroy( data ); |
|
276 } |
|
277 |
|
278 /** |
|
279 * Consume data that is produced to the data storage defined in the constructor. |
|
280 * @param aData On return contains a pointer to heap descriptor containing the received data. |
|
281 * @param aMaxSize The maximum size of the data block. |
|
282 * @param aMore ETrue if there is more data to be consumed. |
|
283 */ |
|
284 void CDataDealer::ConsumeDataL( HBufC8*& aData, const TInt aMaxSize, TBool& aMore ) |
|
285 { |
|
286 // Check for misuse |
|
287 if( iProducer ) User::Leave( KErrNotSupported ); |
|
288 |
|
289 HBufC8* data = HBufC8::NewLC( iPropertySize ); |
|
290 TPtr8 dataPtr = data->Des(); |
|
291 |
|
292 // Loop until data is produced to the property |
|
293 TInt err( KErrNone ); |
|
294 while( err != KErrNotFound && dataPtr.Length() == 0 ) |
|
295 { |
|
296 // check if the property exists and has data |
|
297 err = iProperty.Get( KPropertyCategory, iKey, dataPtr ); |
|
298 TBool empty = ( dataPtr == KNullDesC8 ); |
|
299 |
|
300 if( err == KErrNone ) |
|
301 { |
|
302 if( empty ) |
|
303 { |
|
304 // If the property exists but is empty, wait for data |
|
305 // User::After here is not a critical error. It is used here to wait for the RProperty instance |
|
306 // to be created. The best solution would be to use RProperty notifications to do that but it is |
|
307 // currently too heavy considering how DataDealer is used: in testing context without any UI |
|
308 User::After( KWaitForPropertyTime ); // CSI: 92 # |
|
309 } |
|
310 else if( dataPtr.Length() > aMaxSize ) |
|
311 { |
|
312 // Too long data (%i, max: %i): consuming only the first block", dataPtr.Length(), aMaxSize |
|
313 // Consuming only the first aMaxSize characters; save the rest of the data to the property |
|
314 iProperty.Set( KPropertyCategory, iKey, dataPtr.Right( dataPtr.Length() - aMaxSize ) ); |
|
315 // Take the first aMaxSize characters and ignore the rest |
|
316 data->Des().Copy( dataPtr.Left( aMaxSize ) ); |
|
317 } |
|
318 else |
|
319 { |
|
320 // Clear the property... |
|
321 User::LeaveIfError( iProperty.Set( KPropertyCategory, iKey, KNullDesC8 ) ); |
|
322 } |
|
323 } |
|
324 // If the property is not found, it means that there is (no more) data to be consumed available |
|
325 else if( err == KErrNotFound ) |
|
326 { |
|
327 // CDataDealer: No more data in the property (KErrNotFound) |
|
328 aMore = EFalse; |
|
329 } |
|
330 else |
|
331 { |
|
332 // CDataDealer: Could not open the property. |
|
333 aMore = EFalse; |
|
334 User::Leave( err ); |
|
335 } |
|
336 } |
|
337 |
|
338 CleanupStack::Pop( data ); |
|
339 aData = data; |
|
340 } |
|
341 |