diff options
| author | 2021-01-22 20:59:11 +0000 | |
|---|---|---|
| committer | 2021-01-22 20:59:11 +0000 | |
| commit | a44e6dcc124a7c8679ecf071325542082a21e488 (patch) | |
| tree | ce8fc2b46cc291c3c9c35832f6c0c0a021de78e2 | |
| parent | 631ddf6998544a87a725de3f24fbccfef48cbab4 (diff) | |
| parent | 1f834517305cfa5779dbfe0de86cf285f0a5e704 (diff) | |
Merge "Add Rust/C++ Parcel serialization tests" am: 52e4110da1 am: 1f83451730
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/1357925
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: I3a44e2aef8708ad0857783a7fd1ab14951ce6d86
| -rw-r--r-- | libs/binder/TEST_MAPPING | 3 | ||||
| -rw-r--r-- | libs/binder/rust/src/binder.rs | 2 | ||||
| -rw-r--r-- | libs/binder/rust/src/proxy.rs | 14 | ||||
| -rw-r--r-- | libs/binder/rust/tests/Android.bp | 47 | ||||
| -rw-r--r-- | libs/binder/rust/tests/serialization.cpp | 454 | ||||
| -rw-r--r-- | libs/binder/rust/tests/serialization.hpp | 48 | ||||
| -rw-r--r-- | libs/binder/rust/tests/serialization.rs | 306 |
7 files changed, 873 insertions, 1 deletions
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index 00a14f408d..2e90142af6 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -65,6 +65,9 @@ }, { "name": "binderRustNdkInteropTest" + }, + { + "name": "rustBinderSerializationTest" } ] } diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index ed3b9ec5c3..42c1e0ac0e 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -613,7 +613,7 @@ macro_rules! declare_binder_interface { impl $crate::parcel::Serialize for dyn $interface + '_ where - $interface: $crate::Interface + dyn $interface: $crate::Interface { fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { let binder = $crate::Interface::as_binder(self); diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 17af0992e2..e9e74c05e8 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -70,6 +70,20 @@ impl SpIBinder { ptr.as_mut().map(|p| Self(p)) } + /// Extract a raw `AIBinder` pointer from this wrapper. + /// + /// This method should _only_ be used for testing. Do not try to use the NDK + /// interface directly for anything else. + /// + /// # Safety + /// + /// The resulting pointer is valid only as long as the SpIBinder is alive. + /// The SpIBinder object retains ownership of the AIBinder and the caller + /// should not attempt to free the returned pointer. + pub unsafe fn as_raw(&self) -> *mut sys::AIBinder { + self.0 + } + /// Return true if this binder object is hosted in a different process than /// the current one. pub fn is_remote(&self) -> bool { diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp index 5ae9c53077..8810b5dd16 100644 --- a/libs/binder/rust/tests/Android.bp +++ b/libs/binder/rust/tests/Android.bp @@ -79,3 +79,50 @@ rust_ffi_static { "IBinderRustNdkInteropTest-rust", ], } + +cc_test { + name: "rustBinderSerializationTest", + shared_libs: [ + "libbinder", + "libbinder_ndk", + "libutils", + "libbase", + ], + static_libs: [ + "libbinder_rs_serialization_test" + ], + srcs: [ + "serialization.cpp", + ], + auto_gen_config: true, + test_suites: ["general-tests"], +} + +rust_bindgen { + name: "libbinder_rs_serialization_bindgen", + crate_name: "binder_rs_serialization_bindgen", + wrapper_src: "serialization.hpp", + source_stem: "bindings", + cpp_std: "gnu++17", + bindgen_flags: [ + "--whitelist-type", "Transaction", + "--whitelist-var", "TESTDATA_.*", + ], + + shared_libs: [ + "libbinder", + "libc++", + ], +} + +rust_ffi_static { + name: "libbinder_rs_serialization_test", + crate_name: "binder_rs_serialization_test", + srcs: [ + "serialization.rs", + ":libbinder_rs_serialization_bindgen", + ], + rustlibs: [ + "libbinder_rs", + ], +} diff --git a/libs/binder/rust/tests/serialization.cpp b/libs/binder/rust/tests/serialization.cpp new file mode 100644 index 0000000000..ec780f28a1 --- /dev/null +++ b/libs/binder/rust/tests/serialization.cpp @@ -0,0 +1,454 @@ +/* + * Copyright (C) 2020 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. + */ + +#include <android/binder_ibinder_platform.h> +#include <android/binder_libbinder.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/ParcelFileDescriptor.h> +#include <binder/ProcessState.h> +#include <binder/Status.h> +#include <gtest/gtest.h> +#include <utils/Errors.h> +#include <utils/String16.h> +#include "android-base/file.h" +#include "serialization.hpp" + +#include <cmath> +#include <cstdint> +#include <iostream> +#include <optional> + +using namespace std; +using namespace android; +using android::base::unique_fd; +using android::os::ParcelFileDescriptor; + +// defined in Rust +extern "C" AIBinder *rust_service(); + + +const int8_t TESTDATA_I8[4] = {-128, 0, 117, 127}; +const uint8_t TESTDATA_U8[4] = {0, 42, 117, 255}; +const char16_t TESTDATA_CHARS[4] = {0, 42, 117, numeric_limits<char16_t>::max()}; +const int32_t TESTDATA_I32[4] = {numeric_limits<int32_t>::min(), 0, 117, numeric_limits<int32_t>::max()}; +const int64_t TESTDATA_I64[4] = {numeric_limits<int64_t>::min(), 0, 117, numeric_limits<int64_t>::max()}; +const uint64_t TESTDATA_U64[4] = {0, 42, 117, numeric_limits<uint64_t>::max()}; +const float TESTDATA_FLOAT[4] = { + numeric_limits<float>::quiet_NaN(), + -numeric_limits<float>::infinity(), + 117.0, + numeric_limits<float>::infinity(), +}; +const double TESTDATA_DOUBLE[4] = { + numeric_limits<double>::quiet_NaN(), + -numeric_limits<double>::infinity(), + 117.0, + numeric_limits<double>::infinity(), +}; +const bool TESTDATA_BOOL[4] = {true, false, false, true}; +const char* const TESTDATA_STRS[4] = {"", nullptr, "test", ""}; + +static ::testing::Environment* gEnvironment; + +class SerializationEnvironment : public ::testing::Environment { +public: + void SetUp() override { + m_server = AIBinder_toPlatformBinder(rust_service()); + } + + sp<IBinder> getServer(void) { return m_server; } + +private: + sp<IBinder> m_server; +}; + + +class SerializationTest : public ::testing::Test { +protected: + void SetUp() override { + ASSERT_NE(gEnvironment, nullptr); + m_server = static_cast<SerializationEnvironment *>(gEnvironment)->getServer(); + } + + sp<IBinder> m_server; +}; + + +TEST_F(SerializationTest, SerializeBool) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + vector<bool> bools(begin(TESTDATA_BOOL), end(TESTDATA_BOOL)); + ASSERT_EQ(data.writeBool(true), OK); + ASSERT_EQ(data.writeBool(false), OK); + ASSERT_EQ(data.writeBoolVector(bools), OK); + ASSERT_EQ(data.writeBoolVector(nullopt), OK); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_BOOL, data, &reply), OK); + + vector<bool> read_bools; + optional<vector<bool>> maybe_bools; + ASSERT_EQ(reply.readBool(), true); + ASSERT_EQ(reply.readBool(), false); + ASSERT_EQ(reply.readBoolVector(&read_bools), OK); + ASSERT_EQ(read_bools, bools); + ASSERT_EQ(reply.readBoolVector(&maybe_bools), OK); + ASSERT_EQ(maybe_bools, nullopt); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); +} + +TEST_F(SerializationTest, SerializeByte) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + vector<int8_t> i8s(begin(TESTDATA_I8), end(TESTDATA_I8)); + vector<uint8_t> u8s(begin(TESTDATA_U8), end(TESTDATA_U8)); + data.writeByte(0); + data.writeByte(1); + data.writeByte(numeric_limits<int8_t>::max()); + data.writeByteVector(i8s); + data.writeByteVector(u8s); + data.writeByteVector(optional<vector<int8_t>>({})); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_BYTE, data, &reply), OK); + + vector<int8_t> read_i8s; + vector<uint8_t> read_u8s; + optional<vector<int8_t>> maybe_i8s; + ASSERT_EQ(reply.readByte(), 0); + ASSERT_EQ(reply.readByte(), 1); + ASSERT_EQ(reply.readByte(), numeric_limits<int8_t>::max()); + ASSERT_EQ(reply.readByteVector(&read_i8s), OK); + ASSERT_EQ(read_i8s, i8s); + ASSERT_EQ(reply.readByteVector(&read_u8s), OK); + ASSERT_EQ(read_u8s, u8s); + ASSERT_EQ(reply.readByteVector(&maybe_i8s), OK); + ASSERT_EQ(maybe_i8s, nullopt); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); +} + +TEST_F(SerializationTest, SerializeU16) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + vector<char16_t> chars(begin(TESTDATA_CHARS), end(TESTDATA_CHARS)); + data.writeChar(0); + data.writeChar(1); + data.writeChar(numeric_limits<char16_t>::max()); + data.writeCharVector(chars); + data.writeCharVector(nullopt); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_U16, data, &reply), OK); + + vector<char16_t> read_chars; + optional<vector<char16_t>> maybe_chars; + ASSERT_EQ(reply.readChar(), 0); + ASSERT_EQ(reply.readChar(), 1); + ASSERT_EQ(reply.readChar(), numeric_limits<char16_t>::max()); + ASSERT_EQ(reply.readCharVector(&read_chars), OK); + ASSERT_EQ(read_chars, chars); + ASSERT_EQ(reply.readCharVector(&maybe_chars), OK); + ASSERT_EQ(maybe_chars, nullopt); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); +} + +TEST_F(SerializationTest, SerializeI32) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + vector<int32_t> i32s(begin(TESTDATA_I32), end(TESTDATA_I32)); + data.writeInt32(0); + data.writeInt32(1); + data.writeInt32(numeric_limits<int32_t>::max()); + data.writeInt32Vector(i32s); + data.writeInt32Vector(nullopt); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_I32, data, &reply), OK); + + vector<int32_t> read_i32s; + optional<vector<int32_t>> maybe_i32s; + ASSERT_EQ(reply.readInt32(), 0); + ASSERT_EQ(reply.readInt32(), 1); + ASSERT_EQ(reply.readInt32(), numeric_limits<int32_t>::max()); + ASSERT_EQ(reply.readInt32Vector(&read_i32s), OK); + ASSERT_EQ(read_i32s, i32s); + ASSERT_EQ(reply.readInt32Vector(&maybe_i32s), OK); + ASSERT_EQ(maybe_i32s, nullopt); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); +} + +TEST_F(SerializationTest, SerializeI64) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + vector<int64_t> i64s(begin(TESTDATA_I64), end(TESTDATA_I64)); + data.writeInt64(0); + data.writeInt64(1); + data.writeInt64(numeric_limits<int64_t>::max()); + data.writeInt64Vector(i64s); + data.writeInt64Vector(nullopt); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_I64, data, &reply), OK); + + vector<int64_t> read_i64s; + optional<vector<int64_t>> maybe_i64s; + ASSERT_EQ(reply.readInt64(), 0); + ASSERT_EQ(reply.readInt64(), 1); + ASSERT_EQ(reply.readInt64(), numeric_limits<int64_t>::max()); + ASSERT_EQ(reply.readInt64Vector(&read_i64s), OK); + ASSERT_EQ(read_i64s, i64s); + ASSERT_EQ(reply.readInt64Vector(&maybe_i64s), OK); + ASSERT_EQ(maybe_i64s, nullopt); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); +} + +TEST_F(SerializationTest, SerializeU64) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + vector<uint64_t> u64s(begin(TESTDATA_U64), end(TESTDATA_U64)); + data.writeUint64(0); + data.writeUint64(1); + data.writeUint64(numeric_limits<uint64_t>::max()); + data.writeUint64Vector(u64s); + data.writeUint64Vector(nullopt); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_U64, data, &reply), OK); + + vector<uint64_t> read_u64s; + optional<vector<uint64_t>> maybe_u64s; + ASSERT_EQ(reply.readUint64(), 0); + ASSERT_EQ(reply.readUint64(), 1); + ASSERT_EQ(reply.readUint64(), numeric_limits<uint64_t>::max()); + ASSERT_EQ(reply.readUint64Vector(&read_u64s), OK); + ASSERT_EQ(read_u64s, u64s); + ASSERT_EQ(reply.readUint64Vector(&maybe_u64s), OK); + ASSERT_EQ(maybe_u64s, nullopt); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); +} + +TEST_F(SerializationTest, SerializeF32) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + vector<float> floats(begin(TESTDATA_FLOAT), end(TESTDATA_FLOAT)); + data.writeFloat(0); + data.writeFloatVector(floats); + data.writeFloatVector(nullopt); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_F32, data, &reply), OK); + + vector<float> read_floats; + optional<vector<float>> maybe_floats; + ASSERT_EQ(reply.readFloat(), 0); + ASSERT_EQ(reply.readFloatVector(&read_floats), OK); + ASSERT_TRUE(isnan(read_floats[0])); + ASSERT_EQ(read_floats[1], floats[1]); + ASSERT_EQ(read_floats[2], floats[2]); + ASSERT_EQ(read_floats[3], floats[3]); + ASSERT_EQ(reply.readFloatVector(&maybe_floats), OK); + ASSERT_EQ(maybe_floats, nullopt); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); +} + +TEST_F(SerializationTest, SerializeF64) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + vector<double> doubles(begin(TESTDATA_DOUBLE), end(TESTDATA_DOUBLE)); + data.writeDouble(0); + data.writeDoubleVector(doubles); + data.writeDoubleVector(nullopt); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_F64, data, &reply), OK); + + vector<double> read_doubles; + optional<vector<double>> maybe_doubles; + ASSERT_EQ(reply.readDouble(), 0); + ASSERT_EQ(reply.readDoubleVector(&read_doubles), OK); + ASSERT_TRUE(isnan(read_doubles[0])); + ASSERT_EQ(read_doubles[1], doubles[1]); + ASSERT_EQ(read_doubles[2], doubles[2]); + ASSERT_EQ(read_doubles[3], doubles[3]); + ASSERT_EQ(reply.readDoubleVector(&maybe_doubles), OK); + ASSERT_EQ(maybe_doubles, nullopt); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); +} + +TEST_F(SerializationTest, SerializeString) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + vector<optional<String16>> strings; + for (auto I = begin(TESTDATA_STRS), E = end(TESTDATA_STRS); I != E; ++I) { + if (*I == nullptr) { + strings.push_back(optional<String16>()); + } else { + strings.emplace_back(*I); + } + } + data.writeUtf8AsUtf16(string("testing")); + data.writeString16(nullopt); + data.writeString16Vector(strings); + data.writeString16Vector(nullopt); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_STRING, data, &reply), OK); + + optional<String16> maybe_string; + optional<vector<optional<String16>>> read_strings; + ASSERT_EQ(reply.readString16(), String16("testing")); + ASSERT_EQ(reply.readString16(&maybe_string), OK); + ASSERT_EQ(maybe_string, nullopt); + ASSERT_EQ(reply.readString16Vector(&read_strings), OK); + ASSERT_EQ(read_strings, strings); + ASSERT_EQ(reply.readString16Vector(&read_strings), OK); + ASSERT_EQ(read_strings, nullopt); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); +} + +TEST_F(SerializationTest, SerializeFileDescriptor) { + unique_fd out_file, in_file; + ASSERT_TRUE(base::Pipe(&out_file, &in_file)); + + vector<ParcelFileDescriptor> file_descriptors; + file_descriptors.push_back(ParcelFileDescriptor(std::move(out_file))); + file_descriptors.push_back(ParcelFileDescriptor(std::move(in_file))); + + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + data.writeParcelable(file_descriptors[0]); + data.writeParcelable(file_descriptors[1]); + data.writeParcelableVector(file_descriptors); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_FILE_DESCRIPTOR, data, &reply), OK); + + ParcelFileDescriptor returned_fd1, returned_fd2; + vector<ParcelFileDescriptor> returned_file_descriptors; + ASSERT_EQ(reply.readParcelable(&returned_fd1), OK); + ASSERT_EQ(reply.readParcelable(&returned_fd2), OK); + ASSERT_EQ(reply.readParcelableVector(&returned_file_descriptors), OK); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); + + base::WriteStringToFd("Testing", returned_fd2.get()); + base::WriteStringToFd("File", returned_file_descriptors[1].get()); + base::WriteStringToFd("Descriptors", file_descriptors[1].get()); + + string expected = "TestingFileDescriptors"; + vector<char> buf(expected.length()); + base::ReadFully(file_descriptors[0].release(), buf.data(), buf.size()); + ASSERT_EQ(expected, string(buf.data())); +} + +TEST_F(SerializationTest, SerializeIBinder) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + data.writeStrongBinder(m_server); + data.writeStrongBinder(nullptr); + data.writeStrongBinderVector({m_server, nullptr}); + data.writeStrongBinderVector(nullopt); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_IBINDER, data, &reply), OK); + + optional<vector<sp<IBinder>>> binders; + ASSERT_TRUE(reply.readStrongBinder()); + ASSERT_FALSE(reply.readStrongBinder()); + ASSERT_EQ(reply.readStrongBinderVector(&binders), OK); + ASSERT_EQ(binders->size(), 2); + ASSERT_TRUE((*binders)[0]); + ASSERT_FALSE((*binders)[1]); + ASSERT_EQ(reply.readStrongBinderVector(&binders), OK); + ASSERT_FALSE(binders); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); +} + +TEST_F(SerializationTest, SerializeStatus) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + + binder::Status::ok().writeToParcel(&data); + binder::Status::fromExceptionCode(binder::Status::EX_NULL_POINTER, "a status message") + .writeToParcel(&data); + binder::Status::fromServiceSpecificError(42, "a service-specific error").writeToParcel(&data); + + android::Parcel reply; + ASSERT_EQ(m_server->transact(TEST_STATUS, data, &reply), OK); + + binder::Status status; + + ASSERT_EQ(status.readFromParcel(reply), OK); + ASSERT_TRUE(status.isOk()); + + ASSERT_EQ(status.readFromParcel(reply), OK); + ASSERT_EQ(status.exceptionCode(), binder::Status::EX_NULL_POINTER); + ASSERT_EQ(status.exceptionMessage(), "a status message"); + + ASSERT_EQ(status.readFromParcel(reply), OK); + ASSERT_EQ(status.serviceSpecificErrorCode(), 42); + ASSERT_EQ(status.exceptionMessage(), "a service-specific error"); + + int32_t end; + ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA); +} + +// Test that failures from Rust properly propagate to C++ +TEST_F(SerializationTest, SerializeRustFail) { + android::Parcel data; + data.writeInterfaceToken(String16("read_parcel_test")); + ASSERT_EQ(m_server->transact(TEST_FAIL, data, nullptr), FAILED_TRANSACTION); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + gEnvironment = AddGlobalTestEnvironment(new SerializationEnvironment()); + ProcessState::self()->startThreadPool(); + return RUN_ALL_TESTS(); +} diff --git a/libs/binder/rust/tests/serialization.hpp b/libs/binder/rust/tests/serialization.hpp new file mode 100644 index 0000000000..0041608ae0 --- /dev/null +++ b/libs/binder/rust/tests/serialization.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include <binder/IBinder.h> + +using namespace android; + +enum Transaction { + TEST_BOOL = IBinder::FIRST_CALL_TRANSACTION, + TEST_BYTE, + TEST_U16, + TEST_I32, + TEST_I64, + TEST_U64, + TEST_F32, + TEST_F64, + TEST_STRING, + TEST_FILE_DESCRIPTOR, + TEST_IBINDER, + TEST_STATUS, + TEST_FAIL, +}; + +extern const int8_t TESTDATA_I8[4]; +extern const uint8_t TESTDATA_U8[4]; +extern const char16_t TESTDATA_CHARS[4]; +extern const int32_t TESTDATA_I32[4]; +extern const int64_t TESTDATA_I64[4]; +extern const uint64_t TESTDATA_U64[4]; +extern const float TESTDATA_FLOAT[4]; +extern const double TESTDATA_DOUBLE[4]; +extern const bool TESTDATA_BOOL[4]; +extern const char* const TESTDATA_STRS[4]; diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs new file mode 100644 index 0000000000..2ae13f4dd8 --- /dev/null +++ b/libs/binder/rust/tests/serialization.rs @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2020 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. + */ + +//! Included as a module in the binder crate internal tests for internal API +//! access. + +use binder::declare_binder_interface; +use binder::{ + Binder, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status, + StatusCode, TransactionCode, +}; +use binder::parcel::ParcelFileDescriptor; + +use std::ffi::{c_void, CStr, CString}; +use std::panic::{self, AssertUnwindSafe}; +use std::sync::Once; + +#[allow( + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused, + improper_ctypes, + missing_docs, + clippy::all +)] +mod bindings { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} + +static SERVICE_ONCE: Once = Once::new(); +static mut SERVICE: Option<SpIBinder> = None; + +/// Start binder service and return a raw AIBinder pointer to it. +/// +/// Safe to call multiple times, only creates the service once. +#[no_mangle] +pub extern "C" fn rust_service() -> *mut c_void { + unsafe { + SERVICE_ONCE.call_once(|| { + SERVICE = Some(BnReadParcelTest::new_binder(()).as_binder()); + }); + SERVICE.as_ref().unwrap().as_raw().cast() + } +} + +/// Empty interface just to use the declare_binder_interface macro +pub trait ReadParcelTest: Interface {} + +declare_binder_interface! { + ReadParcelTest["read_parcel_test"] { + native: BnReadParcelTest(on_transact), + proxy: BpReadParcelTest, + } +} + +impl ReadParcelTest for Binder<BnReadParcelTest> {} + +impl ReadParcelTest for BpReadParcelTest {} + +impl ReadParcelTest for () {} + +fn on_transact( + _service: &dyn ReadParcelTest, + code: TransactionCode, + parcel: &Parcel, + reply: &mut Parcel, +) -> Result<()> { + panic::catch_unwind(AssertUnwindSafe(|| transact_inner(code, parcel, reply))).unwrap_or_else( + |e| { + eprintln!("Failure in Rust: {:?}", e.downcast_ref::<String>()); + Err(StatusCode::FAILED_TRANSACTION) + }, + ) +} + +#[allow(clippy::float_cmp)] +fn transact_inner(code: TransactionCode, parcel: &Parcel, reply: &mut Parcel) -> Result<()> { + match code { + bindings::Transaction_TEST_BOOL => { + assert_eq!(parcel.read::<bool>()?, true); + assert_eq!(parcel.read::<bool>()?, false); + assert_eq!(parcel.read::<Vec<bool>>()?, unsafe { + bindings::TESTDATA_BOOL + }); + assert_eq!(parcel.read::<Option<Vec<bool>>>()?, None); + + reply.write(&true)?; + reply.write(&false)?; + reply.write(&unsafe { bindings::TESTDATA_BOOL }[..])?; + reply.write(&(None as Option<Vec<bool>>))?; + } + bindings::Transaction_TEST_BYTE => { + assert_eq!(parcel.read::<i8>()?, 0); + assert_eq!(parcel.read::<i8>()?, 1); + assert_eq!(parcel.read::<i8>()?, i8::max_value()); + assert_eq!(parcel.read::<Vec<i8>>()?, unsafe { bindings::TESTDATA_I8 }); + assert_eq!(parcel.read::<Vec<u8>>()?, unsafe { bindings::TESTDATA_U8 }); + assert_eq!(parcel.read::<Option<Vec<i8>>>()?, None); + + reply.write(&0i8)?; + reply.write(&1i8)?; + reply.write(&i8::max_value())?; + reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?; + reply.write(&unsafe { bindings::TESTDATA_U8 }[..])?; + reply.write(&(None as Option<Vec<i8>>))?; + } + bindings::Transaction_TEST_U16 => { + assert_eq!(parcel.read::<u16>()?, 0); + assert_eq!(parcel.read::<u16>()?, 1); + assert_eq!(parcel.read::<u16>()?, u16::max_value()); + assert_eq!(parcel.read::<Vec<u16>>()?, unsafe { + bindings::TESTDATA_CHARS + }); + assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None); + + reply.write(&0u16)?; + reply.write(&1u16)?; + reply.write(&u16::max_value())?; + reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?; + reply.write(&(None as Option<Vec<u16>>))?; + } + bindings::Transaction_TEST_I32 => { + assert_eq!(parcel.read::<i32>()?, 0); + assert_eq!(parcel.read::<i32>()?, 1); + assert_eq!(parcel.read::<i32>()?, i32::max_value()); + assert_eq!(parcel.read::<Vec<i32>>()?, unsafe { + bindings::TESTDATA_I32 + }); + assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None); + + reply.write(&0i32)?; + reply.write(&1i32)?; + reply.write(&i32::max_value())?; + reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?; + reply.write(&(None as Option<Vec<i32>>))?; + } + bindings::Transaction_TEST_I64 => { + assert_eq!(parcel.read::<i64>()?, 0); + assert_eq!(parcel.read::<i64>()?, 1); + assert_eq!(parcel.read::<i64>()?, i64::max_value()); + assert_eq!(parcel.read::<Vec<i64>>()?, unsafe { + bindings::TESTDATA_I64 + }); + assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None); + + reply.write(&0i64)?; + reply.write(&1i64)?; + reply.write(&i64::max_value())?; + reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?; + reply.write(&(None as Option<Vec<i64>>))?; + } + bindings::Transaction_TEST_U64 => { + assert_eq!(parcel.read::<u64>()?, 0); + assert_eq!(parcel.read::<u64>()?, 1); + assert_eq!(parcel.read::<u64>()?, u64::max_value()); + assert_eq!(parcel.read::<Vec<u64>>()?, unsafe { + bindings::TESTDATA_U64 + }); + assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None); + + reply.write(&0u64)?; + reply.write(&1u64)?; + reply.write(&u64::max_value())?; + reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?; + reply.write(&(None as Option<Vec<u64>>))?; + } + bindings::Transaction_TEST_F32 => { + assert_eq!(parcel.read::<f32>()?, 0f32); + let floats = parcel.read::<Vec<f32>>()?; + assert!(floats[0].is_nan()); + assert_eq!(floats[1..], unsafe { bindings::TESTDATA_FLOAT }[1..]); + assert_eq!(parcel.read::<Option<Vec<f32>>>()?, None); + + reply.write(&0f32)?; + reply.write(&unsafe { bindings::TESTDATA_FLOAT }[..])?; + reply.write(&(None as Option<Vec<f32>>))?; + } + bindings::Transaction_TEST_F64 => { + assert_eq!(parcel.read::<f64>()?, 0f64); + let doubles = parcel.read::<Vec<f64>>()?; + assert!(doubles[0].is_nan()); + assert_eq!(doubles[1..], unsafe { bindings::TESTDATA_DOUBLE }[1..]); + assert_eq!(parcel.read::<Option<Vec<f64>>>()?, None); + + reply.write(&0f64)?; + reply.write(&unsafe { bindings::TESTDATA_DOUBLE }[..])?; + reply.write(&(None as Option<Vec<f64>>))?; + } + bindings::Transaction_TEST_STRING => { + let s: Option<String> = parcel.read()?; + assert_eq!(s.as_deref(), Some("testing")); + let s: Option<String> = parcel.read()?; + assert_eq!(s, None); + let s: Option<Vec<Option<String>>> = parcel.read()?; + for (s, expected) in s + .unwrap() + .iter() + .zip(unsafe { bindings::TESTDATA_STRS }.iter()) + { + let expected = unsafe { + expected + .as_ref() + .and_then(|e| CStr::from_ptr(e).to_str().ok()) + }; + assert_eq!(s.as_deref(), expected); + } + let s: Option<Vec<Option<String>>> = parcel.read()?; + assert_eq!(s, None); + + let strings: Vec<Option<String>> = unsafe { + bindings::TESTDATA_STRS + .iter() + .map(|s| { + s.as_ref().map(|s| { + CStr::from_ptr(s) + .to_str() + .expect("String was not UTF-8") + .to_owned() + }) + }) + .collect() + }; + + reply.write("testing")?; + reply.write(&(None as Option<String>))?; + reply.write(&strings)?; + reply.write(&(None as Option<Vec<String>>))?; + } + bindings::Transaction_TEST_FILE_DESCRIPTOR => { + let file1 = parcel.read::<ParcelFileDescriptor>()?; + let file2 = parcel.read::<ParcelFileDescriptor>()?; + let files = parcel.read::<Vec<Option<ParcelFileDescriptor>>>()?; + + reply.write(&file1)?; + reply.write(&file2)?; + reply.write(&files)?; + } + bindings::Transaction_TEST_IBINDER => { + assert!(parcel.read::<Option<SpIBinder>>()?.is_some()); + assert!(parcel.read::<Option<SpIBinder>>()?.is_none()); + let ibinders = parcel.read::<Option<Vec<Option<SpIBinder>>>>()?.unwrap(); + assert_eq!(ibinders.len(), 2); + assert!(ibinders[0].is_some()); + assert!(ibinders[1].is_none()); + assert!(parcel.read::<Option<Vec<Option<SpIBinder>>>>()?.is_none()); + + let service = unsafe { + SERVICE + .as_ref() + .expect("Global binder service not initialized") + .clone() + }; + reply.write(&service)?; + reply.write(&(None as Option<&SpIBinder>))?; + reply.write(&[Some(&service), None][..])?; + reply.write(&(None as Option<Vec<Option<&SpIBinder>>>))?; + } + bindings::Transaction_TEST_STATUS => { + let status: Status = parcel.read()?; + assert!(status.is_ok()); + let status: Status = parcel.read()?; + assert_eq!(status.exception_code(), ExceptionCode::NULL_POINTER); + assert_eq!( + status.get_description(), + "Status(-4, EX_NULL_POINTER): 'a status message'" + ); + let status: Status = parcel.read()?; + assert_eq!(status.service_specific_error(), 42); + assert_eq!( + status.get_description(), + "Status(-8, EX_SERVICE_SPECIFIC): '42: a service-specific error'" + ); + + reply.write(&Status::ok())?; + reply.write(&Status::new_exception( + ExceptionCode::NULL_POINTER, + Some(&CString::new("a status message").unwrap()), + ))?; + reply.write(&Status::new_service_specific_error( + 42, + Some(&CString::new("a service-specific error").unwrap()), + ))?; + } + bindings::Transaction_TEST_FAIL => { + panic!("Testing expected failure"); + } + _ => return Err(StatusCode::UNKNOWN_TRANSACTION), + } + + assert_eq!(parcel.read::<i32>(), Err(StatusCode::NOT_ENOUGH_DATA)); + Ok(()) +} |