/* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 1 #define LOG_TAG "GraphicsEnv" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // TODO(b/37049319) Get this from a header once one exists extern "C" { android_namespace_t* android_get_exported_namespace(const char*); android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type, const char* permitted_when_isolated_path, android_namespace_t* parent); bool android_link_namespaces(android_namespace_t* from, android_namespace_t* to, const char* shared_libs_sonames); enum { ANDROID_NAMESPACE_TYPE_ISOLATED = 1, ANDROID_NAMESPACE_TYPE_SHARED = 2, }; } // TODO(ianelliott@): Get the following from an ANGLE header: #define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting // Version-2 API: typedef bool (*fpANGLEGetFeatureSupportUtilAPIVersion)(unsigned int* versionToUse); typedef bool (*fpANGLEAndroidParseRulesString)(const char* rulesString, void** rulesHandle, int* rulesVersion); typedef bool (*fpANGLEGetSystemInfo)(void** handle); typedef bool (*fpANGLEAddDeviceInfoToSystemInfo)(const char* deviceMfr, const char* deviceModel, void* handle); typedef bool (*fpANGLEShouldBeUsedForApplication)(void* rulesHandle, int rulesVersion, void* systemInfoHandle, const char* appName); typedef bool (*fpANGLEFreeRulesHandle)(void* handle); typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle); namespace android { enum NativeLibrary { LLNDK = 0, VNDKSP = 1, }; static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/etc/llndk.libraries.txt", "/etc/vndksp.libraries.txt"}; static std::string vndkVersionStr() { #ifdef __BIONIC__ std::string version = android::base::GetProperty("ro.vndk.version", ""); if (version != "" && version != "current") { return "." + version; } #endif return ""; } static void insertVndkVersionStr(std::string* fileName) { LOG_ALWAYS_FATAL_IF(!fileName, "fileName should never be nullptr"); size_t insertPos = fileName->find_last_of("."); if (insertPos == std::string::npos) { insertPos = fileName->length(); } fileName->insert(insertPos, vndkVersionStr()); } static bool readConfig(const std::string& configFile, std::vector* soNames) { // Read list of public native libraries from the config file. std::string fileContent; if (!base::ReadFileToString(configFile, &fileContent)) { return false; } std::vector lines = base::Split(fileContent, "\n"); for (auto& line : lines) { auto trimmedLine = base::Trim(line); if (!trimmedLine.empty()) { soNames->push_back(trimmedLine); } } return true; } static const std::string getSystemNativeLibraries(NativeLibrary type) { static const char* androidRootEnv = getenv("ANDROID_ROOT"); static const std::string rootDir = androidRootEnv != nullptr ? androidRootEnv : "/system"; std::string nativeLibrariesSystemConfig = rootDir + kNativeLibrariesSystemConfigPath[type]; insertVndkVersionStr(&nativeLibrariesSystemConfig); std::vector soNames; if (!readConfig(nativeLibrariesSystemConfig, &soNames)) { ALOGE("Failed to retrieve library names from %s", nativeLibrariesSystemConfig.c_str()); return ""; } return base::Join(soNames, ':'); } /*static*/ GraphicsEnv& GraphicsEnv::getInstance() { static GraphicsEnv env; return env; } int GraphicsEnv::getCanLoadSystemLibraries() { if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { // Return an integer value since this crosses library boundaries return 1; } return 0; } void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries) { if (!mDriverPath.empty() || !mSphalLibraries.empty()) { ALOGV("ignoring attempt to change driver path from '%s' to '%s' or change sphal libraries " "from '%s' to '%s'", mDriverPath.c_str(), path.c_str(), mSphalLibraries.c_str(), sphalLibraries.c_str()); return; } ALOGV("setting driver path to '%s' and sphal libraries to '%s'", path.c_str(), sphalLibraries.c_str()); mDriverPath = path; mSphalLibraries = sphalLibraries; } void GraphicsEnv::hintActivityLaunch() { ATRACE_CALL(); std::thread trySendGpuStatsThread([this]() { // If there's already graphics driver preloaded in the process, just send // the stats info to GpuStats directly through async binder. std::lock_guard lock(mStatsLock); if (mGpuStats.glDriverToSend) { mGpuStats.glDriverToSend = false; sendGpuStatsLocked(GraphicsEnv::Api::API_GL, true, mGpuStats.glDriverLoadingTime); } if (mGpuStats.vkDriverToSend) { mGpuStats.vkDriverToSend = false; sendGpuStatsLocked(GraphicsEnv::Api::API_VK, true, mGpuStats.vkDriverLoadingTime); } }); trySendGpuStatsThread.detach(); } void GraphicsEnv::setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, const int vulkanVersion) { ATRACE_CALL(); std::lock_guard lock(mStatsLock); ALOGV("setGpuStats:\n" "\tdriverPackageName[%s]\n" "\tdriverVersionName[%s]\n" "\tdriverVersionCode[%" PRIu64 "]\n" "\tdriverBuildTime[%" PRId64 "]\n" "\tappPackageName[%s]\n" "\tvulkanVersion[%d]\n", driverPackageName.c_str(), driverVersionName.c_str(), driverVersionCode, driverBuildTime, appPackageName.c_str(), vulkanVersion); mGpuStats.driverPackageName = driverPackageName; mGpuStats.driverVersionName = driverVersionName; mGpuStats.driverVersionCode = driverVersionCode; mGpuStats.driverBuildTime = driverBuildTime; mGpuStats.appPackageName = appPackageName; mGpuStats.vulkanVersion = vulkanVersion; } void GraphicsEnv::setDriverToLoad(GraphicsEnv::Driver driver) { ATRACE_CALL(); std::lock_guard lock(mStatsLock); switch (driver) { case GraphicsEnv::Driver::GL: case GraphicsEnv::Driver::GL_UPDATED: case GraphicsEnv::Driver::ANGLE: { if (mGpuStats.glDriverToLoad == GraphicsEnv::Driver::NONE) { mGpuStats.glDriverToLoad = driver; break; } if (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE) { mGpuStats.glDriverFallback = driver; } break; } case Driver::VULKAN: case Driver::VULKAN_UPDATED: { if (mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::NONE) { mGpuStats.vkDriverToLoad = driver; break; } if (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE) { mGpuStats.vkDriverFallback = driver; } break; } default: break; } } void GraphicsEnv::setDriverLoaded(GraphicsEnv::Api api, bool isDriverLoaded, int64_t driverLoadingTime) { ATRACE_CALL(); std::lock_guard lock(mStatsLock); const bool doNotSend = mGpuStats.appPackageName.empty(); if (api == GraphicsEnv::Api::API_GL) { if (doNotSend) mGpuStats.glDriverToSend = true; mGpuStats.glDriverLoadingTime = driverLoadingTime; } else { if (doNotSend) mGpuStats.vkDriverToSend = true; mGpuStats.vkDriverLoadingTime = driverLoadingTime; } sendGpuStatsLocked(api, isDriverLoaded, driverLoadingTime); } static sp getGpuService() { const sp binder = defaultServiceManager()->checkService(String16("gpu")); if (!binder) { ALOGE("Failed to get gpu service"); return nullptr; } return interface_cast(binder); } void GraphicsEnv::setCpuVulkanInUse() { ATRACE_CALL(); // Use the same stats lock to protect getGpuService() as well. std::lock_guard lock(mStatsLock); const sp gpuService = getGpuService(); if (gpuService) { gpuService->setCpuVulkanInUse(mGpuStats.appPackageName, mGpuStats.driverVersionCode); } } void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Api api, bool isDriverLoaded, int64_t driverLoadingTime) { ATRACE_CALL(); // Do not sendGpuStats for those skipping the GraphicsEnvironment setup if (mGpuStats.appPackageName.empty()) return; ALOGV("sendGpuStats:\n" "\tdriverPackageName[%s]\n" "\tdriverVersionName[%s]\n" "\tdriverVersionCode[%" PRIu64 "]\n" "\tdriverBuildTime[%" PRId64 "]\n" "\tappPackageName[%s]\n" "\tvulkanVersion[%d]\n" "\tapi[%d]\n" "\tisDriverLoaded[%d]\n" "\tdriverLoadingTime[%" PRId64 "]", mGpuStats.driverPackageName.c_str(), mGpuStats.driverVersionName.c_str(), mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, mGpuStats.appPackageName.c_str(), mGpuStats.vulkanVersion, static_cast(api), isDriverLoaded, driverLoadingTime); GraphicsEnv::Driver driver = GraphicsEnv::Driver::NONE; bool isIntendedDriverLoaded = false; if (api == GraphicsEnv::Api::API_GL) { driver = mGpuStats.glDriverToLoad; isIntendedDriverLoaded = isDriverLoaded && (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE); } else { driver = mGpuStats.vkDriverToLoad; isIntendedDriverLoaded = isDriverLoaded && (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE); } const sp gpuService = getGpuService(); if (gpuService) { gpuService->setGpuStats(mGpuStats.driverPackageName, mGpuStats.driverVersionName, mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, mGpuStats.appPackageName, mGpuStats.vulkanVersion, driver, isIntendedDriverLoaded, driverLoadingTime); } } void* GraphicsEnv::loadLibrary(std::string name) { const android_dlextinfo dlextinfo = { .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = getAngleNamespace(), }; std::string libName = std::string("lib") + name + "_angle.so"; void* so = android_dlopen_ext(libName.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); if (so) { ALOGD("dlopen_ext from APK (%s) success at %p", libName.c_str(), so); return so; } else { ALOGE("dlopen_ext(\"%s\") failed: %s", libName.c_str(), dlerror()); } return nullptr; } bool GraphicsEnv::checkAngleRules(void* so) { char manufacturer[PROPERTY_VALUE_MAX]; char model[PROPERTY_VALUE_MAX]; property_get("ro.product.manufacturer", manufacturer, "UNSET"); property_get("ro.product.model", model, "UNSET"); auto ANGLEGetFeatureSupportUtilAPIVersion = (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so, "ANGLEGetFeatureSupportUtilAPIVersion"); if (!ANGLEGetFeatureSupportUtilAPIVersion) { ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function"); return false; } // Negotiate the interface version by requesting most recent known to the platform unsigned int versionToUse = CURRENT_ANGLE_API_VERSION; if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) { ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, " "requested version %u", versionToUse); return false; } // Add and remove versions below as needed bool useAngle = false; switch (versionToUse) { case 2: { ALOGV("Using version %d of ANGLE feature-support library", versionToUse); void* rulesHandle = nullptr; int rulesVersion = 0; void* systemInfoHandle = nullptr; // Get the symbols for the feature-support-utility library: #define GET_SYMBOL(symbol) \ fp##symbol symbol = (fp##symbol)dlsym(so, #symbol); \ if (!symbol) { \ ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \ break; \ } GET_SYMBOL(ANGLEAndroidParseRulesString); GET_SYMBOL(ANGLEGetSystemInfo); GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo); GET_SYMBOL(ANGLEShouldBeUsedForApplication); GET_SYMBOL(ANGLEFreeRulesHandle); GET_SYMBOL(ANGLEFreeSystemInfoHandle); // Parse the rules, obtain the SystemInfo, and evaluate the // application against the rules: if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) { ALOGW("ANGLE feature-support library cannot parse rules file"); break; } if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) { ALOGW("ANGLE feature-support library cannot obtain SystemInfo"); break; } if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) { ALOGW("ANGLE feature-support library cannot add device info to SystemInfo"); break; } useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion, systemInfoHandle, mAngleAppName.c_str()); (ANGLEFreeRulesHandle)(rulesHandle); (ANGLEFreeSystemInfoHandle)(systemInfoHandle); } break; default: ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse); } ALOGV("Close temporarily-loaded ANGLE opt-in/out logic"); return useAngle; } bool GraphicsEnv::shouldUseAngle(std::string appName) { if (appName != mAngleAppName) { // Make sure we are checking the app we were init'ed for ALOGE("App name does not match: expected '%s', got '%s'", mAngleAppName.c_str(), appName.c_str()); return false; } return shouldUseAngle(); } bool GraphicsEnv::shouldUseAngle() { // Make sure we are init'ed if (mAngleAppName.empty()) { ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE."); return false; } return (mUseAngle == YES) ? true : false; } void GraphicsEnv::updateUseAngle() { mUseAngle = NO; const char* ANGLE_PREFER_ANGLE = "angle"; const char* ANGLE_PREFER_NATIVE = "native"; if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) { ALOGV("User set \"Developer Options\" to force the use of ANGLE"); mUseAngle = YES; } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) { ALOGV("User set \"Developer Options\" to force the use of Native"); mUseAngle = NO; } else { // The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily // load ANGLE and call the updatable opt-in/out logic: void* featureSo = loadLibrary("feature_support"); if (featureSo) { ALOGV("loaded ANGLE's opt-in/out logic from namespace"); mUseAngle = checkAngleRules(featureSo) ? YES : NO; dlclose(featureSo); featureSo = nullptr; } else { ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE."); } } } void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName, const std::string developerOptIn, const int rulesFd, const long rulesOffset, const long rulesLength) { if (mUseAngle != UNKNOWN) { // We've already figured out an answer for this app, so just return. ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(), (mUseAngle == YES) ? "true" : "false"); return; } ALOGV("setting ANGLE path to '%s'", path.c_str()); mAnglePath = path; ALOGV("setting ANGLE app name to '%s'", appName.c_str()); mAngleAppName = appName; ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str()); mAngleDeveloperOptIn = developerOptIn; lseek(rulesFd, rulesOffset, SEEK_SET); mRulesBuffer = std::vector(rulesLength + 1); ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength); if (numBytesRead < 0) { ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead); numBytesRead = 0; } else if (numBytesRead == 0) { ALOGW("Empty rules file"); } if (numBytesRead != rulesLength) { ALOGW("Did not read all of the necessary bytes from the rules file." "expected: %ld, got: %zd", rulesLength, numBytesRead); } mRulesBuffer[numBytesRead] = '\0'; // Update the current status of whether we should use ANGLE or not updateUseAngle(); } void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) { if (mLayerPaths.empty()) { mLayerPaths = layerPaths; mAppNamespace = appNamespace; } else { ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'", layerPaths.c_str(), appNamespace); } } NativeLoaderNamespace* GraphicsEnv::getAppNamespace() { return mAppNamespace; } std::string& GraphicsEnv::getAngleAppName() { return mAngleAppName; } const std::string& GraphicsEnv::getLayerPaths() { return mLayerPaths; } const std::string& GraphicsEnv::getDebugLayers() { return mDebugLayers; } const std::string& GraphicsEnv::getDebugLayersGLES() { return mDebugLayersGLES; } void GraphicsEnv::setDebugLayers(const std::string layers) { mDebugLayers = layers; } void GraphicsEnv::setDebugLayersGLES(const std::string layers) { mDebugLayersGLES = layers; } // Return true if all the required libraries from vndk and sphal namespace are // linked to the Game Driver namespace correctly. bool GraphicsEnv::linkDriverNamespaceLocked(android_namespace_t* vndkNamespace) { const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK); if (llndkLibraries.empty()) { return false; } if (!android_link_namespaces(mDriverNamespace, nullptr, llndkLibraries.c_str())) { ALOGE("Failed to link default namespace[%s]", dlerror()); return false; } const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP); if (vndkspLibraries.empty()) { return false; } if (!android_link_namespaces(mDriverNamespace, vndkNamespace, vndkspLibraries.c_str())) { ALOGE("Failed to link vndk namespace[%s]", dlerror()); return false; } if (mSphalLibraries.empty()) { return true; } // Make additional libraries in sphal to be accessible auto sphalNamespace = android_get_exported_namespace("sphal"); if (!sphalNamespace) { ALOGE("Depend on these libraries[%s] in sphal, but failed to get sphal namespace", mSphalLibraries.c_str()); return false; } if (!android_link_namespaces(mDriverNamespace, sphalNamespace, mSphalLibraries.c_str())) { ALOGE("Failed to link sphal namespace[%s]", dlerror()); return false; } return true; } android_namespace_t* GraphicsEnv::getDriverNamespace() { std::lock_guard lock(mNamespaceMutex); if (mDriverNamespace) { return mDriverNamespace; } if (mDriverPath.empty()) { return nullptr; } auto vndkNamespace = android_get_exported_namespace("vndk"); if (!vndkNamespace) { return nullptr; } mDriverNamespace = android_create_namespace("gfx driver", mDriverPath.c_str(), // ld_library_path mDriverPath.c_str(), // default_library_path ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr, // permitted_when_isolated_path nullptr); if (!linkDriverNamespaceLocked(vndkNamespace)) { mDriverNamespace = nullptr; } return mDriverNamespace; } android_namespace_t* GraphicsEnv::getAngleNamespace() { std::lock_guard lock(mNamespaceMutex); if (mAngleNamespace) { return mAngleNamespace; } if (mAnglePath.empty()) { ALOGV("mAnglePath is empty, not creating ANGLE namespace"); return nullptr; } mAngleNamespace = android_create_namespace("ANGLE", nullptr, // ld_library_path mAnglePath.c_str(), // default_library_path ANDROID_NAMESPACE_TYPE_SHARED | ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr, // permitted_when_isolated_path nullptr); ALOGD_IF(!mAngleNamespace, "Could not create ANGLE namespace from default"); return mAngleNamespace; } } // namespace android