diff options
-rw-r--r-- | include/binder/IpPrefix.h | 87 | ||||
-rw-r--r-- | include/binder/Map.h | 39 | ||||
-rw-r--r-- | include/binder/Parcel.h | 15 | ||||
-rw-r--r-- | include/binder/Value.h | 186 | ||||
-rw-r--r-- | include/private/binder/ParcelValTypes.h | 45 | ||||
-rw-r--r-- | libs/binder/Android.bp | 2 | ||||
-rw-r--r-- | libs/binder/IpPrefix.cpp | 174 | ||||
-rw-r--r-- | libs/binder/Parcel.cpp | 123 | ||||
-rw-r--r-- | libs/binder/PersistableBundle.cpp | 17 | ||||
-rw-r--r-- | libs/binder/Value.cpp | 418 | ||||
-rw-r--r-- | libs/binder/tests/Android.bp | 9 | ||||
-rw-r--r-- | libs/binder/tests/binderValueTypeTest.cpp | 111 |
12 files changed, 1211 insertions, 15 deletions
diff --git a/include/binder/IpPrefix.h b/include/binder/IpPrefix.h new file mode 100644 index 0000000000..f8c80dcb4d --- /dev/null +++ b/include/binder/IpPrefix.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_IP_PREFIX_H +#define ANDROID_IP_PREFIX_H + +#include <netinet/in.h> + +#include <binder/Parcelable.h> +#include <utils/String16.h> +#include <utils/StrongPointer.h> + +namespace android { + +namespace net { + +/* + * C++ implementation of the Java class android.net.IpPrefix + */ +class IpPrefix : public Parcelable { +public: + IpPrefix() = default; + virtual ~IpPrefix() = default; + IpPrefix(const IpPrefix& prefix) = default; + + IpPrefix(const struct in6_addr& addr, int32_t plen): + mUnion(addr), mPrefixLength(plen), mIsIpv6(true) { } + + IpPrefix(const struct in_addr& addr, int32_t plen): + mUnion(addr), mPrefixLength(plen), mIsIpv6(false) { } + + bool getAddressAsIn6Addr(struct in6_addr* addr) const; + bool getAddressAsInAddr(struct in_addr* addr) const; + + const struct in6_addr& getAddressAsIn6Addr() const; + const struct in_addr& getAddressAsInAddr() const; + + bool isIpv6() const; + bool isIpv4() const; + + int32_t getPrefixLength() const; + + void setAddress(const struct in6_addr& addr); + void setAddress(const struct in_addr& addr); + + void setPrefixLength(int32_t prefix); + + friend bool operator==(const IpPrefix& lhs, const IpPrefix& rhs); + + friend bool operator!=(const IpPrefix& lhs, const IpPrefix& rhs) { + return !(lhs == rhs); + } + +public: + // Overrides + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + +private: + union InternalUnion { + InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }; + InternalUnion(const struct in_addr &addr):mInAddr(addr) { }; + struct in6_addr mIn6Addr; + struct in_addr mInAddr; + } mUnion; + int32_t mPrefixLength; + bool mIsIpv6; +}; + +} // namespace net + +} // namespace android + +#endif // ANDROID_IP_PREFIX_H diff --git a/include/binder/Map.h b/include/binder/Map.h new file mode 100644 index 0000000000..96a4f8a2a5 --- /dev/null +++ b/include/binder/Map.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_MAP_H +#define ANDROID_MAP_H + +#include <map> +#include <string> + +// --------------------------------------------------------------------------- +namespace android { +namespace binder { + +class Value; + +/** + * Convenience typedef for ::std::map<::std::string,::android::binder::Value> + */ +typedef ::std::map<::std::string, Value> Map; + +} // namespace binder +} // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_MAP_H diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h index 69de13690b..cf2fa47df4 100644 --- a/include/binder/Parcel.h +++ b/include/binder/Parcel.h @@ -31,6 +31,7 @@ #include <binder/IInterface.h> #include <binder/Parcelable.h> +#include <binder/Map.h> // --------------------------------------------------------------------------- namespace android { @@ -43,6 +44,10 @@ class ProcessState; class String8; class TextOutput; +namespace binder { +class Value; +}; + class Parcel { friend class IPCThreadState; public: @@ -162,6 +167,8 @@ public: status_t writeParcelable(const Parcelable& parcelable); + status_t writeValue(const binder::Value& value); + template<typename T> status_t write(const Flattenable<T>& val); @@ -173,6 +180,9 @@ public: template<typename T> status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val); + status_t writeMap(const binder::Map& map); + status_t writeNullableMap(const std::unique_ptr<binder::Map>& map); + // Place a native_handle into the parcel (the native_handle's file- // descriptors are dup'ed, so it is safe to delete the native_handle // when this function returns). @@ -278,6 +288,8 @@ public: template<typename T> status_t readParcelable(std::unique_ptr<T>* parcelable) const; + status_t readValue(binder::Value* value) const; + template<typename T> status_t readStrongBinder(sp<T>* val) const; @@ -321,6 +333,9 @@ public: template<typename T> status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const; + status_t readMap(binder::Map* map)const; + status_t readNullableMap(std::unique_ptr<binder::Map>* map) const; + // Like Parcel.java's readExceptionCode(). Reads the first int32 // off of a Parcel's header, returning 0 or the negative error // code on exceptions, but also deals with skipping over rich diff --git a/include/binder/Value.h b/include/binder/Value.h new file mode 100644 index 0000000000..4dee3d86b0 --- /dev/null +++ b/include/binder/Value.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_VALUE_H +#define ANDROID_VALUE_H + +#include <stdint.h> +#include <map> +#include <set> +#include <vector> +#include <string> + +#include <binder/Parcelable.h> +#include <binder/PersistableBundle.h> +#include <binder/Map.h> +#include <utils/String8.h> +#include <utils/String16.h> +#include <utils/StrongPointer.h> + +namespace android { + +class Parcel; + +namespace binder { + +/** + * A limited C++ generic type. The purpose of this class is to allow C++ + * programs to make use of (or implement) Binder interfaces which make use + * the Java "Object" generic type (either via the use of the Map type or + * some other mechanism). + * + * This class only supports a limited set of types, but additional types + * may be easily added to this class in the future as needed---without + * breaking binary compatability. + * + * This class was written in such a way as to help avoid type errors by + * giving each type their own explicity-named accessor methods (rather than + * overloaded methods). + * + * When reading or writing this class to a Parcel, use the `writeValue()` + * and `readValue()` methods. + */ +class Value { +public: + Value(); + virtual ~Value(); + + Value& swap(Value &); + + bool empty() const; + + void clear(); + +#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO + const std::type_info& type() const; +#endif + + int32_t parcelType() const; + + bool operator==(const Value& rhs) const; + bool operator!=(const Value& rhs) const { return !this->operator==(rhs); } + + Value(const Value& value); + Value(const bool& value); + Value(const int8_t& value); + Value(const int32_t& value); + Value(const int64_t& value); + Value(const double& value); + Value(const String16& value); + Value(const std::vector<bool>& value); + Value(const std::vector<uint8_t>& value); + Value(const std::vector<int32_t>& value); + Value(const std::vector<int64_t>& value); + Value(const std::vector<double>& value); + Value(const std::vector<String16>& value); + Value(const os::PersistableBundle& value); + Value(const binder::Map& value); + + Value& operator=(const Value& rhs); + Value& operator=(const int8_t& rhs); + Value& operator=(const bool& rhs); + Value& operator=(const int32_t& rhs); + Value& operator=(const int64_t& rhs); + Value& operator=(const double& rhs); + Value& operator=(const String16& rhs); + Value& operator=(const std::vector<bool>& rhs); + Value& operator=(const std::vector<uint8_t>& rhs); + Value& operator=(const std::vector<int32_t>& rhs); + Value& operator=(const std::vector<int64_t>& rhs); + Value& operator=(const std::vector<double>& rhs); + Value& operator=(const std::vector<String16>& rhs); + Value& operator=(const os::PersistableBundle& rhs); + Value& operator=(const binder::Map& rhs); + + void putBoolean(const bool& value); + void putByte(const int8_t& value); + void putInt(const int32_t& value); + void putLong(const int64_t& value); + void putDouble(const double& value); + void putString(const String16& value); + void putBooleanVector(const std::vector<bool>& value); + void putByteVector(const std::vector<uint8_t>& value); + void putIntVector(const std::vector<int32_t>& value); + void putLongVector(const std::vector<int64_t>& value); + void putDoubleVector(const std::vector<double>& value); + void putStringVector(const std::vector<String16>& value); + void putPersistableBundle(const os::PersistableBundle& value); + void putMap(const binder::Map& value); + + bool getBoolean(bool* out) const; + bool getByte(int8_t* out) const; + bool getInt(int32_t* out) const; + bool getLong(int64_t* out) const; + bool getDouble(double* out) const; + bool getString(String16* out) const; + bool getBooleanVector(std::vector<bool>* out) const; + bool getByteVector(std::vector<uint8_t>* out) const; + bool getIntVector(std::vector<int32_t>* out) const; + bool getLongVector(std::vector<int64_t>* out) const; + bool getDoubleVector(std::vector<double>* out) const; + bool getStringVector(std::vector<String16>* out) const; + bool getPersistableBundle(os::PersistableBundle* out) const; + bool getMap(binder::Map* out) const; + + bool isBoolean() const; + bool isByte() const; + bool isInt() const; + bool isLong() const; + bool isDouble() const; + bool isString() const; + bool isBooleanVector() const; + bool isByteVector() const; + bool isIntVector() const; + bool isLongVector() const; + bool isDoubleVector() const; + bool isStringVector() const; + bool isPersistableBundle() const; + bool isMap() const; + + // String Convenience Adapters + // --------------------------- + + Value(const String8& value): Value(String16(value)) { } + Value(const ::std::string& value): Value(String8(value.c_str())) { } + void putString(const String8& value) { return putString(String16(value)); } + void putString(const ::std::string& value) { return putString(String8(value.c_str())); } + Value& operator=(const String8& rhs) { return *this = String16(rhs); } + Value& operator=(const ::std::string& rhs) { return *this = String8(rhs.c_str()); } + bool getString(String8* out) const; + bool getString(::std::string* out) const; + +private: + + // This allows ::android::Parcel to call the two methods below. + friend class ::android::Parcel; + + // This is called by ::android::Parcel::writeValue() + status_t writeToParcel(Parcel* parcel) const; + + // This is called by ::android::Parcel::readValue() + status_t readFromParcel(const Parcel* parcel); + + template<typename T> class Content; + class ContentBase; + + ContentBase* mContent; +}; + +} // namespace binder + +} // namespace android + +#endif // ANDROID_VALUE_H diff --git a/include/private/binder/ParcelValTypes.h b/include/private/binder/ParcelValTypes.h new file mode 100644 index 0000000000..666d22a57f --- /dev/null +++ b/include/private/binder/ParcelValTypes.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 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. + */ + +namespace android { +namespace binder { + +// Keep in sync with frameworks/base/core/java/android/os/Parcel.java. +enum { + VAL_NULL = -1, + VAL_STRING = 0, + VAL_INTEGER = 1, + VAL_MAP = 2, + VAL_BUNDLE = 3, + VAL_PARCELABLE = 4, + VAL_SHORT = 5, + VAL_LONG = 6, + VAL_DOUBLE = 8, + VAL_BOOLEAN = 9, + VAL_BYTEARRAY = 13, + VAL_STRINGARRAY = 14, + VAL_IBINDER = 15, + VAL_INTARRAY = 18, + VAL_LONGARRAY = 19, + VAL_BYTE = 20, + VAL_SERIALIZABLE = 21, + VAL_BOOLEANARRAY = 23, + VAL_PERSISTABLEBUNDLE = 25, + VAL_DOUBLEARRAY = 28, +}; + +} // namespace binder +} // namespace android diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 62b75ba5b7..93b8684ec4 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -45,6 +45,8 @@ cc_library { "Static.cpp", "Status.cpp", "TextOutput.cpp", + "IpPrefix.cpp", + "Value.cpp", ], cflags: [ diff --git a/libs/binder/IpPrefix.cpp b/libs/binder/IpPrefix.cpp new file mode 100644 index 0000000000..3a8a63c46e --- /dev/null +++ b/libs/binder/IpPrefix.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2015 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 "IpPrefix" + +#include <binder/IpPrefix.h> +#include <vector> + +#include <binder/IBinder.h> +#include <binder/Parcel.h> +#include <log/log.h> +#include <utils/Errors.h> + +using android::BAD_TYPE; +using android::BAD_VALUE; +using android::NO_ERROR; +using android::Parcel; +using android::status_t; +using android::UNEXPECTED_NULL; +using namespace ::android::binder; + +namespace android { + +namespace net { + +#define RETURN_IF_FAILED(calledOnce) \ + { \ + status_t returnStatus = calledOnce; \ + if (returnStatus) { \ + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return returnStatus; \ + } \ + } + +status_t IpPrefix::writeToParcel(Parcel* parcel) const { + /* + * Keep implementation in sync with writeToParcel() in + * frameworks/base/core/java/android/net/IpPrefix.java. + */ + std::vector<uint8_t> byte_vector; + + if (mIsIpv6) { + const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mIn6Addr); + byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr)); + } else { + const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mInAddr); + byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr)); + } + + RETURN_IF_FAILED(parcel->writeByteVector(byte_vector)); + RETURN_IF_FAILED(parcel->writeInt32(static_cast<int32_t>(mPrefixLength))); + + return NO_ERROR; +} + +status_t IpPrefix::readFromParcel(const Parcel* parcel) { + /* + * Keep implementation in sync with readFromParcel() in + * frameworks/base/core/java/android/net/IpPrefix.java. + */ + std::vector<uint8_t> byte_vector; + + RETURN_IF_FAILED(parcel->readByteVector(&byte_vector)); + RETURN_IF_FAILED(parcel->readInt32(&mPrefixLength)); + + if (byte_vector.size() == 16) { + mIsIpv6 = true; + memcpy((void*)&mUnion.mIn6Addr, &byte_vector[0], sizeof(mUnion.mIn6Addr)); + + } else if (byte_vector.size() == 4) { + mIsIpv6 = false; + memcpy((void*)&mUnion.mInAddr, &byte_vector[0], sizeof(mUnion.mInAddr)); + + } else { + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return BAD_VALUE; + } + + return NO_ERROR; +} + +const struct in6_addr& IpPrefix::getAddressAsIn6Addr() const +{ + return mUnion.mIn6Addr; +} + +const struct in_addr& IpPrefix::getAddressAsInAddr() const +{ + return mUnion.mInAddr; +} + +bool IpPrefix::getAddressAsIn6Addr(struct in6_addr* addr) const +{ + if (isIpv6()) { + *addr = mUnion.mIn6Addr; + return true; + } + return false; +} + +bool IpPrefix::getAddressAsInAddr(struct in_addr* addr) const +{ + if (isIpv4()) { + *addr = mUnion.mInAddr; + return true; + } + return false; +} + +bool IpPrefix::isIpv6() const +{ + return mIsIpv6; +} + +bool IpPrefix::isIpv4() const +{ + return !mIsIpv6; +} + +int32_t IpPrefix::getPrefixLength() const +{ + return mPrefixLength; +} + +void IpPrefix::setAddress(const struct in6_addr& addr) +{ + mUnion.mIn6Addr = addr; + mIsIpv6 = true; +} + +void IpPrefix::setAddress(const struct in_addr& addr) +{ + mUnion.mInAddr = addr; + mIsIpv6 = false; +} + +void IpPrefix::setPrefixLength(int32_t prefix) +{ + mPrefixLength = prefix; +} + +bool operator==(const IpPrefix& lhs, const IpPrefix& rhs) +{ + if (lhs.mIsIpv6 != rhs.mIsIpv6) { + return false; + } + + if (lhs.mPrefixLength != rhs.mPrefixLength) { + return false; + } + + if (lhs.mIsIpv6) { + return 0 == memcmp(lhs.mUnion.mIn6Addr.s6_addr, rhs.mUnion.mIn6Addr.s6_addr, sizeof(struct in6_addr)); + } + + return 0 == memcmp(&lhs.mUnion.mInAddr, &rhs.mUnion.mInAddr, sizeof(struct in_addr)); +} + +} // namespace net + +} // namespace android diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index a6ccb53852..da943051a6 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -37,6 +37,7 @@ #include <binder/ProcessState.h> #include <binder/Status.h> #include <binder/TextOutput.h> +#include <binder/Value.h> #include <cutils/ashmem.h> #include <utils/Debug.h> @@ -1106,6 +1107,10 @@ status_t Parcel::writeParcelable(const Parcelable& parcelable) { return parcelable.writeToParcel(this); } +status_t Parcel::writeValue(const binder::Value& value) { + return value.writeToParcel(this); +} + status_t Parcel::writeNativeHandle(const native_handle* handle) { if (!handle || handle->version != sizeof(native_handle)) @@ -1330,6 +1335,120 @@ status_t Parcel::writeNoException() return status.writeToParcel(this); } +status_t Parcel::writeMap(const ::android::binder::Map& map_in) +{ + using ::std::map; + using ::android::binder::Value; + using ::android::binder::Map; + + Map::const_iterator iter; + status_t ret; + + ret = writeInt32(map_in.size()); + + if (ret != NO_ERROR) { + return ret; + } + + for (iter = map_in.begin(); iter != map_in.end(); ++iter) { + ret = writeValue(Value(iter->first)); + if (ret != NO_ERROR) { + return ret; + } + + ret = writeValue(iter->second); + if (ret != NO_ERROR) { + return ret; + } + } + + return ret; +} + +status_t Parcel::writeNullableMap(const std::unique_ptr<binder::Map>& map) +{ + if (map == NULL) { + return writeInt32(-1); + } + + return writeMap(*map.get()); +} + +status_t Parcel::readMap(::android::binder::Map* map_out)const +{ + using ::std::map; + using ::android::String16; + using ::android::String8; + using ::android::binder::Value; + using ::android::binder::Map; + + status_t ret = NO_ERROR; + int32_t count; + + ret = readInt32(&count); + if (ret != NO_ERROR) { + return ret; + } + + if (count < 0) { + ALOGE("readMap: Unexpected count: %d", count); + return (count == -1) + ? UNEXPECTED_NULL + : BAD_VALUE; + } + + map_out->clear(); + + while (count--) { + Map::key_type key; + Value value; + + ret = readValue(&value); + if (ret != NO_ERROR) { + return ret; + } + + if (!value.getString(&key)) { + ALOGE("readMap: Key type not a string (parcelType = %d)", value.parcelType()); + return BAD_VALUE; + } + + ret = readValue(&value); + if (ret != NO_ERROR) { + return ret; + } + + (*map_out)[key] = value; + } + + return ret; +} + +status_t Parcel::readNullableMap(std::unique_ptr<binder::Map>* map) const +{ + const size_t start = dataPosition(); + int32_t count; + status_t status = readInt32(&count); + map->reset(); + + if (status != OK || count == -1) { + return status; + } + + setDataPosition(start); + map->reset(new binder::Map()); + + status = readMap(map->get()); + + if (status != OK) { + map->reset(); + } + + return status; +} + + + void Parcel::remove(size_t /*start*/, size_t /*amt*/) { LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); @@ -1950,6 +2069,10 @@ status_t Parcel::readParcelable(Parcelable* parcelable) const { return parcelable->readFromParcel(this); } +status_t Parcel::readValue(binder::Value* value) const { + return value->readFromParcel(this); +} + int32_t Parcel::readExceptionCode() const { binder::Status status; diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp index e7078baa82..d617b5a179 100644 --- a/libs/binder/PersistableBundle.cpp +++ b/libs/binder/PersistableBundle.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "PersistableBundle" #include <binder/PersistableBundle.h> +#include <private/binder/ParcelValTypes.h> #include <limits> @@ -35,27 +36,13 @@ using android::UNEXPECTED_NULL; using std::map; using std::set; using std::vector; +using namespace ::android::binder; enum { // Keep in sync with BUNDLE_MAGIC in frameworks/base/core/java/android/os/BaseBundle.java. BUNDLE_MAGIC = 0x4C444E42, }; -enum { - // Keep in sync with frameworks/base/core/java/android/os/Parcel.java. - VAL_STRING = 0, - VAL_INTEGER = 1, - VAL_LONG = 6, - VAL_DOUBLE = 8, - VAL_BOOLEAN = 9, - VAL_STRINGARRAY = 14, - VAL_INTARRAY = 18, - VAL_LONGARRAY = 19, - VAL_BOOLEANARRAY = 23, - VAL_PERSISTABLEBUNDLE = 25, - VAL_DOUBLEARRAY = 28, -}; - namespace { template <typename T> bool getValue(const android::String16& key, T* out, const map<android::String16, T>& map) { diff --git a/libs/binder/Value.cpp b/libs/binder/Value.cpp new file mode 100644 index 0000000000..fd1dfd5ada --- /dev/null +++ b/libs/binder/Value.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2015 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 "Value" + +#include <binder/Value.h> + +#include <limits> + +#include <binder/IBinder.h> +#include <binder/Parcel.h> +#include <binder/Map.h> +#include <private/binder/ParcelValTypes.h> +#include <log/log.h> +#include <utils/Errors.h> + +using android::BAD_TYPE; +using android::BAD_VALUE; +using android::NO_ERROR; +using android::UNEXPECTED_NULL; +using android::Parcel; +using android::sp; +using android::status_t; +using std::map; +using std::set; +using std::vector; +using android::binder::Value; +using android::IBinder; +using android::os::PersistableBundle; +using namespace android::binder; + +// ==================================================================== + +#define RETURN_IF_FAILED(calledOnce) \ + do { \ + status_t returnStatus = calledOnce; \ + if (returnStatus) { \ + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return returnStatus; \ + } \ + } while(false) + +// ==================================================================== + +/* These `internal_type_ptr()` functions allow this + * class to work without C++ RTTI support. This technique + * only works properly when called directly from this file, + * but that is OK because that is the only place we will + * be calling them from. */ +template<class T> const void* internal_type_ptr() +{ + static const T *marker; + return (void*)▮ +} + +/* Allows the type to be specified by the argument + * instead of inside angle brackets. */ +template<class T> const void* internal_type_ptr(const T&) +{ + return internal_type_ptr<T>(); +} + +// ==================================================================== + +namespace android { + +namespace binder { + +class Value::ContentBase { +public: + virtual ~ContentBase() = default; + virtual const void* type_ptr() const = 0; + virtual ContentBase * clone() const = 0; + virtual bool operator==(const ContentBase& rhs) const = 0; + +#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO + virtual const std::type_info &type() const = 0; +#endif + + template<typename T> bool get(T* out) const; +}; + +/* This is the actual class that holds the value. */ +template<typename T> class Value::Content : public Value::ContentBase { +public: + Content() = default; + Content(const T & value) : mValue(value) { } + + virtual ~Content() = default; + +#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO + virtual const std::type_info &type() const override + { + return typeid(T); + } +#endif + + virtual const void* type_ptr() const override + { + return internal_type_ptr<T>(); + } + + virtual ContentBase * clone() const override + { + return new Content(mValue); + }; + + virtual bool operator==(const ContentBase& rhs) const override + { + if (type_ptr() != rhs.type_ptr()) { + return false; + } + return mValue == static_cast<const Content<T>* >(&rhs)->mValue; + } + + T mValue; +}; + +template<typename T> bool Value::ContentBase::get(T* out) const +{ + if (internal_type_ptr(*out) != type_ptr()) + { + return false; + } + + *out = static_cast<const Content<T>*>(this)->mValue; + + return true; +} + +// ==================================================================== + +Value::Value() : mContent(NULL) +{ +} + +Value::Value(const Value& value) + : mContent(value.mContent ? value.mContent->clone() : NULL) +{ +} + +Value::~Value() +{ + delete mContent; +} + +bool Value::operator==(const Value& rhs) const +{ + const Value& lhs(*this); + + if (lhs.empty() && rhs.empty()) { + return true; + } + + if ( (lhs.mContent == NULL) + || (rhs.mContent == NULL) + ) { + return false; + } + + return *lhs.mContent == *rhs.mContent; +} + +Value& Value::swap(Value &rhs) +{ + std::swap(mContent, rhs.mContent); + return *this; +} + +Value& Value::operator=(const Value& rhs) +{ + delete mContent; + mContent = rhs.mContent + ? rhs.mContent->clone() + : NULL; + return *this; +} + +bool Value::empty() const +{ + return mContent == NULL; +} + +void Value::clear() +{ + delete mContent; + mContent = NULL; +} + +int32_t Value::parcelType() const +{ + const void* t_info(mContent ? mContent->type_ptr() : NULL); + + if (t_info == internal_type_ptr<bool>()) return VAL_BOOLEAN; + if (t_info == internal_type_ptr<uint8_t>()) return VAL_BYTE; + if (t_info == internal_type_ptr<int32_t>()) return VAL_INTEGER; + if (t_info == internal_type_ptr<int64_t>()) return VAL_LONG; + if (t_info == internal_type_ptr<double>()) return VAL_DOUBLE; + if (t_info == internal_type_ptr<String16>()) return VAL_STRING; + + if (t_info == internal_type_ptr<vector<bool>>()) return VAL_BOOLEANARRAY; + if (t_info == internal_type_ptr<vector<uint8_t>>()) return VAL_BYTEARRAY; + if (t_info == internal_type_ptr<vector<int32_t>>()) return VAL_INTARRAY; + if (t_info == internal_type_ptr<vector<int64_t>>()) return VAL_LONGARRAY; + if (t_info == internal_type_ptr<vector<double>>()) return VAL_DOUBLEARRAY; + if (t_info == internal_type_ptr<vector<String16>>()) return VAL_STRINGARRAY; + + if (t_info == internal_type_ptr<Map>()) return VAL_MAP; + if (t_info == internal_type_ptr<PersistableBundle>()) return VAL_PERSISTABLEBUNDLE; + + return VAL_NULL; +} + +#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO +const std::type_info& Value::type() const +{ + return mContent != NULL + ? mContent->type() + : typeid(void); +} +#endif // ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO + +#define DEF_TYPE_ACCESSORS(T, TYPENAME) \ + bool Value::is ## TYPENAME() const \ + { \ + return mContent \ + ? internal_type_ptr<T>() == mContent->type_ptr() \ + : false; \ + } \ + bool Value::get ## TYPENAME(T* out) const \ + { \ + return mContent \ + ? mContent->get(out) \ + : false; \ + } \ + void Value::put ## TYPENAME(const T& in) \ + { \ + *this = in; \ + } \ + Value& Value::operator=(const T& rhs) \ + { \ + delete mContent; \ + mContent = new Content< T >(rhs); \ + return *this; \ + } \ + Value::Value(const T& value) \ + : mContent(new Content< T >(value)) \ + { } + +DEF_TYPE_ACCESSORS(bool, Boolean) +DEF_TYPE_ACCESSORS(int8_t, Byte) +DEF_TYPE_ACCESSORS(int32_t, Int) +DEF_TYPE_ACCESSORS(int64_t, Long) +DEF_TYPE_ACCESSORS(double, Double) +DEF_TYPE_ACCESSORS(String16, String) + +DEF_TYPE_ACCESSORS(std::vector<bool>, BooleanVector) +DEF_TYPE_ACCESSORS(std::vector<uint8_t>, ByteVector) +DEF_TYPE_ACCESSORS(std::vector<int32_t>, IntVector) +DEF_TYPE_ACCESSORS(std::vector<int64_t>, LongVector) +DEF_TYPE_ACCESSORS(std::vector<double>, DoubleVector) +DEF_TYPE_ACCESSORS(std::vector<String16>, StringVector) + +DEF_TYPE_ACCESSORS(::android::binder::Map, Map) +DEF_TYPE_ACCESSORS(PersistableBundle, PersistableBundle) + +bool Value::getString(String8* out) const +{ + String16 val; + bool ret = getString(&val); + if (ret) { + *out = String8(val); + } + return ret; +} + +bool Value::getString(::std::string* out) const +{ + String8 val; + bool ret = getString(&val); + if (ret) { + *out = val.string(); + } + return ret; +} + +status_t Value::writeToParcel(Parcel* parcel) const +{ + // This implementation needs to be kept in sync with the writeValue + // implementation in frameworks/base/core/java/android/os/Parcel.java + +#define BEGIN_HANDLE_WRITE() \ + do { \ + const void* t_info(mContent?mContent->type_ptr():NULL); \ + if (false) { } +#define HANDLE_WRITE_TYPE(T, TYPEVAL, TYPEMETHOD) \ + else if (t_info == internal_type_ptr<T>()) { \ + RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \ + RETURN_IF_FAILED(parcel->TYPEMETHOD(static_cast<const Content<T>*>(mContent)->mValue)); \ + } +#define HANDLE_WRITE_PARCELABLE(T, TYPEVAL) \ + else if (t_info == internal_type_ptr<T>()) { \ + RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \ + RETURN_IF_FAILED(static_cast<const Content<T>*>(mContent)->mValue.writeToParcel(parcel)); \ + } +#define END_HANDLE_WRITE() \ + else { \ + ALOGE("writeToParcel: Type not supported"); \ + return BAD_TYPE; \ + } \ + } while (false); + + BEGIN_HANDLE_WRITE() + + HANDLE_WRITE_TYPE(bool, VAL_BOOLEAN, writeBool) + HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte) + HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte) + HANDLE_WRITE_TYPE(int32_t, VAL_INTEGER, writeInt32) + HANDLE_WRITE_TYPE(int64_t, VAL_LONG, writeInt64) + HANDLE_WRITE_TYPE(double, VAL_DOUBLE, writeDouble) + HANDLE_WRITE_TYPE(String16, VAL_STRING, writeString16) + + HANDLE_WRITE_TYPE(vector<bool>, VAL_BOOLEANARRAY, writeBoolVector) + HANDLE_WRITE_TYPE(vector<uint8_t>, VAL_BYTEARRAY, writeByteVector) + HANDLE_WRITE_TYPE(vector<int8_t>, VAL_BYTEARRAY, writeByteVector) + HANDLE_WRITE_TYPE(vector<int32_t>, VAL_INTARRAY, writeInt32Vector) + HANDLE_WRITE_TYPE(vector<int64_t>, VAL_LONGARRAY, writeInt64Vector) + HANDLE_WRITE_TYPE(vector<double>, VAL_DOUBLEARRAY, writeDoubleVector) + HANDLE_WRITE_TYPE(vector<String16>, VAL_STRINGARRAY, writeString16Vector) + + HANDLE_WRITE_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE) + + END_HANDLE_WRITE() + + return NO_ERROR; + +#undef BEGIN_HANDLE_WRITE +#undef HANDLE_WRITE_TYPE +#undef HANDLE_WRITE_PARCELABLE +#undef END_HANDLE_WRITE +} + +status_t Value::readFromParcel(const Parcel* parcel) +{ + // This implementation needs to be kept in sync with the readValue + // implementation in frameworks/base/core/java/android/os/Parcel.javai + +#define BEGIN_HANDLE_READ() \ + switch(value_type) { \ + default: \ + ALOGE("readFromParcel: Parcel type %d is not supported", value_type); \ + return BAD_TYPE; +#define HANDLE_READ_TYPE(T, TYPEVAL, TYPEMETHOD) \ + case TYPEVAL: \ + mContent = new Content<T>(); \ + RETURN_IF_FAILED(parcel->TYPEMETHOD(&static_cast<Content<T>*>(mContent)->mValue)); \ + break; +#define HANDLE_READ_PARCELABLE(T, TYPEVAL) \ + case TYPEVAL: \ + mContent = new Content<T>(); \ + RETURN_IF_FAILED(static_cast<Content<T>*>(mContent)->mValue.readFromParcel(parcel)); \ + break; +#define END_HANDLE_READ() \ + } + + int32_t value_type = VAL_NULL; + + delete mContent; + mContent = NULL; + + RETURN_IF_FAILED(parcel->readInt32(&value_type)); + + BEGIN_HANDLE_READ() + + HANDLE_READ_TYPE(bool, VAL_BOOLEAN, readBool) + HANDLE_READ_TYPE(int8_t, VAL_BYTE, readByte) + HANDLE_READ_TYPE(int32_t, VAL_INTEGER, readInt32) + HANDLE_READ_TYPE(int64_t, VAL_LONG, readInt64) + HANDLE_READ_TYPE(double, VAL_DOUBLE, readDouble) + HANDLE_READ_TYPE(String16, VAL_STRING, readString16) + + HANDLE_READ_TYPE(vector<bool>, VAL_BOOLEANARRAY, readBoolVector) + HANDLE_READ_TYPE(vector<uint8_t>, VAL_BYTEARRAY, readByteVector) + HANDLE_READ_TYPE(vector<int32_t>, VAL_INTARRAY, readInt32Vector) + HANDLE_READ_TYPE(vector<int64_t>, VAL_LONGARRAY, readInt64Vector) + HANDLE_READ_TYPE(vector<double>, VAL_DOUBLEARRAY, readDoubleVector) + HANDLE_READ_TYPE(vector<String16>, VAL_STRINGARRAY, readString16Vector) + + HANDLE_READ_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE) + + END_HANDLE_READ() + + return NO_ERROR; + +#undef BEGIN_HANDLE_READ +#undef HANDLE_READ_TYPE +#undef HANDLE_READ_PARCELABLE +#undef END_HANDLE_READ +} + +} // namespace binder + +} // namespace android + +/* vim: set ts=4 sw=4 tw=0 et :*/ diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 2152206a2a..0dc4469eb1 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -26,6 +26,15 @@ cc_test { } cc_test { + name: "binderValueTypeTest", + srcs: ["binderValueTypeTest.cpp"], + shared_libs: [ + "libbinder", + "libutils", + ], +} + +cc_test { name: "binderLibTest", srcs: ["binderLibTest.cpp"], shared_libs: [ diff --git a/libs/binder/tests/binderValueTypeTest.cpp b/libs/binder/tests/binderValueTypeTest.cpp new file mode 100644 index 0000000000..1a05a52911 --- /dev/null +++ b/libs/binder/tests/binderValueTypeTest.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 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 <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits> +#include <cstddef> +#include <vector> + +#include "android-base/file.h" +#include "android-base/test_utils.h" +#include <gtest/gtest.h> + +#include <binder/Parcel.h> +#include <binder/Value.h> +#include <binder/Debug.h> + +using ::android::binder::Value; +using ::android::os::PersistableBundle; +using ::android::String16; +using ::std::vector; + +#define VALUE_TYPE_TEST(T, TYPENAME, VAL) \ + TEST(ValueType, Handles ## TYPENAME) { \ + T x = VAL; \ + T y = T(); \ + Value value = VAL; \ + ASSERT_FALSE(value.empty()); \ + ASSERT_TRUE(value.is ## TYPENAME ()); \ + ASSERT_TRUE(value.get ## TYPENAME (&y)); \ + ASSERT_EQ(x, y); \ + ASSERT_EQ(value, Value(y)); \ + value.put ## TYPENAME (x); \ + ASSERT_EQ(value, Value(y)); \ + value = Value(); \ + ASSERT_TRUE(value.empty()); \ + ASSERT_NE(value, Value(y)); \ + value = y; \ + ASSERT_EQ(value, Value(x)); \ + } + +#define VALUE_TYPE_VECTOR_TEST(T, TYPENAME, VAL) \ + TEST(ValueType, Handles ## TYPENAME ## Vector) { \ + vector<T> x; \ + vector<T> y; \ + x.push_back(VAL); \ + x.push_back(T()); \ + Value value(x); \ + ASSERT_FALSE(value.empty()); \ + ASSERT_TRUE(value.is ## TYPENAME ## Vector()); \ + ASSERT_TRUE(value.get ## TYPENAME ## Vector(&y)); \ + ASSERT_EQ(x, y); \ + ASSERT_EQ(value, Value(y)); \ + value.put ## TYPENAME ## Vector(x); \ + ASSERT_EQ(value, Value(y)); \ + value = Value(); \ + ASSERT_TRUE(value.empty()); \ + ASSERT_NE(value, Value(y)); \ + value = y; \ + ASSERT_EQ(value, Value(x)); \ + } + +VALUE_TYPE_TEST(bool, Boolean, true) +VALUE_TYPE_TEST(int32_t, Int, 31337) +VALUE_TYPE_TEST(int64_t, Long, 13370133701337l) +VALUE_TYPE_TEST(double, Double, 3.14159265358979323846) +VALUE_TYPE_TEST(String16, String, String16("Lovely")) + +VALUE_TYPE_VECTOR_TEST(bool, Boolean, true) +VALUE_TYPE_VECTOR_TEST(int32_t, Int, 31337) +VALUE_TYPE_VECTOR_TEST(int64_t, Long, 13370133701337l) +VALUE_TYPE_VECTOR_TEST(double, Double, 3.14159265358979323846) +VALUE_TYPE_VECTOR_TEST(String16, String, String16("Lovely")) + +VALUE_TYPE_TEST(PersistableBundle, PersistableBundle, PersistableBundle()) + +TEST(ValueType, HandlesClear) { + Value value; + ASSERT_TRUE(value.empty()); + value.putInt(31337); + ASSERT_FALSE(value.empty()); + value.clear(); + ASSERT_TRUE(value.empty()); +} + +TEST(ValueType, HandlesSwap) { + Value value_a, value_b; + int32_t int_x; + value_a.putInt(31337); + ASSERT_FALSE(value_a.empty()); + ASSERT_TRUE(value_b.empty()); + value_a.swap(value_b); + ASSERT_FALSE(value_b.empty()); + ASSERT_TRUE(value_a.empty()); + ASSERT_TRUE(value_a.getInt(&int_x)); + ASSERT_EQ(31337, int_x); +} |