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