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