Rootless GPU Debug Tests
This adds a new set of tests that ensure debug Vulkan layers can be
enumerated and loaded from the base dir of debuggable apps. It also
runs through several positive and negative scenarios to ensure the
new Settings work as expected.
The source builds multiple APKs: DEBUG and RELEASE for testing the
different combinations, and a LAYER APK just for transporting layers.
The layers themselves are null, only containing enough functionality
to be enumerated, chained, and loaded by the Vulkan loader.
Test: cts-tradefed run singleCommand cts -m CtsGpuToolsHostTestCases
Bug: 63708377
Change-Id: I5d1c7c005d4ff8f8629ad91b956d3769987f6170
diff --git a/hostsidetests/gputools/Android.mk b/hostsidetests/gputools/Android.mk
new file mode 100644
index 0000000..b2946be
--- /dev/null
+++ b/hostsidetests/gputools/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE_TAGS := tests
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MODULE := CtsGpuToolsHostTestCases
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
+
+LOCAL_STATIC_JAVA_LIBRARIES := platform-test-annotations-host
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/gputools/AndroidTest.xml b/hostsidetests/gputools/AndroidTest.xml
new file mode 100644
index 0000000..74d6e3f
--- /dev/null
+++ b/hostsidetests/gputools/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<configuration description="Config for CtsGpuToolsHostTestCases">
+ <option name="config-descriptor:metadata" key="component" value="misc" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsGpuToolsRootlessGpuDebugApp-DEBUG.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsGpuToolsRootlessGpuDebugApp-RELEASE.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsGpuToolsRootlessGpuDebugApp-LAYERS.apk" />
+ </target_preparer>
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsGpuToolsHostTestCases.jar" />
+ </test>
+</configuration>
diff --git a/hostsidetests/gputools/apps/Android.mk b/hostsidetests/gputools/apps/Android.mk
new file mode 100644
index 0000000..2a2cdd9
--- /dev/null
+++ b/hostsidetests/gputools/apps/Android.mk
@@ -0,0 +1,69 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libctsgputools_jni
+LOCAL_SRC_FILES := \
+ jni/CtsGpuToolsJniOnLoad.cpp \
+ jni/android_gputools_cts_RootlessGpuDebug.cpp
+LOCAL_CFLAGS += -std=c++14 -Wall -Werror
+LOCAL_SHARED_LIBRARIES := libandroid libvulkan liblog
+LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_SDK_VERSION := current
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := CtsGpuToolsRootlessGpuDebugApp-DEBUG
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MULTILIB := both
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+libctsgputools_jni
+
+LOCAL_AAPT_FLAGS := \
+--rename-manifest-package android.rootlessgpudebug.DEBUG.app \
+--debug-mode
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := CtsGpuToolsRootlessGpuDebugApp-RELEASE
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MULTILIB := both
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+libctsgputools_jni \
+libVkLayer_nullLayerC
+
+LOCAL_AAPT_FLAGS := \
+--rename-manifest-package android.rootlessgpudebug.RELEASE.app
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/gputools/apps/AndroidManifest.xml b/hostsidetests/gputools/apps/AndroidManifest.xml
new file mode 100755
index 0000000..de8130f
--- /dev/null
+++ b/hostsidetests/gputools/apps/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.rootlessgpudebug.app">>
+
+ <application>
+ <activity android:name=".RootlessGpuDebugDeviceActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
+
+
diff --git a/hostsidetests/gputools/apps/jni/CtsGpuToolsJniOnLoad.cpp b/hostsidetests/gputools/apps/jni/CtsGpuToolsJniOnLoad.cpp
new file mode 100644
index 0000000..2761aea
--- /dev/null
+++ b/hostsidetests/gputools/apps/jni/CtsGpuToolsJniOnLoad.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+extern int register_android_gputools_cts_RootlessGpuDebug(JNIEnv*);
+
+jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
+ JNIEnv* env = nullptr;
+ if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK)
+ return JNI_ERR;
+ if (register_android_gputools_cts_RootlessGpuDebug(env))
+ return JNI_ERR;
+ return JNI_VERSION_1_4;
+}
diff --git a/hostsidetests/gputools/apps/jni/android_gputools_cts_RootlessGpuDebug.cpp b/hostsidetests/gputools/apps/jni/android_gputools_cts_RootlessGpuDebug.cpp
new file mode 100644
index 0000000..4fdddbc
--- /dev/null
+++ b/hostsidetests/gputools/apps/jni/android_gputools_cts_RootlessGpuDebug.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define LOG_TAG "RootlessGpuDebug"
+
+#include <android/log.h>
+#include <jni.h>
+#include <string>
+#include <vulkan/vulkan.h>
+
+#define ALOGI(msg, ...) \
+ __android_log_print(ANDROID_LOG_INFO, LOG_TAG, (msg), __VA_ARGS__)
+#define ALOGE(msg, ...) \
+ __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, (msg), __VA_ARGS__)
+
+namespace {
+
+std::string initVulkan()
+{
+ std::string result = "";
+
+ const VkApplicationInfo app_info = {
+ VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ nullptr, // pNext
+ "RootlessGpuDebug", // app name
+ 0, // app version
+ nullptr, // engine name
+ 0, // engine version
+ VK_API_VERSION_1_0,
+ };
+ const VkInstanceCreateInfo instance_info = {
+ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ nullptr, // pNext
+ 0, // flags
+ &app_info,
+ 0, // layer count
+ nullptr, // layers
+ 0, // extension count
+ nullptr, // extensions
+ };
+ VkInstance instance;
+ VkResult vkResult = vkCreateInstance(&instance_info, nullptr, &instance);
+ if (vkResult == VK_ERROR_INITIALIZATION_FAILED) {
+ result = "vkCreateInstance failed, meaning layers could not be chained.";
+ } else {
+ result = "vkCreateInstance succeeded.";
+ }
+
+ return result;
+}
+
+jstring android_gputools_cts_RootlessGpuDebug_nativeInitVulkan(JNIEnv* env,
+ jclass /*clazz*/)
+{
+ std::string result;
+
+ result = initVulkan();
+
+ return env->NewStringUTF(result.c_str());
+}
+
+static JNINativeMethod gMethods[] = {
+ { "nativeInitVulkan", "()Ljava/lang/String;",
+ (void*) android_gputools_cts_RootlessGpuDebug_nativeInitVulkan },
+};
+
+} // anonymous namespace
+
+int register_android_gputools_cts_RootlessGpuDebug(JNIEnv* env) {
+ jclass clazz = env->FindClass("android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity");
+ return env->RegisterNatives(clazz, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity.java b/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity.java
new file mode 100644
index 0000000..ec5052b
--- /dev/null
+++ b/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.rootlessgpudebug.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.lang.Override;
+
+public class RootlessGpuDebugDeviceActivity extends Activity {
+
+ static {
+ System.loadLibrary("ctsgputools_jni");
+ }
+
+ private static final String TAG = RootlessGpuDebugDeviceActivity.class.getSimpleName();
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ String result = nativeInitVulkan();
+ Log.i(TAG, result);
+ }
+
+ private static native String nativeInitVulkan();
+
+}
+
diff --git a/hostsidetests/gputools/layers/Android.mk b/hostsidetests/gputools/layers/Android.mk
new file mode 100644
index 0000000..69f6930
--- /dev/null
+++ b/hostsidetests/gputools/layers/Android.mk
@@ -0,0 +1,70 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libVkLayer_nullLayerA
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := jni/nullLayer.cpp
+LOCAL_CFLAGS += -std=c++14 -Wall -Werror -fvisibility=hidden -DLAYERNAME="A"
+LOCAL_SHARED_LIBRARIES := libandroid libvulkan liblog
+LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_SDK_VERSION := current
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libVkLayer_nullLayerB
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := jni/nullLayer.cpp
+LOCAL_CFLAGS += -std=c++14 -Wall -Werror -fvisibility=hidden -DLAYERNAME="B"
+LOCAL_SHARED_LIBRARIES := libandroid libvulkan liblog
+LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_SDK_VERSION := current
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libVkLayer_nullLayerC
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := jni/nullLayer.cpp
+LOCAL_CFLAGS += -std=c++14 -Wall -Werror -fvisibility=hidden -DLAYERNAME="C"
+LOCAL_SHARED_LIBRARIES := libandroid libvulkan liblog
+LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_SDK_VERSION := current
+include $(BUILD_SHARED_LIBRARY)
+
+
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_PACKAGE_NAME := CtsGpuToolsRootlessGpuDebugApp-LAYERS
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MULTILIB := both
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+libVkLayer_nullLayerA \
+libVkLayer_nullLayerB \
+libVkLayer_nullLayerC
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/gputools/layers/AndroidManifest.xml b/hostsidetests/gputools/layers/AndroidManifest.xml
new file mode 100755
index 0000000..957b047
--- /dev/null
+++ b/hostsidetests/gputools/layers/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.rootlessgpudebug.LAYERS.app">
+
+ <application android:debuggable="false" android:hasCode="false">
+ </application>
+
+</manifest>
+
+
diff --git a/hostsidetests/gputools/layers/jni/nullLayer.cpp b/hostsidetests/gputools/layers/jni/nullLayer.cpp
new file mode 100644
index 0000000..7253b5c
--- /dev/null
+++ b/hostsidetests/gputools/layers/jni/nullLayer.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+#include <cstring>
+#include <vulkan/vulkan.h>
+#include "vk_layer_interface.h"
+
+#define xstr(a) str(a)
+#define str(a) #a
+
+#define LOG_TAG "nullLayer" xstr(LAYERNAME)
+
+#define ALOGI(msg, ...) \
+ __android_log_print(ANDROID_LOG_INFO, LOG_TAG, (msg), __VA_ARGS__)
+
+
+// Announce if anything loads this layer. LAYERNAME is defined in Android.mk
+class StaticLogMessage {
+ public:
+ StaticLogMessage(const char* msg) {
+ ALOGI("%s", msg);
+ }
+};
+StaticLogMessage g_initMessage("nullLayer" xstr(LAYERNAME) " loaded");
+
+
+namespace {
+
+
+// Minimal dispatch table for this simple layer
+struct {
+ PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
+} g_VulkanDispatchTable;
+
+
+template<class T>
+VkResult getProperties(const uint32_t count, const T *properties, uint32_t *pCount,
+ T *pProperties) {
+ uint32_t copySize;
+
+ if (pProperties == NULL || properties == NULL) {
+ *pCount = count;
+ return VK_SUCCESS;
+ }
+
+ copySize = *pCount < count ? *pCount : count;
+ memcpy(pProperties, properties, copySize * sizeof(T));
+ *pCount = copySize;
+ if (copySize < count) {
+ return VK_INCOMPLETE;
+ }
+
+ return VK_SUCCESS;
+}
+
+static const VkLayerProperties LAYER_PROPERTIES = {
+ "VK_LAYER_ANDROID_nullLayer" xstr(LAYERNAME), VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION), 1, "Layer: nullLayer" xstr(LAYERNAME),
+};
+
+VKAPI_ATTR VkResult VKAPI_CALL EnumerateInstanceLayerProperties(uint32_t *pCount, VkLayerProperties *pProperties) {
+ return getProperties<VkLayerProperties>(1, &LAYER_PROPERTIES, pCount, pProperties);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL EnumerateDeviceLayerProperties(VkPhysicalDevice /* physicalDevice */, uint32_t *pCount,
+ VkLayerProperties *pProperties) {
+ return getProperties<VkLayerProperties>(0, NULL, pCount, pProperties);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL EnumerateInstanceExtensionProperties(const char* /* pLayerName */, uint32_t *pCount,
+ VkExtensionProperties *pProperties) {
+ return getProperties<VkExtensionProperties>(0, NULL, pCount, pProperties);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL EnumerateDeviceExtensionProperties(VkPhysicalDevice /* physicalDevice */, const char* /* pLayerName */,
+ uint32_t *pCount, VkExtensionProperties *pProperties) {
+ return getProperties<VkExtensionProperties>(0, NULL, pCount, pProperties);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL nullCreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkInstance* pInstance) {
+
+ VkLayerInstanceCreateInfo *layerCreateInfo = (VkLayerInstanceCreateInfo *)pCreateInfo->pNext;
+
+ const char* msg = "nullCreateInstance called in nullLayer" xstr(LAYERNAME);
+ ALOGI("%s", msg);
+
+ // Step through the pNext chain until we get to the link function
+ while(layerCreateInfo && (layerCreateInfo->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO ||
+ layerCreateInfo->function != VK_LAYER_FUNCTION_LINK)) {
+
+ layerCreateInfo = (VkLayerInstanceCreateInfo *)layerCreateInfo->pNext;
+ }
+
+ if(layerCreateInfo == NULL)
+ return VK_ERROR_INITIALIZATION_FAILED;
+
+ // Grab GIPA for the next layer
+ PFN_vkGetInstanceProcAddr gpa = layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+
+ // Track is in our dispatch table
+ g_VulkanDispatchTable.GetInstanceProcAddr = gpa;
+
+ // Advance the chain for next layer
+ layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext;
+
+ // Call the next layer
+ PFN_vkCreateInstance createFunc = (PFN_vkCreateInstance)gpa(VK_NULL_HANDLE, "vkCreateInstance");
+ VkResult ret = createFunc(pCreateInfo, pAllocator, pInstance);
+
+ return ret;
+}
+
+VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetDeviceProcAddr(VkDevice /* dev */, const char* /* funcName */) {
+ return nullptr;
+}
+
+VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetInstanceProcAddr(VkInstance instance, const char* funcName) {
+
+ // Our simple layer only intercepts vkCreateInstance
+ const char* targetFunc = "vkCreateInstance";
+ if (!strncmp(targetFunc, funcName, sizeof(&targetFunc)))
+ return (PFN_vkVoidFunction)nullCreateInstance;
+
+ return (PFN_vkVoidFunction)g_VulkanDispatchTable.GetInstanceProcAddr(instance, funcName);
+}
+
+} // namespace
+
+// loader-layer interface v0, just wrappers since there is only a layer
+
+__attribute((visibility("default"))) VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties(uint32_t *pCount,
+ VkLayerProperties *pProperties) {
+ return EnumerateInstanceLayerProperties(pCount, pProperties);
+}
+
+__attribute((visibility("default"))) VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t *pCount,
+ VkLayerProperties *pProperties) {
+ return EnumerateDeviceLayerProperties(physicalDevice, pCount, pProperties);
+}
+
+__attribute((visibility("default"))) VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties(const char *pLayerName, uint32_t *pCount,
+ VkExtensionProperties *pProperties) {
+ return EnumerateInstanceExtensionProperties(pLayerName, pCount, pProperties);
+}
+
+__attribute((visibility("default"))) VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
+ const char *pLayerName, uint32_t *pCount,
+ VkExtensionProperties *pProperties) {
+ return EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pCount, pProperties);
+}
+
+__attribute((visibility("default"))) VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice dev, const char *funcName) {
+ return GetDeviceProcAddr(dev, funcName);
+}
+
+__attribute((visibility("default"))) VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *funcName) {
+ return GetInstanceProcAddr(instance, funcName);
+}
diff --git a/hostsidetests/gputools/layers/jni/vk_layer_interface.h b/hostsidetests/gputools/layers/jni/vk_layer_interface.h
new file mode 100644
index 0000000..f2a5232
--- /dev/null
+++ b/hostsidetests/gputools/layers/jni/vk_layer_interface.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015-2016 The Khronos Group Inc.
+ * Copyright (c) 2015-2016 Valve Corporation
+ * Copyright (c) 2015-2016 LunarG, Inc.
+ * Copyright (c) 2016 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and/or associated documentation files (the "Materials"), to
+ * deal in the Materials without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Materials, and to permit persons to whom the Materials are
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included in
+ * all copies or substantial portions of the Materials.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
+ * USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ */
+#pragma once
+
+#include <vulkan/vulkan.h>
+
+// ------------------------------------------------------------------------------------------------
+// CreateInstance and CreateDevice support structures
+
+typedef enum VkLayerFunction_ {
+ VK_LAYER_FUNCTION_LINK = 0,
+ VK_LAYER_FUNCTION_DATA_CALLBACK = 1
+} VkLayerFunction;
+
+typedef struct VkLayerInstanceLink_ {
+ struct VkLayerInstanceLink_* pNext;
+ PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
+} VkLayerInstanceLink;
+
+typedef VkResult(VKAPI_PTR* PFN_vkSetInstanceLoaderData)(VkInstance instance,
+ void* object);
+typedef VkResult(VKAPI_PTR* PFN_vkSetDeviceLoaderData)(VkDevice device,
+ void* object);
+
+typedef struct {
+ VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
+ const void* pNext;
+ VkLayerFunction function;
+ union {
+ VkLayerInstanceLink* pLayerInfo;
+ PFN_vkSetInstanceLoaderData pfnSetInstanceLoaderData;
+ } u;
+} VkLayerInstanceCreateInfo;
+
+typedef struct VkLayerDeviceLink_ {
+ struct VkLayerDeviceLink_* pNext;
+ PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
+ PFN_vkGetDeviceProcAddr pfnNextGetDeviceProcAddr;
+} VkLayerDeviceLink;
+
+typedef struct {
+ VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO
+ const void* pNext;
+ VkLayerFunction function;
+ union {
+ VkLayerDeviceLink* pLayerInfo;
+ PFN_vkSetDeviceLoaderData pfnSetDeviceLoaderData;
+ } u;
+} VkLayerDeviceCreateInfo;
diff --git a/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java b/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
new file mode 100644
index 0000000..96096fb
--- /dev/null
+++ b/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.gputools.cts;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.IDeviceTest;
+
+import com.android.ddmlib.Log;
+
+import java.util.Scanner;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests that exercise Rootless GPU Debug functionality supported by the loader.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CtsRootlessGpuDebugHostTest implements IDeviceTest {
+
+ public static final String TAG = CtsRootlessGpuDebugHostTest.class.getSimpleName();
+
+ /**
+ * A reference to the device under test.
+ */
+ private ITestDevice mDevice;
+
+ public void setDevice(ITestDevice device) {
+ mDevice = device;
+ }
+
+ @Override
+ public ITestDevice getDevice() {
+ return mDevice;
+ }
+
+ // This test ensures that the Vulkan loader can use Settings to load layer
+ // from the base directory of debuggable applications. Is also tests several
+ // positive and negative scenarios we want to cover (listed below).
+ //
+ // There are three APKs; DEBUG and RELEASE are practically identical with one
+ // being flagged as debuggable. The LAYERS APK is mainly a conduit for getting
+ // layers onto the device without affecting the other APKs.
+ //
+ // The RELEASE APK does contain one layer to ensure using Settings to enable
+ // layers does not interfere with legacy methods using system properties.
+ //
+ // The layers themselves are practically null, only enough functionality to
+ // satisfy loader enumerating and loading. They don't actually chain together.
+ //
+ // Positive tests
+ // - Ensure we can toggle the Enable Setting on and off (testDebugLayerLoadVulkan)
+ // - Ensure we can set the debuggable app (testDebugLayerLoadVulkan)
+ // - Ensure we can set the layer list (testDebugLayerLoadVulkan)
+ // - Ensure we can push a layer to debuggable app (testDebugLayerLoadVulkan)
+ // - Ensure we can specify the app to load layers (testDebugLayerLoadVulkan)
+ // - Ensure we can load a layer from app's data directory (testDebugLayerLoadVulkan)
+ // - Ensure we can load multiple layers, in order, from app's data directory (testDebugLayerLoadVulkan)
+ // - Ensure we can still use system properties if no layers loaded via Settings (testSystemPropertyEnableVulkan)
+ // Negative tests
+ // - Ensure we cannot push a layer to non-debuggable app (testReleaseLayerLoad)
+ // - Ensure non-debuggable app ignores the new Settings (testReleaseLayerLoad)
+ // - Ensure we cannot enumerate layers from debuggable app's data directory if Setting not specified (testDebugNoEnumerateVulkan)
+ // - Ensure we cannot enumerate layers without specifying the debuggable app (testDebugNoEnumerateVulkan)
+ // - Ensure we cannot use system properties when layer is found via Settings with debuggable app (testSystemPropertyIgnoreVulkan)
+
+ private static final String CLASS = "RootlessGpuDebugDeviceActivity";
+ private static final String ACTIVITY = "android.rootlessgpudebug.app.RootlessGpuDebugDeviceActivity";
+ private static final String LAYER_A = "nullLayerA";
+ private static final String LAYER_B = "nullLayerB";
+ private static final String LAYER_C = "nullLayerC";
+ private static final String LAYER_A_LIB = "libVkLayer_" + LAYER_A + ".so";
+ private static final String LAYER_B_LIB = "libVkLayer_" + LAYER_B + ".so";
+ private static final String LAYER_C_LIB = "libVkLayer_" + LAYER_C + ".so";
+ private static final String LAYER_A_NAME = "VK_LAYER_ANDROID_" + LAYER_A;
+ private static final String LAYER_B_NAME = "VK_LAYER_ANDROID_" + LAYER_B;
+ private static final String LAYER_C_NAME = "VK_LAYER_ANDROID_" + LAYER_C;
+ private static final String DEBUG_APP = "android.rootlessgpudebug.DEBUG.app";
+ private static final String RELEASE_APP = "android.rootlessgpudebug.RELEASE.app";
+ private static final String LAYERS_APP = "android.rootlessgpudebug.LAYERS.app";
+
+ // This is how long we'll scan the log for a result before giving up. This limit will only
+ // be reached if something has gone wrong
+ private static final long LOG_SEARCH_TIMEOUT_MS = 5000;
+
+ private String removeWhitespace(String input) {
+ return input.replaceAll(System.getProperty("line.separator"), "").trim();
+ }
+
+ /**
+ * Grab and format the process ID of requested app.
+ */
+ private String getPID(String app) throws Exception {
+ String pid = mDevice.executeAdbCommand("shell", "pidof", app);
+ return removeWhitespace(pid);
+ }
+
+ /**
+ * Extract the requested layer from APK and copy to tmp
+ */
+ private void setupLayer(String layer) throws Exception {
+
+ // We use the LAYERS apk to facilitate getting layers onto the device for mixing and matching
+ String libPath = mDevice.executeAdbCommand("shell", "pm", "path", LAYERS_APP);
+ libPath = libPath.replaceAll("package:", "");
+ libPath = libPath.replaceAll("base.apk", "");
+ libPath = removeWhitespace(libPath);
+ libPath += "lib/";
+
+ // Use find to get the .so so we can ignore ABI
+ String layerPath = mDevice.executeAdbCommand("shell", "find", libPath + " -name " + layer);
+ layerPath = removeWhitespace(layerPath);
+ mDevice.executeAdbCommand("shell", "cp", layerPath + " /data/local/tmp");
+ }
+
+ /**
+ * Simple helper class for returning multiple results
+ */
+ public class LogScanResult {
+ public boolean found;
+ public int lineNumber;
+ }
+
+ private LogScanResult scanLog(String pid, String searchString) throws Exception {
+ return scanLog(pid, searchString, "");
+ }
+
+ /**
+ * Scan the logcat for requested process ID, returning if found and which line
+ */
+ private LogScanResult scanLog(String pid, String searchString, String endString) throws Exception {
+
+ LogScanResult result = new LogScanResult();
+ result.found = false;
+ result.lineNumber = -1;
+
+ // Scan until output from app is found
+ boolean scanComplete= false;
+
+ // Let the test run a reasonable amount of time before moving on
+ long hostStartTime = System.currentTimeMillis();
+
+ while (!scanComplete && ((System.currentTimeMillis() - hostStartTime) < LOG_SEARCH_TIMEOUT_MS)) {
+
+ // Give our activity a chance to run and fill the log
+ Thread.sleep(1000);
+
+ // Pull the logcat for a single process
+ String logcat = mDevice.executeAdbCommand("logcat", "-v", "brief", "-d", "--pid=" + pid, "*:V");
+ int lineNumber = 0;
+ Scanner apkIn = new Scanner(logcat);
+ while (apkIn.hasNextLine()) {
+ lineNumber++;
+ String line = apkIn.nextLine();
+ if (line.contains(searchString) && line.endsWith(endString)) {
+ result.found = true;
+ result.lineNumber = lineNumber;
+ }
+ if (line.contains("vkCreateInstance succeeded")) {
+ // Once we've got output from the app, we've collected what we need
+ scanComplete= true;
+ }
+ }
+ apkIn.close();
+ }
+
+ return result;
+ }
+
+ /**
+ * Remove any temporary files on the device, clear any settings, kill the apps after each test
+ */
+ @After
+ public void cleanup() throws Exception {
+ mDevice.executeAdbCommand("shell", "am", "force-stop", DEBUG_APP);
+ mDevice.executeAdbCommand("shell", "am", "force-stop", RELEASE_APP);
+ mDevice.executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + LAYER_A_LIB);
+ mDevice.executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + LAYER_B_LIB);
+ mDevice.executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + LAYER_C_LIB);
+ mDevice.executeAdbCommand("shell", "settings", "delete", "global", "enable_gpu_debug_layers");
+ mDevice.executeAdbCommand("shell", "settings", "delete", "global", "gpu_debug_app");
+ mDevice.executeAdbCommand("shell", "settings", "delete", "global", "gpu_debug_layers");
+ mDevice.executeAdbCommand("shell", "setprop", "debug.vulkan.layers", "\'\"\"\'");
+ }
+
+ /**
+ * This is the primary test of the feature. It pushes layers to our debuggable app and ensures they are
+ * loaded in the correct order.
+ */
+ @Test
+ public void testDebugLayerLoadVulkan() throws Exception {
+
+ // Set up layers to be loaded
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", DEBUG_APP);
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME + ":" + LAYER_B_NAME);
+
+ // Copy the layers from our LAYERS APK to tmp
+ setupLayer(LAYER_A_LIB);
+ setupLayer(LAYER_B_LIB);
+
+ // Copy them over to our DEBUG app
+ mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", DEBUG_APP,
+ "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'");
+ mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_B_LIB, "|", "run-as", DEBUG_APP,
+ "sh", "-c", "\'cat", ">", LAYER_B_LIB, ";", "chmod", "700", LAYER_B_LIB + "\'");
+
+ // Kick off our DEBUG app
+ mDevice.executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+
+ // Give it a chance to start, then grab process ID
+ Thread.sleep(1000);
+ String pid = getPID(DEBUG_APP);
+
+ // Check that both layers were loaded, in the correct order
+ String searchStringA = "nullCreateInstance called in " + LAYER_A;
+ LogScanResult resultA = scanLog(pid, searchStringA);
+ Assert.assertTrue("LayerA was not loaded", resultA.found);
+
+ String searchStringB = "nullCreateInstance called in " + LAYER_B;
+ LogScanResult resultB = scanLog(pid, searchStringB);
+ Assert.assertTrue("LayerB was not loaded", resultB.found);
+
+ Assert.assertTrue("LayerA should be loaded before LayerB", resultA.lineNumber < resultB.lineNumber);
+ }
+
+ /**
+ * This test ensures that we cannot push a layer to a non-debuggable app
+ * It also ensures non-debuggable apps ignore Settings and don't enumerate layers in the base directory.
+ */
+ @Test
+ public void testReleaseLayerLoadVulkan() throws Exception {
+
+ // Set up a layers to be loaded for RELEASE app
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", RELEASE_APP);
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME + ":" + LAYER_B_NAME);
+
+ // Copy a layer from our LAYERS APK to tmp
+ setupLayer(LAYER_A_LIB);
+
+ // Attempt to copy them over to our RELEASE app (this should fail)
+ mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", RELEASE_APP,
+ "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'", "||", "echo", "run-as", "failed");
+
+ // Kick off our RELEASE app
+ mDevice.executeAdbCommand("shell", "am", "start", "-n", RELEASE_APP + "/" + ACTIVITY);
+
+ // Give it a chance to start, then grab process ID
+ Thread.sleep(1000);
+ String pid = getPID(RELEASE_APP);
+
+ // Ensure we don't load the layer in base dir
+ String searchStringA = LAYER_A_NAME + "loaded";
+ LogScanResult resultA = scanLog(pid, searchStringA);
+ Assert.assertFalse("LayerA was enumerated", resultA.found);
+ }
+
+ /**
+ * This test ensures debuggable apps do not enumerate layers in base
+ * directory if enable_gpu_debug_layers is not enabled.
+ */
+ @Test
+ public void testDebugNotEnabledVulkan() throws Exception {
+
+ // Ensure the global layer enable settings is NOT enabled
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "0");
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", DEBUG_APP);
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME);
+
+ // Copy a layer from our LAYERS APK to tmp
+ setupLayer(LAYER_A_LIB);
+
+ // Copy it over to our DEBUG app
+ mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", DEBUG_APP,
+ "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'");
+
+ // Kick off our DEBUG app
+ mDevice.executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+
+ // Give it a chance to start, then grab process ID
+ Thread.sleep(1000);
+ String pid = getPID(DEBUG_APP);
+
+ // Ensure we don't load the layer in base dir
+ String searchStringA = LAYER_A_NAME + "loaded";
+ LogScanResult resultA = scanLog(pid, searchStringA);
+ Assert.assertFalse("LayerA was enumerated", resultA.found);
+ }
+
+ /**
+ * This test ensures debuggable apps do not enumerate layers in base
+ * directory if gpu_debug_app does not match.
+ */
+ @Test
+ public void testDebugWrongAppVulkan() throws Exception {
+
+ // Ensure the gpu_debug_app does not match what we launch
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", RELEASE_APP);
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME);
+
+ // Copy a layer from our LAYERS APK to tmp
+ setupLayer(LAYER_A_LIB);
+
+ // Copy it over to our DEBUG app
+ mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", DEBUG_APP,
+ "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'");
+
+ // Kick off our DEBUG app
+ mDevice.executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+
+ // Give it a chance to start, then grab process ID
+ Thread.sleep(1000);
+ String pid = getPID(DEBUG_APP);
+
+ // Ensure we don't load the layer in base dir
+ String searchStringA = LAYER_A_NAME + "loaded";
+ LogScanResult resultA = scanLog(pid, searchStringA);
+ Assert.assertFalse("LayerA was enumerated", resultA.found);
+ }
+
+ /**
+ * This test ensures debuggable apps do not enumerate layers in base
+ * directory if gpu_debug_layers are not set.
+ */
+ @Test
+ public void testDebugNoLayersEnabledVulkan() throws Exception {
+
+ // Ensure the global layer enable settings is NOT enabled
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", DEBUG_APP);
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", "foo");
+
+ // Copy a layer from our LAYERS APK to tmp
+ setupLayer(LAYER_A_LIB);
+
+ // Copy it over to our DEBUG app
+ mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", DEBUG_APP,
+ "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'");
+
+ // Kick off our DEBUG app
+ mDevice.executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+
+ // Give it a chance to start, then grab process ID
+ Thread.sleep(1000);
+ String pid = getPID(DEBUG_APP);
+
+ // Ensure layerA is not loaded
+ String searchStringA = "nullCreateInstance called in " + LAYER_A;
+ LogScanResult resultA = scanLog(pid, searchStringA);
+ Assert.assertFalse("LayerA was loaded", resultA.found);
+ }
+
+ /**
+ * This test ensures we can still use properties if no layer found via Settings
+ */
+ @Test
+ public void testSystemPropertyEnableVulkan() throws Exception {
+
+ // Set up layerA to be loaded, but not layerB or layerC
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", RELEASE_APP);
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME);
+
+ // Enable layerC (which is packaged with the RELEASE app) with system properties
+ mDevice.executeAdbCommand("shell", "setprop", "debug.vulkan.layers " + LAYER_C_NAME);
+
+ // Kick off our RELEASE app
+ mDevice.executeAdbCommand("shell", "am", "start", "-n", RELEASE_APP + "/" + ACTIVITY);
+
+ // Give it a chance to start, then grab process ID
+ Thread.sleep(1000);
+ String pid = getPID(RELEASE_APP);
+
+ // Check that both layers were loaded, in the correct order
+ String searchStringA = LAYER_A_NAME + "loaded";
+ LogScanResult resultA = scanLog(pid, searchStringA);
+ Assert.assertFalse("LayerA was enumerated", resultA.found);
+
+ String searchStringC = "nullCreateInstance called in " + LAYER_C;
+ LogScanResult resultC = scanLog(pid, searchStringC);
+ Assert.assertTrue("LayerC was not loaded", resultC.found);
+ }
+
+ /**
+ * This test ensures system properties are ignored if Settings load a layer
+ */
+ @Test
+ public void testSystemPropertyIgnoreVulkan() throws Exception {
+
+ // Set up layerA to be loaded, but not layerB
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "enable_gpu_debug_layers", "1");
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_app", DEBUG_APP);
+ mDevice.executeAdbCommand("shell", "settings", "put", "global", "gpu_debug_layers", LAYER_A_NAME);
+
+ // Copy the layers from our LAYERS APK
+ setupLayer(LAYER_A_LIB);
+ setupLayer(LAYER_B_LIB);
+
+ // Copy them over to our DEBUG app
+ mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_A_LIB, "|", "run-as", DEBUG_APP,
+ "sh", "-c", "\'cat", ">", LAYER_A_LIB, ";", "chmod", "700", LAYER_A_LIB + "\'");
+ mDevice.executeAdbCommand("shell", "cat", "/data/local/tmp/" + LAYER_B_LIB, "|", "run-as", DEBUG_APP,
+ "sh", "-c", "\'cat", ">", LAYER_B_LIB, ";", "chmod", "700", LAYER_B_LIB + "\'");
+
+ // Enable layerB with system properties
+ mDevice.executeAdbCommand("shell", "setprop", "debug.vulkan.layers " + LAYER_B_NAME);
+
+ // Kick off our DEBUG app
+ mDevice.executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+
+ // Give it a chance to start, then grab process ID
+ Thread.sleep(1000);
+ String pid = getPID(DEBUG_APP);
+
+ // Ensure only layerA is loaded
+ String searchStringA = "nullCreateInstance called in " + LAYER_A;
+ LogScanResult resultA = scanLog(pid, searchStringA);
+ Assert.assertTrue("LayerA was not loaded", resultA.found);
+
+ String searchStringB = "nullCreateInstance called in " + LAYER_B;
+ LogScanResult resultB = scanLog(pid, searchStringB);
+ Assert.assertFalse("LayerB was loaded", resultB.found);
+ }
+}