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