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