Skip to content

Commit

Permalink
OpenXR loader: add API layer discovery support.
Browse files Browse the repository at this point in the history
  • Loading branch information
dengkail authored and quic-dengkail committed Oct 10, 2023
1 parent 8270b15 commit 5cb4b4b
Show file tree
Hide file tree
Showing 6 changed files with 427 additions and 14 deletions.
274 changes: 262 additions & 12 deletions src/loader/android_utilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down Expand Up @@ -132,22 +137,27 @@ 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))
.appendPath(BASE_PATH)
.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();
}

Expand All @@ -164,6 +174,95 @@ 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 HAS_FUNCTIONS = "has_functions";
static constexpr auto HAS_INSTANCE_EXTENSIONS = "has_instance_extensions";
};
} // namespace api_layer

} // namespace

static inline jni::Array<std::string> makeArray(std::initializer_list<const char *> &&list) {
Expand Down Expand Up @@ -221,7 +320,7 @@ static int populateFunctions(wrap::android::content::Context const &context, boo
JsonManifestBuilder &builder) {
jni::Array<std::string> 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);
Expand All @@ -245,11 +344,75 @@ static int populateFunctions(wrap::android::content::Context const &context, boo
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<std::string> 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<std::string> 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<std::string> 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<std::string> 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());
try {
cursor = context.getContentResolver().query(uri, projection);
} catch (const std::exception &e) {
Expand Down Expand Up @@ -279,10 +442,10 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte
// First, try getting the installable broker's provider
bool systemBroker = false;
Cursor cursor;
if (!getActiveRuntimeCursor(context, projection, systemBroker, cursor)) {
if (!getCursor(context, projection, RUNTIMES_PATH, systemBroker, cursor)) {
// OK, try the system broker as a fallback.
systemBroker = true;
getActiveRuntimeCursor(context, projection, systemBroker, cursor);
getCursor(context, projection, RUNTIMES_PATH, systemBroker, cursor);
}

if (cursor.isNull()) {
Expand Down Expand Up @@ -314,6 +477,93 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte
virtualManifest = builder.build();
return 0;
}

static bool populateApiLayerManifest(bool systemBroker, std::string layerType, wrap::android::content::Context const &context,
Cursor &cursor, std::vector<Json::Value> &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 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 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;
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 void enumerateApiLayerManifests(std::string layerType, wrap::android::content::Context const &context,
jni::Array<std::string> &projection, std::vector<Json::Value> &virtualManifests) {
Cursor cursor;

// First, try getting the installable broker's provider
bool systemBroker = false;
if (!getCursor(context, projection, layerType, systemBroker, cursor)) {
// OK, try the system broker as a fallback.
systemBroker = true;
getCursor(context, projection, layerType, systemBroker, cursor);
}

if (cursor.isNull()) {
return;
}
cursor.moveToFirst();
for (int i = 0; i < cursor.getCount(); ++i) {
populateApiLayerManifest(systemBroker, layerType, context, cursor, virtualManifests);
cursor.moveToNext();
}

cursor.close();
}

int getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context,
std::vector<Json::Value> &virtualManifests) {
static bool hasQueryBroker = false;
static std::vector<Json::Value> implicitLayerManifest;
static std::vector<Json::Value> explicitLayerManifest;

__android_log_print(ANDROID_LOG_INFO, TAG, "Try to get %s API layer from broker!", layerType.c_str());
if (!hasQueryBroker) {
jni::Array<std::string> 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::HAS_FUNCTIONS, api_layer::Columns::HAS_INSTANCE_EXTENSIONS});

enumerateApiLayerManifests(IMP_LAYER, context, projection, implicitLayerManifest);
enumerateApiLayerManifests(EXP_LAYER, context, projection, explicitLayerManifest);

hasQueryBroker = true;
}

virtualManifests = (layerType == IMP_LAYER) ? implicitLayerManifest : explicitLayerManifest;
return 0;
}

} // namespace openxr_android

#endif // __ANDROID__
12 changes: 12 additions & 0 deletions src/loader/android_utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ using wrap::android::content::Context;
* @return 0 on success, something else on failure.
*/
int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest);

/*!
* 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<Json::Value> &virtualManifests);
} // namespace openxr_android

#endif // __ANDROID__
Loading

0 comments on commit 5cb4b4b

Please sign in to comment.