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