1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.gputools.cts; 17 18 import com.android.tradefed.device.ITestDevice; 19 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 20 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 21 22 import java.util.Scanner; 23 24 import org.junit.After; 25 import org.junit.Before; 26 import org.junit.Assert; 27 import org.junit.Test; 28 import org.junit.runner.RunWith; 29 30 /** 31 * Tests that exercise Rootless GPU Debug functionality supported by the loader. 32 */ 33 @RunWith(DeviceJUnit4ClassRunner.class) 34 public class CtsRootlessGpuDebugHostTest extends BaseHostJUnit4Test { 35 36 public static final String TAG = "RootlessGpuDebugDeviceActivity"; 37 38 // This test ensures that the Vulkan and GLES loaders can use Settings to load layers 39 // from the base directory of debuggable applications. Is also tests several 40 // positive and negative scenarios we want to cover (listed below). 41 // 42 // There are three APKs; DEBUG and RELEASE are practically identical with one 43 // being flagged as debuggable. The LAYERS APK is mainly a conduit for getting 44 // layers onto the device without affecting the other APKs. 45 // 46 // The RELEASE APK does contain one layer to ensure using Settings to enable 47 // layers does not interfere with legacy methods using system properties. 48 // 49 // The layers themselves are practically null, only enough functionality to 50 // satisfy loader enumerating and loading. They don't actually chain together. 51 // 52 // Positive Vulkan tests 53 // - Ensure we can toggle the Enable Setting on and off (testDebugLayerLoadVulkan) 54 // - Ensure we can set the debuggable app (testDebugLayerLoadVulkan) 55 // - Ensure we can set the layer list (testDebugLayerLoadVulkan) 56 // - Ensure we can push a layer to debuggable app (testDebugLayerLoadVulkan) 57 // - Ensure we can specify the app to load layers (testDebugLayerLoadVulkan) 58 // - Ensure we can load a layer from app's data directory (testDebugLayerLoadVulkan) 59 // - Ensure we can load multiple layers, in order, from app's data directory (testDebugLayerLoadVulkan) 60 // - Ensure we can still use system properties if no layers loaded via Settings (testSystemPropertyEnableVulkan) 61 // - Ensure we can find layers in separate specified app and load them in a debuggable app (testDebugLayerLoadExternalVulkan) 62 // - Ensure we can find layers in separate specified app and load them in an injectLayers app (testInjectLayerLoadExternalVulkan) 63 // - Ensure we can enumerate the instance extension advertised by implicitly enabled layer (testInstanceExtensionPropertiesFromImplicitLayerVulkanBasic) 64 // - Ensure we can only enumerate first instance extension closest to application 65 // when multiple implicitly enabled layers advertise the same extension (testInstanceExtensionPropertiesFromImplicitLayerVulkanMultipleLayers) 66 // Negative Vulkan tests 67 // - Ensure we cannot push a layer to non-debuggable app (testReleaseLayerLoadVulkan) 68 // - Ensure non-debuggable app ignores the new Settings (testReleaseLayerLoadVulkan) 69 // - Ensure we cannot push a layer to an injectLayers app (testInjectLayerLoadVulkan) 70 // - Ensure we cannot enumerate layers from debuggable app's data directory if Setting not specified (testDebugNoEnumerateVulkan) 71 // - Ensure we cannot enumerate layers without specifying the debuggable app (testDebugNoEnumerateVulkan) 72 // - Ensure we cannot use system properties when layer is found via Settings with debuggable app (testSystemPropertyIgnoreVulkan) 73 // 74 // Positive GLES tests 75 // - Ensure we can toggle the Enable Setting on and off (testDebugLayerLoadGLES) 76 // - Ensure we can set the debuggable app (testDebugLayerLoadGLES) 77 // - Ensure we can set the layer list (testDebugLayerLoadGLES) 78 // - Ensure we can push a layer to debuggable app (testDebugLayerLoadGLES) 79 // - Ensure we can specify the app to load layers (testDebugLayerLoadGLES) 80 // - Ensure we can load a layer from app's data directory (testDebugLayerLoadGLES) 81 // - Ensure we can load multiple layers, in order, from app's data directory (testDebugLayerLoadGLES) 82 // - Ensure we can find layers in separate specified app and load them in a debuggable app (testDebugLayerLoadExternalGLES) 83 // - Ensure we can find layers in separate specified app and load them in an injectLayers app (testInjectLayerLoadExternalGLES) 84 // Negative GLES tests 85 // - Ensure we cannot push a layer to non-debuggable app (testReleaseLayerLoadGLES) 86 // - Ensure non-debuggable app ignores the new Settings (testReleaseLayerLoadGLES) 87 // - Ensure we cannot enumerate layers from debuggable app's data directory if Setting not specified (testDebugNoEnumerateGLES) 88 // - Ensure we cannot enumerate layers without specifying the debuggable app (testDebugNoEnumerateGLES) 89 // 90 // Positive combined tests 91 // - Ensure we can load Vulkan and GLES layers at the same time, from multiple external apps (testMultipleExternalApps) 92 93 private static final String CLASS = "RootlessGpuDebugDeviceActivity"; 94 private static final String ACTIVITY = "android.rootlessgpudebug.app.RootlessGpuDebugDeviceActivity"; 95 private static final String VK_LAYER_LIB_PREFIX = "libVkLayer_nullLayer"; 96 private static final String VK_LAYER_A_LIB = VK_LAYER_LIB_PREFIX + "A.so"; 97 private static final String VK_LAYER_B_LIB = VK_LAYER_LIB_PREFIX + "B.so"; 98 private static final String VK_LAYER_C_LIB = VK_LAYER_LIB_PREFIX + "C.so"; 99 private static final String VK_LAYER_D_LIB = VK_LAYER_LIB_PREFIX + "D.so"; 100 private static final String VK_LAYER_E_LIB = VK_LAYER_LIB_PREFIX + "E.so"; 101 private static final String VK_LAYER_NAME_PREFIX = "VK_LAYER_ANDROID_nullLayer"; 102 private static final String VK_LAYER_A = VK_LAYER_NAME_PREFIX + "A"; 103 private static final String VK_LAYER_B = VK_LAYER_NAME_PREFIX + "B"; 104 private static final String VK_LAYER_C = VK_LAYER_NAME_PREFIX + "C"; 105 private static final String VK_LAYER_D = VK_LAYER_NAME_PREFIX + "D"; 106 private static final String VK_LAYER_E = VK_LAYER_NAME_PREFIX + "E"; 107 private static final String DEBUG_APP = "android.rootlessgpudebug.DEBUG.app"; 108 private static final String RELEASE_APP = "android.rootlessgpudebug.RELEASE.app"; 109 private static final String INJECT_APP = "android.rootlessgpudebug.INJECT.app"; 110 private static final String LAYERS_APP = "android.rootlessgpudebug.LAYERS.app"; 111 private static final String GLES_LAYERS_APP = "android.rootlessgpudebug.GLES_LAYERS.app"; 112 private static final String DEBUG_APK = "CtsGpuToolsRootlessGpuDebugApp-DEBUG.apk"; 113 private static final String RELEASE_APK = "CtsGpuToolsRootlessGpuDebugApp-RELEASE.apk"; 114 private static final String INJECT_APK = "CtsGpuToolsRootlessGpuDebugApp-INJECT.apk"; 115 private static final String LAYERS_APK = "CtsGpuToolsRootlessGpuDebugApp-LAYERS.apk"; 116 private static final String GLES_LAYERS_APK = "CtsGpuToolsRootlessGpuDebugApp-GLES_LAYERS.apk"; 117 private static final String GLES_LAYER_A = "glesLayerA"; 118 private static final String GLES_LAYER_B = "glesLayerB"; 119 private static final String GLES_LAYER_C = "glesLayerC"; 120 private static final String GLES_LAYER_A_LIB = "libGLES_" + GLES_LAYER_A + ".so"; 121 private static final String GLES_LAYER_B_LIB = "libGLES_" + GLES_LAYER_B + ".so"; 122 private static final String GLES_LAYER_C_LIB = "libGLES_" + GLES_LAYER_C + ".so"; 123 124 // This is how long we'll scan the log for a result before giving up. This limit will only 125 // be reached if something has gone wrong 126 private static final long LOG_SEARCH_TIMEOUT_MS = 5000; 127 private static final long SETTING_APPLY_TIMEOUT_MS = 5000; 128 129 private static boolean initialized = false; 130 removeWhitespace(String input)131 private String removeWhitespace(String input) { 132 return input.replaceAll(System.getProperty("line.separator"), "").trim(); 133 } 134 135 /** 136 * Return current timestamp in format accepted by logcat 137 */ getTime()138 private String getTime() throws Exception { 139 // logcat will accept "MM-DD hh:mm:ss.mmm" 140 return getDevice().executeShellCommand("date +\"%m-%d %H:%M:%S.%3N\""); 141 } 142 143 /** 144 * Apply a setting and refresh the platform's cache 145 */ applySetting(String setting, String value)146 private void applySetting(String setting, String value) throws Exception { 147 getDevice().executeShellCommand("settings put global " + setting + " " + value); 148 getDevice().executeShellCommand("am refresh-settings-cache"); 149 } 150 151 /** 152 * Delete a setting and refresh the platform's cache 153 */ deleteSetting(String setting)154 private void deleteSetting(String setting) throws Exception { 155 getDevice().executeShellCommand("settings delete global " + setting); 156 getDevice().executeShellCommand("am refresh-settings-cache"); 157 } 158 159 /** 160 * Extract the requested layer from APK and copy to tmp 161 */ setupLayer(String layer, String layerApp)162 private void setupLayer(String layer, String layerApp) throws Exception { 163 164 // We use the LAYERS apk to facilitate getting layers onto the device for mixing and matching 165 String libPath = getDevice().executeAdbCommand("shell", "pm", "path", layerApp); 166 libPath = libPath.replaceAll("package:", ""); 167 libPath = libPath.replaceAll("base.apk", ""); 168 libPath = removeWhitespace(libPath); 169 libPath += "lib/"; 170 171 // Use find to get the .so so we can ignore ABI 172 String layerPath = getDevice().executeAdbCommand("shell", "find", libPath + " -name " + layer); 173 layerPath = removeWhitespace(layerPath); 174 getDevice().executeAdbCommand("shell", "cp", layerPath + " /data/local/tmp"); 175 } 176 177 /** 178 * Check that the layer is loaded by only checking the log after startTime. 179 */ assertVkLayerLoading(String startTime, String layerName, boolean loaded)180 private void assertVkLayerLoading(String startTime, String layerName, boolean loaded) throws Exception { 181 String searchString = "nullCreateInstance called in " + layerName; 182 LogScanResult result = scanLog(TAG + "," + layerName, searchString, startTime); 183 if (loaded) { 184 Assert.assertTrue(layerName + " was not loaded", result.found); 185 } else { 186 Assert.assertFalse(layerName + " was loaded", result.found); 187 } 188 } 189 190 /** 191 * Check that the layer is enumerated by only checking the log after startTime. 192 */ assertVkLayerEnumeration(String startTime, String layerName, boolean enumerated)193 private void assertVkLayerEnumeration(String startTime, String layerName, boolean enumerated) throws Exception { 194 String searchString = layerName + " loaded"; 195 LogScanResult result = scanLog(TAG + "," + layerName, searchString, startTime); 196 if (enumerated) { 197 Assert.assertTrue(layerName + " was not enumerated", result.found); 198 } else { 199 Assert.assertFalse(layerName + " was enumerated", result.found); 200 } 201 } 202 203 /** 204 * Check whether an extension is properly advertised by only checking the log after startTime. 205 */ assertVkExtension(String startTime, String extensionName, int specVersion)206 private void assertVkExtension(String startTime, String extensionName, int specVersion) throws Exception { 207 String searchString = extensionName + ": " + specVersion; 208 LogScanResult result = scanLog(TAG + ",RootlessGpuDebug", searchString, startTime); 209 Assert.assertTrue(extensionName + "with spec version: " + specVersion + " was not advertised", result.found); 210 } 211 212 /** 213 * Simple helper class for returning multiple results 214 */ 215 public class LogScanResult { 216 public boolean found; 217 public int lineNumber; 218 } 219 scanLog(String tag, String searchString, String appStartTime)220 private LogScanResult scanLog(String tag, String searchString, String appStartTime) throws Exception { 221 return scanLog(tag, searchString, "", appStartTime); 222 } 223 224 /** 225 * Scan the logcat for requested layer tag, returning if found and which line 226 */ scanLog(String tag, String searchString, String endString, String appStartTime)227 private LogScanResult scanLog(String tag, String searchString, String endString, String appStartTime) throws Exception { 228 229 LogScanResult result = new LogScanResult(); 230 result.found = false; 231 result.lineNumber = -1; 232 233 // Scan until output from app is found 234 boolean scanComplete= false; 235 236 // Let the test run a reasonable amount of time before moving on 237 long hostStartTime = System.currentTimeMillis(); 238 239 while (!scanComplete && ((System.currentTimeMillis() - hostStartTime) < LOG_SEARCH_TIMEOUT_MS)) { 240 241 // Give our activity a chance to run and fill the log 242 Thread.sleep(1000); 243 244 // Pull the logcat since the app started, filter for tags 245 // This command should look something like this: 246 // adb logcat -d -t '03-27 21:35:05.392' -s "RootlessGpuDebugDeviceActivity,nullLayerC" 247 String logcat = getDevice().executeShellCommand( 248 "logcat -d " + 249 "-t '" + removeWhitespace(appStartTime) + "' " + 250 "-s \"" + tag + "\""); 251 int lineNumber = 0; 252 Scanner apkIn = new Scanner(logcat); 253 while (apkIn.hasNextLine()) { 254 lineNumber++; 255 String line = apkIn.nextLine(); 256 if (line.contains(searchString) && line.endsWith(endString)) { 257 result.found = true; 258 result.lineNumber = lineNumber; 259 } 260 if (line.contains("RootlessGpuDebug activity complete")) { 261 // Once we've got output from the app, we've collected what we need 262 scanComplete= true; 263 } 264 } 265 apkIn.close(); 266 } 267 268 // If this assert fires , try increasing the timeout 269 Assert.assertTrue("Log scanning did not complete before timout (" + 270 LOG_SEARCH_TIMEOUT_MS + "ms)", scanComplete); 271 272 return result; 273 } 274 275 /** 276 * Remove any temporary files on the device, clear any settings, kill the apps after each test 277 */ 278 @After cleanup()279 public void cleanup() throws Exception { 280 getDevice().executeAdbCommand("shell", "am", "force-stop", DEBUG_APP); 281 getDevice().executeAdbCommand("shell", "am", "force-stop", RELEASE_APP); 282 getDevice().executeAdbCommand("shell", "am", "force-stop", INJECT_APP); 283 getDevice().executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + VK_LAYER_A_LIB); 284 getDevice().executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + VK_LAYER_B_LIB); 285 getDevice().executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + VK_LAYER_C_LIB); 286 getDevice().executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + GLES_LAYER_A_LIB); 287 getDevice().executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + GLES_LAYER_B_LIB); 288 getDevice().executeAdbCommand("shell", "rm", "-f", "/data/local/tmp/" + GLES_LAYER_C_LIB); 289 getDevice().executeAdbCommand("shell", "settings", "delete", "global", "enable_gpu_debug_layers"); 290 getDevice().executeAdbCommand("shell", "settings", "delete", "global", "gpu_debug_app"); 291 getDevice().executeAdbCommand("shell", "settings", "delete", "global", "gpu_debug_layers"); 292 getDevice().executeAdbCommand("shell", "settings", "delete", "global", "gpu_debug_layers_gles"); 293 getDevice().executeAdbCommand("shell", "settings", "delete", "global", "gpu_debug_layer_app"); 294 getDevice().executeAdbCommand("shell", "setprop", "debug.vulkan.layers", "\'\'"); 295 getDevice().executeAdbCommand("shell", "setprop", "debug.gles.layers", "\'\'"); 296 } 297 298 /** 299 * Clean up before starting any tests, and ensure supporting packages are installed 300 */ 301 @Before init()302 public void init() throws Exception { 303 installPackage(DEBUG_APK); 304 installPackage(RELEASE_APK); 305 installPackage(LAYERS_APK); 306 installPackage(GLES_LAYERS_APK); 307 if (!initialized) { 308 cleanup(); 309 initialized = true; 310 } 311 } 312 313 /** 314 * This is the primary test of the feature. It pushes layers to our debuggable app and ensures they are 315 * loaded in the correct order. 316 */ 317 @Test testDebugLayerLoadVulkan()318 public void testDebugLayerLoadVulkan() throws Exception { 319 320 // Set up layers to be loaded 321 applySetting("enable_gpu_debug_layers", "1"); 322 applySetting("gpu_debug_app", DEBUG_APP); 323 applySetting("gpu_debug_layers", VK_LAYER_A + ":" + VK_LAYER_B); 324 325 // Copy the layers from our LAYERS APK to tmp 326 setupLayer(VK_LAYER_A_LIB, LAYERS_APP); 327 setupLayer(VK_LAYER_B_LIB, LAYERS_APP); 328 329 330 // Copy them over to our DEBUG app 331 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + VK_LAYER_A_LIB, "|", 332 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()), 333 "sh", "-c", "\'cat", ">", VK_LAYER_A_LIB, ";", "chmod", "700", VK_LAYER_A_LIB + "\'"); 334 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + VK_LAYER_B_LIB, "|", 335 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()), 336 "sh", "-c", "\'cat", ">", VK_LAYER_B_LIB, ";", "chmod", "700", VK_LAYER_B_LIB + "\'"); 337 338 339 // Kick off our DEBUG app 340 String appStartTime = getTime(); 341 getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY); 342 343 // Check that both layers were loaded, in the correct order 344 String searchStringA = "nullCreateInstance called in " + VK_LAYER_A; 345 LogScanResult resultA = scanLog(TAG + "," + VK_LAYER_A + "," + VK_LAYER_B, searchStringA, appStartTime); 346 Assert.assertTrue("LayerA was not loaded", resultA.found); 347 348 String searchStringB = "nullCreateInstance called in " + VK_LAYER_B; 349 LogScanResult resultB = scanLog(TAG + "," + VK_LAYER_A + "," + VK_LAYER_B, searchStringB, appStartTime); 350 Assert.assertTrue("LayerB was not loaded", resultB.found); 351 352 Assert.assertTrue("LayerA should be loaded before LayerB", resultA.lineNumber < resultB.lineNumber); 353 } 354 testLayerNotLoadedVulkan(final String APP_NAME)355 public void testLayerNotLoadedVulkan(final String APP_NAME) throws Exception { 356 // Set up a layers to be loaded for RELEASE or INJECT app 357 applySetting("enable_gpu_debug_layers", "1"); 358 applySetting("gpu_debug_app", APP_NAME); 359 applySetting("gpu_debug_layers", VK_LAYER_A + ":" + VK_LAYER_B); 360 361 // Copy a layer from our LAYERS APK to tmp 362 setupLayer(VK_LAYER_A_LIB, LAYERS_APP); 363 364 // Attempt to copy them over to our RELEASE or INJECT app (this should fail) 365 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + VK_LAYER_A_LIB, "|", 366 "run-as", APP_NAME, "--user", Integer.toString(getDevice().getCurrentUser()), 367 "sh", "-c", "\'cat", ">", VK_LAYER_A_LIB, ";", "chmod", "700", VK_LAYER_A_LIB + "\'", "||", "echo", "run-as", "failed"); 368 369 // Kick off our RELEASE app 370 String appStartTime = getTime(); 371 getDevice().executeAdbCommand("shell", "am", "start", "-n", APP_NAME + "/" + ACTIVITY); 372 373 // Ensure we don't load the layer in base dir 374 assertVkLayerEnumeration(appStartTime, VK_LAYER_A, false); 375 } 376 377 /** 378 * This test ensures that we cannot push a layer to a release app 379 * It also ensures non-debuggable apps ignore Settings and don't enumerate layers in the base directory. 380 */ 381 @Test testReleaseLayerLoadVulkan()382 public void testReleaseLayerLoadVulkan() throws Exception { 383 testLayerNotLoadedVulkan(RELEASE_APP); 384 } 385 386 /** 387 * This test ensures that we cannot push a layer to an injectable app 388 * It also ensures non-debuggable apps ignore Settings and don't enumerate layers in the base directory. 389 */ 390 @Test testInjectLayerLoadVulkan()391 public void testInjectLayerLoadVulkan() throws Exception { 392 testLayerNotLoadedVulkan(INJECT_APP); 393 } 394 395 /** 396 * This test ensures debuggable apps do not enumerate layers in base 397 * directory if enable_gpu_debug_layers is not enabled. 398 */ 399 @Test testDebugNotEnabledVulkan()400 public void testDebugNotEnabledVulkan() throws Exception { 401 402 // Ensure the global layer enable settings is NOT enabled 403 applySetting("enable_gpu_debug_layers", "0"); 404 applySetting("gpu_debug_app", DEBUG_APP); 405 applySetting("gpu_debug_layers", VK_LAYER_A); 406 407 // Copy a layer from our LAYERS APK to tmp 408 setupLayer(VK_LAYER_A_LIB, LAYERS_APP); 409 410 // Copy it over to our DEBUG app 411 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + VK_LAYER_A_LIB, "|", 412 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()), 413 "sh", "-c", "\'cat", ">", VK_LAYER_A_LIB, ";", "chmod", "700", VK_LAYER_A_LIB + "\'"); 414 415 // Kick off our DEBUG app 416 String appStartTime = getTime(); 417 getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY); 418 419 // Ensure we don't load the layer in base dir 420 assertVkLayerEnumeration(appStartTime, VK_LAYER_A, false); 421 } 422 423 /** 424 * This test ensures debuggable apps do not enumerate layers in base 425 * directory if gpu_debug_app does not match. 426 */ 427 @Test testDebugWrongAppVulkan()428 public void testDebugWrongAppVulkan() throws Exception { 429 430 // Ensure the gpu_debug_app does not match what we launch 431 applySetting("enable_gpu_debug_layers", "1"); 432 applySetting("gpu_debug_app", RELEASE_APP); 433 applySetting("gpu_debug_layers", VK_LAYER_A); 434 435 // Copy a layer from our LAYERS APK to tmp 436 setupLayer(VK_LAYER_A_LIB, LAYERS_APP); 437 438 // Copy it over to our DEBUG app 439 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + VK_LAYER_A_LIB, "|", 440 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()), 441 "sh", "-c", "\'cat", ">", VK_LAYER_A_LIB, ";", "chmod", "700", VK_LAYER_A_LIB + "\'"); 442 443 // Kick off our DEBUG app 444 String appStartTime = getTime(); 445 getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY); 446 447 // Ensure we don't load the layer in base dir 448 assertVkLayerEnumeration(appStartTime, VK_LAYER_A, false); 449 } 450 451 /** 452 * This test ensures debuggable apps do not enumerate layers in base 453 * directory if gpu_debug_layers are not set. 454 */ 455 @Test testDebugNoLayersEnabledVulkan()456 public void testDebugNoLayersEnabledVulkan() throws Exception { 457 458 // Ensure the global layer enable settings is NOT enabled 459 applySetting("enable_gpu_debug_layers", "1"); 460 applySetting("gpu_debug_app", DEBUG_APP); 461 applySetting("gpu_debug_layers", "foo"); 462 463 // Copy a layer from our LAYERS APK to tmp 464 setupLayer(VK_LAYER_A_LIB, LAYERS_APP); 465 466 // Copy it over to our DEBUG app 467 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + VK_LAYER_A_LIB, "|", 468 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()), 469 "sh", "-c", "\'cat", ">", VK_LAYER_A_LIB, ";", "chmod", "700", VK_LAYER_A_LIB + "\'"); 470 471 // Kick off our DEBUG app 472 String appStartTime = getTime(); 473 getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY); 474 475 // Ensure layerA is not loaded 476 assertVkLayerLoading(appStartTime, VK_LAYER_A, false); 477 } 478 479 /** 480 * This test ensures we can still use properties if no layer specified via Settings 481 */ 482 @Test testSystemPropertyEnableVulkan()483 public void testSystemPropertyEnableVulkan() throws Exception { 484 485 // Don't enable any layers via settings 486 applySetting("enable_gpu_debug_layers", "1"); 487 applySetting("gpu_debug_app", RELEASE_APP); 488 deleteSetting("gpu_debug_layers"); 489 490 // Enable layerC (which is packaged with the RELEASE app) with system properties 491 getDevice().executeAdbCommand("shell", "setprop", "debug.vulkan.layers " + VK_LAYER_C); 492 493 // Kick off our RELEASE app 494 String appStartTime = getTime(); 495 getDevice().executeAdbCommand("shell", "am", "start", "-n", RELEASE_APP + "/" + ACTIVITY); 496 497 // Check that only layerC was loaded 498 assertVkLayerEnumeration(appStartTime, VK_LAYER_A, false); 499 assertVkLayerLoading(appStartTime, VK_LAYER_C, true); 500 } 501 502 /** 503 * This test ensures system properties are ignored if Settings load a layer 504 */ 505 @Test testSystemPropertyIgnoreVulkan()506 public void testSystemPropertyIgnoreVulkan() throws Exception { 507 508 // Set up layerA to be loaded, but not layerB 509 applySetting("enable_gpu_debug_layers", "1"); 510 applySetting("gpu_debug_app", DEBUG_APP); 511 applySetting("gpu_debug_layers", VK_LAYER_A); 512 513 // Copy the layers from our LAYERS APK 514 setupLayer(VK_LAYER_A_LIB, LAYERS_APP); 515 setupLayer(VK_LAYER_B_LIB, LAYERS_APP); 516 517 // Copy them over to our DEBUG app 518 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + VK_LAYER_A_LIB, "|", 519 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()), 520 "sh", "-c", "\'cat", ">", VK_LAYER_A_LIB, ";", "chmod", "700", VK_LAYER_A_LIB + "\'"); 521 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + VK_LAYER_B_LIB, "|", 522 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()), 523 "sh", "-c", "\'cat", ">", VK_LAYER_B_LIB, ";", "chmod", "700", VK_LAYER_B_LIB + "\'"); 524 525 // Enable layerB with system properties 526 getDevice().executeAdbCommand("shell", "setprop", "debug.vulkan.layers " + VK_LAYER_B); 527 528 // Kick off our DEBUG app 529 String appStartTime = getTime(); 530 getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY); 531 532 // Ensure only layerA is loaded 533 assertVkLayerLoading(appStartTime, VK_LAYER_A, true); 534 assertVkLayerLoading(appStartTime, VK_LAYER_B, false); 535 } 536 537 /** 538 * The common functionality to load layers from an external package. 539 * Returns the app start time. 540 */ testLayerLoadExternalVulkan(final String APP_NAME, String layers)541 public String testLayerLoadExternalVulkan(final String APP_NAME, String layers) throws Exception { 542 543 // Set up layers to be loaded 544 applySetting("enable_gpu_debug_layers", "1"); 545 applySetting("gpu_debug_app", APP_NAME); 546 applySetting("gpu_debug_layers", layers); 547 548 // Specify the external app that hosts layers 549 applySetting("gpu_debug_layer_app", LAYERS_APP); 550 551 // Kick off our DEBUG app 552 String appStartTime = getTime(); 553 getDevice().executeAdbCommand("shell", "am", "start", "-n", APP_NAME + "/" + ACTIVITY); 554 555 String[] layerNames = layers.split(":"); 556 for (String layerName : layerNames) { 557 assertVkLayerLoading(appStartTime, layerName, true); 558 } 559 return appStartTime; 560 } 561 562 /** 563 * This test ensures a debuggable app can load layers from an external package 564 */ 565 @Test testDebugLayerLoadExternalVulkan()566 public void testDebugLayerLoadExternalVulkan() throws Exception { 567 testLayerLoadExternalVulkan(DEBUG_APP, VK_LAYER_C); 568 } 569 570 /** 571 * This test ensures an injectLayers app can load layers from an external package 572 */ 573 @Test testInjectLayerLoadExternalVulkan()574 public void testInjectLayerLoadExternalVulkan() throws Exception { 575 testLayerLoadExternalVulkan(INJECT_APP, VK_LAYER_C); 576 } 577 578 /** 579 * Test that the instance extension is advertised properly from the implicitly enabled layer. 580 */ 581 @Test testInstanceExtensionPropertiesFromImplicitLayerVulkanBasic()582 public void testInstanceExtensionPropertiesFromImplicitLayerVulkanBasic() throws Exception { 583 String appStartTime = testLayerLoadExternalVulkan(DEBUG_APP, VK_LAYER_D); 584 assertVkExtension(appStartTime, "VK_EXT_debug_utils", 1); 585 } 586 587 /** 588 * Test that when there are multiple implicit layers are enabled, if there are several instance 589 * extensions with the same extension names advertised by multiple layers, only the extension 590 * that is closer to the application is advertised by the loader. 591 */ 592 @Test testInstanceExtensionPropertiesFromImplicitLayerVulkanMultipleLayers()593 public void testInstanceExtensionPropertiesFromImplicitLayerVulkanMultipleLayers() throws Exception { 594 String appStartTime = testLayerLoadExternalVulkan(DEBUG_APP, VK_LAYER_E + ":" + VK_LAYER_D); 595 assertVkExtension(appStartTime, "VK_EXT_debug_utils", 2); 596 } 597 598 /** 599 * This test pushes GLES layers to our debuggable app and ensures they are 600 * loaded in the correct order. 601 */ 602 @Test testDebugLayerLoadGLES()603 public void testDebugLayerLoadGLES() throws Exception { 604 605 // Set up layers to be loaded 606 applySetting("enable_gpu_debug_layers", "1"); 607 applySetting("gpu_debug_app", DEBUG_APP); 608 applySetting("gpu_debug_layers_gles", GLES_LAYER_A_LIB + ":" + GLES_LAYER_B_LIB); 609 610 // Copy the layers from our LAYERS APK to tmp 611 setupLayer(GLES_LAYER_A_LIB, GLES_LAYERS_APP); 612 setupLayer(GLES_LAYER_B_LIB, GLES_LAYERS_APP); 613 614 // Copy them over to our DEBUG app 615 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_A_LIB, "|", 616 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()), 617 "sh", "-c", "\'cat", ">", GLES_LAYER_A_LIB, ";", "chmod", "700", GLES_LAYER_A_LIB + "\'"); 618 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_B_LIB, "|", 619 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()), 620 "sh", "-c", "\'cat", ">", GLES_LAYER_B_LIB, ";", "chmod", "700", GLES_LAYER_B_LIB + "\'"); 621 622 // Kick off our DEBUG app 623 String appStartTime = getTime(); 624 getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY); 625 626 // Check that both layers were loaded, in the correct order 627 String searchStringA = "glesLayer_eglChooseConfig called in " + GLES_LAYER_A; 628 LogScanResult resultA = scanLog(TAG + "," + GLES_LAYER_A + "," + GLES_LAYER_B, searchStringA, appStartTime); 629 Assert.assertTrue(GLES_LAYER_A + " was not loaded", resultA.found); 630 631 String searchStringB = "glesLayer_eglChooseConfig called in " + GLES_LAYER_B; 632 LogScanResult resultB = scanLog(TAG + "," + GLES_LAYER_A + "," + GLES_LAYER_B, searchStringB, appStartTime); 633 Assert.assertTrue(GLES_LAYER_B + " was not loaded", resultB.found); 634 635 Assert.assertTrue(GLES_LAYER_A + " should be loaded before " + GLES_LAYER_B, resultA.lineNumber < resultB.lineNumber); 636 } 637 638 /** 639 * This test ensures that we cannot push a layer to a non-debuggable GLES app 640 * It also ensures non-debuggable apps ignore Settings and don't enumerate layers in the base directory. 641 */ 642 @Test testReleaseLayerLoadGLES()643 public void testReleaseLayerLoadGLES() throws Exception { 644 645 // Set up a layers to be loaded for RELEASE app 646 applySetting("enable_gpu_debug_layers", "1"); 647 applySetting("gpu_debug_app", RELEASE_APP); 648 applySetting("gpu_debug_layers_gles", GLES_LAYER_A_LIB + ":" + GLES_LAYER_B_LIB); 649 deleteSetting("gpu_debug_layer_app"); 650 651 // Copy a layer from our LAYERS APK to tmp 652 setupLayer(GLES_LAYER_A_LIB, GLES_LAYERS_APP); 653 654 // Attempt to copy them over to our RELEASE app (this should fail) 655 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_A_LIB, "|", "run-as", RELEASE_APP, 656 "sh", "-c", "\'cat", ">", GLES_LAYER_A_LIB, ";", "chmod", "700", GLES_LAYER_A_LIB + "\'", "||", "echo", "run-as", "failed"); 657 658 // Kick off our RELEASE app 659 String appStartTime = getTime(); 660 getDevice().executeAdbCommand("shell", "am", "start", "-n", RELEASE_APP + "/" + ACTIVITY); 661 662 // Ensure we don't load the layer in base dir 663 String searchStringA = GLES_LAYER_A + " loaded"; 664 LogScanResult resultA = scanLog(TAG + "," + GLES_LAYER_A, searchStringA, appStartTime); 665 Assert.assertFalse(GLES_LAYER_A + " was enumerated", resultA.found); 666 } 667 668 /** 669 * This test ensures debuggable GLES apps do not enumerate layers in base 670 * directory if enable_gpu_debug_layers is not enabled. 671 */ 672 @Test testDebugNotEnabledGLES()673 public void testDebugNotEnabledGLES() throws Exception { 674 675 // Ensure the global layer enable settings is NOT enabled 676 applySetting("enable_gpu_debug_layers", "0"); 677 applySetting("gpu_debug_app", DEBUG_APP); 678 applySetting("gpu_debug_layers_gles", GLES_LAYER_A_LIB); 679 680 // Copy a layer from our LAYERS APK to tmp 681 setupLayer(GLES_LAYER_A_LIB, GLES_LAYERS_APP); 682 683 // Copy it over to our DEBUG app 684 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_A_LIB, "|", "run-as", DEBUG_APP, 685 "sh", "-c", "\'cat", ">", GLES_LAYER_A_LIB, ";", "chmod", "700", GLES_LAYER_A_LIB + "\'"); 686 687 // Kick off our DEBUG app 688 String appStartTime = getTime(); 689 getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY); 690 691 // Ensure we don't load the layer in base dir 692 String searchStringA = GLES_LAYER_A + " loaded"; 693 LogScanResult resultA = scanLog(TAG + "," + GLES_LAYER_A, searchStringA, appStartTime); 694 Assert.assertFalse(GLES_LAYER_A + " was enumerated", resultA.found); 695 } 696 697 /** 698 * This test ensures debuggable GLES apps do not enumerate layers in base 699 * directory if gpu_debug_app does not match. 700 */ 701 @Test testDebugWrongAppGLES()702 public void testDebugWrongAppGLES() throws Exception { 703 704 // Ensure the gpu_debug_app does not match what we launch 705 applySetting("enable_gpu_debug_layers", "1"); 706 applySetting("gpu_debug_app", RELEASE_APP); 707 applySetting("gpu_debug_layers_gles", GLES_LAYER_A_LIB); 708 709 // Copy a layer from our LAYERS APK to tmp 710 setupLayer(GLES_LAYER_A_LIB, GLES_LAYERS_APP); 711 712 // Copy it over to our DEBUG app 713 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_A_LIB, "|", "run-as", DEBUG_APP, 714 "sh", "-c", "\'cat", ">", GLES_LAYER_A_LIB, ";", "chmod", "700", GLES_LAYER_A_LIB + "\'"); 715 716 // Kick off our DEBUG app 717 String appStartTime = getTime(); 718 getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY); 719 720 // Ensure we don't load the layer in base dir 721 String searchStringA = GLES_LAYER_A + " loaded"; 722 LogScanResult resultA = scanLog(TAG + "," + GLES_LAYER_A, searchStringA, appStartTime); 723 Assert.assertFalse(GLES_LAYER_A + " was enumerated", resultA.found); 724 } 725 726 /** 727 * This test ensures debuggable GLES apps do not enumerate layers in base 728 * directory if gpu_debug_layers are not set. 729 */ 730 @Test testDebugNoLayersEnabledGLES()731 public void testDebugNoLayersEnabledGLES() throws Exception { 732 733 // Ensure the global layer enable settings is NOT enabled 734 applySetting("enable_gpu_debug_layers", "1"); 735 applySetting("gpu_debug_app", DEBUG_APP); 736 applySetting("gpu_debug_layers_gles", "foo"); 737 738 // Copy a layer from our LAYERS APK to tmp 739 setupLayer(GLES_LAYER_A_LIB, GLES_LAYERS_APP); 740 741 // Copy it over to our DEBUG app 742 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_A_LIB, "|", "run-as", DEBUG_APP, 743 "sh", "-c", "\'cat", ">", GLES_LAYER_A_LIB, ";", "chmod", "700", GLES_LAYER_A_LIB + "\'"); 744 745 // Kick off our DEBUG app 746 String appStartTime = getTime(); 747 getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY); 748 749 // Ensure layerA is not loaded 750 String searchStringA = "glesLayer_eglChooseConfig called in " + GLES_LAYER_A; 751 LogScanResult resultA = scanLog(TAG + "," + GLES_LAYER_A, searchStringA, appStartTime); 752 Assert.assertFalse(GLES_LAYER_A + " was loaded", resultA.found); 753 } 754 755 /** 756 * This test ensures we can still use properties if no GLES layers are specified 757 */ 758 @Test testSystemPropertyEnableGLES()759 public void testSystemPropertyEnableGLES() throws Exception { 760 761 // Set up layerA to be loaded, but not layerB or layerC 762 applySetting("enable_gpu_debug_layers", "1"); 763 applySetting("gpu_debug_app", RELEASE_APP); 764 deleteSetting("gpu_debug_layers_gles"); 765 766 // Enable layerC (which is packaged with the RELEASE app) with system properties 767 getDevice().executeAdbCommand("shell", "setprop", "debug.gles.layers " + GLES_LAYER_C_LIB); 768 769 // Kick off our RELEASE app 770 String appStartTime = getTime(); 771 getDevice().executeAdbCommand("shell", "am", "start", "-n", RELEASE_APP + "/" + ACTIVITY); 772 773 // Check that both layers were loaded, in the correct order 774 String searchStringA = GLES_LAYER_A + "loaded"; 775 LogScanResult resultA = scanLog(TAG + "," + GLES_LAYER_A, searchStringA, appStartTime); 776 Assert.assertFalse(GLES_LAYER_A + " was enumerated", resultA.found); 777 778 String searchStringC = "glesLayer_eglChooseConfig called in " + GLES_LAYER_C; 779 LogScanResult resultC = scanLog(TAG + "," + GLES_LAYER_C, searchStringC, appStartTime); 780 Assert.assertTrue(GLES_LAYER_C + " was not loaded", resultC.found); 781 } 782 783 /** 784 * This test ensures system properties are ignored if Settings load a GLES layer 785 */ 786 @Test testSystemPropertyIgnoreGLES()787 public void testSystemPropertyIgnoreGLES() throws Exception { 788 789 // Set up layerA to be loaded, but not layerB 790 applySetting("enable_gpu_debug_layers", "1"); 791 applySetting("gpu_debug_app", DEBUG_APP); 792 applySetting("gpu_debug_layers_gles", GLES_LAYER_A_LIB); 793 794 // Copy the layers from our LAYERS APK 795 setupLayer(GLES_LAYER_A_LIB, GLES_LAYERS_APP); 796 setupLayer(GLES_LAYER_B_LIB, GLES_LAYERS_APP); 797 798 // Copy them over to our DEBUG app 799 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_A_LIB, "|", 800 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()), 801 "sh", "-c", "\'cat", ">", GLES_LAYER_A_LIB, ";", "chmod", "700", GLES_LAYER_A_LIB + "\'"); 802 getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + GLES_LAYER_B_LIB, "|", 803 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()), 804 "sh", "-c", "\'cat", ">", GLES_LAYER_B_LIB, ";", "chmod", "700", GLES_LAYER_B_LIB + "\'"); 805 806 // Enable layerB with system properties 807 getDevice().executeAdbCommand("shell", "setprop", "debug.gles.layers " + GLES_LAYER_B_LIB); 808 809 // Kick off our DEBUG app 810 String appStartTime = getTime(); 811 getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY); 812 813 // Ensure only layerA is loaded 814 String searchStringA = "glesLayer_eglChooseConfig called in " + GLES_LAYER_A; 815 LogScanResult resultA = scanLog(TAG + "," + GLES_LAYER_A, searchStringA, appStartTime); 816 Assert.assertTrue(GLES_LAYER_A + " was not loaded", resultA.found); 817 818 String searchStringB = "glesLayer_eglChooseConfig called in " + GLES_LAYER_B; 819 LogScanResult resultB = scanLog(TAG + "," + GLES_LAYER_B, searchStringB, appStartTime); 820 Assert.assertFalse(GLES_LAYER_B + " was loaded", resultB.found); 821 } 822 testLayerLoadExternalGLES(final String APP_NAME)823 public void testLayerLoadExternalGLES(final String APP_NAME) throws Exception { 824 // Set up layers to be loaded 825 applySetting("enable_gpu_debug_layers", "1"); 826 applySetting("gpu_debug_app", APP_NAME); 827 applySetting("gpu_debug_layers_gles", GLES_LAYER_C_LIB); 828 829 // Specify the external app that hosts layers 830 applySetting("gpu_debug_layer_app", GLES_LAYERS_APP); 831 832 // Kick off our DEBUG app 833 String appStartTime = getTime(); 834 getDevice().executeAdbCommand("shell", "am", "start", "-n", APP_NAME + "/" + ACTIVITY); 835 836 // Check that our external layer was loaded 837 String searchStringC = "glesLayer_eglChooseConfig called in " + GLES_LAYER_C; 838 LogScanResult resultC = scanLog(TAG + "," + GLES_LAYER_C, searchStringC, appStartTime); 839 Assert.assertTrue(GLES_LAYER_C + " was not loaded", resultC.found); 840 } 841 842 /** 843 * This test ensures that external GLES layers can be loaded by a debuggable app 844 */ 845 @Test testDebugLayerLoadExternalGLES()846 public void testDebugLayerLoadExternalGLES() throws Exception { 847 testLayerLoadExternalGLES(DEBUG_APP); 848 } 849 850 /** 851 * This test ensures that external GLES layers can be loaded by an injectLayers app 852 */ 853 @Test testInjectLayerLoadExternalGLES()854 public void testInjectLayerLoadExternalGLES() throws Exception { 855 testLayerLoadExternalGLES(INJECT_APP); 856 } 857 858 /** 859 * 860 */ 861 @Test testMultipleExternalApps()862 public void testMultipleExternalApps() throws Exception { 863 864 // Set up layers to be loaded 865 applySetting("enable_gpu_debug_layers", "1"); 866 applySetting("gpu_debug_app", DEBUG_APP); 867 applySetting("gpu_debug_layers", VK_LAYER_C); 868 applySetting("gpu_debug_layers_gles", GLES_LAYER_C_LIB); 869 870 // Specify multple external apps that host layers 871 applySetting("gpu_debug_layer_app", LAYERS_APP + ":" + GLES_LAYERS_APP); 872 873 // Kick off our DEBUG app 874 String appStartTime = getTime(); 875 getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY); 876 877 // Check that external layers were loaded from both apps 878 assertVkLayerLoading(appStartTime, VK_LAYER_C, true); 879 880 String glesString = "glesLayer_eglChooseConfig called in " + GLES_LAYER_C; 881 LogScanResult glesResult = scanLog(TAG + "," + GLES_LAYER_C, glesString, appStartTime); 882 Assert.assertTrue(GLES_LAYER_C + " was not loaded", glesResult.found); 883 } 884 } 885