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