| /* |
| * Copyright (C) 2023 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 <android/binder_parcel.h> |
| #include <android/persistable_bundle.h> |
| #include <sys/cdefs.h> |
| |
| #include <set> |
| #include <sstream> |
| |
| // Include llndk-versioning.h only for vendor build as it is not available for NDK headers. |
| #if defined(__ANDROID_VENDOR__) |
| #include <android/llndk-versioning.h> |
| #else // __ANDROID_VENDOR__ |
| #if defined(API_LEVEL_AT_LEAST) |
| // Redefine API_LEVEL_AT_LEAST here to replace the version to __ANDROID_API_FUTURE__ as a workaround |
| #undef API_LEVEL_AT_LEAST |
| #endif |
| // TODO(b/322384429) switch this __ANDROID_API_FUTURE__ to sdk_api_level when V is finalized |
| #define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \ |
| (__builtin_available(android __ANDROID_API_FUTURE__, *)) |
| #endif // __ANDROID_VENDOR__ |
| |
| namespace aidl::android::os { |
| |
| /** |
| * Wrapper class that enables interop with AIDL NDK generation |
| * Takes ownership of the APersistableBundle* given to it in reset() and will automatically |
| * destroy it in the destructor, similar to a smart pointer container |
| */ |
| class PersistableBundle { |
| public: |
| PersistableBundle() noexcept { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| mPBundle = APersistableBundle_new(); |
| } |
| } |
| // takes ownership of the APersistableBundle* |
| PersistableBundle(APersistableBundle* _Nonnull bundle) noexcept : mPBundle(bundle) {} |
| // takes ownership of the APersistableBundle* |
| PersistableBundle(PersistableBundle&& other) noexcept : mPBundle(other.release()) {} |
| // duplicates, does not take ownership of the APersistableBundle* |
| PersistableBundle(const PersistableBundle& other) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| mPBundle = APersistableBundle_dup(other.mPBundle); |
| } |
| } |
| // duplicates, does not take ownership of the APersistableBundle* |
| PersistableBundle& operator=(const PersistableBundle& other) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| mPBundle = APersistableBundle_dup(other.mPBundle); |
| } |
| return *this; |
| } |
| |
| ~PersistableBundle() { reset(); } |
| |
| binder_status_t readFromParcel(const AParcel* _Nonnull parcel) { |
| reset(); |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return APersistableBundle_readFromParcel(parcel, &mPBundle); |
| } else { |
| return STATUS_INVALID_OPERATION; |
| } |
| } |
| |
| binder_status_t writeToParcel(AParcel* _Nonnull parcel) const { |
| if (!mPBundle) { |
| return STATUS_BAD_VALUE; |
| } |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return APersistableBundle_writeToParcel(mPBundle, parcel); |
| } else { |
| return STATUS_INVALID_OPERATION; |
| } |
| } |
| |
| /** |
| * Destroys any currently owned APersistableBundle* and takes ownership of the given |
| * APersistableBundle* |
| * |
| * @param pBundle The APersistableBundle to take ownership of |
| */ |
| void reset(APersistableBundle* _Nullable pBundle = nullptr) noexcept { |
| if (mPBundle) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| APersistableBundle_delete(mPBundle); |
| } |
| mPBundle = nullptr; |
| } |
| mPBundle = pBundle; |
| } |
| |
| /** |
| * Check the actual contents of the bundle for equality. This is typically |
| * what should be used to check for equality. |
| */ |
| bool deepEquals(const PersistableBundle& rhs) const { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return APersistableBundle_isEqual(get(), rhs.get()); |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * NOTE: This does NOT check the contents of the PersistableBundle. This is |
| * implemented for ordering. Use deepEquals() to check for equality between |
| * two different PersistableBundle objects. |
| */ |
| inline bool operator==(const PersistableBundle& rhs) const { return get() == rhs.get(); } |
| inline bool operator!=(const PersistableBundle& rhs) const { return get() != rhs.get(); } |
| |
| inline bool operator<(const PersistableBundle& rhs) const { return get() < rhs.get(); } |
| inline bool operator>(const PersistableBundle& rhs) const { return get() > rhs.get(); } |
| inline bool operator>=(const PersistableBundle& rhs) const { return !(*this < rhs); } |
| inline bool operator<=(const PersistableBundle& rhs) const { return !(*this > rhs); } |
| |
| PersistableBundle& operator=(PersistableBundle&& other) noexcept { |
| reset(other.release()); |
| return *this; |
| } |
| |
| /** |
| * Stops managing any contained APersistableBundle*, returning it to the caller. Ownership |
| * is released. |
| * @return APersistableBundle* or null if this was empty |
| */ |
| [[nodiscard]] APersistableBundle* _Nullable release() noexcept { |
| APersistableBundle* _Nullable ret = mPBundle; |
| mPBundle = nullptr; |
| return ret; |
| } |
| |
| inline std::string toString() const { |
| if (!mPBundle) { |
| return "<PersistableBundle: null>"; |
| } else if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| std::ostringstream os; |
| os << "<PersistableBundle: "; |
| os << "size: " << std::to_string(APersistableBundle_size(mPBundle)); |
| os << " >"; |
| return os.str(); |
| } |
| return "<PersistableBundle (unknown)>"; |
| } |
| |
| int32_t size() const { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return APersistableBundle_size(mPBundle); |
| } else { |
| return 0; |
| } |
| } |
| |
| int32_t erase(const std::string& key) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return APersistableBundle_erase(mPBundle, key.c_str()); |
| } else { |
| return 0; |
| } |
| } |
| |
| void putBoolean(const std::string& key, bool val) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| APersistableBundle_putBoolean(mPBundle, key.c_str(), val); |
| } |
| } |
| |
| void putInt(const std::string& key, int32_t val) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| APersistableBundle_putInt(mPBundle, key.c_str(), val); |
| } |
| } |
| |
| void putLong(const std::string& key, int64_t val) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| APersistableBundle_putLong(mPBundle, key.c_str(), val); |
| } |
| } |
| |
| void putDouble(const std::string& key, double val) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| APersistableBundle_putDouble(mPBundle, key.c_str(), val); |
| } |
| } |
| |
| void putString(const std::string& key, const std::string& val) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| APersistableBundle_putString(mPBundle, key.c_str(), val.c_str()); |
| } |
| } |
| |
| void putBooleanVector(const std::string& key, const std::vector<bool>& vec) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| // std::vector<bool> has no ::data(). |
| int32_t num = vec.size(); |
| if (num > 0) { |
| bool* newVec = (bool*)malloc(num * sizeof(bool)); |
| if (newVec) { |
| for (int32_t i = 0; i < num; i++) { |
| newVec[i] = vec[i]; |
| } |
| APersistableBundle_putBooleanVector(mPBundle, key.c_str(), newVec, num); |
| free(newVec); |
| } |
| } |
| } |
| } |
| |
| void putIntVector(const std::string& key, const std::vector<int32_t>& vec) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| int32_t num = vec.size(); |
| if (num > 0) { |
| APersistableBundle_putIntVector(mPBundle, key.c_str(), vec.data(), num); |
| } |
| } |
| } |
| void putLongVector(const std::string& key, const std::vector<int64_t>& vec) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| int32_t num = vec.size(); |
| if (num > 0) { |
| APersistableBundle_putLongVector(mPBundle, key.c_str(), vec.data(), num); |
| } |
| } |
| } |
| void putDoubleVector(const std::string& key, const std::vector<double>& vec) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| int32_t num = vec.size(); |
| if (num > 0) { |
| APersistableBundle_putDoubleVector(mPBundle, key.c_str(), vec.data(), num); |
| } |
| } |
| } |
| void putStringVector(const std::string& key, const std::vector<std::string>& vec) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| int32_t num = vec.size(); |
| if (num > 0) { |
| char** inVec = (char**)malloc(num * sizeof(char*)); |
| if (inVec) { |
| for (int32_t i = 0; i < num; i++) { |
| inVec[i] = strdup(vec[i].c_str()); |
| } |
| APersistableBundle_putStringVector(mPBundle, key.c_str(), inVec, num); |
| free(inVec); |
| } |
| } |
| } |
| } |
| void putPersistableBundle(const std::string& key, const PersistableBundle& pBundle) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| APersistableBundle_putPersistableBundle(mPBundle, key.c_str(), pBundle.mPBundle); |
| } |
| } |
| |
| bool getBoolean(const std::string& key, bool* _Nonnull val) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return APersistableBundle_getBoolean(mPBundle, key.c_str(), val); |
| } else { |
| return false; |
| } |
| } |
| |
| bool getInt(const std::string& key, int32_t* _Nonnull val) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return APersistableBundle_getInt(mPBundle, key.c_str(), val); |
| } else { |
| return false; |
| } |
| } |
| |
| bool getLong(const std::string& key, int64_t* _Nonnull val) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return APersistableBundle_getLong(mPBundle, key.c_str(), val); |
| } else { |
| return false; |
| } |
| } |
| |
| bool getDouble(const std::string& key, double* _Nonnull val) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return APersistableBundle_getDouble(mPBundle, key.c_str(), val); |
| } else { |
| return false; |
| } |
| } |
| |
| static char* _Nullable stringAllocator(int32_t bufferSizeBytes, void* _Nullable) { |
| return (char*)malloc(bufferSizeBytes); |
| } |
| |
| bool getString(const std::string& key, std::string* _Nonnull val) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| char* outString = nullptr; |
| bool ret = APersistableBundle_getString(mPBundle, key.c_str(), &outString, |
| &stringAllocator, nullptr); |
| if (ret && outString) { |
| *val = std::string(outString); |
| } |
| return ret; |
| } else { |
| return false; |
| } |
| } |
| |
| template <typename T> |
| bool getVecInternal(int32_t (*_Nonnull getVec)(const APersistableBundle* _Nonnull, |
| const char* _Nonnull, T* _Nullable, int32_t), |
| const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, |
| std::vector<T>* _Nonnull vec) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| int32_t bytes = 0; |
| // call first with nullptr to get required size in bytes |
| bytes = getVec(pBundle, key, nullptr, 0); |
| if (bytes > 0) { |
| T* newVec = (T*)malloc(bytes); |
| if (newVec) { |
| bytes = getVec(pBundle, key, newVec, bytes); |
| int32_t elements = bytes / sizeof(T); |
| vec->clear(); |
| for (int32_t i = 0; i < elements; i++) { |
| vec->push_back(newVec[i]); |
| } |
| free(newVec); |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(), |
| vec); |
| } |
| return false; |
| } |
| bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(), |
| vec); |
| } |
| return false; |
| } |
| bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(), |
| vec); |
| } |
| return false; |
| } |
| bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle, |
| key.c_str(), vec); |
| } |
| return false; |
| } |
| |
| // Takes ownership of and frees the char** and its elements. |
| // Creates a new set or vector based on the array of char*. |
| template <typename T> |
| T moveStringsInternal(char* _Nullable* _Nonnull strings, int32_t bufferSizeBytes) { |
| if (strings && bufferSizeBytes > 0) { |
| int32_t num = bufferSizeBytes / sizeof(char*); |
| T ret; |
| for (int32_t i = 0; i < num; i++) { |
| ret.insert(ret.end(), std::string(strings[i])); |
| free(strings[i]); |
| } |
| free(strings); |
| return ret; |
| } |
| return T(); |
| } |
| |
| bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0, |
| &stringAllocator, nullptr); |
| if (bytes > 0) { |
| char** strings = (char**)malloc(bytes); |
| if (strings) { |
| bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), strings, |
| bytes, &stringAllocator, nullptr); |
| *vec = moveStringsInternal<std::vector<std::string>>(strings, bytes); |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| APersistableBundle* bundle = nullptr; |
| bool ret = APersistableBundle_getPersistableBundle(mPBundle, key.c_str(), &bundle); |
| if (ret) { |
| *val = PersistableBundle(bundle); |
| } |
| return ret; |
| } else { |
| return false; |
| } |
| } |
| |
| std::set<std::string> getKeys( |
| int32_t (*_Nonnull getTypedKeys)(const APersistableBundle* _Nonnull pBundle, |
| char* _Nullable* _Nullable outKeys, |
| int32_t bufferSizeBytes, |
| APersistableBundle_stringAllocator stringAllocator, |
| void* _Nullable), |
| const APersistableBundle* _Nonnull pBundle) { |
| // call first with nullptr to get required size in bytes |
| int32_t bytes = getTypedKeys(pBundle, nullptr, 0, &stringAllocator, nullptr); |
| if (bytes > 0) { |
| char** keys = (char**)malloc(bytes); |
| if (keys) { |
| bytes = getTypedKeys(pBundle, keys, bytes, &stringAllocator, nullptr); |
| return moveStringsInternal<std::set<std::string>>(keys, bytes); |
| } |
| } |
| return {}; |
| } |
| |
| std::set<std::string> getBooleanKeys() { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getKeys(&APersistableBundle_getBooleanKeys, mPBundle); |
| } else { |
| return {}; |
| } |
| } |
| std::set<std::string> getIntKeys() { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getKeys(&APersistableBundle_getIntKeys, mPBundle); |
| } else { |
| return {}; |
| } |
| } |
| std::set<std::string> getLongKeys() { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getKeys(&APersistableBundle_getLongKeys, mPBundle); |
| } else { |
| return {}; |
| } |
| } |
| std::set<std::string> getDoubleKeys() { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getKeys(&APersistableBundle_getDoubleKeys, mPBundle); |
| } else { |
| return {}; |
| } |
| } |
| std::set<std::string> getStringKeys() { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getKeys(&APersistableBundle_getStringKeys, mPBundle); |
| } else { |
| return {}; |
| } |
| } |
| std::set<std::string> getBooleanVectorKeys() { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getKeys(&APersistableBundle_getBooleanVectorKeys, mPBundle); |
| } else { |
| return {}; |
| } |
| } |
| std::set<std::string> getIntVectorKeys() { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getKeys(&APersistableBundle_getIntVectorKeys, mPBundle); |
| } else { |
| return {}; |
| } |
| } |
| std::set<std::string> getLongVectorKeys() { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getKeys(&APersistableBundle_getLongVectorKeys, mPBundle); |
| } else { |
| return {}; |
| } |
| } |
| std::set<std::string> getDoubleVectorKeys() { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getKeys(&APersistableBundle_getDoubleVectorKeys, mPBundle); |
| } else { |
| return {}; |
| } |
| } |
| std::set<std::string> getStringVectorKeys() { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getKeys(&APersistableBundle_getStringVectorKeys, mPBundle); |
| } else { |
| return {}; |
| } |
| } |
| std::set<std::string> getPersistableBundleKeys() { |
| if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { |
| return getKeys(&APersistableBundle_getPersistableBundleKeys, mPBundle); |
| } else { |
| return {}; |
| } |
| } |
| std::set<std::string> getMonKeys() { |
| // :P |
| return {"c(o,o)b", "c(o,o)b"}; |
| } |
| |
| private: |
| inline APersistableBundle* _Nullable get() const { return mPBundle; } |
| APersistableBundle* _Nullable mPBundle = nullptr; |
| }; |
| |
| } // namespace aidl::android::os |