blob: 5f656207255a7ffe6a98022a8658392c5fad19f3 [file] [log] [blame]
Jesse Hall79bf3922016-12-12 12:53:02 -08001/*
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
17package android.os;
18
19import android.content.Context;
20import android.content.pm.ApplicationInfo;
21import android.content.pm.PackageManager;
Jesse Hallfd104e72017-01-19 17:59:08 -080022import android.opengl.EGL14;
Jesse Hall0e72f132017-05-08 11:28:01 -070023import android.os.Build;
Jesse Hall79bf3922016-12-12 12:53:02 -080024import android.os.SystemProperties;
Cody Northrop86cedcb2017-10-20 09:03:13 -060025import android.provider.Settings;
Jesse Hall79bf3922016-12-12 12:53:02 -080026import android.util.Log;
27
28import dalvik.system.VMRuntime;
29
30import java.io.File;
31
32/** @hide */
Cody Northrop86cedcb2017-10-20 09:03:13 -060033public class GraphicsEnvironment {
34
35 private static final GraphicsEnvironment sInstance = new GraphicsEnvironment();
36
37 /**
38 * Returns the shared {@link GraphicsEnvironment} instance.
39 */
40 public static GraphicsEnvironment getInstance() {
41 return sInstance;
42 }
Jesse Hall79bf3922016-12-12 12:53:02 -080043
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";
Cody Northrop73c05042018-04-16 13:23:51 -060047 private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
Cody Northrop841c3d92018-08-29 16:36:12 -060048 private static final String GLES_MODE_METADATA_KEY = "com.android.angle.GLES_MODE";
Jesse Hall79bf3922016-12-12 12:53:02 -080049
Cody Northrop86cedcb2017-10-20 09:03:13 -060050 private ClassLoader mClassLoader;
51 private String mLayerPath;
52 private String mDebugLayerPath;
53
54 /**
55 * Set up GraphicsEnvironment
56 */
Cody Northropdeb43282018-10-04 16:04:05 -060057 public void setup(Context context, Bundle coreSettings) {
Cody Northrop2653e622018-10-12 14:32:11 -060058 setupGpuLayers(context);
Cody Northropdeb43282018-10-04 16:04:05 -060059 setupAngle(context, coreSettings);
Cody Northrop86cedcb2017-10-20 09:03:13 -060060 chooseDriver(context);
61 }
62
63 /**
64 * Check whether application is debuggable
65 */
66 private static boolean isDebuggable(Context context) {
67 return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) > 0;
68 }
69
70 /**
71 * Store the layer paths available to the loader.
72 */
73 public void setLayerPaths(ClassLoader classLoader,
74 String layerPath,
75 String debugLayerPath) {
76 // We have to store these in the class because they are set up before we
77 // have access to the Context to properly set up GraphicsEnvironment
78 mClassLoader = classLoader;
79 mLayerPath = layerPath;
80 mDebugLayerPath = debugLayerPath;
81 }
82
83 /**
84 * Set up layer search paths for all apps
85 * If debuggable, check for additional debug settings
86 */
Cody Northrop2653e622018-10-12 14:32:11 -060087 private void setupGpuLayers(Context context) {
Cody Northrop86cedcb2017-10-20 09:03:13 -060088
89 String layerPaths = "";
90
91 // Only enable additional debug functionality if the following conditions are met:
Cody Northrop2653e622018-10-12 14:32:11 -060092 // 1. App is debuggable
Cody Northrop86cedcb2017-10-20 09:03:13 -060093 // 2. ENABLE_GPU_DEBUG_LAYERS is true
94 // 3. Package name is equal to GPU_DEBUG_APP
95
Cody Northrop2653e622018-10-12 14:32:11 -060096 if (isDebuggable(context)) {
Cody Northrop86cedcb2017-10-20 09:03:13 -060097
Cody Northrop2653e622018-10-12 14:32:11 -060098 int enable = Settings.Global.getInt(context.getContentResolver(),
99 Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
Cody Northrop86cedcb2017-10-20 09:03:13 -0600100
101 if (enable != 0) {
102
Cody Northrop2653e622018-10-12 14:32:11 -0600103 String gpuDebugApp = Settings.Global.getString(context.getContentResolver(),
104 Settings.Global.GPU_DEBUG_APP);
Cody Northrop86cedcb2017-10-20 09:03:13 -0600105
106 String packageName = context.getPackageName();
107
108 if ((gpuDebugApp != null && packageName != null)
109 && (!gpuDebugApp.isEmpty() && !packageName.isEmpty())
110 && gpuDebugApp.equals(packageName)) {
111 Log.i(TAG, "GPU debug layers enabled for " + packageName);
112
113 // Prepend the debug layer path as a searchable path.
114 // This will ensure debug layers added will take precedence over
115 // the layers specified by the app.
116 layerPaths = mDebugLayerPath + ":";
117
Cody Northrop2653e622018-10-12 14:32:11 -0600118 String layers = Settings.Global.getString(context.getContentResolver(),
119 Settings.Global.GPU_DEBUG_LAYERS);
Cody Northrop86cedcb2017-10-20 09:03:13 -0600120
121 Log.i(TAG, "Debug layer list: " + layers);
122 if (layers != null && !layers.isEmpty()) {
123 setDebugLayers(layers);
124 }
125 }
126 }
Cody Northrop86cedcb2017-10-20 09:03:13 -0600127 }
128
129 // Include the app's lib directory in all cases
130 layerPaths += mLayerPath;
131
132 setLayerPaths(mClassLoader, layerPaths);
133 }
134
Jesse Hallc37984f2017-05-23 16:55:08 -0700135 /**
Cody Northropeb0ca8e2018-08-09 16:01:08 -0600136 * Pass ANGLE details down to trigger enable logic
Cody Northrop73c05042018-04-16 13:23:51 -0600137 */
Cody Northropdeb43282018-10-04 16:04:05 -0600138 private static void setupAngle(Context context, Bundle coreSettings) {
Cody Northrop73c05042018-04-16 13:23:51 -0600139
140 String angleEnabledApp =
Cody Northropdeb43282018-10-04 16:04:05 -0600141 coreSettings.getString(Settings.Global.ANGLE_ENABLED_APP);
Cody Northrop73c05042018-04-16 13:23:51 -0600142
143 String packageName = context.getPackageName();
144
Cody Northrop841c3d92018-08-29 16:36:12 -0600145 boolean devOptIn = false;
Cody Northrop73c05042018-04-16 13:23:51 -0600146 if ((angleEnabledApp != null && packageName != null)
147 && (!angleEnabledApp.isEmpty() && !packageName.isEmpty())
148 && angleEnabledApp.equals(packageName)) {
149
Cody Northropeb0ca8e2018-08-09 16:01:08 -0600150 if (DEBUG) Log.v(TAG, packageName + " opted in for ANGLE via Developer Setting");
Cody Northrop73c05042018-04-16 13:23:51 -0600151
Cody Northrop841c3d92018-08-29 16:36:12 -0600152 devOptIn = true;
153 }
154
155 ApplicationInfo appInfo;
156 try {
157 appInfo = context.getPackageManager().getApplicationInfo(packageName,
158 PackageManager.GET_META_DATA);
159 } catch (PackageManager.NameNotFoundException e) {
160 Log.w(TAG, "Failed to get info about current application: " + packageName);
161 return;
162 }
163
164 String appPref = "dontcare";
165 final BaseBundle metadata = appInfo.metaData;
166 if (metadata != null) {
167 final String glesMode = metadata.getString(GLES_MODE_METADATA_KEY);
168 if (glesMode != null) {
169 if (glesMode.equals("angle")) {
170 appPref = "angle";
171 if (DEBUG) Log.v(TAG, packageName + " opted for ANGLE via AndroidManifest");
172 } else if (glesMode.equals("native")) {
173 appPref = "native";
174 if (DEBUG) Log.v(TAG, packageName + " opted for NATIVE via AndroidManifest");
175 } else {
176 Log.w(TAG, "Unrecognized GLES_MODE (\"" + glesMode + "\") for " + packageName
177 + ". Supported values are \"angle\" or \"native\"");
178 }
179 }
Cody Northrop73c05042018-04-16 13:23:51 -0600180 }
Cody Northropeb0ca8e2018-08-09 16:01:08 -0600181
182 ApplicationInfo angleInfo;
Cody Northropeb0ca8e2018-08-09 16:01:08 -0600183 try {
184 angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME,
185 PackageManager.MATCH_SYSTEM_ONLY);
186 } catch (PackageManager.NameNotFoundException e) {
187 Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed");
188 return;
189 }
190
191 String abi = chooseAbi(angleInfo);
192
193 // Build a path that includes installed native libs and APK
194 StringBuilder sb = new StringBuilder();
195 sb.append(angleInfo.nativeLibraryDir)
196 .append(File.pathSeparator)
197 .append(angleInfo.sourceDir)
198 .append("!/lib/")
199 .append(abi);
200 String paths = sb.toString();
201
202 if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
203
204 // Further opt-in logic is handled in native, so pass relevant info down
Cody Northrop841c3d92018-08-29 16:36:12 -0600205 setAngleInfo(paths, packageName, appPref, devOptIn);
Cody Northrop73c05042018-04-16 13:23:51 -0600206 }
207
208 /**
Jesse Hallc37984f2017-05-23 16:55:08 -0700209 * Choose whether the current process should use the builtin or an updated driver.
Jesse Hallc37984f2017-05-23 16:55:08 -0700210 */
Cody Northrop86cedcb2017-10-20 09:03:13 -0600211 private static void chooseDriver(Context context) {
Jesse Hall79bf3922016-12-12 12:53:02 -0800212 String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
213 if (driverPackageName == null || driverPackageName.isEmpty()) {
214 return;
215 }
216 // To minimize risk of driver updates crippling the device beyond user repair, never use an
217 // updated driver for privileged or non-updated system apps. Presumably pre-installed apps
218 // were tested thoroughly with the pre-installed driver.
219 ApplicationInfo ai = context.getApplicationInfo();
220 if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
221 if (DEBUG) Log.v(TAG, "ignoring driver package for privileged/non-updated system app");
222 return;
223 }
224 ApplicationInfo driverInfo;
225 try {
226 driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName,
227 PackageManager.MATCH_SYSTEM_ONLY);
228 } catch (PackageManager.NameNotFoundException e) {
229 Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
230 return;
231 }
232 String abi = chooseAbi(driverInfo);
233 if (abi == null) {
234 if (DEBUG) {
235 // This is the normal case for the pre-installed empty driver package, don't spam
236 if (driverInfo.isUpdatedSystemApp()) {
237 Log.w(TAG, "updated driver package has no compatible native libraries");
238 }
239 }
240 return;
241 }
Jesse Hall0e72f132017-05-08 11:28:01 -0700242 if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
243 // O drivers are restricted to the sphal linker namespace, so don't try to use
244 // packages unless they declare they're compatible with that restriction.
245 Log.w(TAG, "updated driver package is not known to be compatible with O");
246 return;
247 }
Jesse Hall79bf3922016-12-12 12:53:02 -0800248
249 StringBuilder sb = new StringBuilder();
250 sb.append(driverInfo.nativeLibraryDir)
251 .append(File.pathSeparator);
252 sb.append(driverInfo.sourceDir)
253 .append("!/lib/")
254 .append(abi);
255 String paths = sb.toString();
256
257 if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths);
258 setDriverPath(paths);
259 }
260
Jesse Hallc37984f2017-05-23 16:55:08 -0700261 /**
262 * Start a background thread to initialize EGL.
263 *
264 * Initializing EGL involves loading and initializing the graphics driver. Some drivers take
265 * several 10s of milliseconds to do this, so doing it on-demand when an app tries to render
266 * its first frame adds directly to user-visible app launch latency. By starting it earlier
267 * on a separate thread, it can usually be finished well before the UI is ready to be drawn.
268 *
269 * Should only be called after chooseDriver().
Jesse Hallc37984f2017-05-23 16:55:08 -0700270 */
271 public static void earlyInitEGL() {
Jesse Hall317fa5a2017-05-23 15:46:55 -0700272 Thread eglInitThread = new Thread(
273 () -> {
274 EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
275 },
276 "EGL Init");
277 eglInitThread.start();
278 }
279
Jesse Hall79bf3922016-12-12 12:53:02 -0800280 private static String chooseAbi(ApplicationInfo ai) {
281 String isa = VMRuntime.getCurrentInstructionSet();
282 if (ai.primaryCpuAbi != null &&
283 isa.equals(VMRuntime.getInstructionSet(ai.primaryCpuAbi))) {
284 return ai.primaryCpuAbi;
285 }
286 if (ai.secondaryCpuAbi != null &&
287 isa.equals(VMRuntime.getInstructionSet(ai.secondaryCpuAbi))) {
288 return ai.secondaryCpuAbi;
289 }
290 return null;
291 }
292
Cody Northrop86cedcb2017-10-20 09:03:13 -0600293 private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
294 private static native void setDebugLayers(String layers);
Jesse Hall79bf3922016-12-12 12:53:02 -0800295 private static native void setDriverPath(String path);
Cody Northrop841c3d92018-08-29 16:36:12 -0600296 private static native void setAngleInfo(String path, String appPackage, String appPref,
297 boolean devOptIn);
Jesse Hall79bf3922016-12-12 12:53:02 -0800298}