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