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