1 /* 2 * Copyright (C) 2018 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 <android/binder_parcel.h> 18 #include "parcel_internal.h" 19 20 #include "ibinder_internal.h" 21 #include "status_internal.h" 22 23 #include <limits> 24 25 #include <android-base/logging.h> 26 #include <android-base/unique_fd.h> 27 #include <binder/Parcel.h> 28 #include <binder/ParcelFileDescriptor.h> 29 #include <utils/Unicode.h> 30 31 using ::android::IBinder; 32 using ::android::Parcel; 33 using ::android::sp; 34 using ::android::status_t; 35 using ::android::base::unique_fd; 36 using ::android::os::ParcelFileDescriptor; 37 38 template <typename T> 39 using ContiguousArrayAllocator = bool (*)(void* arrayData, int32_t length, T** outBuffer); 40 41 template <typename T> 42 using ArrayAllocator = bool (*)(void* arrayData, int32_t length); 43 template <typename T> 44 using ArrayGetter = T (*)(const void* arrayData, size_t index); 45 template <typename T> 46 using ArraySetter = void (*)(void* arrayData, size_t index, T value); 47 48 binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArray, int32_t length) { 49 // only -1 can be used to represent a null array 50 if (length < -1) return STATUS_BAD_VALUE; 51 52 if (!isNullArray && length < 0) { 53 LOG(ERROR) << __func__ << ": null array must be used with length == -1."; 54 return STATUS_BAD_VALUE; 55 } 56 if (isNullArray && length > 0) { 57 LOG(ERROR) << __func__ << ": null buffer cannot be for size " << length << " array."; 58 return STATUS_BAD_VALUE; 59 } 60 61 Parcel* rawParcel = parcel->get(); 62 63 status_t status = rawParcel->writeInt32(static_cast<int32_t>(length)); 64 if (status != STATUS_OK) return PruneStatusT(status); 65 66 return STATUS_OK; 67 } 68 69 template <typename T> 70 binder_status_t WriteArray(AParcel* parcel, const T* array, int32_t length) { 71 binder_status_t status = WriteAndValidateArraySize(parcel, array == nullptr, length); 72 if (status != STATUS_OK) return status; 73 if (length <= 0) return STATUS_OK; 74 75 int32_t size = 0; 76 if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY; 77 78 void* const data = parcel->get()->writeInplace(size); 79 if (data == nullptr) return STATUS_NO_MEMORY; 80 81 memcpy(data, array, size); 82 83 return STATUS_OK; 84 } 85 86 // Each element in a char16_t array is converted to an int32_t (not packed). 87 template <> 88 binder_status_t WriteArray<char16_t>(AParcel* parcel, const char16_t* array, int32_t length) { 89 binder_status_t status = WriteAndValidateArraySize(parcel, array == nullptr, length); 90 if (status != STATUS_OK) return status; 91 if (length <= 0) return STATUS_OK; 92 93 int32_t size = 0; 94 if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY; 95 96 Parcel* rawParcel = parcel->get(); 97 98 for (int32_t i = 0; i < length; i++) { 99 status = rawParcel->writeChar(array[i]); 100 101 if (status != STATUS_OK) return PruneStatusT(status); 102 } 103 104 return STATUS_OK; 105 } 106 107 template <typename T> 108 binder_status_t ReadArray(const AParcel* parcel, void* arrayData, 109 ContiguousArrayAllocator<T> allocator) { 110 const Parcel* rawParcel = parcel->get(); 111 112 int32_t length; 113 status_t status = rawParcel->readInt32(&length); 114 115 if (status != STATUS_OK) return PruneStatusT(status); 116 if (length < -1) return STATUS_BAD_VALUE; 117 118 T* array; 119 if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY; 120 121 if (length <= 0) return STATUS_OK; 122 if (array == nullptr) return STATUS_NO_MEMORY; 123 124 int32_t size = 0; 125 if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY; 126 127 const void* data = rawParcel->readInplace(size); 128 if (data == nullptr) return STATUS_NO_MEMORY; 129 130 memcpy(array, data, size); 131 132 return STATUS_OK; 133 } 134 135 // Each element in a char16_t array is converted to an int32_t (not packed) 136 template <> 137 binder_status_t ReadArray<char16_t>(const AParcel* parcel, void* arrayData, 138 ContiguousArrayAllocator<char16_t> allocator) { 139 const Parcel* rawParcel = parcel->get(); 140 141 int32_t length; 142 status_t status = rawParcel->readInt32(&length); 143 144 if (status != STATUS_OK) return PruneStatusT(status); 145 if (length < -1) return STATUS_BAD_VALUE; 146 147 char16_t* array; 148 if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY; 149 150 if (length <= 0) return STATUS_OK; 151 if (array == nullptr) return STATUS_NO_MEMORY; 152 153 int32_t size = 0; 154 if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY; 155 156 for (int32_t i = 0; i < length; i++) { 157 status = rawParcel->readChar(array + i); 158 159 if (status != STATUS_OK) return PruneStatusT(status); 160 } 161 162 return STATUS_OK; 163 } 164 165 template <typename T> 166 binder_status_t WriteArray(AParcel* parcel, const void* arrayData, int32_t length, 167 ArrayGetter<T> getter, status_t (Parcel::*write)(T)) { 168 // we have no clue if arrayData represents a null object or not, we can only infer from length 169 bool arrayIsNull = length < 0; 170 binder_status_t status = WriteAndValidateArraySize(parcel, arrayIsNull, length); 171 if (status != STATUS_OK) return status; 172 if (length <= 0) return STATUS_OK; 173 174 Parcel* rawParcel = parcel->get(); 175 176 for (int32_t i = 0; i < length; i++) { 177 status = (rawParcel->*write)(getter(arrayData, i)); 178 179 if (status != STATUS_OK) return PruneStatusT(status); 180 } 181 182 return STATUS_OK; 183 } 184 185 template <typename T> 186 binder_status_t ReadArray(const AParcel* parcel, void* arrayData, ArrayAllocator<T> allocator, 187 ArraySetter<T> setter, status_t (Parcel::*read)(T*) const) { 188 const Parcel* rawParcel = parcel->get(); 189 190 int32_t length; 191 status_t status = rawParcel->readInt32(&length); 192 193 if (status != STATUS_OK) return PruneStatusT(status); 194 if (length < -1) return STATUS_BAD_VALUE; 195 196 if (!allocator(arrayData, length)) return STATUS_NO_MEMORY; 197 198 if (length <= 0) return STATUS_OK; 199 200 for (int32_t i = 0; i < length; i++) { 201 T readTarget; 202 status = (rawParcel->*read)(&readTarget); 203 if (status != STATUS_OK) return PruneStatusT(status); 204 205 setter(arrayData, i, readTarget); 206 } 207 208 return STATUS_OK; 209 } 210 211 void AParcel_delete(AParcel* parcel) { 212 delete parcel; 213 } 214 215 binder_status_t AParcel_setDataPosition(const AParcel* parcel, int32_t position) { 216 if (position < 0) { 217 return STATUS_BAD_VALUE; 218 } 219 220 parcel->get()->setDataPosition(position); 221 return STATUS_OK; 222 } 223 224 int32_t AParcel_getDataPosition(const AParcel* parcel) { 225 return parcel->get()->dataPosition(); 226 } 227 228 binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) { 229 sp<IBinder> writeBinder = binder != nullptr ? binder->getBinder() : nullptr; 230 return parcel->get()->writeStrongBinder(writeBinder); 231 } 232 binder_status_t AParcel_readStrongBinder(const AParcel* parcel, AIBinder** binder) { 233 sp<IBinder> readBinder = nullptr; 234 status_t status = parcel->get()->readNullableStrongBinder(&readBinder); 235 if (status != STATUS_OK) { 236 return PruneStatusT(status); 237 } 238 sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(readBinder); 239 AIBinder_incStrong(ret.get()); 240 *binder = ret.get(); 241 return PruneStatusT(status); 242 } 243 244 binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd) { 245 std::unique_ptr<ParcelFileDescriptor> parcelFd; 246 247 if (fd < 0) { 248 if (fd != -1) { 249 return STATUS_UNKNOWN_ERROR; 250 } 251 // parcelFd = nullptr 252 } else { // fd >= 0 253 parcelFd = std::make_unique<ParcelFileDescriptor>(unique_fd(fd)); 254 } 255 256 status_t status = parcel->get()->writeNullableParcelable(parcelFd); 257 258 // ownership is retained by caller 259 if (parcelFd != nullptr) { 260 (void)parcelFd->release().release(); 261 } 262 263 return PruneStatusT(status); 264 } 265 266 binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd) { 267 std::unique_ptr<ParcelFileDescriptor> parcelFd; 268 269 status_t status = parcel->get()->readParcelable(&parcelFd); 270 if (status != STATUS_OK) return PruneStatusT(status); 271 272 if (parcelFd) { 273 *fd = parcelFd->release().release(); 274 } else { 275 *fd = -1; 276 } 277 278 return STATUS_OK; 279 } 280 281 binder_status_t AParcel_writeStatusHeader(AParcel* parcel, const AStatus* status) { 282 return PruneStatusT(status->get()->writeToParcel(parcel->get())); 283 } 284 binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status) { 285 ::android::binder::Status bstatus; 286 binder_status_t ret = PruneStatusT(bstatus.readFromParcel(*parcel->get())); 287 if (ret == STATUS_OK) { 288 *status = new AStatus(std::move(bstatus)); 289 } 290 return PruneStatusT(ret); 291 } 292 293 binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t length) { 294 if (string == nullptr) { 295 if (length != -1) { 296 LOG(WARNING) << __func__ << ": null string must be used with length == -1."; 297 return STATUS_BAD_VALUE; 298 } 299 300 status_t err = parcel->get()->writeInt32(-1); 301 return PruneStatusT(err); 302 } 303 304 if (length < 0) { 305 LOG(WARNING) << __func__ << ": Negative string length: " << length; 306 return STATUS_BAD_VALUE; 307 } 308 309 const uint8_t* str8 = (uint8_t*)string; 310 const ssize_t len16 = utf8_to_utf16_length(str8, length); 311 312 if (len16 < 0 || len16 >= std::numeric_limits<int32_t>::max()) { 313 LOG(WARNING) << __func__ << ": Invalid string length: " << len16; 314 return STATUS_BAD_VALUE; 315 } 316 317 status_t err = parcel->get()->writeInt32(len16); 318 if (err) { 319 return PruneStatusT(err); 320 } 321 322 void* str16 = parcel->get()->writeInplace((len16 + 1) * sizeof(char16_t)); 323 if (str16 == nullptr) { 324 return STATUS_NO_MEMORY; 325 } 326 327 utf8_to_utf16(str8, length, (char16_t*)str16, (size_t)len16 + 1); 328 329 return STATUS_OK; 330 } 331 332 binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, 333 AParcel_stringAllocator allocator) { 334 size_t len16; 335 const char16_t* str16 = parcel->get()->readString16Inplace(&len16); 336 337 if (str16 == nullptr) { 338 if (allocator(stringData, -1, nullptr)) { 339 return STATUS_OK; 340 } 341 342 return STATUS_UNEXPECTED_NULL; 343 } 344 345 ssize_t len8; 346 347 if (len16 == 0) { 348 len8 = 1; 349 } else { 350 len8 = utf16_to_utf8_length(str16, len16) + 1; 351 } 352 353 if (len8 <= 0 || len8 > std::numeric_limits<int32_t>::max()) { 354 LOG(WARNING) << __func__ << ": Invalid string length: " << len8; 355 return STATUS_BAD_VALUE; 356 } 357 358 char* str8; 359 bool success = allocator(stringData, len8, &str8); 360 361 if (!success || str8 == nullptr) { 362 LOG(WARNING) << __func__ << ": AParcel_stringAllocator failed to allocate."; 363 return STATUS_NO_MEMORY; 364 } 365 366 utf16_to_utf8(str16, len16, str8, len8); 367 368 return STATUS_OK; 369 } 370 371 binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, int32_t length, 372 AParcel_stringArrayElementGetter getter) { 373 // we have no clue if arrayData represents a null object or not, we can only infer from length 374 bool arrayIsNull = length < 0; 375 binder_status_t status = WriteAndValidateArraySize(parcel, arrayIsNull, length); 376 if (status != STATUS_OK) return status; 377 if (length <= 0) return STATUS_OK; 378 379 for (int32_t i = 0; i < length; i++) { 380 int32_t elementLength = 0; 381 const char* str = getter(arrayData, i, &elementLength); 382 if (str == nullptr && elementLength != -1) return STATUS_BAD_VALUE; 383 384 binder_status_t status = AParcel_writeString(parcel, str, elementLength); 385 if (status != STATUS_OK) return status; 386 } 387 388 return STATUS_OK; 389 } 390 391 // This implements AParcel_stringAllocator for a string using an array, index, and element 392 // allocator. 393 struct StringArrayElementAllocationAdapter { 394 void* arrayData; // stringData from the NDK 395 int32_t index; // index into the string array 396 AParcel_stringArrayElementAllocator elementAllocator; 397 398 static bool Allocator(void* stringData, int32_t length, char** buffer) { 399 StringArrayElementAllocationAdapter* adapter = 400 static_cast<StringArrayElementAllocationAdapter*>(stringData); 401 return adapter->elementAllocator(adapter->arrayData, adapter->index, length, buffer); 402 } 403 }; 404 405 binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData, 406 AParcel_stringArrayAllocator allocator, 407 AParcel_stringArrayElementAllocator elementAllocator) { 408 const Parcel* rawParcel = parcel->get(); 409 410 int32_t length; 411 status_t status = rawParcel->readInt32(&length); 412 413 if (status != STATUS_OK) return PruneStatusT(status); 414 if (length < -1) return STATUS_BAD_VALUE; 415 416 if (!allocator(arrayData, length)) return STATUS_NO_MEMORY; 417 418 if (length == -1) return STATUS_OK; // null string array 419 420 StringArrayElementAllocationAdapter adapter{ 421 .arrayData = arrayData, 422 .index = 0, 423 .elementAllocator = elementAllocator, 424 }; 425 426 for (; adapter.index < length; adapter.index++) { 427 binder_status_t status = AParcel_readString(parcel, static_cast<void*>(&adapter), 428 StringArrayElementAllocationAdapter::Allocator); 429 430 if (status != STATUS_OK) return status; 431 } 432 433 return STATUS_OK; 434 } 435 436 binder_status_t AParcel_writeParcelableArray(AParcel* parcel, const void* arrayData, int32_t length, 437 AParcel_writeParcelableElement elementWriter) { 438 // we have no clue if arrayData represents a null object or not, we can only infer from length 439 bool arrayIsNull = length < 0; 440 binder_status_t status = WriteAndValidateArraySize(parcel, arrayIsNull, length); 441 if (status != STATUS_OK) return status; 442 if (length <= 0) return STATUS_OK; 443 444 for (int32_t i = 0; i < length; i++) { 445 binder_status_t status = elementWriter(parcel, arrayData, i); 446 if (status != STATUS_OK) return status; 447 } 448 449 return STATUS_OK; 450 } 451 452 binder_status_t AParcel_readParcelableArray(const AParcel* parcel, void* arrayData, 453 AParcel_parcelableArrayAllocator allocator, 454 AParcel_readParcelableElement elementReader) { 455 const Parcel* rawParcel = parcel->get(); 456 457 int32_t length; 458 status_t status = rawParcel->readInt32(&length); 459 460 if (status != STATUS_OK) return PruneStatusT(status); 461 if (length < -1) return STATUS_BAD_VALUE; 462 463 if (!allocator(arrayData, length)) return STATUS_NO_MEMORY; 464 465 if (length == -1) return STATUS_OK; // null array 466 467 for (int32_t i = 0; i < length; i++) { 468 binder_status_t status = elementReader(parcel, arrayData, i); 469 if (status != STATUS_OK) return status; 470 } 471 472 return STATUS_OK; 473 } 474 475 // See gen_parcel_helper.py. These auto-generated read/write methods use the same types for 476 // libbinder and this library. 477 // @START 478 binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value) { 479 status_t status = parcel->get()->writeInt32(value); 480 return PruneStatusT(status); 481 } 482 483 binder_status_t AParcel_writeUint32(AParcel* parcel, uint32_t value) { 484 status_t status = parcel->get()->writeUint32(value); 485 return PruneStatusT(status); 486 } 487 488 binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value) { 489 status_t status = parcel->get()->writeInt64(value); 490 return PruneStatusT(status); 491 } 492 493 binder_status_t AParcel_writeUint64(AParcel* parcel, uint64_t value) { 494 status_t status = parcel->get()->writeUint64(value); 495 return PruneStatusT(status); 496 } 497 498 binder_status_t AParcel_writeFloat(AParcel* parcel, float value) { 499 status_t status = parcel->get()->writeFloat(value); 500 return PruneStatusT(status); 501 } 502 503 binder_status_t AParcel_writeDouble(AParcel* parcel, double value) { 504 status_t status = parcel->get()->writeDouble(value); 505 return PruneStatusT(status); 506 } 507 508 binder_status_t AParcel_writeBool(AParcel* parcel, bool value) { 509 status_t status = parcel->get()->writeBool(value); 510 return PruneStatusT(status); 511 } 512 513 binder_status_t AParcel_writeChar(AParcel* parcel, char16_t value) { 514 status_t status = parcel->get()->writeChar(value); 515 return PruneStatusT(status); 516 } 517 518 binder_status_t AParcel_writeByte(AParcel* parcel, int8_t value) { 519 status_t status = parcel->get()->writeByte(value); 520 return PruneStatusT(status); 521 } 522 523 binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value) { 524 status_t status = parcel->get()->readInt32(value); 525 return PruneStatusT(status); 526 } 527 528 binder_status_t AParcel_readUint32(const AParcel* parcel, uint32_t* value) { 529 status_t status = parcel->get()->readUint32(value); 530 return PruneStatusT(status); 531 } 532 533 binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value) { 534 status_t status = parcel->get()->readInt64(value); 535 return PruneStatusT(status); 536 } 537 538 binder_status_t AParcel_readUint64(const AParcel* parcel, uint64_t* value) { 539 status_t status = parcel->get()->readUint64(value); 540 return PruneStatusT(status); 541 } 542 543 binder_status_t AParcel_readFloat(const AParcel* parcel, float* value) { 544 status_t status = parcel->get()->readFloat(value); 545 return PruneStatusT(status); 546 } 547 548 binder_status_t AParcel_readDouble(const AParcel* parcel, double* value) { 549 status_t status = parcel->get()->readDouble(value); 550 return PruneStatusT(status); 551 } 552 553 binder_status_t AParcel_readBool(const AParcel* parcel, bool* value) { 554 status_t status = parcel->get()->readBool(value); 555 return PruneStatusT(status); 556 } 557 558 binder_status_t AParcel_readChar(const AParcel* parcel, char16_t* value) { 559 status_t status = parcel->get()->readChar(value); 560 return PruneStatusT(status); 561 } 562 563 binder_status_t AParcel_readByte(const AParcel* parcel, int8_t* value) { 564 status_t status = parcel->get()->readByte(value); 565 return PruneStatusT(status); 566 } 567 568 binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* arrayData, int32_t length) { 569 return WriteArray<int32_t>(parcel, arrayData, length); 570 } 571 572 binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* arrayData, 573 int32_t length) { 574 return WriteArray<uint32_t>(parcel, arrayData, length); 575 } 576 577 binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* arrayData, int32_t length) { 578 return WriteArray<int64_t>(parcel, arrayData, length); 579 } 580 581 binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* arrayData, 582 int32_t length) { 583 return WriteArray<uint64_t>(parcel, arrayData, length); 584 } 585 586 binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* arrayData, int32_t length) { 587 return WriteArray<float>(parcel, arrayData, length); 588 } 589 590 binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* arrayData, int32_t length) { 591 return WriteArray<double>(parcel, arrayData, length); 592 } 593 594 binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData, int32_t length, 595 AParcel_boolArrayGetter getter) { 596 return WriteArray<bool>(parcel, arrayData, length, getter, &Parcel::writeBool); 597 } 598 599 binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* arrayData, int32_t length) { 600 return WriteArray<char16_t>(parcel, arrayData, length); 601 } 602 603 binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, int32_t length) { 604 return WriteArray<int8_t>(parcel, arrayData, length); 605 } 606 607 binder_status_t AParcel_readInt32Array(const AParcel* parcel, void* arrayData, 608 AParcel_int32ArrayAllocator allocator) { 609 return ReadArray<int32_t>(parcel, arrayData, allocator); 610 } 611 612 binder_status_t AParcel_readUint32Array(const AParcel* parcel, void* arrayData, 613 AParcel_uint32ArrayAllocator allocator) { 614 return ReadArray<uint32_t>(parcel, arrayData, allocator); 615 } 616 617 binder_status_t AParcel_readInt64Array(const AParcel* parcel, void* arrayData, 618 AParcel_int64ArrayAllocator allocator) { 619 return ReadArray<int64_t>(parcel, arrayData, allocator); 620 } 621 622 binder_status_t AParcel_readUint64Array(const AParcel* parcel, void* arrayData, 623 AParcel_uint64ArrayAllocator allocator) { 624 return ReadArray<uint64_t>(parcel, arrayData, allocator); 625 } 626 627 binder_status_t AParcel_readFloatArray(const AParcel* parcel, void* arrayData, 628 AParcel_floatArrayAllocator allocator) { 629 return ReadArray<float>(parcel, arrayData, allocator); 630 } 631 632 binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void* arrayData, 633 AParcel_doubleArrayAllocator allocator) { 634 return ReadArray<double>(parcel, arrayData, allocator); 635 } 636 637 binder_status_t AParcel_readBoolArray(const AParcel* parcel, void* arrayData, 638 AParcel_boolArrayAllocator allocator, 639 AParcel_boolArraySetter setter) { 640 return ReadArray<bool>(parcel, arrayData, allocator, setter, &Parcel::readBool); 641 } 642 643 binder_status_t AParcel_readCharArray(const AParcel* parcel, void* arrayData, 644 AParcel_charArrayAllocator allocator) { 645 return ReadArray<char16_t>(parcel, arrayData, allocator); 646 } 647 648 binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, 649 AParcel_byteArrayAllocator allocator) { 650 return ReadArray<int8_t>(parcel, arrayData, allocator); 651 } 652 653 // @END 654