From 3175cdd4ae6f43380037672945101010f9c49447 Mon Sep 17 00:00:00 2001 From: dengkail Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH 01/17] OpenXR loader: Remove unused things Co-authored-by: Rylie Pavlik --- src/loader/android_utilities.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/loader/android_utilities.cpp b/src/loader/android_utilities.cpp index 1f7424b8e..6b6eeab59 100644 --- a/src/loader/android_utilities.cpp +++ b/src/loader/android_utilities.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -26,8 +25,6 @@ #define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, "OpenXR-Loader", __VA_ARGS__) namespace openxr_android { -using wrap::android::content::ContentUris; -using wrap::android::content::Context; using wrap::android::database::Cursor; using wrap::android::net::Uri; using wrap::android::net::Uri_Builder; From 296daf4d4cb21f2fbbd31d586f30ebc3278fa21e Mon Sep 17 00:00:00 2001 From: dengkail Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH 02/17] OpenXR loader: Refactor content provider access code. De-duplicate implementations, and be more precise in naming. Co-authored-by: Rylie Pavlik --- src/loader/android_utilities.cpp | 92 ++++++++++++++++---------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/loader/android_utilities.cpp b/src/loader/android_utilities.cpp index 6b6eeab59..a826e2cc5 100644 --- a/src/loader/android_utilities.cpp +++ b/src/loader/android_utilities.cpp @@ -125,7 +125,7 @@ namespace functions { static constexpr auto TABLE_PATH = "functions"; /** - * Create a content URI for querying all rows of the function remapping data for a given + * Create a content URI for querying all rows of the runtime function remapping data for a given * runtime package and major version of OpenXR. * * @param systemBroker If the system runtime broker (instead of the installable one) should be queried. @@ -134,7 +134,7 @@ static constexpr auto TABLE_PATH = "functions"; * @param abi The Android ABI name in use. * @return A content URI for the entire table: the function remapping for that runtime. */ -static Uri makeContentUri(bool systemBroker, int majorVersion, std::string const &packageName, const char *abi) { +static Uri makeRuntimeContentUri(bool systemBroker, int majorVersion, std::string const &packageName, const char *abi) { auto builder = Uri_Builder::construct(); builder.scheme("content") .authority(getBrokerAuthority(systemBroker)) @@ -213,34 +213,6 @@ inline JsonManifestBuilder &JsonManifestBuilder::function(const std::string &fun static constexpr const char *getBrokerTypeName(bool systemBroker) { return systemBroker ? "system" : "installable"; } -static int populateFunctions(wrap::android::content::Context const &context, bool systemBroker, const std::string &packageName, - JsonManifestBuilder &builder) { - jni::Array projection = makeArray({functions::Columns::FUNCTION_NAME, functions::Columns::SYMBOL_NAME}); - - auto uri = functions::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI); - ALOGI("populateFunctions: Querying URI: %s", uri.toString().c_str()); - - Cursor cursor = context.getContentResolver().query(uri, projection); - - if (cursor.isNull()) { - ALOGE("Null cursor when querying content resolver for functions."); - return -1; - } - if (cursor.getCount() < 1) { - ALOGE("Non-null but empty cursor when querying content resolver for functions."); - cursor.close(); - return -1; - } - auto functionIndex = cursor.getColumnIndex(functions::Columns::FUNCTION_NAME); - auto symbolIndex = cursor.getColumnIndex(functions::Columns::SYMBOL_NAME); - while (cursor.moveToNext()) { - builder.function(cursor.getString(functionIndex), cursor.getString(symbolIndex)); - } - - cursor.close(); - return 0; -} - // The current file relies on android-jni-wrappers and jnipp, which may throw on failure. // This is problematic when the loader is compiled with exception handling disabled - the consumers can reasonably // expect that the compilation with -fno-exceptions will succeed, but the compiler will not accept the code that @@ -261,33 +233,61 @@ static int populateFunctions(wrap::android::content::Context const &context, boo #endif // XRLOADER_DISABLE_EXCEPTION_HANDLING -/// Get cursor for active runtime, parameterized by whether or not we use the system broker -static bool getActiveRuntimeCursor(wrap::android::content::Context const &context, jni::Array const &projection, - bool systemBroker, Cursor &cursor) { - auto uri = active_runtime::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), ABI); - ALOGI("getActiveRuntimeCursor: Querying URI: %s", uri.toString().c_str()); +/// Generic content resolver query function +static bool getCursor(wrap::android::content::Context const &context, jni::Array const &projection, Uri const &uri, + bool systemBroker, const char *contentDesc, Cursor &out_cursor) { + ALOGI("getCursor: Querying URI: %s", uri.toString().c_str()); - ANDROID_UTILITIES_TRY { cursor = context.getContentResolver().query(uri, projection); } + ANDROID_UTILITIES_TRY { out_cursor = context.getContentResolver().query(uri, projection); } ANDROID_UTILITIES_CATCH_FALLBACK({ - ALOGW("Exception when querying %s content resolver: %s", getBrokerTypeName(systemBroker), e.what()); - cursor = {}; + ALOGI("Exception when querying %s content resolver for %s: %s", getBrokerTypeName(systemBroker), contentDesc, e.what()); + out_cursor = {}; return false; }) - if (cursor.isNull()) { - ALOGW("Null cursor when querying %s content resolver.", getBrokerTypeName(systemBroker)); - cursor = {}; + if (out_cursor.isNull()) { + ALOGI("Null cursor when querying %s content resolver for %s.", getBrokerTypeName(systemBroker), contentDesc); + out_cursor = {}; return false; } - if (cursor.getCount() < 1) { - ALOGW("Non-null but empty cursor when querying %s content resolver.", getBrokerTypeName(systemBroker)); - cursor.close(); - cursor = {}; + if (out_cursor.getCount() < 1) { + ALOGI("Non-null but empty cursor when querying %s content resolver for %s.", getBrokerTypeName(systemBroker), contentDesc); + out_cursor.close(); + out_cursor = {}; return false; } return true; } +static int populateFunctions(wrap::android::content::Context const &context, bool systemBroker, const std::string &packageName, + JsonManifestBuilder &builder) { + jni::Array projection = makeArray({functions::Columns::FUNCTION_NAME, functions::Columns::SYMBOL_NAME}); + + auto uri = functions::makeRuntimeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI); + ALOGI("populateFunctions: Querying URI: %s", uri.toString().c_str()); + Cursor cursor; + if (!getCursor(context, projection, uri, systemBroker, "functions", cursor)) { + return -1; + } + + auto functionIndex = cursor.getColumnIndex(functions::Columns::FUNCTION_NAME); + auto symbolIndex = cursor.getColumnIndex(functions::Columns::SYMBOL_NAME); + while (cursor.moveToNext()) { + builder.function(cursor.getString(functionIndex), cursor.getString(symbolIndex)); + } + + cursor.close(); + return 0; +} + +/// Get cursor for active runtime, parameterized by whether or not we use the system broker +static bool getActiveRuntimeCursor(wrap::android::content::Context const &context, jni::Array const &projection, + bool systemBroker, Cursor &cursor) { + auto uri = active_runtime::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), ABI); + ALOGI("getActiveRuntimeCursor: Querying URI: %s", uri.toString().c_str()); + return getCursor(context, projection, uri, systemBroker, "active runtime", cursor); +} + int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest) { jni::Array projection = makeArray({active_runtime::Columns::PACKAGE_NAME, active_runtime::Columns::NATIVE_LIB_DIR, active_runtime::Columns::SO_FILENAME, active_runtime::Columns::HAS_FUNCTIONS}); @@ -303,7 +303,7 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte if (cursor.isNull()) { // Couldn't find either broker - ALOGE("Could access neither the installable nor system runtime broker."); + ALOGI("Could access neither the installable nor system runtime broker."); return -1; } From c77d617ce27128e398e1756a3c5cfb01c429ee16 Mon Sep 17 00:00:00 2001 From: Rylie Pavlik Date: Fri, 30 Aug 2024 14:55:43 -0500 Subject: [PATCH 03/17] Loader: Remove overengineered virtual manifest builder on Android --- src/loader/android_utilities.cpp | 39 ++++++++++---------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/loader/android_utilities.cpp b/src/loader/android_utilities.cpp index a826e2cc5..7961e9cb0 100644 --- a/src/loader/android_utilities.cpp +++ b/src/loader/android_utilities.cpp @@ -185,30 +185,15 @@ static constexpr auto ABI = "x86_64"; #error "Unknown ABI!" #endif -/// Helper class to generate the jsoncpp object corresponding to a synthetic runtime manifest. -class JsonManifestBuilder { - public: - JsonManifestBuilder(const std::string &libraryPathParent, const std::string &libraryPath); - JsonManifestBuilder &function(const std::string &functionName, const std::string &symbolName); +static inline Json::Value makeMinimumVirtualRuntimeManifest(const std::string &libraryPath) { + Json::Value root_node(Json::objectValue); - Json::Value build() const { return root_node; } - - private: - Json::Value root_node; -}; - -inline JsonManifestBuilder::JsonManifestBuilder(const std::string &libraryPathParent, const std::string &libraryPath) - : root_node(Json::objectValue) { root_node["file_format_version"] = "1.0.0"; root_node["instance_extensions"] = Json::Value(Json::arrayValue); root_node["functions"] = Json::Value(Json::objectValue); - root_node[libraryPathParent] = Json::objectValue; - root_node[libraryPathParent]["library_path"] = libraryPath; -} - -inline JsonManifestBuilder &JsonManifestBuilder::function(const std::string &functionName, const std::string &symbolName) { - root_node["functions"][functionName] = symbolName; - return *this; + root_node["runtime"] = Json::objectValue; + root_node["runtime"]["library_path"] = libraryPath; + return root_node; } static constexpr const char *getBrokerTypeName(bool systemBroker) { return systemBroker ? "system" : "installable"; } @@ -259,9 +244,9 @@ static bool getCursor(wrap::android::content::Context const &context, jni::Array return true; } -static int populateFunctions(wrap::android::content::Context const &context, bool systemBroker, const std::string &packageName, - JsonManifestBuilder &builder) { - jni::Array projection = makeArray({functions::Columns::FUNCTION_NAME, functions::Columns::SYMBOL_NAME}); +static int populateRuntimeFunctions(wrap::android::content::Context const &context, bool systemBroker, + const std::string &packageName, Json::Value &manifest) { + const jni::Array projection = makeArray({functions::Columns::FUNCTION_NAME, functions::Columns::SYMBOL_NAME}); auto uri = functions::makeRuntimeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI); ALOGI("populateFunctions: Querying URI: %s", uri.toString().c_str()); @@ -273,7 +258,7 @@ static int populateFunctions(wrap::android::content::Context const &context, boo auto functionIndex = cursor.getColumnIndex(functions::Columns::FUNCTION_NAME); auto symbolIndex = cursor.getColumnIndex(functions::Columns::SYMBOL_NAME); while (cursor.moveToNext()) { - builder.function(cursor.getString(functionIndex), cursor.getString(symbolIndex)); + manifest["functions"][cursor.getString(functionIndex)] = cursor.getString(symbolIndex); } cursor.close(); @@ -324,15 +309,15 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte // we found a runtime that we can dlopen, use it. dlclose(lib); - JsonManifestBuilder builder{"runtime", lib_path}; + Json::Value manifest = makeMinimumVirtualRuntimeManifest(lib_path); if (hasFunctions) { - int result = populateFunctions(context, systemBroker, packageName, builder); + int result = populateRuntimeFunctions(context, systemBroker, packageName, manifest); if (result != 0) { ALOGW("Unable to populate functions from runtime: %s, checking for more records...", lib_path.c_str()); continue; } } - virtualManifest = builder.build(); + virtualManifest = manifest; cursor.close(); return 0; } From 1bfb50c1ba803329ec6013bfc22689f45dc5336e Mon Sep 17 00:00:00 2001 From: Rylie Pavlik Date: Fri, 30 Aug 2024 16:22:20 -0500 Subject: [PATCH 04/17] Loader: Cache some json node references. --- src/loader/manifest_file.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/loader/manifest_file.cpp b/src/loader/manifest_file.cpp index ca212d343..00bb3b2aa 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -813,24 +813,29 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin LoaderLogger::LogErrorMessage("", error_ss.str()); return; } + + // Figure out enabled state of implicit layers if (MANIFEST_TYPE_IMPLICIT_API_LAYER == type) { bool enabled = true; // Implicit layers require the disable environment variable. - if (layer_root_node["disable_environment"].isNull() || !layer_root_node["disable_environment"].isString()) { + auto &disable_env_node = layer_root_node["disable_environment"]; + if (disable_env_node.isNull() || !disable_env_node.isString()) { error_ss << "Implicit layer " << filename << " is missing \"disable_environment\""; LoaderLogger::LogErrorMessage("", error_ss.str()); return; } - // Check if there's an enable environment variable provided - if (!layer_root_node["enable_environment"].isNull() && layer_root_node["enable_environment"].isString()) { - std::string env_var = layer_root_node["enable_environment"].asString(); + // Check if there's an enable environment variable provided: If so, it must be set in the environment. + auto &enable_env_node = layer_root_node["enable_environment"]; + if (!enable_env_node.isNull() && enable_env_node.isString()) { + std::string env_var = enable_env_node.asString(); // If it's not set in the environment, disable the layer if (!PlatformUtilsGetEnvSet(env_var.c_str())) { enabled = false; } } + // Check for the disable environment variable, which must be provided in the JSON - std::string env_var = layer_root_node["disable_environment"].asString(); + std::string env_var = disable_env_node.asString(); // If the env var is set, disable the layer. Disable env var overrides enable above if (PlatformUtilsGetEnvSet(env_var.c_str())) { enabled = false; From 992e755a187e93b4bf539c116a472418a72b6c59 Mon Sep 17 00:00:00 2001 From: Rylie Pavlik Date: Fri, 30 Aug 2024 16:29:21 -0500 Subject: [PATCH 05/17] Loader: Clean up a comment --- src/loader/manifest_file.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/loader/manifest_file.cpp b/src/loader/manifest_file.cpp index 00bb3b2aa..0b87564e5 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -640,8 +640,7 @@ void RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std: // Add this runtime manifest file manifest_files.emplace_back(new RuntimeManifestFile(filename, lib_path)); - // Add any extensions to it after the fact. - // Handle any renamed functions + // Add any extensions to it after the fact, while handling any renamed functions manifest_files.back()->ParseCommon(runtime_root_node); } @@ -895,7 +894,7 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin manifest_files.emplace_back( new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path)); - // Add any extensions to it after the fact. + // Add any extensions to it after the fact, while handling any renamed functions manifest_files.back()->ParseCommon(layer_root_node); } From c3a83083f63ccf0a09d33e3b575354791b041f38 Mon Sep 17 00:00:00 2001 From: Rylie Pavlik Date: Fri, 30 Aug 2024 16:31:23 -0500 Subject: [PATCH 06/17] Loader: small exception safety tweak. Cannot use make_unique because we are using a private constructor, but the same reasoning applies. --- changes/sdk/mr.3475.gl.md | 4 ++++ src/loader/api_layer_interface.cpp | 7 ++++--- src/loader/manifest_file.cpp | 8 +++++--- 3 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 changes/sdk/mr.3475.gl.md diff --git a/changes/sdk/mr.3475.gl.md b/changes/sdk/mr.3475.gl.md new file mode 100644 index 000000000..03d9f51c7 --- /dev/null +++ b/changes/sdk/mr.3475.gl.md @@ -0,0 +1,4 @@ +--- +- pr.413.gh.OpenXR-SDK-Source +--- +Improvement: Loader: Code cleanup and robustness improvements. diff --git a/src/loader/api_layer_interface.cpp b/src/loader/api_layer_interface.cpp index a93d45da3..9f58959c6 100644 --- a/src/loader/api_layer_interface.cpp +++ b/src/loader/api_layer_interface.cpp @@ -422,9 +422,10 @@ XrResult ApiLayerInterface::LoadApiLayers(const std::string& openxr_command, uin } // Add this API layer to the vector - api_layer_interfaces.emplace_back(new ApiLayerInterface(manifest_file->LayerName(), layer_library, supported_extensions, - api_layer_info.getInstanceProcAddr, - api_layer_info.createApiLayerInstance)); + std::unique_ptr iface{new ApiLayerInterface(manifest_file->LayerName(), layer_library, + supported_extensions, api_layer_info.getInstanceProcAddr, + api_layer_info.createApiLayerInstance)}; + api_layer_interfaces.emplace_back(std::move(iface)); // If we load one, clear all errors. any_loaded = true; diff --git a/src/loader/manifest_file.cpp b/src/loader/manifest_file.cpp index 0b87564e5..f5e44da72 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -638,7 +638,8 @@ void RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std: } // Add this runtime manifest file - manifest_files.emplace_back(new RuntimeManifestFile(filename, lib_path)); + std::unique_ptr manifest{new RuntimeManifestFile(filename, lib_path)}; + manifest_files.emplace_back(std::move(manifest)); // Add any extensions to it after the fact, while handling any renamed functions manifest_files.back()->ParseCommon(runtime_root_node); @@ -891,8 +892,9 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin } // Add this layer manifest file - manifest_files.emplace_back( - new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path)); + std::unique_ptr manifest{ + new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path)}; + manifest_files.emplace_back(std::move(manifest)); // Add any extensions to it after the fact, while handling any renamed functions manifest_files.back()->ParseCommon(layer_root_node); From d173b91b4da24349695b7dea82e2b218ee1e9764 Mon Sep 17 00:00:00 2001 From: dengkail Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH 07/17] OpenXR loader: Handle parse errors in layer implementation version Co-authored-by: Rylie Pavlik --- src/loader/manifest_file.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/loader/manifest_file.cpp b/src/loader/manifest_file.cpp index f5e44da72..00de5b666 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -861,7 +861,16 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin return; } - uint32_t implementation_version = atoi(layer_root_node["implementation_version"].asString().c_str()); + uint32_t implementation_version = 0; + { + char *end_ptr; + implementation_version = strtol(layer_root_node["implementation_version"].asString().c_str(), &end_ptr, 10); + if (*end_ptr != '\0') { + std::ostringstream oss(error_ss.str()); + oss << "layer " << filename << " has invalid implementation version."; + LoaderLogger::LogWarningMessage("", oss.str()); + } + } std::string library_path = layer_root_node["library_path"].asString(); // If the library_path variable has no directory symbol, it's just a file name and should be accessible on the From ea89384cc0ceab6e02ba7960b31469959f623e58 Mon Sep 17 00:00:00 2001 From: dengkail Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH 08/17] OpenXR loader: Retrieve runtime info source --- src/loader/android_utilities.cpp | 10 ++++++---- src/loader/android_utilities.h | 3 ++- src/loader/runtime_interface.cpp | 3 ++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/loader/android_utilities.cpp b/src/loader/android_utilities.cpp index 7961e9cb0..6cc55b322 100644 --- a/src/loader/android_utilities.cpp +++ b/src/loader/android_utilities.cpp @@ -273,12 +273,14 @@ static bool getActiveRuntimeCursor(wrap::android::content::Context const &contex return getCursor(context, projection, uri, systemBroker, "active runtime", cursor); } -int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest) { - jni::Array projection = makeArray({active_runtime::Columns::PACKAGE_NAME, active_runtime::Columns::NATIVE_LIB_DIR, - active_runtime::Columns::SO_FILENAME, active_runtime::Columns::HAS_FUNCTIONS}); +int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest, + bool &systemBroker) { + const jni::Array projection = + makeArray({active_runtime::Columns::PACKAGE_NAME, active_runtime::Columns::NATIVE_LIB_DIR, + active_runtime::Columns::SO_FILENAME, active_runtime::Columns::HAS_FUNCTIONS}); // First, try getting the installable broker's provider - bool systemBroker = false; + systemBroker = false; Cursor cursor; if (!getActiveRuntimeCursor(context, projection, systemBroker, cursor)) { // OK, try the system broker as a fallback. diff --git a/src/loader/android_utilities.h b/src/loader/android_utilities.h index 582a65056..2165f7ecb 100644 --- a/src/loader/android_utilities.h +++ b/src/loader/android_utilities.h @@ -26,7 +26,8 @@ using wrap::android::content::Context; * * @return 0 on success, something else on failure. */ -int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest); +int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest, + bool &systemBroker); } // namespace openxr_android #endif // __ANDROID__ diff --git a/src/loader/runtime_interface.cpp b/src/loader/runtime_interface.cpp index 32feea6dd..45466abb5 100644 --- a/src/loader/runtime_interface.cpp +++ b/src/loader/runtime_interface.cpp @@ -45,8 +45,9 @@ XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) { if (context.isNull()) { return XR_ERROR_INITIALIZATION_FAILED; } + bool systemBroker = false; Json::Value virtualManifest; - if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest)) { + if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest, systemBroker)) { return XR_ERROR_INITIALIZATION_FAILED; } out_manifest = virtualManifest; From 4dd29bcc49dc2d4f8ebecd5fb0a78b1f8b33cc48 Mon Sep 17 00:00:00 2001 From: dengkail Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH 09/17] OpenXR loader: Propagate runtime info source --- src/loader/loader_init_data.hpp | 5 ++++- src/loader/manifest_file.cpp | 3 ++- src/loader/manifest_file.hpp | 10 ++++++++++ src/loader/runtime_interface.cpp | 7 ++++++- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/loader/loader_init_data.hpp b/src/loader/loader_init_data.hpp index e3a27fc40..1d7cb0fc7 100644 --- a/src/loader/loader_init_data.hpp +++ b/src/loader/loader_init_data.hpp @@ -16,6 +16,7 @@ #include #include #include "android_utilities.h" +#include "manifest_file.hpp" #endif // XR_USE_PLATFORM_ANDROID #ifdef XR_KHR_LOADER_INIT_SUPPORT @@ -84,7 +85,9 @@ class LoaderInitData { XrResult InitializeLoaderInitData(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo); #ifdef XR_USE_PLATFORM_ANDROID -XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest); + +//! Modifies @p out_manifest and @p out_runtime_source only if returning successfully +XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest, ManifestFileSource& out_runtime_source); std::string GetAndroidNativeLibraryDir(); void* Android_Get_Asset_Manager(); #endif // XR_USE_PLATFORM_ANDROID diff --git a/src/loader/manifest_file.cpp b/src/loader/manifest_file.cpp index 00de5b666..a996d9021 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -682,7 +682,8 @@ XrResult RuntimeManifestFile::FindManifestFiles(const std::string &openxr_comman #if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID) Json::Value virtualManifest; - result = GetPlatformRuntimeVirtualManifest(virtualManifest); + ManifestFileSource runtimeSource = ManifestFileSource::FROM_JSON_MANIFEST; + result = GetPlatformRuntimeVirtualManifest(virtualManifest, runtimeSource); if (XR_SUCCESS == result) { RuntimeManifestFile::CreateIfValid(virtualManifest, "", manifest_files); return result; diff --git a/src/loader/manifest_file.hpp b/src/loader/manifest_file.hpp index 801614ad1..a7f1cf34f 100644 --- a/src/loader/manifest_file.hpp +++ b/src/loader/manifest_file.hpp @@ -28,6 +28,16 @@ enum ManifestFileType { MANIFEST_TYPE_EXPLICIT_API_LAYER, }; +//! Where did the data for this manifest file (may be virtual) come from? +enum ManifestFileSource { + //! An actual json file on a file system + FROM_JSON_MANIFEST = 0, + //! The installable runtime broker on Android + FROM_INSTALLABLE_BROKER, + //! The system runtime broker on Android + FROM_SYSTEM_BROKER, +}; + struct JsonVersion { uint32_t major; uint32_t minor; diff --git a/src/loader/runtime_interface.cpp b/src/loader/runtime_interface.cpp index 45466abb5..be7db185a 100644 --- a/src/loader/runtime_interface.cpp +++ b/src/loader/runtime_interface.cpp @@ -35,7 +35,7 @@ #endif // XR_USE_PLATFORM_ANDROID #if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID) -XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) { +XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest, ManifestFileSource& out_runtime_source) { using wrap::android::content::Context; auto& initData = LoaderInitData::instance(); if (!initData.initialized()) { @@ -50,6 +50,11 @@ XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) { if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest, systemBroker)) { return XR_ERROR_INITIALIZATION_FAILED; } + if (systemBroker) { + out_runtime_source = ManifestFileSource::FROM_SYSTEM_BROKER; + } else { + out_runtime_source = ManifestFileSource::FROM_INSTALLABLE_BROKER; + } out_manifest = virtualManifest; return XR_SUCCESS; } From e83f35e12cfdc628c0a9e04a0a757e8d3ce4f78a Mon Sep 17 00:00:00 2001 From: dengkail Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH 10/17] OpenXR loader: add helpers for API layer broker discovery --- src/loader/android_utilities.cpp | 270 +++++++++++++++++++++++++++++++ src/loader/android_utilities.h | 12 ++ 2 files changed, 282 insertions(+) diff --git a/src/loader/android_utilities.cpp b/src/loader/android_utilities.cpp index 6cc55b322..d2dd2718f 100644 --- a/src/loader/android_utilities.cpp +++ b/src/loader/android_utilities.cpp @@ -36,8 +36,12 @@ constexpr auto SYSTEM_AUTHORITY = "org.khronos.openxr.system_runtime_broker"; constexpr auto BASE_PATH = "openxr"; constexpr auto ABI_PATH = "abi"; constexpr auto RUNTIMES_PATH = "runtimes"; +constexpr auto API_LAYERS_PATH = "api_layers"; +constexpr auto IMP_LAYER = "implicit"; +constexpr auto EXP_LAYER = "explicit"; constexpr const char *getBrokerAuthority(bool systemBroker) { return systemBroker ? SYSTEM_AUTHORITY : AUTHORITY; } +constexpr const char *getLayerTypePath(bool implicitLayer) { return implicitLayer ? IMP_LAYER : EXP_LAYER; } struct BaseColumns { /** @@ -148,6 +152,33 @@ static Uri makeRuntimeContentUri(bool systemBroker, int majorVersion, std::strin return builder.build(); } +/** + * Create a content URI for querying all rows of the API Layer function remapping data for a given + * runtime package, major version of OpenXR, and layer name, + * + * @param systemBroker If the system runtime broker (instead of the installable one) should be queried. + * @param majorVer The major version of OpenXR. + * @param packageName The package name of the runtime. + * @param abi The Android ABI name in use. + * @param layerName The name of the API layer. + * @return A content URI for the entire table: the function remapping for that runtime and layer. + */ +static Uri makeLayerContentUri(bool systemBroker, int majorVersion, std::string const &packageName, const char *abi, + std::string const &layerName) { + auto builder = Uri_Builder::construct(); + builder.scheme("content") + .authority(getBrokerAuthority(systemBroker)) + .appendPath(BASE_PATH) + .appendPath(std::to_string(majorVersion)) + .appendPath(ABI_PATH) + .appendPath(abi) + .appendPath(API_LAYERS_PATH) + .appendPath(packageName) + .appendPath(layerName) + .appendPath(TABLE_PATH); + return builder.build(); +} + struct Columns : BaseColumns { /** * Constant for the FUNCTION_NAME column name @@ -161,6 +192,94 @@ struct Columns : BaseColumns { }; } // namespace functions +namespace instance_extensions { +/** + * Final path component to this URI. + */ +static constexpr auto TABLE_PATH = "instance_extensions"; + +/** + * Create a content URI for querying all rows of the instance extensions supported by a given + * API layer. + * + * @param systemBroker If the system runtime broker (instead of the installable one) should be queried. + * @param majorVer The major version of OpenXR. + * @param packageName The package name of the runtime. + * @param abi The Android ABI name in use. + * @param layerName The API layer name. + * @return A content URI for the entire table: the function remapping for that runtime. + */ +static Uri makeContentUri(bool systemBroker, int majorVersion, std::string const &packageName, const char *abi, + std::string const &layerName) { + auto builder = Uri_Builder::construct(); + builder.scheme("content") + .authority(getBrokerAuthority(systemBroker)) + .appendPath(BASE_PATH) + .appendPath(std::to_string(majorVersion)) + .appendPath(ABI_PATH) + .appendPath(abi) + .appendPath(API_LAYERS_PATH) + .appendPath(packageName) + .appendPath(layerName) + .appendPath(TABLE_PATH); + return builder.build(); +} +struct Columns : BaseColumns { + /** + * Constant for the INSTANCE_EXTENSION_NAMES column name + */ + static constexpr auto INSTANCE_EXTENSION_NAMES = "instance_extension_names"; + + /** + * Constant for the INSTANCE_EXTENSION_VERSIONS column name + */ + static constexpr auto INSTANCE_EXTENSION_VERSIONS = "instance_extension_versions"; +}; +} // namespace instance_extensions + +namespace api_layer { + +/** + * Create a content URI for querying all rows of the implicit/explicit API layer data for a given + * runtime package and major version of OpenXR. + * + * @param systemBroker If the system runtime broker (instead of the installable one) should be queried. + * @param majorVer The major version of OpenXR. + * @param layerType The layer type of the API layer. + * @param abi The Android ABI name in use. + * @return A content URI for the entire table: the API layers for that runtime. + */ +static Uri makeContentUri(bool systemBroker, int majorVersion, std::string const &layerType, const char *abi) { + auto builder = Uri_Builder::construct(); + builder.scheme("content") + .authority(getBrokerAuthority(systemBroker)) + .appendPath(BASE_PATH) + .appendPath(std::to_string(majorVersion)) + .appendPath(ABI_PATH) + .appendPath(abi) + .appendPath(API_LAYERS_PATH) + .appendPath(getLayerTypePath(layerType == IMP_LAYER)); + return builder.build(); +} +struct Columns : BaseColumns { + // implicit or explicit + static constexpr auto PACKAGE_NAME = "package_name"; + static constexpr auto FILE_FORMAT_VERSION = "file_format_version"; + static constexpr auto NAME = "name"; + static constexpr auto NATIVE_LIB_DIR = "native_lib_dir"; + static constexpr auto SO_FILENAME = "so_filename"; + static constexpr auto API_VERSION = "api_version"; + static constexpr auto IMPLEMENTATION_VERSION = "implementation_version"; + static constexpr auto DESCRIPTION = "description"; + static constexpr auto DISABLE_ENVIRONMENT = "disable_environment"; + static constexpr auto ENABLE_ENVIRONMENT = "enable_environment"; + static constexpr auto DISABLE_SYS_PROP = "disable_sys_prop"; + static constexpr auto ENABLE_SYS_PROP = "enable_sys_prop"; + static constexpr auto HAS_FUNCTIONS = "has_functions"; + static constexpr auto HAS_INSTANCE_EXTENSIONS = "has_instance_extensions"; +}; +} // namespace api_layer + } // namespace static inline jni::Array makeArray(std::initializer_list &&list) { @@ -332,6 +451,157 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte cursor.close(); return -1; } + +static int populateApiLayerFunctions(wrap::android::content::Context const &context, bool systemBroker, + const std::string &packageName, std::string const &layerName, Json::Value &rootNode) { + const jni::Array projection = makeArray({functions::Columns::FUNCTION_NAME, functions::Columns::SYMBOL_NAME}); + + auto uri = functions::makeLayerContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI, layerName); + ALOGI("populateApiLayerFunctions: Querying URI: %s", uri.toString().c_str()); + + Cursor cursor; + if (!getCursor(context, projection, uri, systemBroker, "API layer functions", cursor)) { + return -1; + } + + auto functionIndex = cursor.getColumnIndex(functions::Columns::FUNCTION_NAME); + auto symbolIndex = cursor.getColumnIndex(functions::Columns::SYMBOL_NAME); + while (cursor.moveToNext()) { + rootNode["api_layer"]["functions"][cursor.getString(functionIndex)] = cursor.getString(symbolIndex); + } + + cursor.close(); + return 0; +} + +static int populateApiLayerInstanceExtensions(wrap::android::content::Context const &context, bool systemBroker, + const std::string &packageName, std::string const &layerName, Json::Value &rootNode) { + const jni::Array projection = makeArray( + {instance_extensions::Columns::INSTANCE_EXTENSION_NAMES, instance_extensions::Columns::INSTANCE_EXTENSION_VERSIONS}); + + auto uri = + instance_extensions::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI, layerName); + ALOGI("populateApiLayerInstanceExtensions: Querying URI: %s", uri.toString().c_str()); + + Cursor cursor; + if (!getCursor(context, projection, uri, systemBroker, "API layer instance extensions", cursor)) { + return -1; + } + + auto nameIndex = cursor.getColumnIndex(instance_extensions::Columns::INSTANCE_EXTENSION_NAMES); + auto versionIndex = cursor.getColumnIndex(instance_extensions::Columns::INSTANCE_EXTENSION_VERSIONS); + Json::Value extension(Json::objectValue); + while (cursor.moveToNext()) { + extension["name"] = cursor.getString(nameIndex); + extension["extension_version"] = cursor.getString(versionIndex); + rootNode["api_layer"]["instance_extensions"].append(extension); + } + + cursor.close(); + return 0; +} + +/// Get cursor for API layers, parameterized by whether or not we use the system broker +static bool getApiLayerCursor(wrap::android::content::Context const &context, jni::Array const &projection, + std::string const &targetType, bool systemBroker, Cursor &cursor) { + auto uri = api_layer::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), targetType, ABI); + ALOGI("getApiLayerCursor: Querying URI: %s", uri.toString().c_str()); + return getCursor(context, projection, uri, systemBroker, "API Layer", cursor); +} + +static Json::Value makeApiLayerManifest(bool systemBroker, std::string layerType, wrap::android::content::Context const &context, + Cursor &cursor) { + auto packageName = cursor.getString(cursor.getColumnIndex(api_layer::Columns::PACKAGE_NAME)); + auto fileFormatVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::FILE_FORMAT_VERSION)); + auto name = cursor.getString(cursor.getColumnIndex(api_layer::Columns::NAME)); + auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR)); + auto fileName = cursor.getString(cursor.getColumnIndex(api_layer::Columns::SO_FILENAME)); + auto apiVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::API_VERSION)); + auto implementationVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::IMPLEMENTATION_VERSION)); + auto description = cursor.getString(cursor.getColumnIndex(api_layer::Columns::DESCRIPTION)); + auto disable_environment = cursor.getString(cursor.getColumnIndex(api_layer::Columns::DISABLE_ENVIRONMENT)); + auto enable_environment = cursor.getString(cursor.getColumnIndex(api_layer::Columns::ENABLE_ENVIRONMENT)); + auto disable_sys_prop = cursor.getString(cursor.getColumnIndex(api_layer::Columns::DISABLE_SYS_PROP)); + auto enable_sys_prop = cursor.getString(cursor.getColumnIndex(api_layer::Columns::ENABLE_SYS_PROP)); + auto has_instance_extensions = cursor.getString(cursor.getColumnIndex(api_layer::Columns::HAS_INSTANCE_EXTENSIONS)); + auto has_functions = cursor.getString(cursor.getColumnIndex(api_layer::Columns::HAS_FUNCTIONS)); + + ALOGI("Got api layer: type: %s, name: %s, native lib dir: %s, fileName: %s", layerType.c_str(), name.c_str(), libDir.c_str(), + fileName.c_str()); + + Json::Value rootNode(Json::objectValue); + rootNode["file_format_version"] = fileFormatVersion; + rootNode["api_layer"] = Json::objectValue; + rootNode["api_layer"]["name"] = name; + rootNode["api_layer"]["library_path"] = libDir + "/" + fileName; + rootNode["api_layer"]["api_version"] = apiVersion; + rootNode["api_layer"]["implementation_version"] = implementationVersion; + rootNode["api_layer"]["description"] = description; + rootNode["api_layer"]["disable_environment"] = disable_environment; + rootNode["api_layer"]["enable_environment"] = enable_environment; + rootNode["api_layer"]["disable_sys_prop"] = disable_sys_prop; + rootNode["api_layer"]["enable_sys_prop"] = enable_sys_prop; + if (has_functions == "true") { + rootNode["api_layer"]["functions"] = Json::Value(Json::objectValue); + populateApiLayerFunctions(context, systemBroker, packageName, name, rootNode); + } + if (has_instance_extensions == "true") { + rootNode["api_layer"]["instance_extensions"] = Json::Value(Json::arrayValue); + populateApiLayerInstanceExtensions(context, systemBroker, packageName, name, rootNode); + } + + return rootNode; +} + +static int enumerateApiLayerManifests(std::string layerType, wrap::android::content::Context const &context, + const jni::Array &projection, std::vector &virtualManifests, + bool systemBroker) { + Cursor cursor; + + if (!getApiLayerCursor(context, projection, layerType, systemBroker, cursor)) { + return -1; + } + + cursor.moveToFirst(); + const int32_t n = cursor.getCount(); + virtualManifests.reserve(virtualManifests.size() + n); + + for (int32_t i = 0; i < n; ++i) { + virtualManifests.emplace_back(makeApiLayerManifest(systemBroker, layerType, context, cursor)); + cursor.moveToNext(); + } + + cursor.close(); + return 0; +} + +int getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context, + std::vector &virtualManifests, bool systemBroker) { + ALOGI("Try to get %s API layer from broker", layerType.c_str()); + const jni::Array projection = makeArray( + {api_layer::Columns::PACKAGE_NAME, api_layer::Columns::FILE_FORMAT_VERSION, api_layer::Columns::NAME, + api_layer::Columns::NATIVE_LIB_DIR, api_layer::Columns::SO_FILENAME, api_layer::Columns::API_VERSION, + api_layer::Columns::IMPLEMENTATION_VERSION, api_layer::Columns::DESCRIPTION, api_layer::Columns::DISABLE_ENVIRONMENT, + api_layer::Columns::ENABLE_ENVIRONMENT, api_layer::Columns::DISABLE_SYS_PROP, api_layer::Columns::ENABLE_SYS_PROP, + api_layer::Columns::HAS_FUNCTIONS, api_layer::Columns::HAS_INSTANCE_EXTENSIONS}); + + if (layerType == IMP_LAYER) { + std::vector implicitLayerManifest; + if (0 != enumerateApiLayerManifests(IMP_LAYER, context, projection, implicitLayerManifest, systemBroker)) { + return -1; + } + virtualManifests = std::move(implicitLayerManifest); + } else { + std::vector explicitLayerManifest; + if (0 != enumerateApiLayerManifests(EXP_LAYER, context, projection, explicitLayerManifest, systemBroker)) { + return -1; + } + virtualManifests = std::move(explicitLayerManifest); + } + + return 0; +} + } // namespace openxr_android #endif // __ANDROID__ diff --git a/src/loader/android_utilities.h b/src/loader/android_utilities.h index 2165f7ecb..a57a8fddc 100644 --- a/src/loader/android_utilities.h +++ b/src/loader/android_utilities.h @@ -28,6 +28,18 @@ using wrap::android::content::Context; */ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest, bool &systemBroker); + +/*! + * Find the implicit/explicit API layers on the system, and return a constructed JSON object representing it. + * + * @param type An String to indicate layer type of API layers, implicit or explicit. + * @param context An Android context, preferably an Activity Context. + * @param[out] virtualManifest The Json::Value to fill with the virtual manifest. + * + * @return 0 on success, something else on failure. + */ +int getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context, + std::vector &virtualManifests, bool systemBroker); } // namespace openxr_android #endif // __ANDROID__ From 8fb9c9496d4b61f7234ed7bef65aa642172fae7d Mon Sep 17 00:00:00 2001 From: dengkail Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH 11/17] OpenXR loader: add API layer discovery support, part 2 --- src/loader/loader_init_data.hpp | 4 ++++ src/loader/runtime_interface.cpp | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/loader/loader_init_data.hpp b/src/loader/loader_init_data.hpp index 1d7cb0fc7..07d6ffc79 100644 --- a/src/loader/loader_init_data.hpp +++ b/src/loader/loader_init_data.hpp @@ -88,6 +88,10 @@ XrResult InitializeLoaderInitData(const XrLoaderInitInfoBaseHeaderKHR* loaderIni //! Modifies @p out_manifest and @p out_runtime_source only if returning successfully XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest, ManifestFileSource& out_runtime_source); + +//! Modifies @p out_manifest only if returning successfully +XrResult GetPlatformApiLayerVirtualManifests(bool is_implicit, bool system_broker, std::vector& out_manifest); + std::string GetAndroidNativeLibraryDir(); void* Android_Get_Asset_Manager(); #endif // XR_USE_PLATFORM_ANDROID diff --git a/src/loader/runtime_interface.cpp b/src/loader/runtime_interface.cpp index be7db185a..ccf181f70 100644 --- a/src/loader/runtime_interface.cpp +++ b/src/loader/runtime_interface.cpp @@ -58,6 +58,25 @@ XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest, ManifestFi out_manifest = virtualManifest; return XR_SUCCESS; } + +XrResult GetPlatformApiLayerVirtualManifests(bool is_implicit, bool system_broker, std::vector& out_manifest) { + using wrap::android::content::Context; + auto& initData = LoaderInitData::instance(); + if (!initData.initialized()) { + return XR_ERROR_INITIALIZATION_FAILED; + } + auto context = Context(reinterpret_cast(initData.getData().applicationContext)); + if (context.isNull()) { + return XR_ERROR_INITIALIZATION_FAILED; + } + std::vector virtualManifests; + if (0 != openxr_android::getApiLayerVirtualManifests(is_implicit ? "implicit" : "explicit", context, virtualManifests, + system_broker)) { + return XR_ERROR_INITIALIZATION_FAILED; + } + out_manifest = virtualManifests; + return XR_SUCCESS; +} #endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT) XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command, From ab20588d978fffa00a3e6c86dc30ae809bf6d204 Mon Sep 17 00:00:00 2001 From: dengkail Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH 12/17] platform_utils: Add PlatformUtilsGetBoolSysProp --- src/common/platform_utils.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/common/platform_utils.hpp b/src/common/platform_utils.hpp index d047c17d9..d6ddf3f52 100644 --- a/src/common/platform_utils.hpp +++ b/src/common/platform_utils.hpp @@ -342,6 +342,16 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) { return {}; } +static inline bool PlatformUtilsGetBoolSysProp(const char* name, bool default_value) { + bool result = default_value; + char value[PROP_VALUE_MAX] = {}; + if (__system_property_get(name, value) != 0) { + result = (value[0] == 't'); + } + + return result; +} + // Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { // Prefix for the runtime JSON file name From 5f8d58f7fc4087647eef8dc819687a109719a264 Mon Sep 17 00:00:00 2001 From: dengkail Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH 13/17] OpenXR loader: add API layer discovery support part 3 Co-authored-by: Rylie Pavlik --- src/loader/manifest_file.cpp | 102 ++++++++++++++++++++++++++++++----- src/loader/manifest_file.hpp | 2 + 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/src/loader/manifest_file.cpp b/src/loader/manifest_file.cpp index a996d9021..ac9366ecb 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -779,22 +779,10 @@ void ApiLayerManifestFile::AddManifestFilesAndroid(const std::string &openxr_com } #endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT) -void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream, +void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, const Json::Value &root_node, LibraryLocator locate_library, std::vector> &manifest_files) { std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid "); - Json::CharReaderBuilder builder; - std::string errors; - Json::Value root_node = Json::nullValue; - if (!Json::parseFromStream(builder, json_stream, &root_node, &errors) || !root_node.isObject()) { - error_ss << "failed to parse " << filename << "."; - if (!errors.empty()) { - error_ss << " (Error message: " << errors << ")"; - } - error_ss << " Is it a valid layer manifest file?"; - LoaderLogger::LogErrorMessage("", error_ss.str()); - return; - } JsonVersion file_version = {}; if (!ManifestFile::IsValidJson(root_node, file_version)) { error_ss << "isValidJson indicates " << filename << " is not a valid manifest file."; @@ -842,6 +830,36 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin enabled = false; } +#if defined(XR_OS_ANDROID) + auto &disable_prop_node = layer_root_node["disable_sys_prop"]; + // Check if there's an system property to enable this API layer + auto &enable_prop_node = layer_root_node["enable_sys_prop"]; + if (!enable_prop_node.isNull() && enable_prop_node.isString()) { + std::string enable_sys_prop = enable_prop_node.asString(); + if (enable_sys_prop.empty()) { + error_ss << "Implicit layer " << filename << " has a present but empty \"enable_sys_prop\""; + LoaderLogger::LogErrorMessage("", error_ss.str()); + return; + } + // TODO other validation on the enable_sys_prop? + // If it's not set to true, disable this layer + if (!PlatformUtilsGetBoolSysProp(enable_sys_prop.c_str(), true)) { + enabled = false; + } + } + + std::string disable_sys_prop = disable_prop_node.asString(); + if (disable_sys_prop.empty()) { + error_ss << "Implicit layer " << filename << " has a present but empty \"disable_sys_prop\""; + LoaderLogger::LogErrorMessage("", error_ss.str()); + return; + } + // TODO other validation on the disable_sys_prop? + if (PlatformUtilsGetBoolSysProp(disable_sys_prop.c_str(), false)) { + enabled = false; + } +#endif + // Not enabled, so pretend like it isn't even there. if (!enabled) { error_ss << "Implicit layer " << filename << " is disabled"; @@ -910,6 +928,26 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin manifest_files.back()->ParseCommon(layer_root_node); } +void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream, + LibraryLocator locate_library, + std::vector> &manifest_files) { + std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid "); + Json::CharReaderBuilder builder; + std::string errors; + Json::Value root_node = Json::nullValue; + if (!Json::parseFromStream(builder, json_stream, &root_node, &errors) || !root_node.isObject()) { + error_ss << "failed to parse " << filename << "."; + if (!errors.empty()) { + error_ss << " (Error message: " << errors << ")"; + } + error_ss << " Is it a valid layer manifest file?"; + LoaderLogger::LogErrorMessage("", error_ss.str()); + return; + } + + CreateIfValid(type, filename, root_node, locate_library, manifest_files); +} + void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, std::vector> &manifest_files) { std::ifstream json_stream(filename, std::ifstream::in); @@ -966,6 +1004,24 @@ void ApiLayerManifestFile::PopulateApiLayerProperties(XrApiLayerProperties &prop // Find all layer manifest files in the appropriate search paths/registries for the given type. XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_command, ManifestFileType type, std::vector> &manifest_files) { + bool search_json_layer = true; + bool search_broker_layer = true; + +#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID) + Json::Value virtual_manifest; + bool system_broker = true; + ManifestFileSource runtime_source = ManifestFileSource::FROM_JSON_MANIFEST; + XrResult result = GetPlatformRuntimeVirtualManifest(virtual_manifest, runtime_source); + if (XR_SUCCESS == result) { + if (runtime_source == ManifestFileSource::FROM_INSTALLABLE_BROKER) { + system_broker = false; + search_json_layer = false; + } + } else { + search_broker_layer = false; + } +#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT) + std::string relative_path; std::string override_env_var; std::string registry_location; @@ -998,7 +1054,9 @@ XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_comma bool override_active = false; std::vector filenames; - ReadDataFilesInSearchPaths(override_env_var, relative_path, override_active, filenames); + if (search_json_layer) { + ReadDataFilesInSearchPaths(override_env_var, relative_path, override_active, filenames); + } #ifdef XR_OS_WINDOWS // Read the registry if the override wasn't active. @@ -1012,6 +1070,22 @@ XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_comma } #if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID) + if (search_broker_layer) { + std::vector virtual_manifests; + result = GetPlatformApiLayerVirtualManifests(type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER, system_broker, + virtual_manifests); + if (XR_SUCCESS == result) { + for (const auto &virtual_manifest : virtual_manifests) { + ApiLayerManifestFile::CreateIfValid(type, "virtual manifest", virtual_manifest, + &ApiLayerManifestFile::LocateLibraryInAssets, manifest_files); + } + } else { + LoaderLogger::LogInfoMessage(openxr_command, + "ApiLayerManifestFile::FindManifestFiles - failed to get virtual manifest files from " + "system/installable broker, likely not supported by current broker version."); + } + } + ApiLayerManifestFile::AddManifestFilesAndroid(openxr_command, type, manifest_files); #endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT) diff --git a/src/loader/manifest_file.hpp b/src/loader/manifest_file.hpp index a7f1cf34f..c5058d369 100644 --- a/src/loader/manifest_file.hpp +++ b/src/loader/manifest_file.hpp @@ -109,6 +109,8 @@ class ApiLayerManifestFile : public ManifestFile { const std::string &description, const JsonVersion &api_version, const uint32_t &implementation_version, const std::string &library_path); + static void CreateIfValid(ManifestFileType type, const std::string &filename, const Json::Value &root_node, + LibraryLocator locate_library, std::vector> &manifest_files); static void CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream, LibraryLocator locate_library, std::vector> &manifest_files); static void CreateIfValid(ManifestFileType type, const std::string &filename, From a6dfa10f255bf003e1d535f6ff08557e5c1ea296 Mon Sep 17 00:00:00 2001 From: Rylie Pavlik Date: Fri, 30 Aug 2024 16:23:03 -0500 Subject: [PATCH 14/17] OpenXR loader: Require disable_sys_prop on implicit layers on Android --- src/loader/manifest_file.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/loader/manifest_file.cpp b/src/loader/manifest_file.cpp index ac9366ecb..49c304fa9 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -831,7 +831,15 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin } #if defined(XR_OS_ANDROID) + + // Implicit layers require the disable system property on Android. auto &disable_prop_node = layer_root_node["disable_sys_prop"]; + if (disable_prop_node.isNull() || !disable_prop_node.isString()) { + error_ss << "Implicit layer " << filename << " is missing \"disable_sys_prop\""; + LoaderLogger::LogErrorMessage("", error_ss.str()); + return; + } + // Check if there's an system property to enable this API layer auto &enable_prop_node = layer_root_node["enable_sys_prop"]; if (!enable_prop_node.isNull() && enable_prop_node.isString()) { From 041c45b62599b1c4055c0fd1ed5225e7261e1666 Mon Sep 17 00:00:00 2001 From: Rylie Pavlik Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH 15/17] OpenXR loader: factor out turning functions content into functions json --- src/loader/android_utilities.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/loader/android_utilities.cpp b/src/loader/android_utilities.cpp index d2dd2718f..33e1969f4 100644 --- a/src/loader/android_utilities.cpp +++ b/src/loader/android_utilities.cpp @@ -363,6 +363,17 @@ static bool getCursor(wrap::android::content::Context const &context, jni::Array return true; } +/// Shared helper for populating the functions object in a manifest +static void populateFunctionsData(Cursor &cursor, Json::Value &functionsObject) { + auto functionIndex = cursor.getColumnIndex(functions::Columns::FUNCTION_NAME); + auto symbolIndex = cursor.getColumnIndex(functions::Columns::SYMBOL_NAME); + while (cursor.moveToNext()) { + functionsObject[cursor.getString(functionIndex)] = cursor.getString(symbolIndex); + } + + cursor.close(); +} + static int populateRuntimeFunctions(wrap::android::content::Context const &context, bool systemBroker, const std::string &packageName, Json::Value &manifest) { const jni::Array projection = makeArray({functions::Columns::FUNCTION_NAME, functions::Columns::SYMBOL_NAME}); @@ -373,14 +384,8 @@ static int populateRuntimeFunctions(wrap::android::content::Context const &conte if (!getCursor(context, projection, uri, systemBroker, "functions", cursor)) { return -1; } + populateFunctionsData(cursor, manifest["functions"]); - auto functionIndex = cursor.getColumnIndex(functions::Columns::FUNCTION_NAME); - auto symbolIndex = cursor.getColumnIndex(functions::Columns::SYMBOL_NAME); - while (cursor.moveToNext()) { - manifest["functions"][cursor.getString(functionIndex)] = cursor.getString(symbolIndex); - } - - cursor.close(); return 0; } @@ -464,11 +469,7 @@ static int populateApiLayerFunctions(wrap::android::content::Context const &cont return -1; } - auto functionIndex = cursor.getColumnIndex(functions::Columns::FUNCTION_NAME); - auto symbolIndex = cursor.getColumnIndex(functions::Columns::SYMBOL_NAME); - while (cursor.moveToNext()) { - rootNode["api_layer"]["functions"][cursor.getString(functionIndex)] = cursor.getString(symbolIndex); - } + populateFunctionsData(cursor, rootNode["api_layer"]["functions"]); cursor.close(); return 0; From b975f0731e11a93b4a2ca0874ad8f8b2cc1a5993 Mon Sep 17 00:00:00 2001 From: Rylie Pavlik Date: Fri, 30 Aug 2024 17:25:59 -0500 Subject: [PATCH 16/17] OpenXR loader: Better placeholder filenames for API layer virtual manifests --- src/loader/manifest_file.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/loader/manifest_file.cpp b/src/loader/manifest_file.cpp index 49c304fa9..ea5729f77 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -1083,8 +1083,24 @@ XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_comma result = GetPlatformApiLayerVirtualManifests(type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER, system_broker, virtual_manifests); if (XR_SUCCESS == result) { - for (const auto &virtual_manifest : virtual_manifests) { - ApiLayerManifestFile::CreateIfValid(type, "virtual manifest", virtual_manifest, + std::string fnBase; + { + std::ostringstream oss{"_virtualManifest_"}; + if (system_broker) { + oss << "systemBroker_"; + } else { + oss << "installableBroker_"; + } + if (type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER) { + oss << "implicit.json"; + } else { + oss << "explicit.json"; + } + fnBase = oss.str(); + } + const size_t n = virtual_manifests.size(); + for (size_t i = 0; i < n; ++i) { + ApiLayerManifestFile::CreateIfValid(type, std::to_string(i) + fnBase, virtual_manifests[i], &ApiLayerManifestFile::LocateLibraryInAssets, manifest_files); } } else { From 12433f4ee4508e7cc3112ef1c520718fb97a29aa Mon Sep 17 00:00:00 2001 From: dengkail Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH 17/17] OpenXR loader: cache runtime lookup Co-authored-by: Rylie Pavlik --- src/loader/android_utilities.cpp | 104 +++++++++++++++++-------------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/src/loader/android_utilities.cpp b/src/loader/android_utilities.cpp index 33e1969f4..9df5135da 100644 --- a/src/loader/android_utilities.cpp +++ b/src/loader/android_utilities.cpp @@ -402,59 +402,67 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte const jni::Array projection = makeArray({active_runtime::Columns::PACKAGE_NAME, active_runtime::Columns::NATIVE_LIB_DIR, active_runtime::Columns::SO_FILENAME, active_runtime::Columns::HAS_FUNCTIONS}); + static bool hasQueryBroker = false; + static Json::Value runtimeManifest; + if (!hasQueryBroker) { + // First, try getting the installable broker's provider + systemBroker = false; + Cursor cursor; + if (!getActiveRuntimeCursor(context, projection, systemBroker, cursor)) { + // OK, try the system broker as a fallback. + systemBroker = true; + getActiveRuntimeCursor(context, projection, systemBroker, cursor); + } + hasQueryBroker = true; - // First, try getting the installable broker's provider - systemBroker = false; - Cursor cursor; - if (!getActiveRuntimeCursor(context, projection, systemBroker, cursor)) { - // OK, try the system broker as a fallback. - systemBroker = true; - getActiveRuntimeCursor(context, projection, systemBroker, cursor); - } - - if (cursor.isNull()) { - // Couldn't find either broker - ALOGI("Could access neither the installable nor system runtime broker."); - return -1; - } - - cursor.moveToFirst(); + if (cursor.isNull()) { + // Couldn't find either broker + ALOGI("Could access neither the installable nor system runtime broker."); + return -1; + } - do { - auto filename = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::SO_FILENAME)); - auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR)); - auto packageName = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::PACKAGE_NAME)); - - auto hasFunctions = cursor.getInt(cursor.getColumnIndex(active_runtime::Columns::HAS_FUNCTIONS)) == 1; - ALOGI("Got runtime: package: %s, so filename: %s, native lib dir: %s, has functions: %s", packageName.c_str(), - filename.c_str(), libDir.c_str(), (hasFunctions ? "yes" : "no")); - - auto lib_path = libDir + "/" + filename; - auto *lib = dlopen(lib_path.c_str(), RTLD_LAZY | RTLD_LOCAL); - if (lib) { - // we found a runtime that we can dlopen, use it. - dlclose(lib); - - Json::Value manifest = makeMinimumVirtualRuntimeManifest(lib_path); - if (hasFunctions) { - int result = populateRuntimeFunctions(context, systemBroker, packageName, manifest); - if (result != 0) { - ALOGW("Unable to populate functions from runtime: %s, checking for more records...", lib_path.c_str()); - continue; + cursor.moveToFirst(); + + do { + auto filename = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::SO_FILENAME)); + auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR)); + auto packageName = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::PACKAGE_NAME)); + + auto hasFunctions = cursor.getInt(cursor.getColumnIndex(active_runtime::Columns::HAS_FUNCTIONS)) == 1; + ALOGI("Got runtime: package: %s, so filename: %s, native lib dir: %s, has functions: %s", packageName.c_str(), + filename.c_str(), libDir.c_str(), (hasFunctions ? "yes" : "no")); + + auto lib_path = libDir + "/" + filename; + auto *lib = dlopen(lib_path.c_str(), RTLD_LAZY | RTLD_LOCAL); + if (lib) { + // we found a runtime that we can dlopen, use it. + dlclose(lib); + + Json::Value manifest = makeMinimumVirtualRuntimeManifest(lib_path); + if (hasFunctions) { + int result = populateRuntimeFunctions(context, systemBroker, packageName, manifest); + if (result != 0) { + ALOGW("Unable to populate functions from runtime: %s, checking for more records...", lib_path.c_str()); + continue; + } } + runtimeManifest = manifest; + cursor.close(); + virtualManifest = runtimeManifest; + return 0; } - virtualManifest = manifest; - cursor.close(); - return 0; - } - // this runtime was not accessible, see if the broker has more runtimes on - // offer. - ALOGV("Unable to open broker provided runtime at %s, checking for more records...", lib_path.c_str()); - } while (cursor.moveToNext()); + // this runtime was not accessible, see if the broker has more runtimes on + // offer. + ALOGV("Unable to open broker provided runtime at %s, checking for more records...", lib_path.c_str()); + } while (cursor.moveToNext()); - ALOGE("Unable to open any of the broker provided runtimes."); - cursor.close(); - return -1; + ALOGE("Unable to open any of the broker provided runtimes."); + cursor.close(); + return -1; + } + + virtualManifest = runtimeManifest; + return 0; } static int populateApiLayerFunctions(wrap::android::content::Context const &context, bool systemBroker,