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,