Rootless GPU Debug

Add the ability to load GPU debug layers from the base
directory of debuggable applications.

This commit:
* Adds a new Setting to Developer options: "Enable GPU debug layers"
* Adds a new way to discover and specify GPU debug layers per app
* Moves much of the layer enabling logic into GraphicsEnvironment
* Removes the JNI component of ApplicationLoaders

Bug: 63708377
Test: Manual, CTS tests to follow
Change-Id: I7d33e9f835f49aa8d86e63bdb32037728bc8b6a4
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 21e454f..95c5fb5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5380,7 +5380,7 @@
             }
         }
 
-        GraphicsEnvironment.chooseDriver(context);
+        GraphicsEnvironment.getInstance().setup(context);
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index b7c1f4e..7257044 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -17,9 +17,12 @@
 package android.app;
 
 import android.os.Build;
+import android.os.GraphicsEnvironment;
 import android.os.Trace;
 import android.util.ArrayMap;
+
 import com.android.internal.os.ClassLoaderFactory;
+
 import dalvik.system.PathClassLoader;
 
 /** @hide */
@@ -72,8 +75,9 @@
 
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
-                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setupVulkanLayerPath");
-                setupVulkanLayerPath(classloader, librarySearchPath);
+                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setLayerPaths");
+                GraphicsEnvironment.getInstance().setLayerPaths(
+                        classloader, librarySearchPath, libraryPermittedPath);
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
                 mLoaders.put(cacheKey, classloader);
@@ -105,8 +109,6 @@
                               cacheKey, null /* classLoaderName */);
     }
 
-    private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath);
-
     /**
      * Adds a new path the classpath of the given loader.
      * @throws IllegalStateException if the provided class loader is not a {@link PathClassLoader}.
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 07c6055..f2e0bdd 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -22,6 +22,7 @@
 import android.opengl.EGL14;
 import android.os.Build;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.util.Log;
 
 import dalvik.system.VMRuntime;
@@ -29,18 +30,110 @@
 import java.io.File;
 
 /** @hide */
-public final class GraphicsEnvironment {
+public class GraphicsEnvironment {
+
+    private static final GraphicsEnvironment sInstance = new GraphicsEnvironment();
+
+    /**
+     * Returns the shared {@link GraphicsEnvironment} instance.
+     */
+    public static GraphicsEnvironment getInstance() {
+        return sInstance;
+    }
 
     private static final boolean DEBUG = false;
     private static final String TAG = "GraphicsEnvironment";
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
 
+    private ClassLoader mClassLoader;
+    private String mLayerPath;
+    private String mDebugLayerPath;
+
+    /**
+     * Set up GraphicsEnvironment
+     */
+    public void setup(Context context) {
+        setupGpuLayers(context);
+        chooseDriver(context);
+    }
+
+    /**
+     * Check whether application is debuggable
+     */
+    private static boolean isDebuggable(Context context) {
+        return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) > 0;
+    }
+
+    /**
+     * Store the layer paths available to the loader.
+     */
+    public void setLayerPaths(ClassLoader classLoader,
+                              String layerPath,
+                              String debugLayerPath) {
+        // We have to store these in the class because they are set up before we
+        // have access to the Context to properly set up GraphicsEnvironment
+        mClassLoader = classLoader;
+        mLayerPath = layerPath;
+        mDebugLayerPath = debugLayerPath;
+    }
+
+    /**
+     * Set up layer search paths for all apps
+     * If debuggable, check for additional debug settings
+     */
+    private void setupGpuLayers(Context context) {
+
+        String layerPaths = "";
+
+        // Only enable additional debug functionality if the following conditions are met:
+        // 1. App is debuggable
+        // 2. ENABLE_GPU_DEBUG_LAYERS is true
+        // 3. Package name is equal to GPU_DEBUG_APP
+
+        if (isDebuggable(context)) {
+
+            int enable = Settings.Global.getInt(context.getContentResolver(),
+                                                Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
+
+            if (enable != 0) {
+
+                String gpuDebugApp = Settings.Global.getString(context.getContentResolver(),
+                                                               Settings.Global.GPU_DEBUG_APP);
+
+                String packageName = context.getPackageName();
+
+                if ((gpuDebugApp != null && packageName != null)
+                        && (!gpuDebugApp.isEmpty() && !packageName.isEmpty())
+                        && gpuDebugApp.equals(packageName)) {
+                    Log.i(TAG, "GPU debug layers enabled for " + packageName);
+
+                    // Prepend the debug layer path as a searchable path.
+                    // This will ensure debug layers added will take precedence over
+                    // the layers specified by the app.
+                    layerPaths = mDebugLayerPath + ":";
+
+                    String layers = Settings.Global.getString(context.getContentResolver(),
+                                                              Settings.Global.GPU_DEBUG_LAYERS);
+
+                    Log.i(TAG, "Debug layer list: " + layers);
+                    if (layers != null && !layers.isEmpty()) {
+                        setDebugLayers(layers);
+                    }
+                }
+            }
+
+        }
+
+        // Include the app's lib directory in all cases
+        layerPaths += mLayerPath;
+
+        setLayerPaths(mClassLoader, layerPaths);
+    }
+
     /**
      * Choose whether the current process should use the builtin or an updated driver.
-     *
-     * @hide
      */
-    public static void chooseDriver(Context context) {
+    private static void chooseDriver(Context context) {
         String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
         if (driverPackageName == null || driverPackageName.isEmpty()) {
             return;
@@ -99,8 +192,6 @@
      * on a separate thread, it can usually be finished well before the UI is ready to be drawn.
      *
      * Should only be called after chooseDriver().
-     *
-     * @hide
      */
     public static void earlyInitEGL() {
         Thread eglInitThread = new Thread(
@@ -124,6 +215,7 @@
         return null;
     }
 
+    private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
+    private static native void setDebugLayers(String layers);
     private static native void setDriverPath(String path);
-
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 16e7f30..c945ceb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9844,6 +9844,27 @@
         public static final String WAIT_FOR_DEBUGGER = "wait_for_debugger";
 
         /**
+         * Allow GPU debug layers?
+         * 0 = no
+         * 1 = yes
+         * @hide
+         */
+        public static final String ENABLE_GPU_DEBUG_LAYERS = "enable_gpu_debug_layers";
+
+        /**
+         * App allowed to load GPU debug layers
+         * @hide
+         */
+        public static final String GPU_DEBUG_APP = "gpu_debug_app";
+
+        /**
+         * Ordered GPU debug layer list
+         * i.e. <layer1>:<layer2>:...:<layerN>
+         * @hide
+         */
+        public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers";
+
+        /**
          * Control whether the process CPU usage meter should be shown.
          *
          * @deprecated This functionality is no longer available as of
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 3552e43..9d036dc 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -42,7 +42,6 @@
         "com_google_android_gles_jni_EGLImpl.cpp",
         "com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
         "android_app_Activity.cpp",
-        "android_app_ApplicationLoaders.cpp",
         "android_app_NativeActivity.cpp",
         "android_app_admin_SecurityLog.cpp",
         "android_opengl_EGL14.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 8977891..2a65cde 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -187,7 +187,6 @@
 extern int register_android_app_backup_FullBackup(JNIEnv *env);
 extern int register_android_app_Activity(JNIEnv *env);
 extern int register_android_app_ActivityThread(JNIEnv *env);
-extern int register_android_app_ApplicationLoaders(JNIEnv *env);
 extern int register_android_app_NativeActivity(JNIEnv *env);
 extern int register_android_media_RemoteDisplay(JNIEnv *env);
 extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
@@ -1456,7 +1455,6 @@
     REG_JNI(register_android_app_backup_FullBackup),
     REG_JNI(register_android_app_Activity),
     REG_JNI(register_android_app_ActivityThread),
-    REG_JNI(register_android_app_ApplicationLoaders),
     REG_JNI(register_android_app_NativeActivity),
     REG_JNI(register_android_util_jar_StrictJarFile),
     REG_JNI(register_android_view_InputChannel),
diff --git a/core/jni/android_app_ApplicationLoaders.cpp b/core/jni/android_app_ApplicationLoaders.cpp
deleted file mode 100644
index 8bbf24a..0000000
--- a/core/jni/android_app_ApplicationLoaders.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "ApplicationLoaders"
-
-#include <nativehelper/ScopedUtfChars.h>
-#include <nativeloader/native_loader.h>
-#include <vulkan/vulkan_loader_data.h>
-
-#include "core_jni_helpers.h"
-
-static void setupVulkanLayerPath_native(JNIEnv* env, jobject clazz,
-        jobject classLoader, jstring librarySearchPath) {
-    android_namespace_t* ns = android::FindNamespaceByClassLoader(env, classLoader);
-    ScopedUtfChars layerPathChars(env, librarySearchPath);
-
-    vulkan::LoaderData& loader_data = vulkan::LoaderData::GetInstance();
-    if (loader_data.layer_path.empty()) {
-        loader_data.layer_path = layerPathChars.c_str();
-        loader_data.app_namespace = ns;
-    } else {
-        ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'",
-                layerPathChars.c_str(), ns);
-    }
-}
-
-static const JNINativeMethod g_methods[] = {
-    { "setupVulkanLayerPath", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V",
-      reinterpret_cast<void*>(setupVulkanLayerPath_native) },
-};
-
-static const char* const kApplicationLoadersName = "android/app/ApplicationLoaders";
-
-namespace android
-{
-
-int register_android_app_ApplicationLoaders(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, kApplicationLoadersName, g_methods, NELEM(g_methods));
-}
-
-} // namespace android
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index f749488..4ecfd4b 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -18,6 +18,7 @@
 
 #include <graphicsenv/GraphicsEnv.h>
 #include <nativehelper/ScopedUtfChars.h>
+#include <nativeloader/native_loader.h>
 #include "core_jni_helpers.h"
 
 namespace {
@@ -27,8 +28,23 @@
     android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
 }
 
+void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
+    android_namespace_t* appNamespace = android::FindNamespaceByClassLoader(env, classLoader);
+    ScopedUtfChars layerPathsChars(env, layerPaths);
+    android::GraphicsEnv::getInstance().setLayerPaths(appNamespace, layerPathsChars.c_str());
+}
+
+void setDebugLayers_native(JNIEnv* env, jobject clazz, jstring layers) {
+    if (layers != nullptr) {
+        ScopedUtfChars layersChars(env, layers);
+        android::GraphicsEnv::getInstance().setDebugLayers(layersChars.c_str());
+    }
+}
+
 const JNINativeMethod g_methods[] = {
     { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
+    { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
+    { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
 };
 
 const char* const kGraphicsEnvironmentName = "android/os/GraphicsEnvironment";
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 764288c..5d5aea2 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -305,6 +305,9 @@
     optional SettingProto preferred_network_mode = 226;
     optional SettingProto debug_app = 227;
     optional SettingProto wait_for_debugger = 228;
+    optional SettingProto enable_gpu_debug_layers = 342;
+    optional SettingProto gpu_debug_app = 343;
+    optional SettingProto gpu_debug_layers = 344;
     optional SettingProto low_power_mode = 229;
     optional SettingProto low_power_mode_trigger_level = 230;
     optional SettingProto always_finish_activities = 231;
@@ -384,7 +387,7 @@
     optional SettingProto enable_deletion_helper_no_threshold_toggle = 340;
     optional SettingProto notification_snooze_options = 341;
 
-    // Next tag = 342;
+    // Next tag = 345;
 }
 
 message SecureSettingsProto {
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index d36ed63..0d35f4e 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -356,6 +356,9 @@
                     Settings.Global.USE_GOOGLE_MAIL,
                     Settings.Global.VT_IMS_ENABLED,
                     Settings.Global.WAIT_FOR_DEBUGGER,
+                    Settings.Global.ENABLE_GPU_DEBUG_LAYERS,
+                    Settings.Global.GPU_DEBUG_APP,
+                    Settings.Global.GPU_DEBUG_LAYERS,
                     Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
                     Settings.Global.WARNING_TEMPERATURE,
                     Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,