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