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