• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app.Activity;
20 import android.app.GameManager;
21 import android.content.BroadcastReceiver;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.IPackageManager;
27 import android.content.pm.PackageInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ResolveInfo;
30 import android.provider.Settings;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.widget.Toast;
34 
35 import dalvik.system.VMRuntime;
36 
37 import java.io.BufferedReader;
38 import java.io.File;
39 import java.io.IOException;
40 import java.io.InputStreamReader;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.List;
44 
45 /**
46  * GraphicsEnvironment sets up necessary properties for the graphics environment of the
47  * application process.
48  * GraphicsEnvironment uses a bunch of settings global variables to determine the setup,
49  * the change of settings global variables will only take effect before setup() is called,
50  * and any subsequent change will not impact the current running processes.
51  *
52  * @hide
53  */
54 public class GraphicsEnvironment {
55 
56     private static final GraphicsEnvironment sInstance = new GraphicsEnvironment();
57 
58     /**
59      * Returns the shared {@link GraphicsEnvironment} instance.
60      */
getInstance()61     public static GraphicsEnvironment getInstance() {
62         return sInstance;
63     }
64 
65     private static final boolean DEBUG = false;
66     private static final String TAG = "GraphicsEnvironment";
67     private static final String SYSTEM_DRIVER_NAME = "system";
68     private static final String SYSTEM_DRIVER_VERSION_NAME = "";
69     private static final long SYSTEM_DRIVER_VERSION_CODE = 0;
70     private static final String ANGLE_DRIVER_NAME = "angle";
71     private static final String ANGLE_DRIVER_VERSION_NAME = "";
72     private static final long ANGLE_DRIVER_VERSION_CODE = 0;
73 
74     // System properties related to updatable graphics drivers.
75     private static final String PROPERTY_GFX_DRIVER_PRODUCTION = "ro.gfx.driver.0";
76     private static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1";
77     private static final String PROPERTY_GFX_DRIVER_BUILD_TIME = "ro.gfx.driver_build_time";
78 
79     // Metadata flags within the <application> tag in the AndroidManifest.xml file.
80     private static final String METADATA_DRIVER_BUILD_TIME =
81             "com.android.graphics.driver.build_time";
82     private static final String METADATA_DEVELOPER_DRIVER_ENABLE =
83             "com.android.graphics.developerdriver.enable";
84     private static final String METADATA_INJECT_LAYERS_ENABLE =
85             "com.android.graphics.injectLayers.enable";
86 
87     private static final String UPDATABLE_DRIVER_ALLOWLIST_ALL = "*";
88     private static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
89 
90     private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
91     private static final String ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE =
92             "android.app.action.ANGLE_FOR_ANDROID_TOAST_MESSAGE";
93     private static final String INTENT_KEY_A4A_TOAST_MESSAGE = "A4A Toast Message";
94 
95     private static final int VULKAN_1_0 = 0x00400000;
96     private static final int VULKAN_1_1 = 0x00401000;
97     private static final int VULKAN_1_2 = 0x00402000;
98     private static final int VULKAN_1_3 = 0x00403000;
99 
100     // Values for UPDATABLE_DRIVER_ALL_APPS
101     // 0: Default (Invalid values fallback to default as well)
102     // 1: All apps use updatable production driver
103     // 2: All apps use updatable prerelease driver
104     // 3: All apps use system graphics driver
105     private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_DEFAULT = 0;
106     private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRODUCTION_DRIVER = 1;
107     private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER = 2;
108     private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF = 3;
109 
110     // Values for ANGLE_GL_DRIVER_ALL_ANGLE
111     private static final int ANGLE_GL_DRIVER_ALL_ANGLE_ON = 1;
112     private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0;
113 
114     // Values for ANGLE_GL_DRIVER_SELECTION_VALUES
115     private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default";
116     private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle";
117     private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
118 
119     private ClassLoader mClassLoader;
120     private String mLibrarySearchPaths;
121     private String mLibraryPermittedPaths;
122     private GameManager mGameManager;
123 
124     private int mAngleOptInIndex = -1;
125 
126     /**
127      * Set up GraphicsEnvironment
128      */
setup(Context context, Bundle coreSettings)129     public void setup(Context context, Bundle coreSettings) {
130         final PackageManager pm = context.getPackageManager();
131         final String packageName = context.getPackageName();
132         final ApplicationInfo appInfoWithMetaData =
133                 getAppInfoWithMetadata(context, pm, packageName);
134 
135         mGameManager = context.getSystemService(GameManager.class);
136 
137         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers");
138         setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData);
139         Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
140 
141         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupAngle");
142         boolean useAngle = false;
143         if (setupAngle(context, coreSettings, pm, packageName)) {
144             if (shouldUseAngle(context, coreSettings, packageName)) {
145                 useAngle = true;
146                 setGpuStats(ANGLE_DRIVER_NAME, ANGLE_DRIVER_VERSION_NAME, ANGLE_DRIVER_VERSION_CODE,
147                         0, packageName, getVulkanVersion(pm));
148             }
149         }
150         Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
151 
152         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver");
153         if (!chooseDriver(context, coreSettings, pm, packageName, appInfoWithMetaData)) {
154             if (!useAngle) {
155                 setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME,
156                         SYSTEM_DRIVER_VERSION_CODE,
157                         SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0),
158                         packageName, getVulkanVersion(pm));
159             }
160         }
161         Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
162 
163         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "notifyGraphicsEnvironmentSetup");
164         if (mGameManager != null
165                 && appInfoWithMetaData.category == ApplicationInfo.CATEGORY_GAME) {
166             mGameManager.notifyGraphicsEnvironmentSetup();
167         }
168         Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
169     }
170 
171     /**
172      * Query to determine if the Game Mode has enabled ANGLE.
173      */
isAngleEnabledByGameMode(Context context, String packageName)174     private boolean isAngleEnabledByGameMode(Context context, String packageName) {
175         try {
176             final boolean gameModeEnabledAngle =
177                     (mGameManager != null) && mGameManager.isAngleEnabled(packageName);
178             Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle);
179             return gameModeEnabledAngle;
180         } catch (SecurityException e) {
181             Log.e(TAG, "Caught exception while querying GameManagerService if ANGLE is enabled "
182                     + "for package: " + packageName);
183         }
184 
185         return false;
186     }
187 
188     /**
189      * Query to determine if ANGLE should be used
190      */
shouldUseAngle(Context context, Bundle coreSettings, String packageName)191     private boolean shouldUseAngle(Context context, Bundle coreSettings,
192             String packageName) {
193         if (TextUtils.isEmpty(packageName)) {
194             Log.v(TAG, "No package name specified, ANGLE should not be used");
195             return false;
196         }
197 
198         final String devOptIn = getDriverForPackage(context, coreSettings, packageName);
199         Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
200                 + "set to: '" + devOptIn + "'");
201 
202         // We only want to use ANGLE if the developer has explicitly chosen something other than
203         // default driver.
204         final boolean forceAngle = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
205         final boolean forceNative = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE);
206         if (forceAngle || forceNative) {
207             Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
208         }
209 
210         final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName);
211 
212         return !forceNative && (forceAngle || gameModeEnabledAngle);
213     }
214 
getVulkanVersion(PackageManager pm)215     private int getVulkanVersion(PackageManager pm) {
216         // PackageManager doesn't have an API to retrieve the version of a specific feature, and we
217         // need to avoid retrieving all system features here and looping through them.
218         if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_3)) {
219             return VULKAN_1_3;
220         }
221 
222         if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_2)) {
223             return VULKAN_1_2;
224         }
225 
226         if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_1)) {
227             return VULKAN_1_1;
228         }
229 
230         if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_0)) {
231             return VULKAN_1_0;
232         }
233 
234         return 0;
235     }
236 
237     /**
238      * Check whether application is has set the manifest metadata for layer injection.
239      */
canInjectLayers(ApplicationInfo ai)240     private boolean canInjectLayers(ApplicationInfo ai) {
241         return (ai.metaData != null && ai.metaData.getBoolean(METADATA_INJECT_LAYERS_ENABLE)
242                 && setInjectLayersPrSetDumpable());
243     }
244 
245     /**
246      * Store the class loader for namespace lookup later.
247      */
setLayerPaths(ClassLoader classLoader, String searchPaths, String permittedPaths)248     public void setLayerPaths(ClassLoader classLoader,
249                               String searchPaths,
250                               String permittedPaths) {
251         // We have to store these in the class because they are set up before we
252         // have access to the Context to properly set up GraphicsEnvironment
253         mClassLoader = classLoader;
254         mLibrarySearchPaths = searchPaths;
255         mLibraryPermittedPaths = permittedPaths;
256     }
257 
258     /**
259      * Returns the debug layer paths from settings.
260      * Returns null if:
261      *     1) The application process is not debuggable or layer injection metadata flag is not
262      *        true; Or
263      *     2) ENABLE_GPU_DEBUG_LAYERS is not true; Or
264      *     3) Package name is not equal to GPU_DEBUG_APP.
265      */
getDebugLayerPathsFromSettings( Bundle coreSettings, IPackageManager pm, String packageName, ApplicationInfo ai)266     public String getDebugLayerPathsFromSettings(
267             Bundle coreSettings, IPackageManager pm, String packageName,
268             ApplicationInfo ai) {
269         if (!debugLayerEnabled(coreSettings, packageName, ai)) {
270             return null;
271         }
272         Log.i(TAG, "GPU debug layers enabled for " + packageName);
273         String debugLayerPaths = "";
274 
275         // Grab all debug layer apps and add to paths.
276         final String gpuDebugLayerApps =
277                 coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP, "");
278         if (!gpuDebugLayerApps.isEmpty()) {
279             Log.i(TAG, "GPU debug layer apps: " + gpuDebugLayerApps);
280             // If a colon is present, treat this as multiple apps, so Vulkan and GLES
281             // layer apps can be provided at the same time.
282             final String[] layerApps = gpuDebugLayerApps.split(":");
283             for (int i = 0; i < layerApps.length; i++) {
284                 String paths = getDebugLayerAppPaths(pm, layerApps[i]);
285                 if (!paths.isEmpty()) {
286                     // Append the path so files placed in the app's base directory will
287                     // override the external path
288                     debugLayerPaths += paths + File.pathSeparator;
289                 }
290             }
291         }
292         return debugLayerPaths;
293     }
294 
295     /**
296      * Return the debug layer app's on-disk and in-APK lib directories
297      */
getDebugLayerAppPaths(IPackageManager pm, String packageName)298     private String getDebugLayerAppPaths(IPackageManager pm, String packageName) {
299         final ApplicationInfo appInfo;
300         try {
301             appInfo = pm.getApplicationInfo(packageName, PackageManager.MATCH_ALL,
302                     UserHandle.myUserId());
303         } catch (RemoteException e) {
304             return "";
305         }
306         if (appInfo == null) {
307             Log.w(TAG, "Debug layer app '" + packageName + "' not installed");
308             return "";
309         }
310 
311         final String abi = chooseAbi(appInfo);
312         final StringBuilder sb = new StringBuilder();
313         sb.append(appInfo.nativeLibraryDir)
314             .append(File.pathSeparator)
315             .append(appInfo.sourceDir)
316             .append("!/lib/")
317             .append(abi);
318         final String paths = sb.toString();
319         if (DEBUG) Log.v(TAG, "Debug layer app libs: " + paths);
320 
321         return paths;
322     }
323 
debugLayerEnabled(Bundle coreSettings, String packageName, ApplicationInfo ai)324     private boolean debugLayerEnabled(Bundle coreSettings, String packageName, ApplicationInfo ai) {
325         // Only enable additional debug functionality if the following conditions are met:
326         // 1. App is debuggable or device is rooted or layer injection metadata flag is true
327         // 2. ENABLE_GPU_DEBUG_LAYERS is true
328         // 3. Package name is equal to GPU_DEBUG_APP
329         if (!isDebuggable() && !canInjectLayers(ai)) {
330             return false;
331         }
332         final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
333         if (enable == 0) {
334             return false;
335         }
336         final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP, "");
337         if (packageName == null
338                 || (gpuDebugApp.isEmpty() || packageName.isEmpty())
339                 || !gpuDebugApp.equals(packageName)) {
340             return false;
341         }
342         return true;
343     }
344 
345     /**
346      * Set up layer search paths for all apps
347      */
setupGpuLayers( Context context, Bundle coreSettings, PackageManager pm, String packageName, ApplicationInfo ai)348     private void setupGpuLayers(
349             Context context, Bundle coreSettings, PackageManager pm, String packageName,
350             ApplicationInfo ai) {
351         final boolean enabled = debugLayerEnabled(coreSettings, packageName, ai);
352         String layerPaths = "";
353         if (enabled) {
354             layerPaths = mLibraryPermittedPaths;
355 
356             final String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS);
357             Log.i(TAG, "Vulkan debug layer list: " + layers);
358             if (layers != null && !layers.isEmpty()) {
359                 setDebugLayers(layers);
360             }
361 
362             final String layersGLES =
363                     coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES);
364             Log.i(TAG, "GLES debug layer list: " + layersGLES);
365             if (layersGLES != null && !layersGLES.isEmpty()) {
366                 setDebugLayersGLES(layersGLES);
367             }
368         }
369 
370         // Include the app's lib directory in all cases
371         layerPaths += mLibrarySearchPaths;
372         setLayerPaths(mClassLoader, layerPaths);
373     }
374 
getGlobalSettingsString(ContentResolver contentResolver, Bundle bundle, String globalSetting)375     private static List<String> getGlobalSettingsString(ContentResolver contentResolver,
376                                                         Bundle bundle,
377                                                         String globalSetting) {
378         final List<String> valueList;
379         final String settingsValue;
380 
381         if (bundle != null) {
382             settingsValue = bundle.getString(globalSetting);
383         } else {
384             settingsValue = Settings.Global.getString(contentResolver, globalSetting);
385         }
386 
387         if (settingsValue != null) {
388             valueList = new ArrayList<>(Arrays.asList(settingsValue.split(",")));
389         } else {
390             valueList = new ArrayList<>();
391         }
392 
393         return valueList;
394     }
395 
getPackageIndex(String packageName, List<String> packages)396     private static int getPackageIndex(String packageName, List<String> packages) {
397         for (int idx = 0; idx < packages.size(); idx++) {
398             if (packages.get(idx).equals(packageName)) {
399                 return idx;
400             }
401         }
402 
403         return -1;
404     }
405 
getAppInfoWithMetadata(Context context, PackageManager pm, String packageName)406     private static ApplicationInfo getAppInfoWithMetadata(Context context,
407                                                           PackageManager pm, String packageName) {
408         ApplicationInfo ai;
409         try {
410             // Get the ApplicationInfo from PackageManager so that metadata fields present.
411             ai = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
412         } catch (PackageManager.NameNotFoundException e) {
413             // Unlikely to fail for applications, but in case of failure, fall back to use the
414             // ApplicationInfo from context directly.
415             ai = context.getApplicationInfo();
416         }
417         return ai;
418     }
419 
getDriverForPackage(Context context, Bundle bundle, String packageName)420     private String getDriverForPackage(Context context, Bundle bundle, String packageName) {
421         final int allUseAngle;
422         if (bundle != null) {
423             allUseAngle =
424                     bundle.getInt(Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE);
425         } else {
426             ContentResolver contentResolver = context.getContentResolver();
427             allUseAngle = Settings.Global.getInt(contentResolver,
428                     Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE,
429                     ANGLE_GL_DRIVER_ALL_ANGLE_OFF);
430         }
431         if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) {
432             Log.v(TAG, "Turn on ANGLE for all applications.");
433             return ANGLE_GL_DRIVER_CHOICE_ANGLE;
434         }
435 
436         // Make sure we have a good package name
437         if (TextUtils.isEmpty(packageName)) {
438             return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
439         }
440 
441         final ContentResolver contentResolver = context.getContentResolver();
442         final List<String> optInPackages =
443                 getGlobalSettingsString(contentResolver, bundle,
444                         Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS);
445         final List<String> optInValues =
446                 getGlobalSettingsString(contentResolver, bundle,
447                         Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES);
448 
449         // Make sure we have good settings to use
450         if (optInPackages.size() != optInValues.size()) {
451             Log.w(TAG,
452                     "Global.Settings values are invalid: "
453                         + "number of packages: "
454                             + optInPackages.size() + ", "
455                         + "number of values: "
456                             + optInValues.size());
457             return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
458         }
459 
460         final int pkgIndex = getPackageIndex(packageName, optInPackages);
461 
462         if (pkgIndex < 0) {
463             return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
464         }
465         mAngleOptInIndex = pkgIndex;
466 
467         return optInValues.get(pkgIndex);
468     }
469 
470     /**
471      * Get the ANGLE package name.
472      */
getAnglePackageName(PackageManager pm)473     private String getAnglePackageName(PackageManager pm) {
474         final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID);
475 
476         final List<ResolveInfo> resolveInfos =
477                 pm.queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
478         if (resolveInfos.size() != 1) {
479             Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
480                     + resolveInfos.size());
481             for (ResolveInfo resolveInfo : resolveInfos) {
482                 Log.e(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
483             }
484             return "";
485         }
486 
487         // Must be exactly 1 ANGLE PKG found to get here.
488         return resolveInfos.get(0).activityInfo.packageName;
489     }
490 
491     /**
492      * Check for ANGLE debug package, but only for apps that can load them.
493      * An application can load ANGLE debug package if it is a debuggable application, or
494      * the device is debuggable.
495      */
getAngleDebugPackage(Context context, Bundle coreSettings)496     private String getAngleDebugPackage(Context context, Bundle coreSettings) {
497         if (!isDebuggable()) {
498             return "";
499         }
500         final String debugPackage;
501 
502         if (coreSettings != null) {
503             debugPackage =
504                     coreSettings.getString(Settings.Global.ANGLE_DEBUG_PACKAGE);
505         } else {
506             ContentResolver contentResolver = context.getContentResolver();
507             debugPackage = Settings.Global.getString(contentResolver,
508                     Settings.Global.ANGLE_DEBUG_PACKAGE);
509         }
510         if (TextUtils.isEmpty(debugPackage)) {
511             return "";
512         }
513         return debugPackage;
514     }
515 
516     /**
517      * Pass ANGLE details down to trigger enable logic
518      *
519      * @param context
520      * @param bundle
521      * @param pm
522      * @param packageName - package name of the application.
523      * @return true: ANGLE setup successfully
524      *         false: ANGLE not setup (not on allowlist, ANGLE not present, etc.)
525      */
setupAngle(Context context, Bundle bundle, PackageManager pm, String packageName)526     private boolean setupAngle(Context context, Bundle bundle, PackageManager pm,
527             String packageName) {
528 
529         if (!shouldUseAngle(context, bundle, packageName)) {
530             return false;
531         }
532 
533         ApplicationInfo angleInfo = null;
534 
535         // If the developer has specified a debug package over ADB, attempt to find it
536         String anglePkgName = getAngleDebugPackage(context, bundle);
537         if (!anglePkgName.isEmpty()) {
538             Log.i(TAG, "ANGLE debug package enabled: " + anglePkgName);
539             try {
540                 // Note the debug package does not have to be pre-installed
541                 angleInfo = pm.getApplicationInfo(anglePkgName, 0);
542             } catch (PackageManager.NameNotFoundException e) {
543                 Log.w(TAG, "ANGLE debug package '" + anglePkgName + "' not installed");
544                 return false;
545             }
546         }
547 
548         // Otherwise, check to see if ANGLE is properly installed
549         if (angleInfo == null) {
550             anglePkgName = getAnglePackageName(pm);
551             if (TextUtils.isEmpty(anglePkgName)) {
552                 Log.w(TAG, "Failed to find ANGLE package.");
553                 return false;
554             }
555 
556             Log.i(TAG, "ANGLE package enabled: " + anglePkgName);
557             try {
558                 // Production ANGLE libraries must be pre-installed as a system app
559                 angleInfo = pm.getApplicationInfo(anglePkgName,
560                         PackageManager.MATCH_SYSTEM_ONLY);
561             } catch (PackageManager.NameNotFoundException e) {
562                 Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed");
563                 return false;
564             }
565         }
566 
567         final String abi = chooseAbi(angleInfo);
568 
569         // Build a path that includes installed native libs and APK
570         final String paths = angleInfo.nativeLibraryDir
571                 + File.pathSeparator
572                 + angleInfo.sourceDir
573                 + "!/lib/"
574                 + abi;
575 
576         if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
577 
578         // We need to call setAngleInfo() with the package name and the developer option value
579         //(native/angle/other). Then later when we are actually trying to load a driver,
580         //GraphicsEnv::getShouldUseAngle() has seen the package name before and can confidently
581         //answer yes/no based on the previously set developer option value.
582         final String devOptIn;
583         final String[] features = getAngleEglFeatures(context, bundle);
584         final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName);
585         if (gameModeEnabledAngle) {
586             devOptIn = ANGLE_GL_DRIVER_CHOICE_ANGLE;
587         } else {
588             devOptIn = getDriverForPackage(context, bundle, packageName);
589         }
590         setAngleInfo(paths, packageName, devOptIn, features);
591 
592         return true;
593     }
594 
595     /**
596      * Determine if the "ANGLE In Use" dialog box should be shown.
597      */
shouldShowAngleInUseDialogBox(Context context)598     private boolean shouldShowAngleInUseDialogBox(Context context) {
599         try {
600             ContentResolver contentResolver = context.getContentResolver();
601             final int showDialogBox = Settings.Global.getInt(contentResolver,
602                     Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX);
603 
604             return (showDialogBox == 1);
605         } catch (Settings.SettingNotFoundException | SecurityException e) {
606             // Do nothing and move on
607         }
608 
609         // No setting, so assume false
610         return false;
611     }
612 
613     /**
614      * Determine if ANGLE will be used and setup the environment
615      */
setupAndUseAngle(Context context, String packageName)616     private boolean setupAndUseAngle(Context context, String packageName) {
617         // Need to make sure we are evaluating ANGLE usage for the correct circumstances
618         if (!setupAngle(context, null, context.getPackageManager(), packageName)) {
619             Log.v(TAG, "Package '" + packageName + "' should not use ANGLE");
620             return false;
621         }
622 
623         final boolean useAngle = getShouldUseAngle(packageName);
624         Log.v(TAG, "Package '" + packageName + "' should use ANGLE = '" + useAngle + "'");
625 
626         return useAngle;
627     }
628 
629     /**
630      * Show the ANGLE in Use Dialog Box
631      * @param context
632      */
showAngleInUseDialogBox(Context context)633     public void showAngleInUseDialogBox(Context context) {
634         final String packageName = context.getPackageName();
635 
636         if (shouldShowAngleInUseDialogBox(context) && setupAndUseAngle(context, packageName)) {
637             final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE);
638             String anglePkg = getAnglePackageName(context.getPackageManager());
639             intent.setPackage(anglePkg);
640 
641             context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
642                 @Override
643                 public void onReceive(Context context, Intent intent) {
644                     Bundle results = getResultExtras(true);
645 
646                     String toastMsg = results.getString(INTENT_KEY_A4A_TOAST_MESSAGE);
647                     final Toast toast = Toast.makeText(context, toastMsg, Toast.LENGTH_LONG);
648                     toast.show();
649                 }
650             }, null, Activity.RESULT_OK, null, null);
651         }
652     }
653 
getAngleEglFeatures(Context context, Bundle coreSettings)654     private String[] getAngleEglFeatures(Context context, Bundle coreSettings) {
655         if (mAngleOptInIndex < 0) {
656             return null;
657         }
658 
659         final List<String> featuresLists = getGlobalSettingsString(
660                 context.getContentResolver(), coreSettings, Settings.Global.ANGLE_EGL_FEATURES);
661         if (featuresLists.size() <= mAngleOptInIndex) {
662             return null;
663         }
664         return featuresLists.get(mAngleOptInIndex).split(":");
665     }
666 
667     /**
668      * Return the driver package name to use. Return null for system driver.
669      */
chooseDriverInternal(Bundle coreSettings, ApplicationInfo ai)670     private String chooseDriverInternal(Bundle coreSettings, ApplicationInfo ai) {
671         final String productionDriver = SystemProperties.get(PROPERTY_GFX_DRIVER_PRODUCTION);
672         final boolean hasProductionDriver = productionDriver != null && !productionDriver.isEmpty();
673 
674         final String prereleaseDriver = SystemProperties.get(PROPERTY_GFX_DRIVER_PRERELEASE);
675         final boolean hasPrereleaseDriver = prereleaseDriver != null && !prereleaseDriver.isEmpty();
676 
677         if (!hasProductionDriver && !hasPrereleaseDriver) {
678             Log.v(TAG, "Neither updatable production driver nor prerelease driver is supported.");
679             return null;
680         }
681 
682         // To minimize risk of driver updates crippling the device beyond user repair, never use the
683         // updatable drivers for privileged or non-updated system apps. Presumably pre-installed
684         // apps were tested thoroughly with the system driver.
685         if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
686             if (DEBUG) {
687                 Log.v(TAG,
688                         "Ignore updatable driver package for privileged/non-updated system app.");
689             }
690             return null;
691         }
692 
693         final boolean enablePrereleaseDriver =
694                 (ai.metaData != null && ai.metaData.getBoolean(METADATA_DEVELOPER_DRIVER_ENABLE))
695                 || isDebuggable();
696 
697         // Priority of updatable driver settings on confliction (Higher priority comes first):
698         // 1. UPDATABLE_DRIVER_ALL_APPS
699         // 2. UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS
700         // 3. UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS
701         // 4. UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS
702         // 5. UPDATABLE_DRIVER_PRODUCTION_DENYLIST
703         // 6. UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST
704         switch (coreSettings.getInt(Settings.Global.UPDATABLE_DRIVER_ALL_APPS, 0)) {
705             case UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF:
706                 Log.v(TAG, "The updatable driver is turned off on this device.");
707                 return null;
708             case UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRODUCTION_DRIVER:
709                 Log.v(TAG, "All apps opt in to use updatable production driver.");
710                 return hasProductionDriver ? productionDriver : null;
711             case UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER:
712                 Log.v(TAG, "All apps opt in to use updatable prerelease driver.");
713                 return hasPrereleaseDriver && enablePrereleaseDriver ? prereleaseDriver : null;
714             case UPDATABLE_DRIVER_GLOBAL_OPT_IN_DEFAULT:
715             default:
716                 break;
717         }
718 
719         final String appPackageName = ai.packageName;
720         if (getGlobalSettingsString(null, coreSettings,
721                                     Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS)
722                         .contains(appPackageName)) {
723             Log.v(TAG, "App opts out for updatable production driver.");
724             return null;
725         }
726 
727         if (getGlobalSettingsString(
728                     null, coreSettings, Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS)
729                         .contains(appPackageName)) {
730             Log.v(TAG, "App opts in for updatable prerelease driver.");
731             return hasPrereleaseDriver && enablePrereleaseDriver ? prereleaseDriver : null;
732         }
733 
734         // Early return here since the rest logic is only for updatable production Driver.
735         if (!hasProductionDriver) {
736             Log.v(TAG, "Updatable production driver is not supported on the device.");
737             return null;
738         }
739 
740         final boolean isOptIn =
741                 getGlobalSettingsString(null, coreSettings,
742                                         Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS)
743                         .contains(appPackageName);
744         final List<String> allowlist =
745                 getGlobalSettingsString(null, coreSettings,
746                                         Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST);
747         if (!isOptIn && allowlist.indexOf(UPDATABLE_DRIVER_ALLOWLIST_ALL) != 0
748                 && !allowlist.contains(appPackageName)) {
749             Log.v(TAG, "App is not on the allowlist for updatable production driver.");
750             return null;
751         }
752 
753         // If the application is not opted-in, then check whether it's on the denylist,
754         // terminate early if it's on the denylist and fallback to system driver.
755         if (!isOptIn
756                 && getGlobalSettingsString(
757                         null, coreSettings, Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST)
758                            .contains(appPackageName)) {
759             Log.v(TAG, "App is on the denylist for updatable production driver.");
760             return null;
761         }
762 
763         return productionDriver;
764     }
765 
766     /**
767      * Choose whether the current process should use the builtin or an updated driver.
768      */
chooseDriver( Context context, Bundle coreSettings, PackageManager pm, String packageName, ApplicationInfo ai)769     private boolean chooseDriver(
770             Context context, Bundle coreSettings, PackageManager pm, String packageName,
771             ApplicationInfo ai) {
772         final String driverPackageName = chooseDriverInternal(coreSettings, ai);
773         if (driverPackageName == null) {
774             return false;
775         }
776 
777         final PackageInfo driverPackageInfo;
778         try {
779             driverPackageInfo = pm.getPackageInfo(driverPackageName,
780                     PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
781         } catch (PackageManager.NameNotFoundException e) {
782             Log.w(TAG, "updatable driver package '" + driverPackageName + "' not installed");
783             return false;
784         }
785 
786         // O drivers are restricted to the sphal linker namespace, so don't try to use
787         // packages unless they declare they're compatible with that restriction.
788         final ApplicationInfo driverAppInfo = driverPackageInfo.applicationInfo;
789         if (driverAppInfo.targetSdkVersion < Build.VERSION_CODES.O) {
790             if (DEBUG) {
791                 Log.w(TAG, "updatable driver package is not compatible with O");
792             }
793             return false;
794         }
795 
796         final String abi = chooseAbi(driverAppInfo);
797         if (abi == null) {
798             if (DEBUG) {
799                 // This is the normal case for the pre-installed empty driver package, don't spam
800                 if (driverAppInfo.isUpdatedSystemApp()) {
801                     Log.w(TAG, "Updatable driver package has no compatible native libraries");
802                 }
803             }
804             return false;
805         }
806 
807         final StringBuilder sb = new StringBuilder();
808         sb.append(driverAppInfo.nativeLibraryDir)
809           .append(File.pathSeparator);
810         sb.append(driverAppInfo.sourceDir)
811           .append("!/lib/")
812           .append(abi);
813         final String paths = sb.toString();
814         final String sphalLibraries = getSphalLibraries(context, driverPackageName);
815         Log.v(TAG, "Updatable driver package search path: " + paths
816                 + ", required sphal libraries: " + sphalLibraries);
817         setDriverPathAndSphalLibraries(paths, sphalLibraries);
818 
819         if (driverAppInfo.metaData == null) {
820             throw new NullPointerException("apk's meta-data cannot be null");
821         }
822 
823         String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME);
824         if (driverBuildTime == null || driverBuildTime.length() <= 1) {
825             Log.w(TAG, "com.android.graphics.driver.build_time is not set");
826             driverBuildTime = "L0";
827         }
828         // driver_build_time in the meta-data is in "L<Unix epoch timestamp>" format. e.g. L123456.
829         // Long.parseLong will throw if the meta-data "driver_build_time" is not set properly.
830         setGpuStats(driverPackageName, driverPackageInfo.versionName, driverAppInfo.longVersionCode,
831                 Long.parseLong(driverBuildTime.substring(1)), packageName, 0);
832 
833         return true;
834     }
835 
chooseAbi(ApplicationInfo ai)836     private static String chooseAbi(ApplicationInfo ai) {
837         final String isa = VMRuntime.getCurrentInstructionSet();
838         if (ai.primaryCpuAbi != null &&
839                 isa.equals(VMRuntime.getInstructionSet(ai.primaryCpuAbi))) {
840             return ai.primaryCpuAbi;
841         }
842         if (ai.secondaryCpuAbi != null &&
843                 isa.equals(VMRuntime.getInstructionSet(ai.secondaryCpuAbi))) {
844             return ai.secondaryCpuAbi;
845         }
846         return null;
847     }
848 
getSphalLibraries(Context context, String driverPackageName)849     private String getSphalLibraries(Context context, String driverPackageName) {
850         try {
851             final Context driverContext =
852                     context.createPackageContext(driverPackageName, Context.CONTEXT_RESTRICTED);
853             final BufferedReader reader = new BufferedReader(new InputStreamReader(
854                     driverContext.getAssets().open(UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME)));
855             final ArrayList<String> assetStrings = new ArrayList<>();
856             for (String assetString; (assetString = reader.readLine()) != null;) {
857                 assetStrings.add(assetString);
858             }
859             return String.join(":", assetStrings);
860         } catch (PackageManager.NameNotFoundException e) {
861             if (DEBUG) {
862                 Log.w(TAG, "Driver package '" + driverPackageName + "' not installed");
863             }
864         } catch (IOException e) {
865             if (DEBUG) {
866                 Log.w(TAG, "Failed to load '" + UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME + "'");
867             }
868         }
869         return "";
870     }
871 
isDebuggable()872     private static native boolean isDebuggable();
setLayerPaths(ClassLoader classLoader, String layerPaths)873     private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
setDebugLayers(String layers)874     private static native void setDebugLayers(String layers);
setDebugLayersGLES(String layers)875     private static native void setDebugLayersGLES(String layers);
setDriverPathAndSphalLibraries(String path, String sphalLibraries)876     private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries);
setGpuStats(String driverPackageName, String driverVersionName, long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion)877     private static native void setGpuStats(String driverPackageName, String driverVersionName,
878             long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
setAngleInfo(String path, String appPackage, String devOptIn, String[] features)879     private static native void setAngleInfo(String path, String appPackage, String devOptIn,
880             String[] features);
getShouldUseAngle(String packageName)881     private static native boolean getShouldUseAngle(String packageName);
setInjectLayersPrSetDumpable()882     private static native boolean setInjectLayersPrSetDumpable();
883 
884     /**
885      * Hint for GraphicsEnvironment that an activity is launching on the process.
886      * Then the app process is allowed to send stats to GpuStats module.
887      */
hintActivityLaunch()888     public static native void hintActivityLaunch();
889 }
890