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