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 java.nio.charset.StandardCharsets.UTF_8; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 import android.os.SystemClock; 25 26 import com.android.internal.annotations.GuardedBy; 27 import com.android.internal.annotations.VisibleForTesting; 28 29 import java.util.Arrays; 30 31 /** 32 * StatsEvent builds and stores the buffer sent over the statsd socket. 33 * This class defines and encapsulates the socket protocol. 34 * 35 * <p>Usage:</p> 36 * <pre> 37 * // Pushed event 38 * StatsEvent statsEvent = StatsEvent.newBuilder() 39 * .setAtomId(atomId) 40 * .writeBoolean(false) 41 * .writeString("annotated String field") 42 * .addBooleanAnnotation(annotationId, true) 43 * .usePooledBuffer() 44 * .build(); 45 * StatsLog.write(statsEvent); 46 * 47 * // Pulled event 48 * StatsEvent statsEvent = StatsEvent.newBuilder() 49 * .setAtomId(atomId) 50 * .writeBoolean(false) 51 * .writeString("annotated String field") 52 * .addBooleanAnnotation(annotationId, true) 53 * .build(); 54 * </pre> 55 * @hide 56 **/ 57 @SystemApi 58 public final class StatsEvent { 59 // Type Ids. 60 /** 61 * @hide 62 **/ 63 @VisibleForTesting 64 public static final byte TYPE_INT = 0x00; 65 66 /** 67 * @hide 68 **/ 69 @VisibleForTesting 70 public static final byte TYPE_LONG = 0x01; 71 72 /** 73 * @hide 74 **/ 75 @VisibleForTesting 76 public static final byte TYPE_STRING = 0x02; 77 78 /** 79 * @hide 80 **/ 81 @VisibleForTesting 82 public static final byte TYPE_LIST = 0x03; 83 84 /** 85 * @hide 86 **/ 87 @VisibleForTesting 88 public static final byte TYPE_FLOAT = 0x04; 89 90 /** 91 * @hide 92 **/ 93 @VisibleForTesting 94 public static final byte TYPE_BOOLEAN = 0x05; 95 96 /** 97 * @hide 98 **/ 99 @VisibleForTesting 100 public static final byte TYPE_BYTE_ARRAY = 0x06; 101 102 /** 103 * @hide 104 **/ 105 @VisibleForTesting 106 public static final byte TYPE_OBJECT = 0x07; 107 108 /** 109 * @hide 110 **/ 111 @VisibleForTesting 112 public static final byte TYPE_KEY_VALUE_PAIRS = 0x08; 113 114 /** 115 * @hide 116 **/ 117 @VisibleForTesting 118 public static final byte TYPE_ATTRIBUTION_CHAIN = 0x09; 119 120 /** 121 * @hide 122 **/ 123 @VisibleForTesting 124 public static final byte TYPE_ERRORS = 0x0F; 125 126 // Error flags. 127 /** 128 * @hide 129 **/ 130 @VisibleForTesting 131 public static final int ERROR_NO_TIMESTAMP = 0x1; 132 133 /** 134 * @hide 135 **/ 136 @VisibleForTesting 137 public static final int ERROR_NO_ATOM_ID = 0x2; 138 139 /** 140 * @hide 141 **/ 142 @VisibleForTesting 143 public static final int ERROR_OVERFLOW = 0x4; 144 145 /** 146 * @hide 147 **/ 148 @VisibleForTesting 149 public static final int ERROR_ATTRIBUTION_CHAIN_TOO_LONG = 0x8; 150 151 /** 152 * @hide 153 **/ 154 @VisibleForTesting 155 public static final int ERROR_TOO_MANY_KEY_VALUE_PAIRS = 0x10; 156 157 /** 158 * @hide 159 **/ 160 @VisibleForTesting 161 public static final int ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD = 0x20; 162 163 /** 164 * @hide 165 **/ 166 @VisibleForTesting 167 public static final int ERROR_INVALID_ANNOTATION_ID = 0x40; 168 169 /** 170 * @hide 171 **/ 172 @VisibleForTesting 173 public static final int ERROR_ANNOTATION_ID_TOO_LARGE = 0x80; 174 175 /** 176 * @hide 177 **/ 178 @VisibleForTesting 179 public static final int ERROR_TOO_MANY_ANNOTATIONS = 0x100; 180 181 /** 182 * @hide 183 **/ 184 @VisibleForTesting 185 public static final int ERROR_TOO_MANY_FIELDS = 0x200; 186 187 /** 188 * @hide 189 **/ 190 @VisibleForTesting 191 public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x1000; 192 193 /** 194 * @hide 195 **/ 196 @VisibleForTesting 197 public static final int ERROR_ATOM_ID_INVALID_POSITION = 0x2000; 198 199 // Size limits. 200 201 /** 202 * @hide 203 **/ 204 @VisibleForTesting 205 public static final int MAX_ANNOTATION_COUNT = 15; 206 207 /** 208 * @hide 209 **/ 210 @VisibleForTesting 211 public static final int MAX_ATTRIBUTION_NODES = 127; 212 213 /** 214 * @hide 215 **/ 216 @VisibleForTesting 217 public static final int MAX_NUM_ELEMENTS = 127; 218 219 /** 220 * @hide 221 **/ 222 @VisibleForTesting 223 public static final int MAX_KEY_VALUE_PAIRS = 127; 224 225 private static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068; 226 227 // Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag. 228 // See android_util_StatsLog.cpp. 229 private static final int MAX_PUSH_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4; 230 231 private static final int MAX_PULL_PAYLOAD_SIZE = 50 * 1024; // 50 KB 232 233 private final int mAtomId; 234 private final byte[] mPayload; 235 private Buffer mBuffer; 236 private final int mNumBytes; 237 StatsEvent(final int atomId, @Nullable final Buffer buffer, @NonNull final byte[] payload, final int numBytes)238 private StatsEvent(final int atomId, @Nullable final Buffer buffer, 239 @NonNull final byte[] payload, final int numBytes) { 240 mAtomId = atomId; 241 mBuffer = buffer; 242 mPayload = payload; 243 mNumBytes = numBytes; 244 } 245 246 /** 247 * Returns a new StatsEvent.Builder for building StatsEvent object. 248 **/ 249 @NonNull newBuilder()250 public static StatsEvent.Builder newBuilder() { 251 return new StatsEvent.Builder(Buffer.obtain()); 252 } 253 254 /** 255 * Get the atom Id of the atom encoded in this StatsEvent object. 256 * 257 * @hide 258 **/ getAtomId()259 public int getAtomId() { 260 return mAtomId; 261 } 262 263 /** 264 * Get the byte array that contains the encoded payload that can be sent to statsd. 265 * 266 * @hide 267 **/ 268 @NonNull getBytes()269 public byte[] getBytes() { 270 return mPayload; 271 } 272 273 /** 274 * Get the number of bytes used to encode the StatsEvent payload. 275 * 276 * @hide 277 **/ getNumBytes()278 public int getNumBytes() { 279 return mNumBytes; 280 } 281 282 /** 283 * Recycle resources used by this StatsEvent object. 284 * No actions should be taken on this StatsEvent after release() is called. 285 * 286 * @hide 287 **/ release()288 public void release() { 289 if (mBuffer != null) { 290 mBuffer.release(); 291 mBuffer = null; 292 } 293 } 294 295 /** 296 * Builder for constructing a StatsEvent object. 297 * 298 * <p>This class defines and encapsulates the socket encoding for the buffer. 299 * The write methods must be called in the same order as the order of fields in the 300 * atom definition.</p> 301 * 302 * <p>setAtomId() can be called anytime before build().</p> 303 * 304 * <p>Example:</p> 305 * <pre> 306 * // Atom definition. 307 * message MyAtom { 308 * optional int32 field1 = 1; 309 * optional int64 field2 = 2; 310 * optional string field3 = 3 [(annotation1) = true]; 311 * } 312 * 313 * // StatsEvent construction for pushed event. 314 * StatsEvent.newBuilder() 315 * StatsEvent statsEvent = StatsEvent.newBuilder() 316 * .setAtomId(atomId) 317 * .writeInt(3) // field1 318 * .writeLong(8L) // field2 319 * .writeString("foo") // field 3 320 * .addBooleanAnnotation(annotation1Id, true) 321 * .usePooledBuffer() 322 * .build(); 323 * 324 * // StatsEvent construction for pulled event. 325 * StatsEvent.newBuilder() 326 * StatsEvent statsEvent = StatsEvent.newBuilder() 327 * .setAtomId(atomId) 328 * .writeInt(3) // field1 329 * .writeLong(8L) // field2 330 * .writeString("foo") // field 3 331 * .addBooleanAnnotation(annotation1Id, true) 332 * .build(); 333 * </pre> 334 **/ 335 public static final class Builder { 336 // Fixed positions. 337 private static final int POS_NUM_ELEMENTS = 1; 338 private static final int POS_TIMESTAMP_NS = POS_NUM_ELEMENTS + Byte.BYTES; 339 private static final int POS_ATOM_ID = POS_TIMESTAMP_NS + Byte.BYTES + Long.BYTES; 340 341 private final Buffer mBuffer; 342 private long mTimestampNs; 343 private int mAtomId; 344 private byte mCurrentAnnotationCount; 345 private int mPos; 346 private int mPosLastField; 347 private byte mLastType; 348 private int mNumElements; 349 private int mErrorMask; 350 private boolean mUsePooledBuffer = false; 351 Builder(final Buffer buffer)352 private Builder(final Buffer buffer) { 353 mBuffer = buffer; 354 mCurrentAnnotationCount = 0; 355 mAtomId = 0; 356 mTimestampNs = SystemClock.elapsedRealtimeNanos(); 357 mNumElements = 0; 358 359 // Set mPos to 0 for writing TYPE_OBJECT at 0th position. 360 mPos = 0; 361 writeTypeId(TYPE_OBJECT); 362 363 // Write timestamp. 364 mPos = POS_TIMESTAMP_NS; 365 writeLong(mTimestampNs); 366 } 367 368 /** 369 * Sets the atom id for this StatsEvent. 370 * 371 * This should be called immediately after StatsEvent.newBuilder() 372 * and should only be called once. 373 * Not calling setAtomId will result in ERROR_NO_ATOM_ID. 374 * Calling setAtomId out of order will result in ERROR_ATOM_ID_INVALID_POSITION. 375 **/ 376 @NonNull setAtomId(final int atomId)377 public Builder setAtomId(final int atomId) { 378 if (0 == mAtomId) { 379 mAtomId = atomId; 380 381 if (1 == mNumElements) { // Only timestamp is written so far. 382 writeInt(atomId); 383 } else { 384 // setAtomId called out of order. 385 mErrorMask |= ERROR_ATOM_ID_INVALID_POSITION; 386 } 387 } 388 389 return this; 390 } 391 392 /** 393 * Write a boolean field to this StatsEvent. 394 **/ 395 @NonNull writeBoolean(final boolean value)396 public Builder writeBoolean(final boolean value) { 397 // Write boolean typeId byte followed by boolean byte representation. 398 writeTypeId(TYPE_BOOLEAN); 399 mPos += mBuffer.putBoolean(mPos, value); 400 mNumElements++; 401 return this; 402 } 403 404 /** 405 * Write an integer field to this StatsEvent. 406 **/ 407 @NonNull writeInt(final int value)408 public Builder writeInt(final int value) { 409 // Write integer typeId byte followed by 4-byte representation of value. 410 writeTypeId(TYPE_INT); 411 mPos += mBuffer.putInt(mPos, value); 412 mNumElements++; 413 return this; 414 } 415 416 /** 417 * Write a long field to this StatsEvent. 418 **/ 419 @NonNull writeLong(final long value)420 public Builder writeLong(final long value) { 421 // Write long typeId byte followed by 8-byte representation of value. 422 writeTypeId(TYPE_LONG); 423 mPos += mBuffer.putLong(mPos, value); 424 mNumElements++; 425 return this; 426 } 427 428 /** 429 * Write a float field to this StatsEvent. 430 **/ 431 @NonNull writeFloat(final float value)432 public Builder writeFloat(final float value) { 433 // Write float typeId byte followed by 4-byte representation of value. 434 writeTypeId(TYPE_FLOAT); 435 mPos += mBuffer.putFloat(mPos, value); 436 mNumElements++; 437 return this; 438 } 439 440 /** 441 * Write a String field to this StatsEvent. 442 **/ 443 @NonNull writeString(@onNull final String value)444 public Builder writeString(@NonNull final String value) { 445 // Write String typeId byte, followed by 4-byte representation of number of bytes 446 // in the UTF-8 encoding, followed by the actual UTF-8 byte encoding of value. 447 final byte[] valueBytes = stringToBytes(value); 448 writeByteArray(valueBytes, TYPE_STRING); 449 return this; 450 } 451 452 /** 453 * Write a byte array field to this StatsEvent. 454 **/ 455 @NonNull writeByteArray(@onNull final byte[] value)456 public Builder writeByteArray(@NonNull final byte[] value) { 457 // Write byte array typeId byte, followed by 4-byte representation of number of bytes 458 // in value, followed by the actual byte array. 459 writeByteArray(value, TYPE_BYTE_ARRAY); 460 return this; 461 } 462 writeByteArray(@onNull final byte[] value, final byte typeId)463 private void writeByteArray(@NonNull final byte[] value, final byte typeId) { 464 writeTypeId(typeId); 465 final int numBytes = value.length; 466 mPos += mBuffer.putInt(mPos, numBytes); 467 mPos += mBuffer.putByteArray(mPos, value); 468 mNumElements++; 469 } 470 471 /** 472 * Write an attribution chain field to this StatsEvent. 473 * 474 * The sizes of uids and tags must be equal. The AttributionNode at position i is 475 * made up of uids[i] and tags[i]. 476 * 477 * @param uids array of uids in the attribution nodes. 478 * @param tags array of tags in the attribution nodes. 479 **/ 480 @NonNull writeAttributionChain( @onNull final int[] uids, @NonNull final String[] tags)481 public Builder writeAttributionChain( 482 @NonNull final int[] uids, @NonNull final String[] tags) { 483 final byte numUids = (byte) uids.length; 484 final byte numTags = (byte) tags.length; 485 486 if (numUids != numTags) { 487 mErrorMask |= ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL; 488 } else if (numUids > MAX_ATTRIBUTION_NODES) { 489 mErrorMask |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG; 490 } else { 491 // Write attribution chain typeId byte, followed by 1-byte representation of 492 // number of attribution nodes, followed by encoding of each attribution node. 493 writeTypeId(TYPE_ATTRIBUTION_CHAIN); 494 mPos += mBuffer.putByte(mPos, numUids); 495 for (int i = 0; i < numUids; i++) { 496 // Each uid is encoded as 4-byte representation of its int value. 497 mPos += mBuffer.putInt(mPos, uids[i]); 498 499 // Each tag is encoded as 4-byte representation of number of bytes in its 500 // UTF-8 encoding, followed by the actual UTF-8 bytes. 501 final byte[] tagBytes = stringToBytes(tags[i]); 502 mPos += mBuffer.putInt(mPos, tagBytes.length); 503 mPos += mBuffer.putByteArray(mPos, tagBytes); 504 } 505 mNumElements++; 506 } 507 return this; 508 } 509 510 /** 511 * Write KeyValuePairsAtom entries to this StatsEvent. 512 * 513 * @param intMap Integer key-value pairs. 514 * @param longMap Long key-value pairs. 515 * @param stringMap String key-value pairs. 516 * @param floatMap Float key-value pairs. 517 **/ 518 @NonNull writeKeyValuePairs( @ullable final SparseIntArray intMap, @Nullable final SparseLongArray longMap, @Nullable final SparseArray<String> stringMap, @Nullable final SparseArray<Float> floatMap)519 public Builder writeKeyValuePairs( 520 @Nullable final SparseIntArray intMap, 521 @Nullable final SparseLongArray longMap, 522 @Nullable final SparseArray<String> stringMap, 523 @Nullable final SparseArray<Float> floatMap) { 524 final int intMapSize = null == intMap ? 0 : intMap.size(); 525 final int longMapSize = null == longMap ? 0 : longMap.size(); 526 final int stringMapSize = null == stringMap ? 0 : stringMap.size(); 527 final int floatMapSize = null == floatMap ? 0 : floatMap.size(); 528 final int totalCount = intMapSize + longMapSize + stringMapSize + floatMapSize; 529 530 if (totalCount > MAX_KEY_VALUE_PAIRS) { 531 mErrorMask |= ERROR_TOO_MANY_KEY_VALUE_PAIRS; 532 } else { 533 writeTypeId(TYPE_KEY_VALUE_PAIRS); 534 mPos += mBuffer.putByte(mPos, (byte) totalCount); 535 536 for (int i = 0; i < intMapSize; i++) { 537 final int key = intMap.keyAt(i); 538 final int value = intMap.valueAt(i); 539 mPos += mBuffer.putInt(mPos, key); 540 writeTypeId(TYPE_INT); 541 mPos += mBuffer.putInt(mPos, value); 542 } 543 544 for (int i = 0; i < longMapSize; i++) { 545 final int key = longMap.keyAt(i); 546 final long value = longMap.valueAt(i); 547 mPos += mBuffer.putInt(mPos, key); 548 writeTypeId(TYPE_LONG); 549 mPos += mBuffer.putLong(mPos, value); 550 } 551 552 for (int i = 0; i < stringMapSize; i++) { 553 final int key = stringMap.keyAt(i); 554 final String value = stringMap.valueAt(i); 555 mPos += mBuffer.putInt(mPos, key); 556 writeTypeId(TYPE_STRING); 557 final byte[] valueBytes = stringToBytes(value); 558 mPos += mBuffer.putInt(mPos, valueBytes.length); 559 mPos += mBuffer.putByteArray(mPos, valueBytes); 560 } 561 562 for (int i = 0; i < floatMapSize; i++) { 563 final int key = floatMap.keyAt(i); 564 final float value = floatMap.valueAt(i); 565 mPos += mBuffer.putInt(mPos, key); 566 writeTypeId(TYPE_FLOAT); 567 mPos += mBuffer.putFloat(mPos, value); 568 } 569 570 mNumElements++; 571 } 572 573 return this; 574 } 575 576 /** 577 * Write a boolean annotation for the last field written. 578 **/ 579 @NonNull addBooleanAnnotation( final byte annotationId, final boolean value)580 public Builder addBooleanAnnotation( 581 final byte annotationId, final boolean value) { 582 // Ensure there's a field written to annotate. 583 if (mNumElements < 2) { 584 mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD; 585 } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) { 586 mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS; 587 } else { 588 mPos += mBuffer.putByte(mPos, annotationId); 589 mPos += mBuffer.putByte(mPos, TYPE_BOOLEAN); 590 mPos += mBuffer.putBoolean(mPos, value); 591 mCurrentAnnotationCount++; 592 writeAnnotationCount(); 593 } 594 595 return this; 596 } 597 598 /** 599 * Write an integer annotation for the last field written. 600 **/ 601 @NonNull addIntAnnotation(final byte annotationId, final int value)602 public Builder addIntAnnotation(final byte annotationId, final int value) { 603 if (mNumElements < 2) { 604 mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD; 605 } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) { 606 mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS; 607 } else { 608 mPos += mBuffer.putByte(mPos, annotationId); 609 mPos += mBuffer.putByte(mPos, TYPE_INT); 610 mPos += mBuffer.putInt(mPos, value); 611 mCurrentAnnotationCount++; 612 writeAnnotationCount(); 613 } 614 615 return this; 616 } 617 618 /** 619 * Indicates to reuse Buffer's byte array as the underlying payload in StatsEvent. 620 * This should be called for pushed events to reduce memory allocations and garbage 621 * collections. 622 **/ 623 @NonNull usePooledBuffer()624 public Builder usePooledBuffer() { 625 mUsePooledBuffer = true; 626 mBuffer.setMaxSize(MAX_PUSH_PAYLOAD_SIZE, mPos); 627 return this; 628 } 629 630 /** 631 * Builds a StatsEvent object with values entered in this Builder. 632 **/ 633 @NonNull build()634 public StatsEvent build() { 635 if (0L == mTimestampNs) { 636 mErrorMask |= ERROR_NO_TIMESTAMP; 637 } 638 if (0 == mAtomId) { 639 mErrorMask |= ERROR_NO_ATOM_ID; 640 } 641 if (mBuffer.hasOverflowed()) { 642 mErrorMask |= ERROR_OVERFLOW; 643 } 644 if (mNumElements > MAX_NUM_ELEMENTS) { 645 mErrorMask |= ERROR_TOO_MANY_FIELDS; 646 } 647 648 if (0 == mErrorMask) { 649 mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements); 650 } else { 651 // Write atom id and error mask. Overwrite any annotations for atom Id. 652 mPos = POS_ATOM_ID; 653 mPos += mBuffer.putByte(mPos, TYPE_INT); 654 mPos += mBuffer.putInt(mPos, mAtomId); 655 mPos += mBuffer.putByte(mPos, TYPE_ERRORS); 656 mPos += mBuffer.putInt(mPos, mErrorMask); 657 mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3); 658 } 659 660 final int size = mPos; 661 662 if (mUsePooledBuffer) { 663 return new StatsEvent(mAtomId, mBuffer, mBuffer.getBytes(), size); 664 } else { 665 // Create a copy of the buffer with the required number of bytes. 666 final byte[] payload = new byte[size]; 667 System.arraycopy(mBuffer.getBytes(), 0, payload, 0, size); 668 669 // Return Buffer instance to the pool. 670 mBuffer.release(); 671 672 return new StatsEvent(mAtomId, null, payload, size); 673 } 674 } 675 writeTypeId(final byte typeId)676 private void writeTypeId(final byte typeId) { 677 mPosLastField = mPos; 678 mLastType = typeId; 679 mCurrentAnnotationCount = 0; 680 final byte encodedId = (byte) (typeId & 0x0F); 681 mPos += mBuffer.putByte(mPos, encodedId); 682 } 683 writeAnnotationCount()684 private void writeAnnotationCount() { 685 // Use first 4 bits for annotation count and last 4 bits for typeId. 686 final byte encodedId = (byte) ((mCurrentAnnotationCount << 4) | (mLastType & 0x0F)); 687 mBuffer.putByte(mPosLastField, encodedId); 688 } 689 690 @NonNull stringToBytes(@ullable final String value)691 private static byte[] stringToBytes(@Nullable final String value) { 692 return (null == value ? "" : value).getBytes(UTF_8); 693 } 694 } 695 696 private static final class Buffer { 697 private static Object sLock = new Object(); 698 699 @GuardedBy("sLock") 700 private static Buffer sPool; 701 702 private byte[] mBytes = new byte[MAX_PUSH_PAYLOAD_SIZE]; 703 private boolean mOverflow = false; 704 private int mMaxSize = MAX_PULL_PAYLOAD_SIZE; 705 706 @NonNull obtain()707 private static Buffer obtain() { 708 final Buffer buffer; 709 synchronized (sLock) { 710 buffer = null == sPool ? new Buffer() : sPool; 711 sPool = null; 712 } 713 buffer.reset(); 714 return buffer; 715 } 716 Buffer()717 private Buffer() { 718 } 719 720 @NonNull getBytes()721 private byte[] getBytes() { 722 return mBytes; 723 } 724 release()725 private void release() { 726 // Recycle this Buffer if its size is MAX_PUSH_PAYLOAD_SIZE or under. 727 if (mBytes.length <= MAX_PUSH_PAYLOAD_SIZE) { 728 synchronized (sLock) { 729 if (null == sPool) { 730 sPool = this; 731 } 732 } 733 } 734 } 735 reset()736 private void reset() { 737 mOverflow = false; 738 mMaxSize = MAX_PULL_PAYLOAD_SIZE; 739 } 740 setMaxSize(final int maxSize, final int numBytesWritten)741 private void setMaxSize(final int maxSize, final int numBytesWritten) { 742 mMaxSize = maxSize; 743 if (numBytesWritten > maxSize) { 744 mOverflow = true; 745 } 746 } 747 hasOverflowed()748 private boolean hasOverflowed() { 749 return mOverflow; 750 } 751 752 /** 753 * Checks for available space in the byte array. 754 * 755 * @param index starting position in the buffer to start the check. 756 * @param numBytes number of bytes to check from index. 757 * @return true if space is available, false otherwise. 758 **/ hasEnoughSpace(final int index, final int numBytes)759 private boolean hasEnoughSpace(final int index, final int numBytes) { 760 final int totalBytesNeeded = index + numBytes; 761 762 if (totalBytesNeeded > mMaxSize) { 763 mOverflow = true; 764 return false; 765 } 766 767 // Expand buffer if needed. 768 if (mBytes.length < mMaxSize && totalBytesNeeded > mBytes.length) { 769 int newSize = mBytes.length; 770 do { 771 newSize *= 2; 772 } while (newSize <= totalBytesNeeded); 773 774 if (newSize > mMaxSize) { 775 newSize = mMaxSize; 776 } 777 778 mBytes = Arrays.copyOf(mBytes, newSize); 779 } 780 781 return true; 782 } 783 784 /** 785 * Writes a byte into the buffer. 786 * 787 * @param index position in the buffer where the byte is written. 788 * @param value the byte to write. 789 * @return number of bytes written to buffer from this write operation. 790 **/ putByte(final int index, final byte value)791 private int putByte(final int index, final byte value) { 792 if (hasEnoughSpace(index, Byte.BYTES)) { 793 mBytes[index] = (byte) (value); 794 return Byte.BYTES; 795 } 796 return 0; 797 } 798 799 /** 800 * Writes a boolean into the buffer. 801 * 802 * @param index position in the buffer where the boolean is written. 803 * @param value the boolean to write. 804 * @return number of bytes written to buffer from this write operation. 805 **/ putBoolean(final int index, final boolean value)806 private int putBoolean(final int index, final boolean value) { 807 return putByte(index, (byte) (value ? 1 : 0)); 808 } 809 810 /** 811 * Writes an integer into the buffer. 812 * 813 * @param index position in the buffer where the integer is written. 814 * @param value the integer to write. 815 * @return number of bytes written to buffer from this write operation. 816 **/ putInt(final int index, final int value)817 private int putInt(final int index, final int value) { 818 if (hasEnoughSpace(index, Integer.BYTES)) { 819 // Use little endian byte order. 820 mBytes[index] = (byte) (value); 821 mBytes[index + 1] = (byte) (value >> 8); 822 mBytes[index + 2] = (byte) (value >> 16); 823 mBytes[index + 3] = (byte) (value >> 24); 824 return Integer.BYTES; 825 } 826 return 0; 827 } 828 829 /** 830 * Writes a long into the buffer. 831 * 832 * @param index position in the buffer where the long is written. 833 * @param value the long to write. 834 * @return number of bytes written to buffer from this write operation. 835 **/ putLong(final int index, final long value)836 private int putLong(final int index, final long value) { 837 if (hasEnoughSpace(index, Long.BYTES)) { 838 // Use little endian byte order. 839 mBytes[index] = (byte) (value); 840 mBytes[index + 1] = (byte) (value >> 8); 841 mBytes[index + 2] = (byte) (value >> 16); 842 mBytes[index + 3] = (byte) (value >> 24); 843 mBytes[index + 4] = (byte) (value >> 32); 844 mBytes[index + 5] = (byte) (value >> 40); 845 mBytes[index + 6] = (byte) (value >> 48); 846 mBytes[index + 7] = (byte) (value >> 56); 847 return Long.BYTES; 848 } 849 return 0; 850 } 851 852 /** 853 * Writes a float into the buffer. 854 * 855 * @param index position in the buffer where the float is written. 856 * @param value the float to write. 857 * @return number of bytes written to buffer from this write operation. 858 **/ putFloat(final int index, final float value)859 private int putFloat(final int index, final float value) { 860 return putInt(index, Float.floatToIntBits(value)); 861 } 862 863 /** 864 * Copies a byte array into the buffer. 865 * 866 * @param index position in the buffer where the byte array is copied. 867 * @param value the byte array to copy. 868 * @return number of bytes written to buffer from this write operation. 869 **/ putByteArray(final int index, @NonNull final byte[] value)870 private int putByteArray(final int index, @NonNull final byte[] value) { 871 final int numBytes = value.length; 872 if (hasEnoughSpace(index, numBytes)) { 873 System.arraycopy(value, 0, mBytes, index, numBytes); 874 return numBytes; 875 } 876 return 0; 877 } 878 } 879 } 880