From 8cbba7236e867ef821224991e67a423ed7df63d0 Mon Sep 17 00:00:00 2001 From: dengkail Date: Mon, 4 Mar 2024 17:31:41 +0800 Subject: [PATCH] OpenXR loader: add API layer discovery support. --- src/common/platform_utils.hpp | 11 + src/loader/android_utilities.cpp | 334 +++++++++++++++++++++++++++---- src/loader/android_utilities.h | 15 +- src/loader/loader_init_data.hpp | 4 +- src/loader/manifest_file.cpp | 176 +++++++++++++++- src/loader/manifest_file.hpp | 8 + src/loader/runtime_interface.cpp | 29 ++- 7 files changed, 535 insertions(+), 42 deletions(-) diff --git a/src/common/platform_utils.hpp b/src/common/platform_utils.hpp index 50194051..b4343e4b 100644 --- a/src/common/platform_utils.hpp +++ b/src/common/platform_utils.hpp @@ -349,6 +349,7 @@ static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { } #elif defined(XR_OS_ANDROID) +#include static inline bool PlatformUtilsGetEnvSet(const char* /* name */) { // Stub func @@ -370,6 +371,16 @@ static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* va return false; } +static inline bool PlatformUtilsGetSysProp(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 diff --git a/src/loader/android_utilities.cpp b/src/loader/android_utilities.cpp index 5c9b846b..8294e983 100644 --- a/src/loader/android_utilities.cpp +++ b/src/loader/android_utilities.cpp @@ -38,8 +38,13 @@ 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; } +constexpr const char *getFunctionTypePath(bool runtimeFunctions) { return runtimeFunctions ? RUNTIMES_PATH : API_LAYERS_PATH; } struct BaseColumns { /** @@ -132,12 +137,14 @@ static constexpr auto TABLE_PATH = "functions"; * runtime package and major version of OpenXR. * * @param systemBroker If the system runtime broker (instead of the installable one) should be queried. + * @param runtimeFunctions If the runtime functions (instead of the API layer functions) 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. * @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 makeContentUri(bool systemBroker, bool runtimeFunctions, int majorVersion, std::string const &packageName, + const char *abi, std::string const &layerName) { auto builder = Uri_Builder::construct(); builder.scheme("content") .authority(getBrokerAuthority(systemBroker)) @@ -145,9 +152,12 @@ static Uri makeContentUri(bool systemBroker, int majorVersion, std::string const .appendPath(std::to_string(majorVersion)) .appendPath(ABI_PATH) .appendPath(abi) - .appendPath(RUNTIMES_PATH) - .appendPath(packageName) - .appendPath(TABLE_PATH); + .appendPath(getFunctionTypePath(runtimeFunctions)) + .appendPath(packageName); + if (!runtimeFunctions) { + builder.appendPath(layerName); + } + builder.appendPath(TABLE_PATH); return builder.build(); } @@ -164,6 +174,97 @@ 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 { +/** + * Final path component to this URI. + */ + +/** + * 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 function remapping 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) { @@ -221,7 +322,7 @@ static int populateFunctions(wrap::android::content::Context const &context, boo 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); + auto uri = functions::makeContentUri(systemBroker, true, 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); @@ -265,11 +366,75 @@ 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()); +static int populateApiLayerFunctions(wrap::android::content::Context const &context, bool systemBroker, + const std::string &packageName, std::string const &layerName, Json::Value &rootNode) { + jni::Array projection = makeArray({functions::Columns::FUNCTION_NAME, functions::Columns::SYMBOL_NAME}); + + auto uri = + functions::makeContentUri(systemBroker, false, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI, layerName); + ALOGI("populateApiLayerFunctions: 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 API layer functions."); + return -1; + } + if (cursor.getCount() < 1) { + ALOGE("Non-null but empty cursor when querying content resolver for API layer functions."); + cursor.close(); + 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) { + 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 = context.getContentResolver().query(uri, projection); + + if (cursor.isNull()) { + ALOGE("Null cursor when querying content resolver for API layer instance extensions."); + return -1; + } + if (cursor.getCount() < 1) { + ALOGE("Non-null but empty cursor when querying content resolver for API layer instance extensions."); + cursor.close(); + 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 active runtime or API layer, parameterized by target type and whether or not we use the system broker +static bool getCursor(wrap::android::content::Context const &context, jni::Array const &projection, + std::string const &targetType, bool systemBroker, Cursor &cursor) { + auto uri = (targetType == RUNTIMES_PATH) + ? active_runtime::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), ABI) + : api_layer::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), targetType, ABI); + ALOGI("getCursor: Querying URI: %s", uri.toString().c_str()); ANDROID_UTILITIES_TRY { cursor = context.getContentResolver().query(uri, projection); } ANDROID_UTILITIES_CATCH_FALLBACK({ @@ -292,48 +457,145 @@ static bool getActiveRuntimeCursor(wrap::android::content::Context const &contex return true; } -int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest) { +int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest, + bool &systemBroker) { 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 (!getCursor(context, projection, RUNTIMES_PATH, systemBroker, cursor)) { + // OK, try the system broker as a fallback. + systemBroker = true; + getCursor(context, projection, RUNTIMES_PATH, systemBroker, cursor); + } + hasQueryBroker = true; - // First, try getting the installable broker's provider - bool 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 + ALOGE("Could access neither the installable nor system runtime broker."); + return -1; + } - if (cursor.isNull()) { - // Couldn't find either broker - ALOGE("Could access neither the installable nor system runtime broker."); - return -1; + cursor.moveToFirst(); + + 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; + __android_log_print(ANDROID_LOG_INFO, TAG, + "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; + cursor.close(); + + JsonManifestBuilder builder{"runtime", lib_path}; + if (hasFunctions) { + int result = populateFunctions(context, systemBroker, packageName, builder); + if (result != 0) { + return result; + } + } + runtimeManifest = builder.build(); } - cursor.moveToFirst(); + virtualManifest = runtimeManifest; + return 0; +} - auto filename = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::SO_FILENAME)); +static bool populateApiLayerManifest(bool systemBroker, std::string layerType, wrap::android::content::Context const &context, + Cursor &cursor, std::vector &layerRootNode) { + 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 packageName = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::PACKAGE_NAME)); + 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_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)); + + __android_log_print(ANDROID_LOG_INFO, TAG, "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_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); + } + + layerRootNode.push_back(rootNode); + + return true; +} + +static int enumerateApiLayerManifests(std::string layerType, wrap::android::content::Context const &context, + jni::Array &projection, std::vector &virtualManifests, + bool systemBroker) { + Cursor cursor; + + if (!getCursor(context, projection, layerType, systemBroker, cursor)) { + return -1; + } - auto hasFunctions = cursor.getInt(cursor.getColumnIndex(active_runtime::Columns::HAS_FUNCTIONS)) == 1; - __android_log_print(ANDROID_LOG_INFO, TAG, "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")); + cursor.moveToFirst(); + for (int i = 0; i < cursor.getCount(); ++i) { + populateApiLayerManifest(systemBroker, layerType, context, cursor, virtualManifests); + cursor.moveToNext(); + } - auto lib_path = libDir + "/" + filename; cursor.close(); + return 0; +} - JsonManifestBuilder builder{"runtime", lib_path}; - if (hasFunctions) { - int result = populateFunctions(context, systemBroker, packageName, builder); - if (result != 0) { - return result; +int getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context, + std::vector &virtualManifests, bool systemBroker) { + static bool hasQueryBroker = false; + static std::vector implicitLayerManifest; + static std::vector explicitLayerManifest; + + __android_log_print(ANDROID_LOG_INFO, TAG, "Try to get %s API layer from broker!", layerType.c_str()); + if (!hasQueryBroker) { + 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_SYS_PROP, + api_layer::Columns::ENABLE_SYS_PROP, api_layer::Columns::HAS_FUNCTIONS, api_layer::Columns::HAS_INSTANCE_EXTENSIONS}); + + int impLayerQuery = enumerateApiLayerManifests(IMP_LAYER, context, projection, implicitLayerManifest, systemBroker); + int expLayerQuery = enumerateApiLayerManifests(EXP_LAYER, context, projection, explicitLayerManifest, systemBroker); + hasQueryBroker = true; + + if (impLayerQuery && expLayerQuery) { + return -1; } } - virtualManifest = builder.build(); + + virtualManifests = (layerType == IMP_LAYER) ? implicitLayerManifest : explicitLayerManifest; return 0; } + } // namespace openxr_android #endif // __ANDROID__ diff --git a/src/loader/android_utilities.h b/src/loader/android_utilities.h index 582a6505..a57a8fdd 100644 --- a/src/loader/android_utilities.h +++ b/src/loader/android_utilities.h @@ -26,7 +26,20 @@ 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); + +/*! + * 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__ diff --git a/src/loader/loader_init_data.hpp b/src/loader/loader_init_data.hpp index e3a27fc4..863f2e5b 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,8 @@ class LoaderInitData { XrResult InitializeLoaderInitData(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo); #ifdef XR_USE_PLATFORM_ANDROID -XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest); +XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest, ManifestFileSource& runtime_source); +XrResult GetPlatformApiLayerVirtualManifests(std::string type, std::vector& out_manifest, bool system_broker); 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 4e3e5b49..76f0749c 100644 --- a/src/loader/manifest_file.cpp +++ b/src/loader/manifest_file.cpp @@ -666,7 +666,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; @@ -763,6 +764,140 @@ 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 Json::Value &root_node, const std::string &filename, + std::vector> &manifest_files) { + std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid "); + JsonVersion file_version = {}; + if (!ManifestFile::IsValidJson(root_node, file_version)) { + error_ss << "isValidJson indicates " << filename << " is not a valid manifest file."; + LoaderLogger::LogErrorMessage("", error_ss.str()); + return; + } + + Json::Value layer_root_node = root_node["api_layer"]; + + // The API Layer manifest file needs the "api_layer" root as well as other sub-nodes. + // If any of those aren't there, fail. + if (layer_root_node.isNull() || layer_root_node["name"].isNull() || !layer_root_node["name"].isString() || + layer_root_node["api_version"].isNull() || !layer_root_node["api_version"].isString() || + layer_root_node["library_path"].isNull() || !layer_root_node["library_path"].isString() || + layer_root_node["implementation_version"].isNull() || !layer_root_node["implementation_version"].isString()) { + error_ss << filename << " is missing required fields. Verify all proper fields exist."; + LoaderLogger::LogErrorMessage("", error_ss.str()); + return; + } + if (MANIFEST_TYPE_IMPLICIT_API_LAYER == type) { + bool enabled = true; +#if !defined(XR_OS_ANDROID) + // Implicit layers require the disable environment variable. + if (layer_root_node["disable_environment"].isNull() || !layer_root_node["disable_environment"].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(); + // 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(); + // If the env var is set, disable the layer. Disable env var overrides enable above + if (PlatformUtilsGetEnvSet(env_var.c_str())) { + enabled = false; + } +#else + // Implicit layers require system property to disable it on Android + if (layer_root_node["disable_sys_prop"].isNull() || !layer_root_node["disable_sys_prop"].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 + if (!layer_root_node["enable_sys_prop"].isNull() && layer_root_node["enable_sys_prop"].isString()) { + std::string enable_sys_prop = layer_root_node["enable_sys_prop"].asString(); + // If it's not set to true, disable this layer + if (!PlatformUtilsGetSysProp(enable_sys_prop.c_str(), true)) { + enabled = false; + } + } + + std::string disable_sys_prop = layer_root_node["disable_sys_prop"].asString(); + if (PlatformUtilsGetSysProp(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"; + LoaderLogger::LogInfoMessage("", error_ss.str()); + return; + } + } + std::string layer_name = layer_root_node["name"].asString(); + std::string api_version_string = layer_root_node["api_version"].asString(); + JsonVersion api_version = {}; + const int num_fields = sscanf(api_version_string.c_str(), "%u.%u", &api_version.major, &api_version.minor); + api_version.patch = 0; + + if ((num_fields != 2) || (api_version.major == 0 && api_version.minor == 0) || + api_version.major > XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) { + error_ss << "layer " << filename << " has invalid API Version. Skipping layer."; + LoaderLogger::LogWarningMessage("", error_ss.str()); + return; + } + + char *end_ptr; + uint32_t implementation_version = strtol(layer_root_node["implementation_version"].asString().c_str(), &end_ptr, 10); + if (*end_ptr != '\0') { + error_ss << "layer " << filename << " has invalid implementation version."; + LoaderLogger::LogWarningMessage("", error_ss.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 + // global library path. + if (library_path.find('\\') != std::string::npos || library_path.find('/') != std::string::npos) { + // If the library_path is an absolute path, just use that if it exists + if (FileSysUtilsIsAbsolutePath(library_path)) { + if (!FileSysUtilsPathExists(library_path)) { + error_ss << filename << " library " << library_path << " does not appear to exist"; + LoaderLogger::LogErrorMessage("", error_ss.str()); + return; + } + } else { + // Otherwise, treat the library path as a relative path based on the JSON file. + std::string combined_path; + std::string file_parent; + if (!FileSysUtilsGetParentPath(filename, file_parent) || + !FileSysUtilsCombinePaths(file_parent, library_path, combined_path) || !FileSysUtilsPathExists(combined_path)) { + error_ss << filename << " library " << combined_path << " does not appear to exist"; + LoaderLogger::LogErrorMessage("", error_ss.str()); + return; + } + library_path = combined_path; + } + } + + std::string description; + if (!layer_root_node["description"].isNull() && layer_root_node["description"].isString()) { + description = layer_root_node["description"].asString(); + } + + // Add this layer manifest file + 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. + // Handle any renamed functions + 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) { @@ -876,6 +1011,7 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path)); // Add any extensions to it after the fact. + // Handle any renamed functions manifest_files.back()->ParseCommon(layer_root_node); } @@ -935,6 +1071,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 virtualManifest; + bool systemBroker = true; + ManifestFileSource runtimeSource = ManifestFileSource::FROM_JSON_MANIFEST; + XrResult result = GetPlatformRuntimeVirtualManifest(virtualManifest, runtimeSource); + if (XR_SUCCESS == result) { + if (runtimeSource == ManifestFileSource::FROM_INSTALLABLE_BROKER) { + systemBroker = 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; @@ -967,7 +1121,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. @@ -981,6 +1137,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 virtualManifests; + std::string layerType = (type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER) ? "implicit" : "explicit"; + result = GetPlatformApiLayerVirtualManifests(layerType, virtualManifests, systemBroker); + if (XR_SUCCESS == result) { + for (const auto &virtualManifest : virtualManifests) { + ApiLayerManifestFile::CreateIfValid(type, virtualManifest, "virtual manifest", manifest_files); + } + } else { + LoaderLogger::LogErrorMessage( + "", + "ApiLayerManifestFile::FindManifestFiles - failed to get virtual manifest files from system/installable broker."); + assert(0); + } + } + 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 801614ad..4cdebbd5 100644 --- a/src/loader/manifest_file.hpp +++ b/src/loader/manifest_file.hpp @@ -28,6 +28,12 @@ enum ManifestFileType { MANIFEST_TYPE_EXPLICIT_API_LAYER, }; +enum ManifestFileSource { + FROM_JSON_MANIFEST = 0, + FROM_INSTALLABLE_BROKER, + FROM_SYSTEM_BROKER, +}; + struct JsonVersion { uint32_t major; uint32_t minor; @@ -101,6 +107,8 @@ class ApiLayerManifestFile : public ManifestFile { 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 Json::Value &root_node, const std::string &filename, + std::vector> &manifest_files); static void CreateIfValid(ManifestFileType type, const std::string &filename, std::vector> &manifest_files); /// @return false if we could not find the library. diff --git a/src/loader/runtime_interface.cpp b/src/loader/runtime_interface.cpp index a0296c73..df2491e6 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& runtime_source) { using wrap::android::content::Context; auto& initData = LoaderInitData::instance(); if (!initData.initialized()) { @@ -45,13 +45,38 @@ 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)) { + runtime_source = ManifestFileSource::FROM_JSON_MANIFEST; return XR_ERROR_INITIALIZATION_FAILED; } + if (systemBroker) { + runtime_source = ManifestFileSource::FROM_SYSTEM_BROKER; + } else { + runtime_source = ManifestFileSource::FROM_INSTALLABLE_BROKER; + } out_manifest = virtualManifest; return XR_SUCCESS; } + +XrResult GetPlatformApiLayerVirtualManifests(std::string type, std::vector& out_manifest, bool system_broker) { + 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(type, 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,