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