• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package android.util;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.Truth.assertWithMessage;
21 import static java.nio.charset.StandardCharsets.UTF_8;
22 
23 import android.os.SystemClock;
24 import androidx.test.filters.SmallTest;
25 import androidx.test.runner.AndroidJUnit4;
26 import com.android.modules.utils.build.SdkLevel;
27 import com.google.common.collect.Range;
28 import java.nio.ByteBuffer;
29 import java.nio.ByteOrder;
30 import java.util.Random;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 
34 /**
35  * Internal tests for {@link StatsEvent}.
36  */
37 @SmallTest
38 @RunWith(AndroidJUnit4.class)
39 public class StatsEventTest {
40 
41     @Test
testNoFields()42     public void testNoFields() {
43         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
44         final StatsEvent statsEvent = StatsEvent.newBuilder().usePooledBuffer().build();
45         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
46 
47         final int expectedAtomId = 0;
48         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
49 
50         final ByteBuffer buffer =
51                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
52 
53         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
54                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
55 
56         assertWithMessage("Incorrect number of elements in root object")
57                 .that(buffer.get()).isEqualTo(3);
58 
59         assertWithMessage("First element is not timestamp")
60                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
61 
62         assertWithMessage("Incorrect timestamp")
63                 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
64 
65         assertWithMessage("Second element is not atom id")
66                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
67 
68         assertWithMessage("Incorrect atom id")
69                 .that(buffer.getInt()).isEqualTo(expectedAtomId);
70 
71         assertWithMessage("Third element is not errors type")
72                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS);
73 
74         final int errorMask = buffer.getInt();
75 
76         assertWithMessage("ERROR_NO_ATOM_ID should be the only error in the error mask")
77                 .that(errorMask).isEqualTo(StatsEvent.ERROR_NO_ATOM_ID);
78 
79         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
80 
81         statsEvent.release();
82     }
83 
84     @Test
testOnlyAtomId()85     public void testOnlyAtomId() {
86         final int expectedAtomId = 109;
87 
88         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
89         final StatsEvent statsEvent = StatsEvent.newBuilder()
90                 .setAtomId(expectedAtomId)
91                 .usePooledBuffer()
92                 .build();
93         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
94 
95         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
96 
97         final ByteBuffer buffer =
98                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
99 
100         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
101                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
102 
103         assertWithMessage("Incorrect number of elements in root object")
104                 .that(buffer.get()).isEqualTo(2);
105 
106         assertWithMessage("First element is not timestamp")
107                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
108 
109         assertWithMessage("Incorrect timestamp")
110                 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
111 
112         assertWithMessage("Second element is not atom id")
113                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
114 
115         assertWithMessage("Incorrect atom id")
116                 .that(buffer.getInt()).isEqualTo(expectedAtomId);
117 
118         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
119 
120         statsEvent.release();
121     }
122 
123     @Test
testIntBooleanIntInt()124     public void testIntBooleanIntInt() {
125         final int expectedAtomId = 109;
126         final int field1 = 1;
127         final boolean field2 = true;
128         final int field3 = 3;
129         final int field4 = 4;
130 
131         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
132         final StatsEvent statsEvent = StatsEvent.newBuilder()
133                 .setAtomId(expectedAtomId)
134                 .writeInt(field1)
135                 .writeBoolean(field2)
136                 .writeInt(field3)
137                 .writeInt(field4)
138                 .usePooledBuffer()
139                 .build();
140         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
141 
142         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
143 
144         final ByteBuffer buffer =
145                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
146 
147         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
148                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
149 
150         assertWithMessage("Incorrect number of elements in root object")
151                 .that(buffer.get()).isEqualTo(6);
152 
153         assertWithMessage("First element is not timestamp")
154                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
155 
156         assertWithMessage("Incorrect timestamp")
157                 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
158 
159         assertWithMessage("Second element is not atom id")
160                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
161 
162         assertWithMessage("Incorrect atom id")
163                 .that(buffer.getInt()).isEqualTo(expectedAtomId);
164 
165         assertWithMessage("First field is not Int")
166                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
167 
168         assertWithMessage("Incorrect field 1")
169                 .that(buffer.getInt()).isEqualTo(field1);
170 
171         assertWithMessage("Second field is not Boolean")
172                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BOOLEAN);
173 
174         assertWithMessage("Incorrect field 2")
175                 .that(buffer.get()).isEqualTo(1);
176 
177         assertWithMessage("Third field is not Int")
178                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
179 
180         assertWithMessage("Incorrect field 3")
181                 .that(buffer.getInt()).isEqualTo(field3);
182 
183         assertWithMessage("Fourth field is not Int")
184                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
185 
186         assertWithMessage("Incorrect field 4")
187                 .that(buffer.getInt()).isEqualTo(field4);
188 
189         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
190 
191         statsEvent.release();
192     }
193 
194     @Test
testStringFloatByteArray()195     public void testStringFloatByteArray() {
196         final int expectedAtomId = 109;
197         final String field1 = "Str 1";
198         final float field2 = 9.334f;
199         final byte[] field3 = new byte[] { 56, 23, 89, -120 };
200 
201         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
202         final StatsEvent statsEvent = StatsEvent.newBuilder()
203                 .setAtomId(expectedAtomId)
204                 .writeString(field1)
205                 .writeFloat(field2)
206                 .writeByteArray(field3)
207                 .usePooledBuffer()
208                 .build();
209         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
210 
211         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
212 
213         final ByteBuffer buffer =
214                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
215 
216         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
217                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
218 
219         assertWithMessage("Incorrect number of elements in root object")
220                 .that(buffer.get()).isEqualTo(5);
221 
222         assertWithMessage("First element is not timestamp")
223                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
224 
225         assertWithMessage("Incorrect timestamp")
226                 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
227 
228         assertWithMessage("Second element is not atom id")
229                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
230 
231         assertWithMessage("Incorrect atom id")
232                 .that(buffer.getInt()).isEqualTo(expectedAtomId);
233 
234         assertWithMessage("First field is not String")
235                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_STRING);
236 
237         final String field1Actual = getStringFromByteBuffer(buffer);
238         assertWithMessage("Incorrect field 1")
239                 .that(field1Actual).isEqualTo(field1);
240 
241         assertWithMessage("Second field is not Float")
242                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_FLOAT);
243 
244         assertWithMessage("Incorrect field 2")
245                 .that(buffer.getFloat()).isEqualTo(field2);
246 
247         assertWithMessage("Third field is not byte array")
248                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BYTE_ARRAY);
249 
250         final byte[] field3Actual = getByteArrayFromByteBuffer(buffer);
251         assertWithMessage("Incorrect field 3")
252                 .that(field3Actual).isEqualTo(field3);
253 
254         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
255 
256         statsEvent.release();
257     }
258 
259     @Test
testAttributionChainLong()260     public void testAttributionChainLong() {
261         final int expectedAtomId = 109;
262         final int[] uids = new int[] { 1, 2, 3, 4, 5 };
263         final String[] tags = new String[] { "1", "2", "3", "4", "5" };
264         final long field2 = -230909823L;
265 
266         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
267         final StatsEvent statsEvent = StatsEvent.newBuilder()
268                 .setAtomId(expectedAtomId)
269                 .writeAttributionChain(uids, tags)
270                 .writeLong(field2)
271                 .usePooledBuffer()
272                 .build();
273         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
274 
275         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
276 
277         final ByteBuffer buffer =
278                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
279 
280         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
281                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
282 
283         assertWithMessage("Incorrect number of elements in root object")
284                 .that(buffer.get()).isEqualTo(4);
285 
286         assertWithMessage("First element is not timestamp")
287                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
288 
289         assertWithMessage("Incorrect timestamp")
290                 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
291 
292         assertWithMessage("Second element is not atom id")
293                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
294 
295         assertWithMessage("Incorrect atom id")
296                 .that(buffer.getInt()).isEqualTo(expectedAtomId);
297 
298         assertWithMessage("First field is not Attribution Chain")
299                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ATTRIBUTION_CHAIN);
300 
301         assertWithMessage("Incorrect number of attribution nodes")
302                 .that(buffer.get()).isEqualTo((byte) uids.length);
303 
304         for (int i = 0; i < tags.length; i++) {
305             assertWithMessage("Incorrect uid in Attribution Chain")
306                     .that(buffer.getInt()).isEqualTo(uids[i]);
307 
308             final String tag = getStringFromByteBuffer(buffer);
309             assertWithMessage("Incorrect tag in Attribution Chain")
310                     .that(tag).isEqualTo(tags[i]);
311         }
312 
313         assertWithMessage("Second field is not Long")
314                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
315 
316         assertWithMessage("Incorrect field 2")
317                 .that(buffer.getLong()).isEqualTo(field2);
318 
319         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
320 
321         statsEvent.release();
322     }
323 
324     @Test
testKeyValuePairs()325     public void testKeyValuePairs() {
326         final int expectedAtomId = 109;
327         final SparseIntArray intMap = new SparseIntArray();
328         final SparseLongArray longMap = new SparseLongArray();
329         final SparseArray<String> stringMap = new SparseArray<>();
330         final SparseArray<Float> floatMap = new SparseArray<>();
331         intMap.put(1, -1);
332         intMap.put(2, -2);
333         stringMap.put(3, "abc");
334         stringMap.put(4, "2h");
335         floatMap.put(9, -234.344f);
336 
337         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
338         final StatsEvent statsEvent = StatsEvent.newBuilder()
339                 .setAtomId(expectedAtomId)
340                 .writeKeyValuePairs(intMap, longMap, stringMap, floatMap)
341                 .usePooledBuffer()
342                 .build();
343         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
344 
345         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
346 
347         final ByteBuffer buffer =
348                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
349 
350         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
351                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
352 
353         assertWithMessage("Incorrect number of elements in root object")
354                 .that(buffer.get()).isEqualTo(3);
355 
356         assertWithMessage("First element is not timestamp")
357                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
358 
359         assertWithMessage("Incorrect timestamp")
360                 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
361 
362         assertWithMessage("Second element is not atom id")
363                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
364 
365         assertWithMessage("Incorrect atom id")
366                 .that(buffer.getInt()).isEqualTo(expectedAtomId);
367 
368         assertWithMessage("First field is not KeyValuePairs")
369                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_KEY_VALUE_PAIRS);
370 
371         assertWithMessage("Incorrect number of key value pairs")
372                 .that(buffer.get()).isEqualTo(
373                         (byte) (intMap.size() + longMap.size() + stringMap.size()
374                                 + floatMap.size()));
375 
376         for (int i = 0; i < intMap.size(); i++) {
377             assertWithMessage("Incorrect key in intMap")
378                     .that(buffer.getInt()).isEqualTo(intMap.keyAt(i));
379             assertWithMessage("The type id of the value should be TYPE_INT in intMap")
380                     .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
381             assertWithMessage("Incorrect value in intMap")
382                     .that(buffer.getInt()).isEqualTo(intMap.valueAt(i));
383         }
384 
385         for (int i = 0; i < longMap.size(); i++) {
386             assertWithMessage("Incorrect key in longMap")
387                     .that(buffer.getInt()).isEqualTo(longMap.keyAt(i));
388             assertWithMessage("The type id of the value should be TYPE_LONG in longMap")
389                     .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
390             assertWithMessage("Incorrect value in longMap")
391                     .that(buffer.getLong()).isEqualTo(longMap.valueAt(i));
392         }
393 
394         for (int i = 0; i < stringMap.size(); i++) {
395             assertWithMessage("Incorrect key in stringMap")
396                     .that(buffer.getInt()).isEqualTo(stringMap.keyAt(i));
397             assertWithMessage("The type id of the value should be TYPE_STRING in stringMap")
398                     .that(buffer.get()).isEqualTo(StatsEvent.TYPE_STRING);
399             final String value = getStringFromByteBuffer(buffer);
400             assertWithMessage("Incorrect value in stringMap")
401                     .that(value).isEqualTo(stringMap.valueAt(i));
402         }
403 
404         for (int i = 0; i < floatMap.size(); i++) {
405             assertWithMessage("Incorrect key in floatMap")
406                     .that(buffer.getInt()).isEqualTo(floatMap.keyAt(i));
407             assertWithMessage("The type id of the value should be TYPE_FLOAT in floatMap")
408                     .that(buffer.get()).isEqualTo(StatsEvent.TYPE_FLOAT);
409             assertWithMessage("Incorrect value in floatMap")
410                     .that(buffer.getFloat()).isEqualTo(floatMap.valueAt(i));
411         }
412 
413         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
414 
415         statsEvent.release();
416     }
417 
418     @Test
testBoolArrayIntArrayLongArray()419     public void testBoolArrayIntArrayLongArray() {
420         // Skip test if build version isn't T or greater.
421         if (!SdkLevel.isAtLeastT()) {
422             return;
423         }
424 
425         final int expectedAtomId = 109;
426         final boolean[] field1 = new boolean[] {true, false, false};
427         final int[] field1Converted = new int[] {1, 0, 0};
428         final int[] field2 = new int[] {4, 11};
429         final long[] field3 = new long[] {10000L, 10000L, 10000L};
430 
431         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
432         final StatsEvent statsEvent = StatsEvent.newBuilder()
433                                         .setAtomId(expectedAtomId)
434                                         .writeBooleanArray(field1)
435                                         .writeIntArray(field2)
436                                         .writeLongArray(field3)
437                                         .usePooledBuffer()
438                                         .build();
439         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
440 
441         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
442 
443         final ByteBuffer buffer =
444           ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
445 
446         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
447                 .that(buffer.get())
448                 .isEqualTo(StatsEvent.TYPE_OBJECT);
449 
450         assertWithMessage("Incorrect number of elements in root object")
451                 .that(buffer.get())
452                 .isEqualTo(5);
453 
454         assertWithMessage("First element is not timestamp")
455                 .that(buffer.get())
456                 .isEqualTo(StatsEvent.TYPE_LONG);
457 
458         assertWithMessage("Incorrect timestamp")
459                 .that(buffer.getLong())
460                 .isIn(Range.closed(minTimestamp, maxTimestamp));
461 
462         assertWithMessage("Second element is not atom id")
463                 .that(buffer.get())
464                 .isEqualTo(StatsEvent.TYPE_INT);
465 
466         assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
467 
468         assertWithMessage("First field is not list")
469                 .that(buffer.get())
470                 .isEqualTo(StatsEvent.TYPE_LIST);
471 
472         assertWithMessage("Incorrect number of elements in field 1 object")
473                 .that(buffer.get())
474                 .isEqualTo(3);
475 
476         assertWithMessage("Element type of field 1 is not boolean")
477                 .that(buffer.get())
478                 .isEqualTo(StatsEvent.TYPE_BOOLEAN);
479 
480         for (int i = 0; i < field1.length; i++) {
481             assertWithMessage("Incorrect field of field 1")
482                     .that(buffer.get())
483                     .isEqualTo(field1Converted[i]);
484         }
485 
486         assertWithMessage("Second field is not list")
487                 .that(buffer.get())
488                 .isEqualTo(StatsEvent.TYPE_LIST);
489 
490         assertWithMessage("Incorrect number of elements in field 2 object")
491                 .that(buffer.get())
492                 .isEqualTo(2);
493 
494         assertWithMessage("Element type of field 2 is not int")
495                 .that(buffer.get())
496                 .isEqualTo(StatsEvent.TYPE_INT);
497 
498         for (int i = 0; i < field2.length; i++) {
499             assertWithMessage("Incorrect field of field 2")
500                     .that(buffer.getInt())
501                     .isEqualTo(field2[i]);
502         }
503 
504         assertWithMessage("Third field is not list")
505                 .that(buffer.get())
506                 .isEqualTo(StatsEvent.TYPE_LIST);
507 
508         assertWithMessage("Incorrect number of elements in field 3 object")
509                 .that(buffer.get())
510                 .isEqualTo(3);
511 
512         assertWithMessage("Element type of field 3 is not long")
513                 .that(buffer.get())
514                 .isEqualTo(StatsEvent.TYPE_LONG);
515 
516         for (int i = 0; i < field3.length; i++) {
517             assertWithMessage("Incorrect field of field 3")
518                     .that(buffer.getLong())
519                     .isEqualTo(field3[i]);
520         }
521 
522         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
523 
524         statsEvent.release();
525     }
526 
527     @Test
testFloatArrayStringArray()528     public void testFloatArrayStringArray() {
529         // Skip test if build version isn't T or greater.
530         if (!SdkLevel.isAtLeastT()) {
531             return;
532         }
533 
534         final int expectedAtomId = 109;
535         final float[] field1 = new float[] {0.21f, 0.13f};
536         final String[] field2 = new String[] {"str1", "str2", "str3"};
537 
538         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
539         final StatsEvent statsEvent = StatsEvent.newBuilder()
540                                         .setAtomId(expectedAtomId)
541                                         .writeFloatArray(field1)
542                                         .writeStringArray(field2)
543                                         .usePooledBuffer()
544                                         .build();
545         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
546 
547         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
548 
549         final ByteBuffer buffer =
550                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
551 
552         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
553                 .that(buffer.get())
554                 .isEqualTo(StatsEvent.TYPE_OBJECT);
555 
556         assertWithMessage("Incorrect number of elements in root object")
557                 .that(buffer.get())
558                 .isEqualTo(4);
559 
560         assertWithMessage("First element is not timestamp")
561                 .that(buffer.get())
562                 .isEqualTo(StatsEvent.TYPE_LONG);
563 
564         assertWithMessage("Incorrect timestamp")
565                 .that(buffer.getLong())
566                 .isIn(Range.closed(minTimestamp, maxTimestamp));
567 
568         assertWithMessage("Second element is not atom id")
569                 .that(buffer.get())
570                 .isEqualTo(StatsEvent.TYPE_INT);
571 
572         assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
573 
574         assertWithMessage("First field is not list")
575                 .that(buffer.get())
576                 .isEqualTo(StatsEvent.TYPE_LIST);
577 
578         assertWithMessage("Incorrect number of elements in field 1 object")
579                 .that(buffer.get())
580                 .isEqualTo(2);
581 
582         assertWithMessage("Element type of field 1 is not float")
583                 .that(buffer.get())
584                 .isEqualTo(StatsEvent.TYPE_FLOAT);
585 
586         for (int i = 0; i < field1.length; i++) {
587            assertWithMessage("Incorrect field of field 1")
588                    .that(buffer.getFloat())
589                    .isEqualTo(field1[i]);
590         }
591 
592         assertWithMessage("Second field is not list")
593                 .that(buffer.get())
594                 .isEqualTo(StatsEvent.TYPE_LIST);
595 
596         assertWithMessage("Incorrect number of elements in field 2 object")
597                 .that(buffer.get())
598                 .isEqualTo(3);
599 
600         assertWithMessage("Element type of field 2 is not string")
601                 .that(buffer.get())
602                 .isEqualTo(StatsEvent.TYPE_STRING);
603 
604         for (int i = 0; i < field2.length; i++) {
605            final String fieldElementActual = getStringFromByteBuffer(buffer);
606            assertWithMessage("Incorrect field of field 2")
607                    .that(fieldElementActual)
608                    .isEqualTo(field2[i]);
609         }
610 
611         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
612 
613         statsEvent.release();
614     }
615 
616     @Test
testSingleAnnotations()617     public void testSingleAnnotations() {
618         final int expectedAtomId = 109;
619         final int field1 = 1;
620         final byte field1AnnotationId = 45;
621         final boolean field1AnnotationValue = false;
622         final boolean field2 = true;
623         final byte field2AnnotationId = 1;
624         final int field2AnnotationValue = 23;
625 
626         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
627         final StatsEvent statsEvent = StatsEvent.newBuilder()
628                 .setAtomId(expectedAtomId)
629                 .writeInt(field1)
630                 .addBooleanAnnotation(field1AnnotationId, field1AnnotationValue)
631                 .writeBoolean(field2)
632                 .addIntAnnotation(field2AnnotationId, field2AnnotationValue)
633                 .usePooledBuffer()
634                 .build();
635         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
636 
637         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
638 
639         final ByteBuffer buffer =
640                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
641 
642         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
643                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
644 
645         assertWithMessage("Incorrect number of elements in root object")
646                 .that(buffer.get()).isEqualTo(4);
647 
648         assertWithMessage("First element is not timestamp")
649                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
650 
651         assertWithMessage("Incorrect timestamp")
652                 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
653 
654         assertWithMessage("Second element is not atom id")
655                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
656 
657         assertWithMessage("Incorrect atom id")
658                 .that(buffer.getInt()).isEqualTo(expectedAtomId);
659 
660         final byte field1Header = buffer.get();
661         final int field1AnnotationValueCount = field1Header >> 4;
662         final byte field1Type = (byte) (field1Header & 0x0F);
663         assertWithMessage("First field is not Int")
664                 .that(field1Type).isEqualTo(StatsEvent.TYPE_INT);
665         assertWithMessage("First field annotation count is wrong")
666                 .that(field1AnnotationValueCount).isEqualTo(1);
667         assertWithMessage("Incorrect field 1")
668                 .that(buffer.getInt()).isEqualTo(field1);
669         assertWithMessage("First field's annotation id is wrong")
670                 .that(buffer.get()).isEqualTo(field1AnnotationId);
671         assertWithMessage("First field's annotation type is wrong")
672                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BOOLEAN);
673         assertWithMessage("First field's annotation value is wrong")
674                 .that(buffer.get()).isEqualTo(field1AnnotationValue ? 1 : 0);
675 
676         final byte field2Header = buffer.get();
677         final int field2AnnotationValueCount = field2Header >> 4;
678         final byte field2Type = (byte) (field2Header & 0x0F);
679         assertWithMessage("Second field is not boolean")
680                 .that(field2Type).isEqualTo(StatsEvent.TYPE_BOOLEAN);
681         assertWithMessage("Second field annotation count is wrong")
682                 .that(field2AnnotationValueCount).isEqualTo(1);
683         assertWithMessage("Incorrect field 2")
684                 .that(buffer.get()).isEqualTo(field2 ? 1 : 0);
685         assertWithMessage("Second field's annotation id is wrong")
686                 .that(buffer.get()).isEqualTo(field2AnnotationId);
687         assertWithMessage("Second field's annotation type is wrong")
688                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
689         assertWithMessage("Second field's annotation value is wrong")
690                 .that(buffer.getInt()).isEqualTo(field2AnnotationValue);
691 
692         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
693 
694         statsEvent.release();
695     }
696 
697     @Test
testArrayFieldAnnotations()698     public void testArrayFieldAnnotations() {
699         // Skip test if build version isn't T or greater.
700         if (!SdkLevel.isAtLeastT()) {
701             return;
702         }
703 
704         final int expectedAtomId = 109;
705         final int[] field1 = new int[] {4, 11};
706         final byte boolAnnotationId = 45;
707         final boolean boolAnnotationValue = false;
708         final byte intAnnotationId = 1;
709         final int intAnnotationValue = 23;
710 
711         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
712         final StatsEvent statsEvent = StatsEvent.newBuilder()
713                                         .setAtomId(expectedAtomId)
714                                         .writeIntArray(field1)
715                                         .addBooleanAnnotation(boolAnnotationId, boolAnnotationValue)
716                                         .addIntAnnotation(intAnnotationId, intAnnotationValue)
717                                         .usePooledBuffer()
718                                         .build();
719         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
720 
721         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
722 
723         final ByteBuffer buffer =
724                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
725 
726         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
727                 .that(buffer.get())
728                 .isEqualTo(StatsEvent.TYPE_OBJECT);
729 
730         assertWithMessage("Incorrect number of elements in root object")
731                 .that(buffer.get())
732                 .isEqualTo(3);
733 
734         assertWithMessage("First element is not timestamp")
735                 .that(buffer.get())
736                 .isEqualTo(StatsEvent.TYPE_LONG);
737 
738         assertWithMessage("Incorrect timestamp")
739                 .that(buffer.getLong())
740                 .isIn(Range.closed(minTimestamp, maxTimestamp));
741 
742         assertWithMessage("Second element is not atom id")
743                 .that(buffer.get())
744                 .isEqualTo(StatsEvent.TYPE_INT);
745 
746         assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
747 
748         final byte field1Header = buffer.get();
749         final int field1AnnotationValueCount = field1Header >> 4;
750         final byte field1Type = (byte) (field1Header & 0x0F);
751         assertWithMessage("First field is not list")
752                 .that(field1Type).isEqualTo(StatsEvent.TYPE_LIST);
753 
754         assertWithMessage("First field annotation count is wrong")
755                 .that(field1AnnotationValueCount)
756                 .isEqualTo(2);
757 
758         assertWithMessage("Incorrect number of elements in field 1 object")
759                 .that(buffer.get())
760                 .isEqualTo(2);
761 
762         assertWithMessage("Element type of field 1 is not int")
763                 .that(buffer.get())
764                 .isEqualTo(StatsEvent.TYPE_INT);
765 
766         for (int i = 0; i < field1.length; i++) {
767            assertWithMessage("Incorrect field of field 1")
768                    .that(buffer.getInt()).isEqualTo(field1[i]);
769         }
770 
771         assertWithMessage("Field 1's first annotation id is wrong")
772                 .that(buffer.get())
773                 .isEqualTo(boolAnnotationId);
774         assertWithMessage("Field 1's first annotation type is wrong")
775                 .that(buffer.get())
776                 .isEqualTo(StatsEvent.TYPE_BOOLEAN);
777         assertWithMessage("Field 1's first annotation value is wrong")
778                 .that(buffer.get())
779                 .isEqualTo(boolAnnotationValue ? 1 : 0);
780 
781         assertWithMessage("Field 1's second annotation id is wrong")
782                 .that(buffer.get())
783                 .isEqualTo(intAnnotationId);
784         assertWithMessage("Field 1's second annotation type is wrong")
785                 .that(buffer.get())
786                 .isEqualTo(StatsEvent.TYPE_INT);
787         assertWithMessage("Field 1's second annotation value is wrong")
788                 .that(buffer.getInt())
789                 .isEqualTo(intAnnotationValue);
790 
791         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
792 
793         statsEvent.release();
794     }
795 
796     @Test
testAtomIdAnnotations()797     public void testAtomIdAnnotations() {
798         final int expectedAtomId = 109;
799         final byte atomAnnotationId = 84;
800         final int atomAnnotationValue = 9;
801         final int field1 = 1;
802         final byte field1AnnotationId = 45;
803         final boolean field1AnnotationValue = false;
804         final boolean field2 = true;
805         final byte field2AnnotationId = 1;
806         final int field2AnnotationValue = 23;
807 
808         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
809         final StatsEvent statsEvent = StatsEvent.newBuilder()
810                 .setAtomId(expectedAtomId)
811                 .addIntAnnotation(atomAnnotationId, atomAnnotationValue)
812                 .writeInt(field1)
813                 .addBooleanAnnotation(field1AnnotationId, field1AnnotationValue)
814                 .writeBoolean(field2)
815                 .addIntAnnotation(field2AnnotationId, field2AnnotationValue)
816                 .usePooledBuffer()
817                 .build();
818         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
819 
820         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
821 
822         final ByteBuffer buffer =
823                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
824 
825         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
826                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
827 
828         assertWithMessage("Incorrect number of elements in root object")
829                 .that(buffer.get()).isEqualTo(4);
830 
831         assertWithMessage("First element is not timestamp")
832                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
833 
834         assertWithMessage("Incorrect timestamp")
835                 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
836 
837         final byte atomIdHeader = buffer.get();
838         final int atomIdAnnotationValueCount = atomIdHeader >> 4;
839         final byte atomIdValueType = (byte) (atomIdHeader & 0x0F);
840         assertWithMessage("Second element is not atom id")
841                 .that(atomIdValueType).isEqualTo(StatsEvent.TYPE_INT);
842         assertWithMessage("Atom id annotation count is wrong")
843                 .that(atomIdAnnotationValueCount).isEqualTo(1);
844         assertWithMessage("Incorrect atom id")
845                 .that(buffer.getInt()).isEqualTo(expectedAtomId);
846         assertWithMessage("Atom id's annotation id is wrong")
847                 .that(buffer.get()).isEqualTo(atomAnnotationId);
848         assertWithMessage("Atom id's annotation type is wrong")
849                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
850         assertWithMessage("Atom id's annotation value is wrong")
851                 .that(buffer.getInt()).isEqualTo(atomAnnotationValue);
852 
853         final byte field1Header = buffer.get();
854         final int field1AnnotationValueCount = field1Header >> 4;
855         final byte field1Type = (byte) (field1Header & 0x0F);
856         assertWithMessage("First field is not Int")
857                 .that(field1Type).isEqualTo(StatsEvent.TYPE_INT);
858         assertWithMessage("First field annotation count is wrong")
859                 .that(field1AnnotationValueCount).isEqualTo(1);
860         assertWithMessage("Incorrect field 1")
861                 .that(buffer.getInt()).isEqualTo(field1);
862         assertWithMessage("First field's annotation id is wrong")
863                 .that(buffer.get()).isEqualTo(field1AnnotationId);
864         assertWithMessage("First field's annotation type is wrong")
865                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BOOLEAN);
866         assertWithMessage("First field's annotation value is wrong")
867                 .that(buffer.get()).isEqualTo(field1AnnotationValue ? 1 : 0);
868 
869         final byte field2Header = buffer.get();
870         final int field2AnnotationValueCount = field2Header >> 4;
871         final byte field2Type = (byte) (field2Header & 0x0F);
872         assertWithMessage("Second field is not boolean")
873                 .that(field2Type).isEqualTo(StatsEvent.TYPE_BOOLEAN);
874         assertWithMessage("Second field annotation count is wrong")
875                 .that(field2AnnotationValueCount).isEqualTo(1);
876         assertWithMessage("Incorrect field 2")
877                 .that(buffer.get()).isEqualTo(field2 ? 1 : 0);
878         assertWithMessage("Second field's annotation id is wrong")
879                 .that(buffer.get()).isEqualTo(field2AnnotationId);
880         assertWithMessage("Second field's annotation type is wrong")
881                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
882         assertWithMessage("Second field's annotation value is wrong")
883                 .that(buffer.getInt()).isEqualTo(field2AnnotationValue);
884 
885         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
886 
887         statsEvent.release();
888     }
889 
890     @Test
testSetAtomIdNotCalledImmediately()891     public void testSetAtomIdNotCalledImmediately() {
892         final int expectedAtomId = 109;
893         final int field1 = 25;
894         final boolean field2 = true;
895 
896         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
897         final StatsEvent statsEvent = StatsEvent.newBuilder()
898                 .writeInt(field1)
899                 .setAtomId(expectedAtomId)
900                 .writeBoolean(field2)
901                 .usePooledBuffer()
902                 .build();
903         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
904 
905         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
906 
907         final ByteBuffer buffer =
908                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
909 
910         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
911                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
912 
913         assertWithMessage("Incorrect number of elements in root object")
914                 .that(buffer.get()).isEqualTo(3);
915 
916         assertWithMessage("First element is not timestamp")
917                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
918 
919         assertWithMessage("Incorrect timestamp")
920                 .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
921 
922         assertWithMessage("Second element is not atom id")
923                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
924 
925         assertWithMessage("Incorrect atom id")
926                 .that(buffer.getInt()).isEqualTo(expectedAtomId);
927 
928         assertWithMessage("Third element is not errors type")
929                 .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS);
930 
931         final int errorMask = buffer.getInt();
932 
933         assertWithMessage("ERROR_ATOM_ID_INVALID_POSITION should be the only error in the mask")
934                 .that(errorMask).isEqualTo(StatsEvent.ERROR_ATOM_ID_INVALID_POSITION);
935 
936         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
937 
938         statsEvent.release();
939     }
940 
941     @Test
testLargePulledEvent()942     public void testLargePulledEvent() {
943         final int expectedAtomId = 10_020;
944         byte[] field1 = new byte[10 * 1024];
945         new Random().nextBytes(field1);
946 
947         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
948         final StatsEvent statsEvent =
949                 StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build();
950         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
951 
952         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
953 
954         final ByteBuffer buffer =
955                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
956 
957         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
958                 .that(buffer.get())
959                 .isEqualTo(StatsEvent.TYPE_OBJECT);
960 
961         assertWithMessage("Incorrect number of elements in root object")
962                 .that(buffer.get())
963                 .isEqualTo(3);
964 
965         assertWithMessage("First element is not timestamp")
966                 .that(buffer.get())
967                 .isEqualTo(StatsEvent.TYPE_LONG);
968 
969         assertWithMessage("Incorrect timestamp")
970                 .that(buffer.getLong())
971                 .isIn(Range.closed(minTimestamp, maxTimestamp));
972 
973         assertWithMessage("Second element is not atom id")
974                 .that(buffer.get())
975                 .isEqualTo(StatsEvent.TYPE_INT);
976 
977         assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
978 
979         assertWithMessage("Third element is not byte array")
980                 .that(buffer.get())
981                 .isEqualTo(StatsEvent.TYPE_BYTE_ARRAY);
982 
983         final byte[] field1Actual = getByteArrayFromByteBuffer(buffer);
984         assertWithMessage("Incorrect field 1").that(field1Actual).isEqualTo(field1);
985 
986         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
987 
988         statsEvent.release();
989     }
990 
991     @Test
testPulledEventOverflow()992     public void testPulledEventOverflow() {
993         final int expectedAtomId = 10_020;
994         byte[] field1 = new byte[50 * 1024];
995         new Random().nextBytes(field1);
996 
997         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
998         final StatsEvent statsEvent =
999                 StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build();
1000         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
1001 
1002         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
1003 
1004         final ByteBuffer buffer =
1005                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
1006 
1007         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
1008                 .that(buffer.get())
1009                 .isEqualTo(StatsEvent.TYPE_OBJECT);
1010 
1011         assertWithMessage("Incorrect number of elements in root object")
1012                 .that(buffer.get())
1013                 .isEqualTo(3);
1014 
1015         assertWithMessage("First element is not timestamp")
1016                 .that(buffer.get())
1017                 .isEqualTo(StatsEvent.TYPE_LONG);
1018 
1019         assertWithMessage("Incorrect timestamp")
1020                 .that(buffer.getLong())
1021                 .isIn(Range.closed(minTimestamp, maxTimestamp));
1022 
1023         assertWithMessage("Second element is not atom id")
1024                 .that(buffer.get())
1025                 .isEqualTo(StatsEvent.TYPE_INT);
1026 
1027         assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
1028 
1029         assertWithMessage("Third element is not errors type")
1030                 .that(buffer.get())
1031                 .isEqualTo(StatsEvent.TYPE_ERRORS);
1032 
1033         final int errorMask = buffer.getInt();
1034 
1035         assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask")
1036                 .that(errorMask)
1037                 .isEqualTo(StatsEvent.ERROR_OVERFLOW);
1038 
1039         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
1040 
1041         statsEvent.release();
1042     }
1043 
1044     @Test
testPushedEventOverflow()1045     public void testPushedEventOverflow() {
1046         final int expectedAtomId = 10_020;
1047         byte[] field1 = new byte[10 * 1024];
1048         new Random().nextBytes(field1);
1049 
1050         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
1051         final StatsEvent statsEvent = StatsEvent.newBuilder()
1052                                               .setAtomId(expectedAtomId)
1053                                               .writeByteArray(field1)
1054                                               .usePooledBuffer()
1055                                               .build();
1056         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
1057 
1058         assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
1059 
1060         final ByteBuffer buffer =
1061                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
1062 
1063         assertWithMessage("Root element in buffer is not TYPE_OBJECT")
1064                 .that(buffer.get())
1065                 .isEqualTo(StatsEvent.TYPE_OBJECT);
1066 
1067         assertWithMessage("Incorrect number of elements in root object")
1068                 .that(buffer.get())
1069                 .isEqualTo(3);
1070 
1071         assertWithMessage("First element is not timestamp")
1072                 .that(buffer.get())
1073                 .isEqualTo(StatsEvent.TYPE_LONG);
1074 
1075         assertWithMessage("Incorrect timestamp")
1076                 .that(buffer.getLong())
1077                 .isIn(Range.closed(minTimestamp, maxTimestamp));
1078 
1079         assertWithMessage("Second element is not atom id")
1080                 .that(buffer.get())
1081                 .isEqualTo(StatsEvent.TYPE_INT);
1082 
1083         assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
1084 
1085         assertWithMessage("Third element is not errors type")
1086                 .that(buffer.get())
1087                 .isEqualTo(StatsEvent.TYPE_ERRORS);
1088 
1089         final int errorMask = buffer.getInt();
1090 
1091         assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask")
1092                 .that(errorMask)
1093                 .isEqualTo(StatsEvent.ERROR_OVERFLOW);
1094 
1095         assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
1096 
1097         statsEvent.release();
1098     }
1099 
getByteArrayFromByteBuffer(final ByteBuffer buffer)1100     private static byte[] getByteArrayFromByteBuffer(final ByteBuffer buffer) {
1101         final int numBytes = buffer.getInt();
1102         byte[] bytes = new byte[numBytes];
1103         buffer.get(bytes);
1104         return bytes;
1105     }
1106 
getStringFromByteBuffer(final ByteBuffer buffer)1107     private static String getStringFromByteBuffer(final ByteBuffer buffer) {
1108         final byte[] bytes = getByteArrayFromByteBuffer(buffer);
1109         return new String(bytes, UTF_8);
1110     }
1111 }
1112