From eb1c5a4c28ec7cd2577e7182ed249a8ec4cc4c0b Mon Sep 17 00:00:00 2001 From: Peiyong Lin Date: Tue, 2 Jun 2020 00:56:14 -0700 Subject: [PATCH] Allow to load implicit layer from /vendor/app. Currently when loading implicit layers from apks, NativeLoaderNamespace doesn't allow to dlopen the binaries if they come from apks from /vendor/app. Implicit layers ship within /vendor/app should work like other implicit layers. This patch extracts the construction of library paths of the implicit layers and includes those paths when NativeLoaderNamespace is created as the part of the permitted library paths. Bug: b/157832445 Test: atest android.gputools.cts.CtsRootlessGpuDebugHostTest Test: setup debug layer and use adb logcat to check vulkan loader output Change-Id: Ie2ca989bcab890578b5aa540d07f2aee2a0182bd --- core/java/android/app/ActivityThread.java | 16 ++ core/java/android/app/LoadedApk.java | 28 +++ core/java/android/os/GraphicsEnvironment.java | 170 ++++++++++-------- 3 files changed, 136 insertions(+), 78 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index cffa59c06a538..d275159e9f879 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -7388,6 +7388,10 @@ public final class ActivityThread extends ClientTransactionHandler { } } + public Bundle getCoreSettings() { + return mCoreSettings; + } + public int getIntCoreSetting(String key, int defaultValue) { synchronized (mResourcesManager) { if (mCoreSettings != null) { @@ -7397,6 +7401,18 @@ public final class ActivityThread extends ClientTransactionHandler { } } + /** + * Get the string value of the given key from core settings. + */ + public String getStringCoreSetting(String key, String defaultValue) { + synchronized (mResourcesManager) { + if (mCoreSettings != null) { + return mCoreSettings.getString(key, defaultValue); + } + return defaultValue; + } + } + float getFloatCoreSetting(String key, float defaultValue) { synchronized (mResourcesManager) { if (mCoreSettings != null) { diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 10f7835b3d693..f9b48e7101483 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -38,6 +38,7 @@ import android.content.res.Resources; import android.os.Build; import android.os.Bundle; import android.os.FileUtils; +import android.os.GraphicsEnvironment; import android.os.Handler; import android.os.IBinder; import android.os.Process; @@ -46,6 +47,7 @@ import android.os.StrictMode; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; +import android.provider.Settings; import android.security.net.config.NetworkSecurityConfigProvider; import android.sysprop.VndkProperties; import android.text.TextUtils; @@ -824,6 +826,32 @@ public final class LoadedApk { final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths); + if (mActivityThread != null) { + final String gpuDebugApp = mActivityThread.getStringCoreSetting( + Settings.Global.GPU_DEBUG_APP, ""); + if (!gpuDebugApp.isEmpty() && mPackageName.equals(gpuDebugApp)) { + + // The current application is used to debug, attempt to get the debug layers. + try { + // Get the ApplicationInfo from PackageManager so that metadata fields present. + final ApplicationInfo ai = ActivityThread.getPackageManager() + .getApplicationInfo(mPackageName, PackageManager.GET_META_DATA, + UserHandle.myUserId()); + final String debugLayerPath = GraphicsEnvironment.getInstance() + .getDebugLayerPathsFromSettings(mActivityThread.getCoreSettings(), + ActivityThread.getPackageManager(), mPackageName, ai); + if (debugLayerPath != null) { + libraryPermittedPath += File.pathSeparator + debugLayerPath; + } + } catch (RemoteException e) { + // Unlikely to fail for applications, but in case of failure, something is wrong + // inside the system server, hence just skip. + Slog.e(ActivityThread.TAG, + "RemoteException when fetching debug layer paths for: " + mPackageName); + } + } + } + // If we're not asked to include code, we construct a classloader that has // no code path included. We still need to set up the library search paths // and permitted path because NativeActivity relies on it (it attempts to diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 034e6a7a06c4d..df58a6c636f5a 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -22,6 +22,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -93,8 +94,8 @@ public class GraphicsEnvironment { private static final int GAME_DRIVER_GLOBAL_OPT_IN_OFF = 3; private ClassLoader mClassLoader; - private String mLayerPath; - private String mDebugLayerPath; + private String mLibrarySearchPaths; + private String mLibraryPermittedPaths; /** * Set up GraphicsEnvironment @@ -185,118 +186,131 @@ public class GraphicsEnvironment { } /** - * Store the layer paths available to the loader. + * Store the class loader for namespace lookup later. */ public void setLayerPaths(ClassLoader classLoader, - String layerPath, - String debugLayerPath) { + String searchPaths, + String permittedPaths) { // 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; + mLibrarySearchPaths = searchPaths; + mLibraryPermittedPaths = permittedPaths; + } + + /** + * Returns the debug layer paths from settings. + * Returns null if: + * 1) The application process is not debuggable or layer injection metadata flag is not + * true; Or + * 2) ENABLE_GPU_DEBUG_LAYERS is not true; Or + * 3) Package name is not equal to GPU_DEBUG_APP. + */ + public String getDebugLayerPathsFromSettings( + Bundle coreSettings, IPackageManager pm, String packageName, + ApplicationInfo ai) { + if (!debugLayerEnabled(coreSettings, packageName, ai)) { + return null; + } + Log.i(TAG, "GPU debug layers enabled for " + packageName); + String debugLayerPaths = ""; + + // Grab all debug layer apps and add to paths. + final String gpuDebugLayerApps = + coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP, ""); + if (!gpuDebugLayerApps.isEmpty()) { + Log.i(TAG, "GPU debug layer apps: " + gpuDebugLayerApps); + // If a colon is present, treat this as multiple apps, so Vulkan and GLES + // layer apps can be provided at the same time. + final String[] layerApps = gpuDebugLayerApps.split(":"); + for (int i = 0; i < layerApps.length; i++) { + String paths = getDebugLayerAppPaths(pm, layerApps[i]); + if (!paths.isEmpty()) { + // Append the path so files placed in the app's base directory will + // override the external path + debugLayerPaths += paths + File.pathSeparator; + } + } + } + return debugLayerPaths; } /** * Return the debug layer app's on-disk and in-APK lib directories */ - private static String getDebugLayerAppPaths(PackageManager pm, String app) { + private static String getDebugLayerAppPaths(IPackageManager pm, String packageName) { final ApplicationInfo appInfo; try { - appInfo = pm.getApplicationInfo(app, PackageManager.MATCH_ALL); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Debug layer app '" + app + "' not installed"); - - return null; + appInfo = pm.getApplicationInfo(packageName, PackageManager.MATCH_ALL, + UserHandle.myUserId()); + } catch (RemoteException e) { + return ""; + } + if (appInfo == null) { + Log.w(TAG, "Debug layer app '" + packageName + "' not installed"); } final String abi = chooseAbi(appInfo); - final StringBuilder sb = new StringBuilder(); sb.append(appInfo.nativeLibraryDir) - .append(File.pathSeparator); - sb.append(appInfo.sourceDir) + .append(File.pathSeparator) + .append(appInfo.sourceDir) .append("!/lib/") .append(abi); final String paths = sb.toString(); - if (DEBUG) Log.v(TAG, "Debug layer app libs: " + paths); return paths; } - /** - * Set up layer search paths for all apps - * If debuggable, check for additional debug settings - */ - private void setupGpuLayers( - Context context, Bundle coreSettings, PackageManager pm, String packageName, - ApplicationInfo ai) { - String layerPaths = ""; - + private boolean debugLayerEnabled(Bundle coreSettings, String packageName, ApplicationInfo ai) { // Only enable additional debug functionality if the following conditions are met: // 1. App is debuggable or device is rooted or layer injection metadata flag is true // 2. ENABLE_GPU_DEBUG_LAYERS is true // 3. Package name is equal to GPU_DEBUG_APP + if (!isDebuggable() && !canInjectLayers(ai)) { + return false; + } + final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0); + if (enable == 0) { + return false; + } + final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP, ""); + if (packageName == null + || (gpuDebugApp.isEmpty() || packageName.isEmpty()) + || !gpuDebugApp.equals(packageName)) { + return false; + } + return true; + } - if (isDebuggable() || canInjectLayers(ai)) { + /** + * Set up layer search paths for all apps + */ + private void setupGpuLayers( + Context context, Bundle coreSettings, PackageManager pm, String packageName, + ApplicationInfo ai) { + final boolean enabled = debugLayerEnabled(coreSettings, packageName, ai); + String layerPaths = ""; + if (enabled) { + layerPaths = mLibraryPermittedPaths; - final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0); + final String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS); + Log.i(TAG, "Vulkan debug layer list: " + layers); + if (layers != null && !layers.isEmpty()) { + setDebugLayers(layers); + } - if (enable != 0) { - - final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP); - - 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 + ":"; - - // If there is a debug layer app specified, add its path. - final String gpuDebugLayerApp = - coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP); - - if (gpuDebugLayerApp != null && !gpuDebugLayerApp.isEmpty()) { - Log.i(TAG, "GPU debug layer app: " + gpuDebugLayerApp); - // If a colon is present, treat this as multiple apps, so Vulkan and GLES - // layer apps can be provided at the same time. - String[] layerApps = gpuDebugLayerApp.split(":"); - for (int i = 0; i < layerApps.length; i++) { - String paths = getDebugLayerAppPaths(pm, layerApps[i]); - if (paths != null) { - // Append the path so files placed in the app's base directory will - // override the external path - layerPaths += paths + ":"; - } - } - } - - final String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS); - - Log.i(TAG, "Vulkan debug layer list: " + layers); - if (layers != null && !layers.isEmpty()) { - setDebugLayers(layers); - } - - final String layersGLES = - coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES); - - Log.i(TAG, "GLES debug layer list: " + layersGLES); - if (layersGLES != null && !layersGLES.isEmpty()) { - setDebugLayersGLES(layersGLES); - } - } + final String layersGLES = + coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES); + Log.i(TAG, "GLES debug layer list: " + layersGLES); + if (layersGLES != null && !layersGLES.isEmpty()) { + setDebugLayersGLES(layersGLES); } } // Include the app's lib directory in all cases - layerPaths += mLayerPath; - + layerPaths += mLibrarySearchPaths; setLayerPaths(mClassLoader, layerPaths); }