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