1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "stats_event.h"
18 #include <gtest/gtest.h>
19 #include <utils/SystemClock.h>
20 
21 // Keep in sync with stats_event.c. Consider moving to separate header file to avoid duplication.
22 /* ERRORS */
23 #define ERROR_NO_TIMESTAMP 0x1
24 #define ERROR_NO_ATOM_ID 0x2
25 #define ERROR_OVERFLOW 0x4
26 #define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
27 #define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
28 #define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
29 #define ERROR_INVALID_ANNOTATION_ID 0x40
30 #define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
31 #define ERROR_TOO_MANY_ANNOTATIONS 0x100
32 #define ERROR_TOO_MANY_FIELDS 0x200
33 #define ERROR_INVALID_VALUE_TYPE 0x400
34 #define ERROR_STRING_NOT_NULL_TERMINATED 0x800
35 #define ERROR_ATOM_ID_INVALID_POSITION 0x2000
36 
37 /* TYPE IDS */
38 #define INT32_TYPE 0x00
39 #define INT64_TYPE 0x01
40 #define STRING_TYPE 0x02
41 #define LIST_TYPE 0x03
42 #define FLOAT_TYPE 0x04
43 #define BOOL_TYPE 0x05
44 #define BYTE_ARRAY_TYPE 0x06
45 #define OBJECT_TYPE 0x07
46 #define KEY_VALUE_PAIRS_TYPE 0x08
47 #define ATTRIBUTION_CHAIN_TYPE 0x09
48 #define ERROR_TYPE 0x0F
49 
50 using std::string;
51 using std::vector;
52 
53 // Side-effect: this function moves the start of the buffer past the read value
54 template <class T>
readNext(uint8_t ** buffer)55 T readNext(uint8_t** buffer) {
56     T value;
57     if ((reinterpret_cast<uintptr_t>(*buffer) % alignof(T)) == 0) {
58         value = *(T*)(*buffer);
59     } else {
60         memcpy(&value, *buffer, sizeof(T));
61     }
62     *buffer += sizeof(T);
63     return value;
64 }
65 
checkTypeHeader(uint8_t ** buffer,uint8_t typeId,uint8_t numAnnotations=0)66 void checkTypeHeader(uint8_t** buffer, uint8_t typeId, uint8_t numAnnotations = 0) {
67     uint8_t typeHeader = (numAnnotations << 4) | typeId;
68     EXPECT_EQ(readNext<uint8_t>(buffer), typeHeader);
69 }
70 
71 template <class T>
checkScalar(uint8_t ** buffer,T expectedValue)72 void checkScalar(uint8_t** buffer, T expectedValue) {
73     EXPECT_EQ(readNext<T>(buffer), expectedValue);
74 }
75 
checkString(uint8_t ** buffer,const string & expectedString)76 void checkString(uint8_t** buffer, const string& expectedString) {
77     uint32_t size = readNext<uint32_t>(buffer);
78     string parsedString((char*)(*buffer), size);
79     EXPECT_EQ(parsedString, expectedString);
80     *buffer += size;  // move buffer past string we just read
81 }
82 
checkByteArray(uint8_t ** buffer,const vector<uint8_t> & expectedByteArray)83 void checkByteArray(uint8_t** buffer, const vector<uint8_t>& expectedByteArray) {
84     uint32_t size = readNext<uint32_t>(buffer);
85     vector<uint8_t> parsedByteArray(*buffer, *buffer + size);
86     EXPECT_EQ(parsedByteArray, expectedByteArray);
87     *buffer += size;  // move buffer past byte array we just read
88 }
89 
90 template <class T>
checkAnnotation(uint8_t ** buffer,uint8_t annotationId,uint8_t typeId,T annotationValue)91 void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) {
92     EXPECT_EQ(readNext<uint8_t>(buffer), annotationId);
93     EXPECT_EQ(readNext<uint8_t>(buffer), typeId);
94     checkScalar<T>(buffer, annotationValue);
95 }
96 
checkMetadata(uint8_t ** buffer,uint8_t numElements,int64_t startTime,int64_t endTime,uint32_t atomId,uint8_t numAtomLevelAnnotations=0)97 void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
98                    uint32_t atomId, uint8_t numAtomLevelAnnotations = 0) {
99     // All events start with OBJECT_TYPE id.
100     checkTypeHeader(buffer, OBJECT_TYPE);
101 
102     // We increment by 2 because the number of elements listed in the
103     // serialization accounts for the timestamp and atom id as well.
104     checkScalar(buffer, static_cast<uint8_t>(numElements + 2));
105 
106     // Check timestamp
107     checkTypeHeader(buffer, INT64_TYPE);
108     int64_t timestamp = readNext<int64_t>(buffer);
109     EXPECT_GE(timestamp, startTime);
110     EXPECT_LE(timestamp, endTime);
111 
112     // Check atom id
113     checkTypeHeader(buffer, INT32_TYPE, numAtomLevelAnnotations);
114     checkScalar(buffer, atomId);
115 }
116 
TEST(StatsEventTest,TestScalars)117 TEST(StatsEventTest, TestScalars) {
118     uint32_t atomId = 100;
119     int32_t int32Value = -5;
120     int64_t int64Value = -2 * android::elapsedRealtimeNano();
121     float floatValue = 2.0;
122     bool boolValue = false;
123 
124     int64_t startTime = android::elapsedRealtimeNano();
125     AStatsEvent* event = AStatsEvent_obtain();
126     AStatsEvent_setAtomId(event, atomId);
127     AStatsEvent_writeInt32(event, int32Value);
128     AStatsEvent_writeInt64(event, int64Value);
129     AStatsEvent_writeFloat(event, floatValue);
130     AStatsEvent_writeBool(event, boolValue);
131     AStatsEvent_build(event);
132     int64_t endTime = android::elapsedRealtimeNano();
133 
134     size_t bufferSize;
135     uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
136     uint8_t* bufferEnd = buffer + bufferSize;
137 
138     checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
139 
140     // check int32 element
141     checkTypeHeader(&buffer, INT32_TYPE);
142     checkScalar(&buffer, int32Value);
143 
144     // check int64 element
145     checkTypeHeader(&buffer, INT64_TYPE);
146     checkScalar(&buffer, int64Value);
147 
148     // check float element
149     checkTypeHeader(&buffer, FLOAT_TYPE);
150     checkScalar(&buffer, floatValue);
151 
152     // check bool element
153     checkTypeHeader(&buffer, BOOL_TYPE);
154     checkScalar(&buffer, boolValue);
155 
156     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
157     EXPECT_EQ(AStatsEvent_getErrors(event), 0);
158     AStatsEvent_release(event);
159 }
160 
TEST(StatsEventTest,TestStrings)161 TEST(StatsEventTest, TestStrings) {
162     uint32_t atomId = 100;
163     string str = "test_string";
164 
165     int64_t startTime = android::elapsedRealtimeNano();
166     AStatsEvent* event = AStatsEvent_obtain();
167     AStatsEvent_setAtomId(event, atomId);
168     AStatsEvent_writeString(event, str.c_str());
169     AStatsEvent_build(event);
170     int64_t endTime = android::elapsedRealtimeNano();
171 
172     size_t bufferSize;
173     uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
174     uint8_t* bufferEnd = buffer + bufferSize;
175 
176     checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
177 
178     checkTypeHeader(&buffer, STRING_TYPE);
179     checkString(&buffer, str);
180 
181     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
182     EXPECT_EQ(AStatsEvent_getErrors(event), 0);
183     AStatsEvent_release(event);
184 }
185 
TEST(StatsEventTest,TestNullString)186 TEST(StatsEventTest, TestNullString) {
187     uint32_t atomId = 100;
188     char* str = nullptr;
189 
190     int64_t startTime = android::elapsedRealtimeNano();
191     AStatsEvent* event = AStatsEvent_obtain();
192     AStatsEvent_setAtomId(event, atomId);
193     AStatsEvent_writeString(event, str);
194     AStatsEvent_build(event);
195     int64_t endTime = android::elapsedRealtimeNano();
196 
197     size_t bufferSize;
198     uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
199     uint8_t* bufferEnd = buffer + bufferSize;
200 
201     checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
202 
203     checkTypeHeader(&buffer, STRING_TYPE);
204     checkString(&buffer, "");
205 
206     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
207     EXPECT_EQ(AStatsEvent_getErrors(event), 0);
208     AStatsEvent_release(event);
209 }
210 
TEST(StatsEventTest,TestByteArrays)211 TEST(StatsEventTest, TestByteArrays) {
212     uint32_t atomId = 100;
213     vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
214 
215     int64_t startTime = android::elapsedRealtimeNano();
216     AStatsEvent* event = AStatsEvent_obtain();
217     AStatsEvent_setAtomId(event, atomId);
218     AStatsEvent_writeByteArray(event, message.data(), message.size());
219     AStatsEvent_build(event);
220     int64_t endTime = android::elapsedRealtimeNano();
221 
222     size_t bufferSize;
223     uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
224     uint8_t* bufferEnd = buffer + bufferSize;
225 
226     checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
227 
228     checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
229     checkByteArray(&buffer, message);
230 
231     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
232     EXPECT_EQ(AStatsEvent_getErrors(event), 0);
233     AStatsEvent_release(event);
234 }
235 
TEST(StatsEventTest,TestNullByteArrays)236 TEST(StatsEventTest, TestNullByteArrays) {
237     uint32_t atomId = 100;
238     uint8_t* buf = nullptr;
239     vector<uint8_t> message;
240 
241     int64_t startTime = android::elapsedRealtimeNano();
242     AStatsEvent* event = AStatsEvent_obtain();
243     AStatsEvent_setAtomId(event, atomId);
244     AStatsEvent_writeByteArray(event, buf, 2);
245     AStatsEvent_build(event);
246     int64_t endTime = android::elapsedRealtimeNano();
247 
248     size_t bufferSize;
249     uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
250     uint8_t* bufferEnd = buffer + bufferSize;
251 
252     checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
253 
254     checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
255     checkByteArray(&buffer, message);
256 
257     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
258     EXPECT_EQ(AStatsEvent_getErrors(event), 0);
259     AStatsEvent_release(event);
260 }
261 
TEST(StatsEventTest,TestAttributionChains)262 TEST(StatsEventTest, TestAttributionChains) {
263     uint32_t atomId = 100;
264 
265     uint8_t numNodes = 50;
266     uint32_t uids[numNodes];
267     vector<string> tags(numNodes);  // storage that cTag elements point to
268     const char* cTags[numNodes];
269     for (int i = 0; i < (int)numNodes; i++) {
270         uids[i] = i;
271         if (0 == i) {
272             tags.push_back("");
273             cTags[i] = nullptr;
274         } else {
275             tags.push_back("test" + std::to_string(i));
276             cTags[i] = tags[i].c_str();
277         }
278     }
279 
280     int64_t startTime = android::elapsedRealtimeNano();
281     AStatsEvent* event = AStatsEvent_obtain();
282     AStatsEvent_setAtomId(event, atomId);
283     AStatsEvent_writeAttributionChain(event, uids, cTags, numNodes);
284     AStatsEvent_build(event);
285     int64_t endTime = android::elapsedRealtimeNano();
286 
287     size_t bufferSize;
288     uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
289     uint8_t* bufferEnd = buffer + bufferSize;
290 
291     checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
292 
293     checkTypeHeader(&buffer, ATTRIBUTION_CHAIN_TYPE);
294     checkScalar(&buffer, numNodes);
295     for (int i = 0; i < numNodes; i++) {
296         checkScalar(&buffer, uids[i]);
297         checkString(&buffer, tags[i]);
298     }
299 
300     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
301     EXPECT_EQ(AStatsEvent_getErrors(event), 0);
302     AStatsEvent_release(event);
303 }
304 
TEST(StatsEventTest,TestFieldAnnotations)305 TEST(StatsEventTest, TestFieldAnnotations) {
306     uint32_t atomId = 100;
307 
308     // first element information
309     bool boolValue = false;
310     uint8_t boolAnnotation1Id = 1;
311     uint8_t boolAnnotation2Id = 2;
312     bool boolAnnotation1Value = true;
313     int32_t boolAnnotation2Value = 3;
314 
315     // second element information
316     float floatValue = -5.0;
317     uint8_t floatAnnotation1Id = 3;
318     uint8_t floatAnnotation2Id = 4;
319     int32_t floatAnnotation1Value = 8;
320     bool floatAnnotation2Value = false;
321 
322     int64_t startTime = android::elapsedRealtimeNano();
323     AStatsEvent* event = AStatsEvent_obtain();
324     AStatsEvent_setAtomId(event, atomId);
325     AStatsEvent_writeBool(event, boolValue);
326     AStatsEvent_addBoolAnnotation(event, boolAnnotation1Id, boolAnnotation1Value);
327     AStatsEvent_addInt32Annotation(event, boolAnnotation2Id, boolAnnotation2Value);
328     AStatsEvent_writeFloat(event, floatValue);
329     AStatsEvent_addInt32Annotation(event, floatAnnotation1Id, floatAnnotation1Value);
330     AStatsEvent_addBoolAnnotation(event, floatAnnotation2Id, floatAnnotation2Value);
331     AStatsEvent_build(event);
332     int64_t endTime = android::elapsedRealtimeNano();
333 
334     size_t bufferSize;
335     uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
336     uint8_t* bufferEnd = buffer + bufferSize;
337 
338     checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
339 
340     // check first element
341     checkTypeHeader(&buffer, BOOL_TYPE, /*numAnnotations=*/2);
342     checkScalar(&buffer, boolValue);
343     checkAnnotation(&buffer, boolAnnotation1Id, BOOL_TYPE, boolAnnotation1Value);
344     checkAnnotation(&buffer, boolAnnotation2Id, INT32_TYPE, boolAnnotation2Value);
345 
346     // check second element
347     checkTypeHeader(&buffer, FLOAT_TYPE, /*numAnnotations=*/2);
348     checkScalar(&buffer, floatValue);
349     checkAnnotation(&buffer, floatAnnotation1Id, INT32_TYPE, floatAnnotation1Value);
350     checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
351 
352     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
353     EXPECT_EQ(AStatsEvent_getErrors(event), 0);
354     AStatsEvent_release(event);
355 }
356 
TEST(StatsEventTest,TestAtomLevelAnnotations)357 TEST(StatsEventTest, TestAtomLevelAnnotations) {
358     uint32_t atomId = 100;
359     // atom-level annotation information
360     uint8_t boolAnnotationId = 1;
361     uint8_t int32AnnotationId = 2;
362     bool boolAnnotationValue = false;
363     int32_t int32AnnotationValue = 5;
364 
365     float fieldValue = -3.5;
366 
367     int64_t startTime = android::elapsedRealtimeNano();
368     AStatsEvent* event = AStatsEvent_obtain();
369     AStatsEvent_setAtomId(event, atomId);
370     AStatsEvent_addBoolAnnotation(event, boolAnnotationId, boolAnnotationValue);
371     AStatsEvent_addInt32Annotation(event, int32AnnotationId, int32AnnotationValue);
372     AStatsEvent_writeFloat(event, fieldValue);
373     AStatsEvent_build(event);
374     int64_t endTime = android::elapsedRealtimeNano();
375 
376     size_t bufferSize;
377     uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
378     uint8_t* bufferEnd = buffer + bufferSize;
379 
380     checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId,
381                   /*numAtomLevelAnnotations=*/2);
382 
383     // check atom-level annotations
384     checkAnnotation(&buffer, boolAnnotationId, BOOL_TYPE, boolAnnotationValue);
385     checkAnnotation(&buffer, int32AnnotationId, INT32_TYPE, int32AnnotationValue);
386 
387     // check first element
388     checkTypeHeader(&buffer, FLOAT_TYPE);
389     checkScalar(&buffer, fieldValue);
390 
391     EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
392     EXPECT_EQ(AStatsEvent_getErrors(event), 0);
393     AStatsEvent_release(event);
394 }
395 
TEST(StatsEventTest,TestNoAtomIdError)396 TEST(StatsEventTest, TestNoAtomIdError) {
397     AStatsEvent* event = AStatsEvent_obtain();
398     // Don't set the atom id in order to trigger the error.
399     AStatsEvent_build(event);
400 
401     uint32_t errors = AStatsEvent_getErrors(event);
402     EXPECT_EQ(errors & ERROR_NO_ATOM_ID, ERROR_NO_ATOM_ID);
403 
404     AStatsEvent_release(event);
405 }
406 
TEST(StatsEventTest,TestPushOverflowError)407 TEST(StatsEventTest, TestPushOverflowError) {
408     const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
409     const int writeCount = 120;  // Number of times to write str in the event.
410 
411     AStatsEvent* event = AStatsEvent_obtain();
412     AStatsEvent_setAtomId(event, 100);
413 
414     // Add str to the event 120 times. Each str takes >35 bytes so this will
415     // overflow the 4068 byte buffer.
416     // We want to keep writeCount less than 127 to avoid hitting
417     // ERROR_TOO_MANY_FIELDS.
418     for (int i = 0; i < writeCount; i++) {
419         AStatsEvent_writeString(event, str);
420     }
421     AStatsEvent_write(event);
422 
423     uint32_t errors = AStatsEvent_getErrors(event);
424     EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW);
425 
426     AStatsEvent_release(event);
427 }
428 
TEST(StatsEventTest,TestPullOverflowError)429 TEST(StatsEventTest, TestPullOverflowError) {
430     const uint32_t atomId = 10100;
431     const vector<uint8_t> bytes(430 /* number of elements */, 1 /* value of each element */);
432     const int writeCount = 120;  // Number of times to write bytes in the event.
433 
434     AStatsEvent* event = AStatsEvent_obtain();
435     AStatsEvent_setAtomId(event, atomId);
436 
437     // Add bytes to the event 120 times. Size of bytes is 430 so this will
438     // overflow the 50 KB pulled event buffer.
439     // We want to keep writeCount less than 127 to avoid hitting
440     // ERROR_TOO_MANY_FIELDS.
441     for (int i = 0; i < writeCount; i++) {
442         AStatsEvent_writeByteArray(event, bytes.data(), bytes.size());
443     }
444     AStatsEvent_build(event);
445 
446     uint32_t errors = AStatsEvent_getErrors(event);
447     EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW);
448 
449     AStatsEvent_release(event);
450 }
451 
TEST(StatsEventTest,TestLargePull)452 TEST(StatsEventTest, TestLargePull) {
453     const uint32_t atomId = 100;
454     const string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
455     const int writeCount = 120;  // Number of times to write str in the event.
456     const int64_t startTime = android::elapsedRealtimeNano();
457 
458     AStatsEvent* event = AStatsEvent_obtain();
459     AStatsEvent_setAtomId(event, atomId);
460 
461     // Add str to the event 120 times.
462     // We want to keep writeCount less than 127 to avoid hitting
463     // ERROR_TOO_MANY_FIELDS.
464     for (int i = 0; i < writeCount; i++) {
465         AStatsEvent_writeString(event, str.c_str());
466     }
467     AStatsEvent_build(event);
468     int64_t endTime = android::elapsedRealtimeNano();
469 
470     size_t bufferSize;
471     uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
472     uint8_t* bufferEnd = buffer + bufferSize;
473 
474     checkMetadata(&buffer, writeCount, startTime, endTime, atomId);
475 
476     // Check all instances of str have been written.
477     for (int i = 0; i < writeCount; i++) {
478         checkTypeHeader(&buffer, STRING_TYPE);
479         checkString(&buffer, str);
480     }
481 
482     EXPECT_EQ(buffer, bufferEnd);  // Ensure that we have read the entire buffer.
483     EXPECT_EQ(AStatsEvent_getErrors(event), 0);
484     AStatsEvent_release(event);
485 }
486 
TEST(StatsEventTest,TestAtomIdInvalidPositionError)487 TEST(StatsEventTest, TestAtomIdInvalidPositionError) {
488     AStatsEvent* event = AStatsEvent_obtain();
489     AStatsEvent_writeInt32(event, 0);
490     AStatsEvent_setAtomId(event, 100);
491     AStatsEvent_writeBool(event, true);
492     AStatsEvent_build(event);
493 
494     uint32_t errors = AStatsEvent_getErrors(event);
495     EXPECT_EQ(errors & ERROR_ATOM_ID_INVALID_POSITION, ERROR_ATOM_ID_INVALID_POSITION);
496 
497     AStatsEvent_release(event);
498 }
499 
TEST(StatsEventTest,TestOverwriteTimestamp)500 TEST(StatsEventTest, TestOverwriteTimestamp) {
501     uint32_t atomId = 100;
502     int64_t expectedTimestamp = 0x123456789;
503     AStatsEvent* event = AStatsEvent_obtain();
504     AStatsEvent_setAtomId(event, atomId);
505     AStatsEvent_overwriteTimestamp(event, expectedTimestamp);
506     AStatsEvent_build(event);
507 
508     uint8_t* buffer = AStatsEvent_getBuffer(event, NULL);
509 
510     // Make sure that the timestamp is being overwritten.
511     checkMetadata(&buffer, /*numElements=*/0, /*startTime=*/expectedTimestamp,
512                   /*endTime=*/expectedTimestamp, atomId);
513 
514     EXPECT_EQ(AStatsEvent_getErrors(event), 0);
515     AStatsEvent_release(event);
516 }
517