1 /* 2 * Copyright 2016 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 17 package android.os; 18 19 import android.content.Context; 20 import android.content.pm.ApplicationInfo; 21 import android.content.pm.PackageManager; 22 import android.opengl.EGL14; 23 import android.os.Build; 24 import android.os.SystemProperties; 25 import android.provider.Settings; 26 import android.util.Log; 27 28 import dalvik.system.VMRuntime; 29 30 import java.io.File; 31 32 /** @hide */ 33 public class GraphicsEnvironment { 34 35 private static final GraphicsEnvironment sInstance = new GraphicsEnvironment(); 36 37 /** 38 * Returns the shared {@link GraphicsEnvironment} instance. 39 */ getInstance()40 public static GraphicsEnvironment getInstance() { 41 return sInstance; 42 } 43 44 private static final boolean DEBUG = false; 45 private static final String TAG = "GraphicsEnvironment"; 46 private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; 47 48 private ClassLoader mClassLoader; 49 private String mLayerPath; 50 private String mDebugLayerPath; 51 52 /** 53 * Set up GraphicsEnvironment 54 */ setup(Context context)55 public void setup(Context context) { 56 setupGpuLayers(context); 57 chooseDriver(context); 58 } 59 60 /** 61 * Check whether application is debuggable 62 */ isDebuggable(Context context)63 private static boolean isDebuggable(Context context) { 64 return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) > 0; 65 } 66 67 /** 68 * Store the layer paths available to the loader. 69 */ setLayerPaths(ClassLoader classLoader, String layerPath, String debugLayerPath)70 public void setLayerPaths(ClassLoader classLoader, 71 String layerPath, 72 String debugLayerPath) { 73 // We have to store these in the class because they are set up before we 74 // have access to the Context to properly set up GraphicsEnvironment 75 mClassLoader = classLoader; 76 mLayerPath = layerPath; 77 mDebugLayerPath = debugLayerPath; 78 } 79 80 /** 81 * Set up layer search paths for all apps 82 * If debuggable, check for additional debug settings 83 */ setupGpuLayers(Context context)84 private void setupGpuLayers(Context context) { 85 86 String layerPaths = ""; 87 88 // Only enable additional debug functionality if the following conditions are met: 89 // 1. App is debuggable 90 // 2. ENABLE_GPU_DEBUG_LAYERS is true 91 // 3. Package name is equal to GPU_DEBUG_APP 92 93 if (isDebuggable(context)) { 94 95 int enable = Settings.Global.getInt(context.getContentResolver(), 96 Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0); 97 98 if (enable != 0) { 99 100 String gpuDebugApp = Settings.Global.getString(context.getContentResolver(), 101 Settings.Global.GPU_DEBUG_APP); 102 103 String packageName = context.getPackageName(); 104 105 if ((gpuDebugApp != null && packageName != null) 106 && (!gpuDebugApp.isEmpty() && !packageName.isEmpty()) 107 && gpuDebugApp.equals(packageName)) { 108 Log.i(TAG, "GPU debug layers enabled for " + packageName); 109 110 // Prepend the debug layer path as a searchable path. 111 // This will ensure debug layers added will take precedence over 112 // the layers specified by the app. 113 layerPaths = mDebugLayerPath + ":"; 114 115 String layers = Settings.Global.getString(context.getContentResolver(), 116 Settings.Global.GPU_DEBUG_LAYERS); 117 118 Log.i(TAG, "Debug layer list: " + layers); 119 if (layers != null && !layers.isEmpty()) { 120 setDebugLayers(layers); 121 } 122 } 123 } 124 125 } 126 127 // Include the app's lib directory in all cases 128 layerPaths += mLayerPath; 129 130 setLayerPaths(mClassLoader, layerPaths); 131 } 132 133 /** 134 * Choose whether the current process should use the builtin or an updated driver. 135 */ chooseDriver(Context context)136 private static void chooseDriver(Context context) { 137 String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER); 138 if (driverPackageName == null || driverPackageName.isEmpty()) { 139 return; 140 } 141 // To minimize risk of driver updates crippling the device beyond user repair, never use an 142 // updated driver for privileged or non-updated system apps. Presumably pre-installed apps 143 // were tested thoroughly with the pre-installed driver. 144 ApplicationInfo ai = context.getApplicationInfo(); 145 if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) { 146 if (DEBUG) Log.v(TAG, "ignoring driver package for privileged/non-updated system app"); 147 return; 148 } 149 ApplicationInfo driverInfo; 150 try { 151 driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName, 152 PackageManager.MATCH_SYSTEM_ONLY); 153 } catch (PackageManager.NameNotFoundException e) { 154 Log.w(TAG, "driver package '" + driverPackageName + "' not installed"); 155 return; 156 } 157 String abi = chooseAbi(driverInfo); 158 if (abi == null) { 159 if (DEBUG) { 160 // This is the normal case for the pre-installed empty driver package, don't spam 161 if (driverInfo.isUpdatedSystemApp()) { 162 Log.w(TAG, "updated driver package has no compatible native libraries"); 163 } 164 } 165 return; 166 } 167 if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) { 168 // O drivers are restricted to the sphal linker namespace, so don't try to use 169 // packages unless they declare they're compatible with that restriction. 170 Log.w(TAG, "updated driver package is not known to be compatible with O"); 171 return; 172 } 173 174 StringBuilder sb = new StringBuilder(); 175 sb.append(driverInfo.nativeLibraryDir) 176 .append(File.pathSeparator); 177 sb.append(driverInfo.sourceDir) 178 .append("!/lib/") 179 .append(abi); 180 String paths = sb.toString(); 181 182 if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths); 183 setDriverPath(paths); 184 } 185 186 /** 187 * Start a background thread to initialize EGL. 188 * 189 * Initializing EGL involves loading and initializing the graphics driver. Some drivers take 190 * several 10s of milliseconds to do this, so doing it on-demand when an app tries to render 191 * its first frame adds directly to user-visible app launch latency. By starting it earlier 192 * on a separate thread, it can usually be finished well before the UI is ready to be drawn. 193 * 194 * Should only be called after chooseDriver(). 195 */ earlyInitEGL()196 public static void earlyInitEGL() { 197 Thread eglInitThread = new Thread( 198 () -> { 199 EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); 200 }, 201 "EGL Init"); 202 eglInitThread.start(); 203 } 204 chooseAbi(ApplicationInfo ai)205 private static String chooseAbi(ApplicationInfo ai) { 206 String isa = VMRuntime.getCurrentInstructionSet(); 207 if (ai.primaryCpuAbi != null && 208 isa.equals(VMRuntime.getInstructionSet(ai.primaryCpuAbi))) { 209 return ai.primaryCpuAbi; 210 } 211 if (ai.secondaryCpuAbi != null && 212 isa.equals(VMRuntime.getInstructionSet(ai.secondaryCpuAbi))) { 213 return ai.secondaryCpuAbi; 214 } 215 return null; 216 } 217 setLayerPaths(ClassLoader classLoader, String layerPaths)218 private static native void setLayerPaths(ClassLoader classLoader, String layerPaths); setDebugLayers(String layers)219 private static native void setDebugLayers(String layers); setDriverPath(String path)220 private static native void setDriverPath(String path); 221 } 222