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