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