blob: 80c731390383f06ab2ff3a50dd02f9f04120dc1b [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "Cts-NdkBinderTest"
#include <android/binder_ibinder.h>
#include <android/binder_parcel.h>
#include <android/binder_parcel_utils.h>
#include <android/log.h>
#include <gtest/gtest.h>
#include "utilities.h"
#include <limits>
#include <vector>
class NdkBinderTest_AParcel : public NdkBinderTest {};
template <typename T, typename Enable = void>
struct WriteFrom {
using type = const T;
};
template <>
struct WriteFrom<AStatus*> {
// not 'const T' = 'AStatus* const' where T = AStatus*.
using type = const AStatus*;
};
template <typename T>
bool NdkBinderSenseOfEquality(T a, T b) {
return a == b;
}
template <>
bool NdkBinderSenseOfEquality<const AStatus*>(const AStatus* a,
const AStatus* b) {
if (a == b) return true;
return AStatus_isOk(a) == AStatus_isOk(b) &&
AStatus_getExceptionCode(a) == AStatus_getExceptionCode(b) &&
AStatus_getServiceSpecificError(a) ==
AStatus_getServiceSpecificError(b) &&
AStatus_getStatus(a) == AStatus_getStatus(b) &&
std::string(AStatus_getMessage(a)) == AStatus_getMessage(b);
}
template <>
bool NdkBinderSenseOfEquality<AStatus*>(AStatus* a, AStatus* b) {
return NdkBinderSenseOfEquality<const AStatus*>(a, b);
}
// These reads and writes an array of possible values all of the same type.
template <typename T,
binder_status_t (*write)(AParcel*, typename WriteFrom<T>::type),
binder_status_t (*read)(const AParcel*, T*)>
void ExpectInOut(std::vector<T> in) {
AIBinder* binder = SampleData::newBinder(
[](transaction_code_t, const AParcel* in, AParcel* out) {
T readTarget = {};
EXPECT_OK(read(in, &readTarget));
EXPECT_OK(write(out, readTarget));
return STATUS_OK;
},
ExpectLifetimeTransactions(in.size()));
for (const auto& value : in) {
EXPECT_OK(SampleData::transact(
binder, kCode,
[&](AParcel* in) {
EXPECT_OK(write(in, value));
return STATUS_OK;
},
[&](const AParcel* out) {
T readTarget = {};
EXPECT_OK(read(out, &readTarget));
EXPECT_TRUE(NdkBinderSenseOfEquality<T>(value, readTarget))
<< value << " is not " << readTarget;
return STATUS_OK;
}));
}
AIBinder_decStrong(binder);
}
template <typename T,
binder_status_t (*write)(AParcel*, typename WriteFrom<T>::type),
binder_status_t (*read)(const AParcel*, T*)>
void ExpectInOutMinMax() {
ExpectInOut<T, write, read>(
{std::numeric_limits<T>::min(), std::numeric_limits<T>::max()});
}
TEST_F(NdkBinderTest_AParcel, BindersInMustComeOut) {
AIBinder* binder = SampleData::newBinder();
ExpectInOut<AIBinder*, AParcel_writeStrongBinder, AParcel_readStrongBinder>(
{binder});
// copy which is read when this binder is sent in a transaction to this
// process
AIBinder_decStrong(binder);
// copy which is read when this binder is returned in a transaction within
// this same process and is read again
AIBinder_decStrong(binder);
ExpectInOut<AIBinder*, AParcel_writeStrongBinder, AParcel_readStrongBinder>(
{nullptr, binder});
// copy which is read when this binder is sent in a transaction to this
// process
AIBinder_decStrong(binder);
// copy which is read when this binder is returned in a transaction within
// this same process and is read again
AIBinder_decStrong(binder);
AIBinder_decStrong(binder);
}
TEST_F(NdkBinderTest_AParcel, StatusesInMustComeOut) {
// This does not clean up status objects.
ExpectInOut<AStatus*, AParcel_writeStatusHeader, AParcel_readStatusHeader>({
AStatus_newOk(),
AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT),
AStatus_fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"+++++++++[->++++++++<]>.+."),
AStatus_fromServiceSpecificError(1776),
AStatus_fromServiceSpecificErrorWithMessage(0xBEA, "utiful!"),
});
}
TEST_F(NdkBinderTest_AParcel, LowLevelErrorsHaveNoStatusHeader) {
AIBinder* binder =
SampleData::newBinder(nullptr, ExpectLifetimeTransactions(0));
EXPECT_EQ(
STATUS_UNKNOWN_ERROR,
SampleData::transact(binder, kCode, [&](AParcel* in) {
AStatus* status = nullptr;
status = AStatus_fromExceptionCode(EX_TRANSACTION_FAILED);
EXPECT_EQ(STATUS_FAILED_TRANSACTION,
AParcel_writeStatusHeader(in, status));
AStatus_delete(status);
status = AStatus_fromExceptionCodeWithMessage(EX_TRANSACTION_FAILED,
"something or other");
EXPECT_EQ(STATUS_FAILED_TRANSACTION,
AParcel_writeStatusHeader(in, status));
AStatus_delete(status);
status = AStatus_fromStatus(STATUS_UNKNOWN_ERROR);
EXPECT_EQ(STATUS_UNKNOWN_ERROR, AParcel_writeStatusHeader(in, status));
AStatus_delete(status);
status = AStatus_fromStatus(STATUS_BAD_VALUE);
EXPECT_EQ(STATUS_BAD_VALUE, AParcel_writeStatusHeader(in, status));
AStatus_delete(status);
return STATUS_UNKNOWN_ERROR;
}));
AIBinder_decStrong(binder);
}
TEST_F(NdkBinderTest_AParcel, WhatGoesInMustComeOut) {
ExpectInOut<int32_t, AParcel_writeInt32, AParcel_readInt32>(
{-7, -1, 0, 1, 45});
ExpectInOut<uint32_t, AParcel_writeUint32, AParcel_readUint32>(
{0, 1, 2, 100});
ExpectInOut<int64_t, AParcel_writeInt64, AParcel_readInt64>(
{-7, -1, 0, 1, 45});
ExpectInOut<uint64_t, AParcel_writeUint64, AParcel_readUint64>(
{0, 1, 2, 100});
ExpectInOut<float, AParcel_writeFloat, AParcel_readFloat>(
{-1.0f, 0.0f, 1.0f, 0.24975586f, 0.3f});
ExpectInOut<double, AParcel_writeDouble, AParcel_readDouble>(
{-1.0, 0.0, 1.0, 0.24975586, 0.3});
ExpectInOut<bool, AParcel_writeBool, AParcel_readBool>({true, false});
ExpectInOut<char16_t, AParcel_writeChar, AParcel_readChar>(
{L'\0', L'S', L'@', L'\n'});
ExpectInOut<int8_t, AParcel_writeByte, AParcel_readByte>({-7, -1, 0, 1, 45});
}
TEST_F(NdkBinderTest_AParcel, ExtremeValues) {
ExpectInOutMinMax<int32_t, AParcel_writeInt32, AParcel_readInt32>();
ExpectInOutMinMax<uint32_t, AParcel_writeUint32, AParcel_readUint32>();
ExpectInOutMinMax<int64_t, AParcel_writeInt64, AParcel_readInt64>();
ExpectInOutMinMax<uint64_t, AParcel_writeUint64, AParcel_readUint64>();
ExpectInOutMinMax<float, AParcel_writeFloat, AParcel_readFloat>();
ExpectInOutMinMax<double, AParcel_writeDouble, AParcel_readDouble>();
ExpectInOutMinMax<bool, AParcel_writeBool, AParcel_readBool>();
ExpectInOutMinMax<char16_t, AParcel_writeChar, AParcel_readChar>();
ExpectInOutMinMax<int8_t, AParcel_writeByte, AParcel_readByte>();
}
TEST_F(NdkBinderTest_AParcel, NonNullTerminatedString) {
// This is a helper to write a vector of strings which are not
// null-terminated. It has infinite length, and every element is the same
// value (element.substr(0, elementLen)). However, when it is written, no
// copies of element are made to produce a null-terminated string.
struct PartialStringCycle {
// every element of the vector is a prefix of this string
const std::string& element;
// this is the number of characters of the string to write, < element.size()
int32_t elementLen;
binder_status_t writeToParcel(AParcel* p, size_t length) const {
return AParcel_writeStringArray(p, static_cast<const void*>(this), length,
ElementGetter);
}
private:
static const char* ElementGetter(const void* vectorData, size_t /*index*/, int32_t* outLength) {
const PartialStringCycle* vector =
static_cast<const PartialStringCycle*>(vectorData);
*outLength = vector->elementLen;
return vector->element.c_str();
}
};
const std::string kTestcase = "aoeuhtns";
for (size_t i = 0; i < kTestcase.size(); i++) {
const std::string expectedString = kTestcase.substr(0, i);
const std::vector<std::string> expectedVector = {expectedString,
expectedString};
const PartialStringCycle writeVector{.element = kTestcase,
.elementLen = static_cast<int32_t>(i)};
AIBinder* binder = SampleData::newBinder(
[&](transaction_code_t, const AParcel* in, AParcel* /*out*/) {
std::string readString;
EXPECT_OK(::ndk::AParcel_readString(in, &readString));
EXPECT_EQ(expectedString, readString);
std::vector<std::string> readVector;
EXPECT_OK(::ndk::AParcel_readVector(in, &readVector));
EXPECT_EQ(expectedVector, readVector);
return STATUS_OK;
},
ExpectLifetimeTransactions(1));
EXPECT_OK(SampleData::transact(
binder, kCode,
[&](AParcel* in) {
EXPECT_OK(AParcel_writeString(in, kTestcase.c_str(), i));
EXPECT_OK(writeVector.writeToParcel(in, expectedVector.size()));
return STATUS_OK;
},
ReadNothingFromParcel));
AIBinder_decStrong(binder);
}
}
TEST_F(NdkBinderTest_AParcel, CantReadFromEmptyParcel) {
AIBinder* binder = SampleData::newBinder(TransactionsReturn(STATUS_OK),
ExpectLifetimeTransactions(1));
EXPECT_OK(SampleData::transact(
binder, kCode, WriteNothingToParcel, [&](const AParcel* out) {
bool readTarget = false;
EXPECT_EQ(STATUS_NOT_ENOUGH_DATA, AParcel_readBool(out, &readTarget));
EXPECT_FALSE(readTarget);
return STATUS_OK;
}));
AIBinder_decStrong(binder);
}
TEST_F(NdkBinderTest_AParcel, ReturnParcelPosition) {
AIBinder* binder = SampleData::newBinder(
[&](transaction_code_t, const AParcel* /*in*/, AParcel* out) {
size_t position = AParcel_getDataPosition(out);
EXPECT_EQ(position, AParcel_getDataPosition(out));
EXPECT_OK(AParcel_setDataPosition(out, position));
EXPECT_EQ(position, AParcel_getDataPosition(out));
return STATUS_OK;
},
ExpectLifetimeTransactions(1));
EXPECT_OK(SampleData::transact(binder, kCode, WriteNothingToParcel,
ReadNothingFromParcel));
AIBinder_decStrong(binder);
}
TEST_F(NdkBinderTest_AParcel, TooLargePosition) {
AIBinder* binder = SampleData::newBinder(
[&](transaction_code_t, const AParcel* /*in*/, AParcel* out) {
EXPECT_OK(AParcel_setDataPosition(out, 0));
EXPECT_OK(AParcel_setDataPosition(out, INT32_MAX));
EXPECT_EQ(STATUS_BAD_VALUE, AParcel_setDataPosition(out, -1));
EXPECT_EQ(STATUS_BAD_VALUE, AParcel_setDataPosition(out, -2));
return STATUS_OK;
},
ExpectLifetimeTransactions(1));
EXPECT_OK(SampleData::transact(binder, kCode, WriteNothingToParcel,
ReadNothingFromParcel));
AIBinder_decStrong(binder);
}
TEST_F(NdkBinderTest_AParcel, RewritePositions) {
const std::string kTestString1 = "asdf";
const std::string kTestString2 = "aoeu";
// v-- position v-- postPosition
// | delta | "asdf" | "aoeu" |
// ^-- prePosition
//
// uint32_t delta = postPosition - prePosition
AIBinder* binder = SampleData::newBinder(
[&](transaction_code_t, const AParcel* in, AParcel* /*out*/) {
uint32_t delta;
EXPECT_OK(AParcel_readUint32(in, &delta));
size_t prePosition = AParcel_getDataPosition(in);
size_t postPosition = prePosition + delta;
std::string readString;
EXPECT_OK(AParcel_setDataPosition(in, postPosition));
EXPECT_OK(::ndk::AParcel_readString(in, &readString));
EXPECT_EQ(kTestString2, readString);
EXPECT_OK(AParcel_setDataPosition(in, prePosition));
EXPECT_OK(::ndk::AParcel_readString(in, &readString));
EXPECT_EQ(kTestString1, readString);
EXPECT_EQ(postPosition, AParcel_getDataPosition(in));
return STATUS_OK;
},
ExpectLifetimeTransactions(1));
EXPECT_OK(SampleData::transact(
binder, kCode,
[&](AParcel* in) {
size_t position = AParcel_getDataPosition(in);
EXPECT_OK(AParcel_writeUint32(in, 0)); // placeholder
size_t prePosition = AParcel_getDataPosition(in);
EXPECT_OK(::ndk::AParcel_writeString(in, kTestString1));
size_t postPosition = AParcel_getDataPosition(in);
EXPECT_OK(::ndk::AParcel_writeString(in, kTestString2));
size_t delta = postPosition - prePosition;
EXPECT_OK(AParcel_setDataPosition(in, position));
EXPECT_OK(AParcel_writeUint32(in, static_cast<uint32_t>(delta)));
return STATUS_OK;
},
ReadNothingFromParcel));
AIBinder_decStrong(binder);
}