Skip to content

Commit 8cbba72

Browse files
committed
OpenXR loader: add API layer discovery support.
1 parent 99256e9 commit 8cbba72

7 files changed

+535
-42
lines changed

src/common/platform_utils.hpp

+11
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ static inline bool PlatformUtilsSetEnv(const char* name, const char* value) {
349349
}
350350

351351
#elif defined(XR_OS_ANDROID)
352+
#include <sys/system_properties.h>
352353

353354
static inline bool PlatformUtilsGetEnvSet(const char* /* name */) {
354355
// Stub func
@@ -370,6 +371,16 @@ static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* va
370371
return false;
371372
}
372373

374+
static inline bool PlatformUtilsGetSysProp(const char* name, bool default_value) {
375+
bool result = default_value;
376+
char value[PROP_VALUE_MAX] = {};
377+
if (__system_property_get(name, value) != 0) {
378+
result = (value[0] == 't');
379+
}
380+
381+
return result;
382+
}
383+
373384
// Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases
374385
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {
375386
// Prefix for the runtime JSON file name

src/loader/android_utilities.cpp

+298-36
Large diffs are not rendered by default.

src/loader/android_utilities.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,20 @@ using wrap::android::content::Context;
2626
*
2727
* @return 0 on success, something else on failure.
2828
*/
29-
int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest);
29+
int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest,
30+
bool &systemBroker);
31+
32+
/*!
33+
* Find the implicit/explicit API layers on the system, and return a constructed JSON object representing it.
34+
*
35+
* @param type An String to indicate layer type of API layers, implicit or explicit.
36+
* @param context An Android context, preferably an Activity Context.
37+
* @param[out] virtualManifest The Json::Value to fill with the virtual manifest.
38+
*
39+
* @return 0 on success, something else on failure.
40+
*/
41+
int getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context,
42+
std::vector<Json::Value> &virtualManifests, bool systemBroker);
3043
} // namespace openxr_android
3144

3245
#endif // __ANDROID__

src/loader/loader_init_data.hpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <json/value.h>
1717
#include <android/asset_manager_jni.h>
1818
#include "android_utilities.h"
19+
#include "manifest_file.hpp"
1920
#endif // XR_USE_PLATFORM_ANDROID
2021

2122
#ifdef XR_KHR_LOADER_INIT_SUPPORT
@@ -84,7 +85,8 @@ class LoaderInitData {
8485
XrResult InitializeLoaderInitData(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo);
8586

8687
#ifdef XR_USE_PLATFORM_ANDROID
87-
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest);
88+
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest, ManifestFileSource& runtime_source);
89+
XrResult GetPlatformApiLayerVirtualManifests(std::string type, std::vector<Json::Value>& out_manifest, bool system_broker);
8890
std::string GetAndroidNativeLibraryDir();
8991
void* Android_Get_Asset_Manager();
9092
#endif // XR_USE_PLATFORM_ANDROID

src/loader/manifest_file.cpp

+174-2
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,8 @@ XrResult RuntimeManifestFile::FindManifestFiles(const std::string &openxr_comman
666666

667667
#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
668668
Json::Value virtualManifest;
669-
result = GetPlatformRuntimeVirtualManifest(virtualManifest);
669+
ManifestFileSource runtimeSource = ManifestFileSource::FROM_JSON_MANIFEST;
670+
result = GetPlatformRuntimeVirtualManifest(virtualManifest, runtimeSource);
670671
if (XR_SUCCESS == result) {
671672
RuntimeManifestFile::CreateIfValid(virtualManifest, "", manifest_files);
672673
return result;
@@ -763,6 +764,140 @@ void ApiLayerManifestFile::AddManifestFilesAndroid(const std::string &openxr_com
763764
}
764765
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)
765766

767+
void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const Json::Value &root_node, const std::string &filename,
768+
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
769+
std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid ");
770+
JsonVersion file_version = {};
771+
if (!ManifestFile::IsValidJson(root_node, file_version)) {
772+
error_ss << "isValidJson indicates " << filename << " is not a valid manifest file.";
773+
LoaderLogger::LogErrorMessage("", error_ss.str());
774+
return;
775+
}
776+
777+
Json::Value layer_root_node = root_node["api_layer"];
778+
779+
// The API Layer manifest file needs the "api_layer" root as well as other sub-nodes.
780+
// If any of those aren't there, fail.
781+
if (layer_root_node.isNull() || layer_root_node["name"].isNull() || !layer_root_node["name"].isString() ||
782+
layer_root_node["api_version"].isNull() || !layer_root_node["api_version"].isString() ||
783+
layer_root_node["library_path"].isNull() || !layer_root_node["library_path"].isString() ||
784+
layer_root_node["implementation_version"].isNull() || !layer_root_node["implementation_version"].isString()) {
785+
error_ss << filename << " is missing required fields. Verify all proper fields exist.";
786+
LoaderLogger::LogErrorMessage("", error_ss.str());
787+
return;
788+
}
789+
if (MANIFEST_TYPE_IMPLICIT_API_LAYER == type) {
790+
bool enabled = true;
791+
#if !defined(XR_OS_ANDROID)
792+
// Implicit layers require the disable environment variable.
793+
if (layer_root_node["disable_environment"].isNull() || !layer_root_node["disable_environment"].isString()) {
794+
error_ss << "Implicit layer " << filename << " is missing \"disable_environment\"";
795+
LoaderLogger::LogErrorMessage("", error_ss.str());
796+
return;
797+
}
798+
// Check if there's an enable environment variable provided
799+
if (!layer_root_node["enable_environment"].isNull() && layer_root_node["enable_environment"].isString()) {
800+
std::string env_var = layer_root_node["enable_environment"].asString();
801+
// If it's not set in the environment, disable the layer
802+
if (!PlatformUtilsGetEnvSet(env_var.c_str())) {
803+
enabled = false;
804+
}
805+
}
806+
// Check for the disable environment variable, which must be provided in the JSON
807+
std::string env_var = layer_root_node["disable_environment"].asString();
808+
// If the env var is set, disable the layer. Disable env var overrides enable above
809+
if (PlatformUtilsGetEnvSet(env_var.c_str())) {
810+
enabled = false;
811+
}
812+
#else
813+
// Implicit layers require system property to disable it on Android
814+
if (layer_root_node["disable_sys_prop"].isNull() || !layer_root_node["disable_sys_prop"].isString()) {
815+
error_ss << "Implicit layer " << filename << " is missing \"disable_sys_prop\"";
816+
LoaderLogger::LogErrorMessage("", error_ss.str());
817+
return;
818+
}
819+
// Check if there's an system property to enable this API layer
820+
if (!layer_root_node["enable_sys_prop"].isNull() && layer_root_node["enable_sys_prop"].isString()) {
821+
std::string enable_sys_prop = layer_root_node["enable_sys_prop"].asString();
822+
// If it's not set to true, disable this layer
823+
if (!PlatformUtilsGetSysProp(enable_sys_prop.c_str(), true)) {
824+
enabled = false;
825+
}
826+
}
827+
828+
std::string disable_sys_prop = layer_root_node["disable_sys_prop"].asString();
829+
if (PlatformUtilsGetSysProp(disable_sys_prop.c_str(), false)) {
830+
enabled = false;
831+
}
832+
#endif
833+
834+
// Not enabled, so pretend like it isn't even there.
835+
if (!enabled) {
836+
error_ss << "Implicit layer " << filename << " is disabled";
837+
LoaderLogger::LogInfoMessage("", error_ss.str());
838+
return;
839+
}
840+
}
841+
std::string layer_name = layer_root_node["name"].asString();
842+
std::string api_version_string = layer_root_node["api_version"].asString();
843+
JsonVersion api_version = {};
844+
const int num_fields = sscanf(api_version_string.c_str(), "%u.%u", &api_version.major, &api_version.minor);
845+
api_version.patch = 0;
846+
847+
if ((num_fields != 2) || (api_version.major == 0 && api_version.minor == 0) ||
848+
api_version.major > XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) {
849+
error_ss << "layer " << filename << " has invalid API Version. Skipping layer.";
850+
LoaderLogger::LogWarningMessage("", error_ss.str());
851+
return;
852+
}
853+
854+
char *end_ptr;
855+
uint32_t implementation_version = strtol(layer_root_node["implementation_version"].asString().c_str(), &end_ptr, 10);
856+
if (*end_ptr != '\0') {
857+
error_ss << "layer " << filename << " has invalid implementation version.";
858+
LoaderLogger::LogWarningMessage("", error_ss.str());
859+
}
860+
861+
std::string library_path = layer_root_node["library_path"].asString();
862+
863+
// If the library_path variable has no directory symbol, it's just a file name and should be accessible on the
864+
// global library path.
865+
if (library_path.find('\\') != std::string::npos || library_path.find('/') != std::string::npos) {
866+
// If the library_path is an absolute path, just use that if it exists
867+
if (FileSysUtilsIsAbsolutePath(library_path)) {
868+
if (!FileSysUtilsPathExists(library_path)) {
869+
error_ss << filename << " library " << library_path << " does not appear to exist";
870+
LoaderLogger::LogErrorMessage("", error_ss.str());
871+
return;
872+
}
873+
} else {
874+
// Otherwise, treat the library path as a relative path based on the JSON file.
875+
std::string combined_path;
876+
std::string file_parent;
877+
if (!FileSysUtilsGetParentPath(filename, file_parent) ||
878+
!FileSysUtilsCombinePaths(file_parent, library_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
879+
error_ss << filename << " library " << combined_path << " does not appear to exist";
880+
LoaderLogger::LogErrorMessage("", error_ss.str());
881+
return;
882+
}
883+
library_path = combined_path;
884+
}
885+
}
886+
887+
std::string description;
888+
if (!layer_root_node["description"].isNull() && layer_root_node["description"].isString()) {
889+
description = layer_root_node["description"].asString();
890+
}
891+
892+
// Add this layer manifest file
893+
manifest_files.emplace_back(
894+
new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));
895+
896+
// Add any extensions to it after the fact.
897+
// Handle any renamed functions
898+
manifest_files.back()->ParseCommon(layer_root_node);
899+
}
900+
766901
void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream,
767902
LibraryLocator locate_library,
768903
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
@@ -876,6 +1011,7 @@ void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::strin
8761011
new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));
8771012

8781013
// Add any extensions to it after the fact.
1014+
// Handle any renamed functions
8791015
manifest_files.back()->ParseCommon(layer_root_node);
8801016
}
8811017

@@ -935,6 +1071,24 @@ void ApiLayerManifestFile::PopulateApiLayerProperties(XrApiLayerProperties &prop
9351071
// Find all layer manifest files in the appropriate search paths/registries for the given type.
9361072
XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_command, ManifestFileType type,
9371073
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
1074+
bool search_json_layer = true;
1075+
bool search_broker_layer = true;
1076+
1077+
#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
1078+
Json::Value virtualManifest;
1079+
bool systemBroker = true;
1080+
ManifestFileSource runtimeSource = ManifestFileSource::FROM_JSON_MANIFEST;
1081+
XrResult result = GetPlatformRuntimeVirtualManifest(virtualManifest, runtimeSource);
1082+
if (XR_SUCCESS == result) {
1083+
if (runtimeSource == ManifestFileSource::FROM_INSTALLABLE_BROKER) {
1084+
systemBroker = false;
1085+
search_json_layer = false;
1086+
}
1087+
} else {
1088+
search_broker_layer = false;
1089+
}
1090+
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)
1091+
9381092
std::string relative_path;
9391093
std::string override_env_var;
9401094
std::string registry_location;
@@ -967,7 +1121,9 @@ XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_comma
9671121

9681122
bool override_active = false;
9691123
std::vector<std::string> filenames;
970-
ReadDataFilesInSearchPaths(override_env_var, relative_path, override_active, filenames);
1124+
if (search_json_layer) {
1125+
ReadDataFilesInSearchPaths(override_env_var, relative_path, override_active, filenames);
1126+
}
9711127

9721128
#ifdef XR_OS_WINDOWS
9731129
// Read the registry if the override wasn't active.
@@ -981,6 +1137,22 @@ XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_comma
9811137
}
9821138

9831139
#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
1140+
if (search_broker_layer) {
1141+
std::vector<Json::Value> virtualManifests;
1142+
std::string layerType = (type == ManifestFileType::MANIFEST_TYPE_IMPLICIT_API_LAYER) ? "implicit" : "explicit";
1143+
result = GetPlatformApiLayerVirtualManifests(layerType, virtualManifests, systemBroker);
1144+
if (XR_SUCCESS == result) {
1145+
for (const auto &virtualManifest : virtualManifests) {
1146+
ApiLayerManifestFile::CreateIfValid(type, virtualManifest, "virtual manifest", manifest_files);
1147+
}
1148+
} else {
1149+
LoaderLogger::LogErrorMessage(
1150+
"",
1151+
"ApiLayerManifestFile::FindManifestFiles - failed to get virtual manifest files from system/installable broker.");
1152+
assert(0);
1153+
}
1154+
}
1155+
9841156
ApiLayerManifestFile::AddManifestFilesAndroid(openxr_command, type, manifest_files);
9851157
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)
9861158

src/loader/manifest_file.hpp

+8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ enum ManifestFileType {
2828
MANIFEST_TYPE_EXPLICIT_API_LAYER,
2929
};
3030

31+
enum ManifestFileSource {
32+
FROM_JSON_MANIFEST = 0,
33+
FROM_INSTALLABLE_BROKER,
34+
FROM_SYSTEM_BROKER,
35+
};
36+
3137
struct JsonVersion {
3238
uint32_t major;
3339
uint32_t minor;
@@ -101,6 +107,8 @@ class ApiLayerManifestFile : public ManifestFile {
101107

102108
static void CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream,
103109
LibraryLocator locate_library, std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files);
110+
static void CreateIfValid(ManifestFileType type, const Json::Value &root_node, const std::string &filename,
111+
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files);
104112
static void CreateIfValid(ManifestFileType type, const std::string &filename,
105113
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files);
106114
/// @return false if we could not find the library.

src/loader/runtime_interface.cpp

+27-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
#endif // XR_USE_PLATFORM_ANDROID
3636

3737
#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
38-
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) {
38+
XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest, ManifestFileSource& runtime_source) {
3939
using wrap::android::content::Context;
4040
auto& initData = LoaderInitData::instance();
4141
if (!initData.initialized()) {
@@ -45,13 +45,38 @@ XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) {
4545
if (context.isNull()) {
4646
return XR_ERROR_INITIALIZATION_FAILED;
4747
}
48+
bool systemBroker = false;
4849
Json::Value virtualManifest;
49-
if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest)) {
50+
if (0 != openxr_android::getActiveRuntimeVirtualManifest(context, virtualManifest, systemBroker)) {
51+
runtime_source = ManifestFileSource::FROM_JSON_MANIFEST;
5052
return XR_ERROR_INITIALIZATION_FAILED;
5153
}
54+
if (systemBroker) {
55+
runtime_source = ManifestFileSource::FROM_SYSTEM_BROKER;
56+
} else {
57+
runtime_source = ManifestFileSource::FROM_INSTALLABLE_BROKER;
58+
}
5259
out_manifest = virtualManifest;
5360
return XR_SUCCESS;
5461
}
62+
63+
XrResult GetPlatformApiLayerVirtualManifests(std::string type, std::vector<Json::Value>& out_manifest, bool system_broker) {
64+
using wrap::android::content::Context;
65+
auto& initData = LoaderInitData::instance();
66+
if (!initData.initialized()) {
67+
return XR_ERROR_INITIALIZATION_FAILED;
68+
}
69+
auto context = Context(reinterpret_cast<jobject>(initData.getData().applicationContext));
70+
if (context.isNull()) {
71+
return XR_ERROR_INITIALIZATION_FAILED;
72+
}
73+
std::vector<Json::Value> virtualManifests;
74+
if (0 != openxr_android::getApiLayerVirtualManifests(type, context, virtualManifests, system_broker)) {
75+
return XR_ERROR_INITIALIZATION_FAILED;
76+
}
77+
out_manifest = virtualManifests;
78+
return XR_SUCCESS;
79+
}
5580
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)
5681

5782
XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_command,

0 commit comments

Comments
 (0)