Skip to content

Commit 5cb4b4b

Browse files
dengkailquic-dengkail
authored andcommitted
OpenXR loader: add API layer discovery support.
1 parent 8270b15 commit 5cb4b4b

File tree

6 files changed

+427
-14
lines changed

6 files changed

+427
-14
lines changed

src/loader/android_utilities.cpp

Lines changed: 262 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,13 @@ constexpr auto SYSTEM_AUTHORITY = "org.khronos.openxr.system_runtime_broker";
3838
constexpr auto BASE_PATH = "openxr";
3939
constexpr auto ABI_PATH = "abi";
4040
constexpr auto RUNTIMES_PATH = "runtimes";
41+
constexpr auto API_LAYERS_PATH = "api_layers";
42+
constexpr auto IMP_LAYER = "implicit";
43+
constexpr auto EXP_LAYER = "explicit";
4144

4245
constexpr const char *getBrokerAuthority(bool systemBroker) { return systemBroker ? SYSTEM_AUTHORITY : AUTHORITY; }
46+
constexpr const char *getLayerTypePath(bool implicitLayer) { return implicitLayer ? IMP_LAYER : EXP_LAYER; }
47+
constexpr const char *getFunctionTypePath(bool runtimeFunctions) { return runtimeFunctions ? RUNTIMES_PATH : API_LAYERS_PATH; }
4348

4449
struct BaseColumns {
4550
/**
@@ -132,22 +137,27 @@ static constexpr auto TABLE_PATH = "functions";
132137
* runtime package and major version of OpenXR.
133138
*
134139
* @param systemBroker If the system runtime broker (instead of the installable one) should be queried.
140+
* @param runtimeFunctions If the runtime functions (instead of the API layer functions) should be queried.
135141
* @param majorVer The major version of OpenXR.
136142
* @param packageName The package name of the runtime.
137143
* @param abi The Android ABI name in use.
138144
* @return A content URI for the entire table: the function remapping for that runtime.
139145
*/
140-
static Uri makeContentUri(bool systemBroker, int majorVersion, std::string const &packageName, const char *abi) {
146+
static Uri makeContentUri(bool systemBroker, bool runtimeFunctions, int majorVersion, std::string const &packageName,
147+
const char *abi, std::string const &layerName) {
141148
auto builder = Uri_Builder::construct();
142149
builder.scheme("content")
143150
.authority(getBrokerAuthority(systemBroker))
144151
.appendPath(BASE_PATH)
145152
.appendPath(std::to_string(majorVersion))
146153
.appendPath(ABI_PATH)
147154
.appendPath(abi)
148-
.appendPath(RUNTIMES_PATH)
149-
.appendPath(packageName)
150-
.appendPath(TABLE_PATH);
155+
.appendPath(getFunctionTypePath(runtimeFunctions))
156+
.appendPath(packageName);
157+
if (!runtimeFunctions) {
158+
builder.appendPath(layerName);
159+
}
160+
builder.appendPath(TABLE_PATH);
151161
return builder.build();
152162
}
153163

@@ -164,6 +174,95 @@ struct Columns : BaseColumns {
164174
};
165175
} // namespace functions
166176

177+
namespace instance_extensions {
178+
/**
179+
* Final path component to this URI.
180+
*/
181+
static constexpr auto TABLE_PATH = "instance_extensions";
182+
183+
/**
184+
* Create a content URI for querying all rows of the instance extensions supported by a given
185+
* API layer.
186+
*
187+
* @param systemBroker If the system runtime broker (instead of the installable one) should be queried.
188+
* @param majorVer The major version of OpenXR.
189+
* @param packageName The package name of the runtime.
190+
* @param abi The Android ABI name in use.
191+
* @param layerName The API layer name.
192+
* @return A content URI for the entire table: the function remapping for that runtime.
193+
*/
194+
static Uri makeContentUri(bool systemBroker, int majorVersion, std::string const &packageName, const char *abi,
195+
std::string const &layerName) {
196+
auto builder = Uri_Builder::construct();
197+
builder.scheme("content")
198+
.authority(getBrokerAuthority(systemBroker))
199+
.appendPath(BASE_PATH)
200+
.appendPath(std::to_string(majorVersion))
201+
.appendPath(ABI_PATH)
202+
.appendPath(abi)
203+
.appendPath(API_LAYERS_PATH)
204+
.appendPath(packageName)
205+
.appendPath(layerName)
206+
.appendPath(TABLE_PATH);
207+
return builder.build();
208+
}
209+
struct Columns : BaseColumns {
210+
/**
211+
* Constant for the INSTANCE_EXTENSION_NAMES column name
212+
*/
213+
static constexpr auto INSTANCE_EXTENSION_NAMES = "instance_extension_names";
214+
215+
/**
216+
* Constant for the INSTANCE_EXTENSION_VERSIONS column name
217+
*/
218+
static constexpr auto INSTANCE_EXTENSION_VERSIONS = "instance_extension_versions";
219+
};
220+
} // namespace instance_extensions
221+
222+
namespace api_layer {
223+
/**
224+
* Final path component to this URI.
225+
*/
226+
227+
/**
228+
* Create a content URI for querying all rows of the implicit/explicit API layer data for a given
229+
* runtime package and major version of OpenXR.
230+
*
231+
* @param systemBroker If the system runtime broker (instead of the installable one) should be queried.
232+
* @param majorVer The major version of OpenXR.
233+
* @param layerType The layer type of the API layer.
234+
* @param abi The Android ABI name in use.
235+
* @return A content URI for the entire table: the function remapping for that runtime.
236+
*/
237+
static Uri makeContentUri(bool systemBroker, int majorVersion, std::string const &layerType, const char *abi) {
238+
auto builder = Uri_Builder::construct();
239+
builder.scheme("content")
240+
.authority(getBrokerAuthority(systemBroker))
241+
.appendPath(BASE_PATH)
242+
.appendPath(std::to_string(majorVersion))
243+
.appendPath(ABI_PATH)
244+
.appendPath(abi)
245+
.appendPath(API_LAYERS_PATH)
246+
.appendPath(getLayerTypePath(layerType == IMP_LAYER));
247+
return builder.build();
248+
}
249+
struct Columns : BaseColumns {
250+
// implicit or explicit
251+
static constexpr auto PACKAGE_NAME = "package_name";
252+
static constexpr auto FILE_FORMAT_VERSION = "file_format_version";
253+
static constexpr auto NAME = "name";
254+
static constexpr auto NATIVE_LIB_DIR = "native_lib_dir";
255+
static constexpr auto SO_FILENAME = "so_filename";
256+
static constexpr auto API_VERSION = "api_version";
257+
static constexpr auto IMPLEMENTATION_VERSION = "implementation_version";
258+
static constexpr auto DESCRIPTION = "description";
259+
static constexpr auto DISABLE_ENVIRONMENT = "disable_environment";
260+
static constexpr auto ENABLE_ENVIRONMENT = "enable_environment";
261+
static constexpr auto HAS_FUNCTIONS = "has_functions";
262+
static constexpr auto HAS_INSTANCE_EXTENSIONS = "has_instance_extensions";
263+
};
264+
} // namespace api_layer
265+
167266
} // namespace
168267

169268
static inline jni::Array<std::string> makeArray(std::initializer_list<const char *> &&list) {
@@ -221,7 +320,7 @@ static int populateFunctions(wrap::android::content::Context const &context, boo
221320
JsonManifestBuilder &builder) {
222321
jni::Array<std::string> projection = makeArray({functions::Columns::FUNCTION_NAME, functions::Columns::SYMBOL_NAME});
223322

224-
auto uri = functions::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI);
323+
auto uri = functions::makeContentUri(systemBroker, true, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI, "");
225324
ALOGI("populateFunctions: Querying URI: %s", uri.toString().c_str());
226325

227326
Cursor cursor = context.getContentResolver().query(uri, projection);
@@ -245,11 +344,75 @@ static int populateFunctions(wrap::android::content::Context const &context, boo
245344
return 0;
246345
}
247346

248-
/// Get cursor for active runtime, parameterized by whether or not we use the system broker
249-
static bool getActiveRuntimeCursor(wrap::android::content::Context const &context, jni::Array<std::string> const &projection,
250-
bool systemBroker, Cursor &cursor) {
251-
auto uri = active_runtime::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), ABI);
252-
ALOGI("getActiveRuntimeCursor: Querying URI: %s", uri.toString().c_str());
347+
static int populateApiLayerFunctions(wrap::android::content::Context const &context, bool systemBroker,
348+
const std::string &packageName, std::string const &layerName, Json::Value &rootNode) {
349+
jni::Array<std::string> projection = makeArray({functions::Columns::FUNCTION_NAME, functions::Columns::SYMBOL_NAME});
350+
351+
auto uri =
352+
functions::makeContentUri(systemBroker, false, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI, layerName);
353+
ALOGI("populateApiLayerFunctions: Querying URI: %s", uri.toString().c_str());
354+
355+
Cursor cursor = context.getContentResolver().query(uri, projection);
356+
357+
if (cursor.isNull()) {
358+
ALOGE("Null cursor when querying content resolver for API layer functions.");
359+
return -1;
360+
}
361+
if (cursor.getCount() < 1) {
362+
ALOGE("Non-null but empty cursor when querying content resolver for API layer functions.");
363+
cursor.close();
364+
return -1;
365+
}
366+
auto functionIndex = cursor.getColumnIndex(functions::Columns::FUNCTION_NAME);
367+
auto symbolIndex = cursor.getColumnIndex(functions::Columns::SYMBOL_NAME);
368+
while (cursor.moveToNext()) {
369+
rootNode["api_layer"]["functions"][cursor.getString(functionIndex)] = cursor.getString(symbolIndex);
370+
}
371+
372+
cursor.close();
373+
return 0;
374+
}
375+
376+
static int populateApiLayerInstanceExtensions(wrap::android::content::Context const &context, bool systemBroker,
377+
const std::string &packageName, std::string const &layerName, Json::Value &rootNode) {
378+
jni::Array<std::string> projection = makeArray(
379+
{instance_extensions::Columns::INSTANCE_EXTENSION_NAMES, instance_extensions::Columns::INSTANCE_EXTENSION_VERSIONS});
380+
381+
auto uri =
382+
instance_extensions::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI, layerName);
383+
ALOGI("populateApiLayerInstanceExtensions: Querying URI: %s", uri.toString().c_str());
384+
385+
Cursor cursor = context.getContentResolver().query(uri, projection);
386+
387+
if (cursor.isNull()) {
388+
ALOGE("Null cursor when querying content resolver for API layer instance extensions.");
389+
return -1;
390+
}
391+
if (cursor.getCount() < 1) {
392+
ALOGE("Non-null but empty cursor when querying content resolver for API layer instance extensions.");
393+
cursor.close();
394+
return -1;
395+
}
396+
auto nameIndex = cursor.getColumnIndex(instance_extensions::Columns::INSTANCE_EXTENSION_NAMES);
397+
auto versionIndex = cursor.getColumnIndex(instance_extensions::Columns::INSTANCE_EXTENSION_VERSIONS);
398+
Json::Value extension(Json::objectValue);
399+
while (cursor.moveToNext()) {
400+
extension["name"] = cursor.getString(nameIndex);
401+
extension["extension_version"] = cursor.getString(versionIndex);
402+
rootNode["api_layer"]["instance_extensions"].append(extension);
403+
}
404+
405+
cursor.close();
406+
return 0;
407+
}
408+
409+
/// Get cursor for active runtime or API layer, parameterized by target type and whether or not we use the system broker
410+
static bool getCursor(wrap::android::content::Context const &context, jni::Array<std::string> const &projection,
411+
std::string const &targetType, bool systemBroker, Cursor &cursor) {
412+
auto uri = (targetType == RUNTIMES_PATH)
413+
? active_runtime::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), ABI)
414+
: api_layer::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), targetType, ABI);
415+
ALOGI("getCursor: Querying URI: %s", uri.toString().c_str());
253416
try {
254417
cursor = context.getContentResolver().query(uri, projection);
255418
} catch (const std::exception &e) {
@@ -279,10 +442,10 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte
279442
// First, try getting the installable broker's provider
280443
bool systemBroker = false;
281444
Cursor cursor;
282-
if (!getActiveRuntimeCursor(context, projection, systemBroker, cursor)) {
445+
if (!getCursor(context, projection, RUNTIMES_PATH, systemBroker, cursor)) {
283446
// OK, try the system broker as a fallback.
284447
systemBroker = true;
285-
getActiveRuntimeCursor(context, projection, systemBroker, cursor);
448+
getCursor(context, projection, RUNTIMES_PATH, systemBroker, cursor);
286449
}
287450

288451
if (cursor.isNull()) {
@@ -314,6 +477,93 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte
314477
virtualManifest = builder.build();
315478
return 0;
316479
}
480+
481+
static bool populateApiLayerManifest(bool systemBroker, std::string layerType, wrap::android::content::Context const &context,
482+
Cursor &cursor, std::vector<Json::Value> &layerRootNode) {
483+
auto packageName = cursor.getString(cursor.getColumnIndex(api_layer::Columns::PACKAGE_NAME));
484+
auto fileFormatVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::FILE_FORMAT_VERSION));
485+
auto name = cursor.getString(cursor.getColumnIndex(api_layer::Columns::NAME));
486+
auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR));
487+
auto fileName = cursor.getString(cursor.getColumnIndex(api_layer::Columns::SO_FILENAME));
488+
auto apiVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::API_VERSION));
489+
auto implementationVersion = cursor.getString(cursor.getColumnIndex(api_layer::Columns::IMPLEMENTATION_VERSION));
490+
auto description = cursor.getString(cursor.getColumnIndex(api_layer::Columns::DESCRIPTION));
491+
auto has_instance_extensions = cursor.getString(cursor.getColumnIndex(api_layer::Columns::HAS_INSTANCE_EXTENSIONS));
492+
auto has_functions = cursor.getString(cursor.getColumnIndex(api_layer::Columns::HAS_FUNCTIONS));
493+
494+
__android_log_print(ANDROID_LOG_INFO, TAG, "Got api layer: type: %s, name: %s, native lib dir: %s, fileName: %s",
495+
layerType.c_str(), name.c_str(), libDir.c_str(), fileName.c_str());
496+
497+
Json::Value rootNode(Json::objectValue);
498+
rootNode["file_format_version"] = fileFormatVersion;
499+
rootNode["api_layer"] = Json::objectValue;
500+
rootNode["api_layer"]["name"] = name;
501+
rootNode["api_layer"]["library_path"] = libDir + "/" + fileName;
502+
rootNode["api_layer"]["api_version"] = apiVersion;
503+
rootNode["api_layer"]["implementation_version"] = implementationVersion;
504+
rootNode["api_layer"]["description"] = description;
505+
if (has_functions == "true") {
506+
rootNode["api_layer"]["functions"] = Json::Value(Json::objectValue);
507+
populateApiLayerFunctions(context, systemBroker, packageName, name, rootNode);
508+
}
509+
if (has_instance_extensions == "true") {
510+
rootNode["api_layer"]["instance_extensions"] = Json::Value(Json::arrayValue);
511+
populateApiLayerInstanceExtensions(context, systemBroker, packageName, name, rootNode);
512+
}
513+
514+
layerRootNode.push_back(rootNode);
515+
516+
return true;
517+
}
518+
519+
static void enumerateApiLayerManifests(std::string layerType, wrap::android::content::Context const &context,
520+
jni::Array<std::string> &projection, std::vector<Json::Value> &virtualManifests) {
521+
Cursor cursor;
522+
523+
// First, try getting the installable broker's provider
524+
bool systemBroker = false;
525+
if (!getCursor(context, projection, layerType, systemBroker, cursor)) {
526+
// OK, try the system broker as a fallback.
527+
systemBroker = true;
528+
getCursor(context, projection, layerType, systemBroker, cursor);
529+
}
530+
531+
if (cursor.isNull()) {
532+
return;
533+
}
534+
cursor.moveToFirst();
535+
for (int i = 0; i < cursor.getCount(); ++i) {
536+
populateApiLayerManifest(systemBroker, layerType, context, cursor, virtualManifests);
537+
cursor.moveToNext();
538+
}
539+
540+
cursor.close();
541+
}
542+
543+
int getApiLayerVirtualManifests(std::string layerType, wrap::android::content::Context const &context,
544+
std::vector<Json::Value> &virtualManifests) {
545+
static bool hasQueryBroker = false;
546+
static std::vector<Json::Value> implicitLayerManifest;
547+
static std::vector<Json::Value> explicitLayerManifest;
548+
549+
__android_log_print(ANDROID_LOG_INFO, TAG, "Try to get %s API layer from broker!", layerType.c_str());
550+
if (!hasQueryBroker) {
551+
jni::Array<std::string> projection =
552+
makeArray({api_layer::Columns::PACKAGE_NAME, api_layer::Columns::FILE_FORMAT_VERSION, api_layer::Columns::NAME,
553+
api_layer::Columns::NATIVE_LIB_DIR, api_layer::Columns::SO_FILENAME, api_layer::Columns::API_VERSION,
554+
api_layer::Columns::IMPLEMENTATION_VERSION, api_layer::Columns::DESCRIPTION,
555+
api_layer::Columns::HAS_FUNCTIONS, api_layer::Columns::HAS_INSTANCE_EXTENSIONS});
556+
557+
enumerateApiLayerManifests(IMP_LAYER, context, projection, implicitLayerManifest);
558+
enumerateApiLayerManifests(EXP_LAYER, context, projection, explicitLayerManifest);
559+
560+
hasQueryBroker = true;
561+
}
562+
563+
virtualManifests = (layerType == IMP_LAYER) ? implicitLayerManifest : explicitLayerManifest;
564+
return 0;
565+
}
566+
317567
} // namespace openxr_android
318568

319569
#endif // __ANDROID__

src/loader/android_utilities.h

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

3244
#endif // __ANDROID__

0 commit comments

Comments
 (0)