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