| /* |
| * Copyright (C) 2019 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 LOG_TAG "TypeManager" |
| |
| #include "TypeManager.h" |
| |
| #include "Utils.h" |
| |
| #include <android-base/file.h> |
| #include <android-base/properties.h> |
| #include <android/content/pm/IPackageManagerNative.h> |
| #include <binder/IServiceManager.h> |
| #include <procpartition/procpartition.h> |
| #include <algorithm> |
| #include <string_view> |
| |
| namespace android { |
| namespace nn { |
| |
| // Replacement function for std::string_view::starts_with() |
| // which shall be available in C++20. |
| #if __cplusplus >= 202000L |
| #error "When upgrading to C++20, remove this error and file a bug to remove this workaround." |
| #endif |
| inline bool StartsWith(std::string_view sv, std::string_view prefix) { |
| return sv.substr(0u, prefix.size()) == prefix; |
| } |
| |
| namespace { |
| |
| using namespace hal; |
| |
| const uint8_t kLowBitsType = static_cast<uint8_t>(Model::ExtensionTypeEncoding::LOW_BITS_TYPE); |
| const uint32_t kMaxPrefix = |
| (1 << static_cast<uint8_t>(Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX)) - 1; |
| |
| // Checks if the two structures contain the same information. The order of |
| // operand types within the structures does not matter. |
| bool equal(const Extension& a, const Extension& b) { |
| NN_RET_CHECK_EQ(a.name, b.name); |
| // Relies on the fact that TypeManager sorts operandTypes. |
| NN_RET_CHECK(a.operandTypes == b.operandTypes); |
| return true; |
| } |
| |
| // Property for disabling NNAPI vendor extensions on product image (used on GSI /product image, |
| // which can't use NNAPI vendor extensions). |
| const char kVExtProductDeny[] = "ro.nnapi.extensions.deny_on_product"; |
| bool isNNAPIVendorExtensionsUseAllowedInProductImage() { |
| const std::string vExtProductDeny = android::base::GetProperty(kVExtProductDeny, ""); |
| return vExtProductDeny.empty(); |
| } |
| |
| // The file containing the list of Android apps and binaries allowed to use vendor extensions. |
| // Each line of the file contains new entry. If entry is prefixed by |
| // '/' slash, then it's a native binary path (e.g. '/data/foo'). If not, it's a name |
| // of Android app package (e.g. 'com.foo.bar'). |
| const char kAppAllowlistPath[] = "/vendor/etc/nnapi_extensions_app_allowlist"; |
| const char kCtsAllowlist[] = "/data/local/tmp/CTSNNAPITestCases"; |
| std::vector<std::string> getVendorExtensionAllowlistedApps() { |
| std::string data; |
| // Allowlist CTS by default. |
| std::vector<std::string> allowlist = {kCtsAllowlist}; |
| |
| if (!android::base::ReadFileToString(kAppAllowlistPath, &data)) { |
| // Return default allowlist (no app can use extensions). |
| LOG(INFO) << "Failed to read " << kAppAllowlistPath |
| << " ; No app allowlisted for vendor extensions use."; |
| return allowlist; |
| } |
| |
| std::istringstream streamData(data); |
| std::string line; |
| while (std::getline(streamData, line)) { |
| // Do some basic sanity check on entry, it's either |
| // fs path or package name. |
| if (StartsWith(line, "/") || line.find('.') != std::string::npos) { |
| allowlist.push_back(line); |
| } else { |
| LOG(ERROR) << kAppAllowlistPath << " - Invalid entry: " << line; |
| } |
| } |
| return allowlist; |
| } |
| |
| // Query PackageManagerNative service about Android app properties. |
| // On success, it will populate appPackageInfo->app* fields. |
| bool fetchAppPackageLocationInfo(uid_t uid, TypeManager::AppPackageInfo* appPackageInfo) { |
| sp<::android::IServiceManager> sm(::android::defaultServiceManager()); |
| sp<::android::IBinder> binder(sm->getService(String16("package_native"))); |
| if (binder == nullptr) { |
| LOG(ERROR) << "getService package_native failed"; |
| return false; |
| } |
| |
| sp<content::pm::IPackageManagerNative> packageMgr = |
| interface_cast<content::pm::IPackageManagerNative>(binder); |
| std::vector<int> uids{static_cast<int>(uid)}; |
| std::vector<std::string> names; |
| binder::Status status = packageMgr->getNamesForUids(uids, &names); |
| if (!status.isOk()) { |
| LOG(ERROR) << "package_native::getNamesForUids failed: " |
| << status.exceptionMessage().c_str(); |
| return false; |
| } |
| const std::string& packageName = names[0]; |
| |
| appPackageInfo->appPackageName = packageName; |
| int flags = 0; |
| status = packageMgr->getLocationFlags(packageName, &flags); |
| if (!status.isOk()) { |
| LOG(ERROR) << "package_native::getLocationFlags failed: " |
| << status.exceptionMessage().c_str(); |
| return false; |
| } |
| // isSystemApp() |
| appPackageInfo->appIsSystemApp = |
| ((flags & content::pm::IPackageManagerNative::LOCATION_SYSTEM) != 0); |
| // isVendor() |
| appPackageInfo->appIsOnVendorImage = |
| ((flags & content::pm::IPackageManagerNative::LOCATION_VENDOR) != 0); |
| // isProduct() |
| appPackageInfo->appIsOnProductImage = |
| ((flags & content::pm::IPackageManagerNative::LOCATION_PRODUCT) != 0); |
| return true; |
| } |
| |
| // Check if this process is allowed to use NNAPI Vendor extensions. |
| bool isNNAPIVendorExtensionsUseAllowed(const std::vector<std::string>& allowlist) { |
| TypeManager::AppPackageInfo appPackageInfo = { |
| .binaryPath = ::android::procpartition::getExe(getpid()), |
| .appPackageName = "", |
| .appIsSystemApp = false, |
| .appIsOnVendorImage = false, |
| .appIsOnProductImage = false}; |
| |
| if (appPackageInfo.binaryPath == "/system/bin/app_process64" || |
| appPackageInfo.binaryPath == "/system/bin/app_process32") { |
| if (!fetchAppPackageLocationInfo(getuid(), &appPackageInfo)) { |
| LOG(ERROR) << "Failed to get app information from package_manager_native"; |
| return false; |
| } |
| } |
| return TypeManager::isExtensionsUseAllowed( |
| appPackageInfo, isNNAPIVendorExtensionsUseAllowedInProductImage(), allowlist); |
| } |
| |
| } // namespace |
| |
| TypeManager::TypeManager() { |
| VLOG(MANAGER) << "TypeManager::TypeManager"; |
| mExtensionsAllowed = isNNAPIVendorExtensionsUseAllowed(getVendorExtensionAllowlistedApps()); |
| VLOG(MANAGER) << "NNAPI Vendor extensions enabled: " << mExtensionsAllowed; |
| findAvailableExtensions(); |
| } |
| |
| bool TypeManager::isExtensionsUseAllowed(const AppPackageInfo& appPackageInfo, |
| bool useOnProductImageEnabled, |
| const std::vector<std::string>& allowlist) { |
| // Only selected partitions and user-installed apps (/data) |
| // are allowed to use extensions. |
| if (StartsWith(appPackageInfo.binaryPath, "/vendor/") || |
| StartsWith(appPackageInfo.binaryPath, "/odm/") || |
| StartsWith(appPackageInfo.binaryPath, "/data/") || |
| (StartsWith(appPackageInfo.binaryPath, "/product/") && useOnProductImageEnabled)) { |
| #ifdef NN_DEBUGGABLE |
| // Only on userdebug and eng builds. |
| // When running tests with mma and adb push. |
| if (StartsWith(appPackageInfo.binaryPath, "/data/nativetest") || |
| // When running tests with Atest. |
| StartsWith(appPackageInfo.binaryPath, "/data/local/tmp/NeuralNetworksTest_")) { |
| return true; |
| } |
| #endif // NN_DEBUGGABLE |
| |
| return std::find(allowlist.begin(), allowlist.end(), appPackageInfo.binaryPath) != |
| allowlist.end(); |
| } else if (appPackageInfo.binaryPath == "/system/bin/app_process64" || |
| appPackageInfo.binaryPath == "/system/bin/app_process32") { |
| // App is not system app OR vendor app OR (product app AND product enabled) |
| // AND app is on allowlist. |
| return (!appPackageInfo.appIsSystemApp || appPackageInfo.appIsOnVendorImage || |
| (appPackageInfo.appIsOnProductImage && useOnProductImageEnabled)) && |
| std::find(allowlist.begin(), allowlist.end(), appPackageInfo.appPackageName) != |
| allowlist.end(); |
| } |
| return false; |
| } |
| |
| void TypeManager::findAvailableExtensions() { |
| for (const std::shared_ptr<Device>& device : mDeviceManager->getDrivers()) { |
| for (const Extension extension : device->getSupportedExtensions()) { |
| registerExtension(extension, device->getName()); |
| } |
| } |
| } |
| |
| bool TypeManager::registerExtension(Extension extension, const std::string& deviceName) { |
| if (mDisabledExtensions.find(extension.name) != mDisabledExtensions.end()) { |
| LOG(ERROR) << "Extension " << extension.name << " is disabled"; |
| return false; |
| } |
| |
| std::sort(extension.operandTypes.begin(), extension.operandTypes.end(), |
| [](const Extension::OperandTypeInformation& a, |
| const Extension::OperandTypeInformation& b) { |
| return static_cast<uint16_t>(a.type) < static_cast<uint16_t>(b.type); |
| }); |
| |
| std::map<std::string, Extension>::iterator it; |
| bool isNew; |
| std::tie(it, isNew) = mExtensionNameToExtension.emplace(extension.name, extension); |
| if (isNew) { |
| VLOG(MANAGER) << "Registered extension " << extension.name; |
| mExtensionNameToFirstDevice.emplace(extension.name, deviceName); |
| } else if (!equal(extension, it->second)) { |
| LOG(ERROR) << "Devices " << mExtensionNameToFirstDevice[extension.name] << " and " |
| << deviceName << " provide inconsistent information for extension " |
| << extension.name << ", which is therefore disabled"; |
| mExtensionNameToExtension.erase(it); |
| mDisabledExtensions.insert(extension.name); |
| return false; |
| } |
| return true; |
| } |
| |
| bool TypeManager::getExtensionPrefix(const std::string& extensionName, uint16_t* prefix) { |
| auto it = mExtensionNameToPrefix.find(extensionName); |
| if (it != mExtensionNameToPrefix.end()) { |
| *prefix = it->second; |
| } else { |
| NN_RET_CHECK_LE(mPrefixToExtension.size(), kMaxPrefix) << "Too many extensions in use"; |
| *prefix = mPrefixToExtension.size(); |
| mExtensionNameToPrefix[extensionName] = *prefix; |
| mPrefixToExtension.push_back(&mExtensionNameToExtension[extensionName]); |
| } |
| return true; |
| } |
| |
| bool TypeManager::getExtensionType(const char* extensionName, uint16_t typeWithinExtension, |
| int32_t* type) { |
| uint16_t prefix; |
| NN_RET_CHECK(getExtensionPrefix(extensionName, &prefix)); |
| *type = (prefix << kLowBitsType) | typeWithinExtension; |
| return true; |
| } |
| |
| bool TypeManager::getExtensionInfo(uint16_t prefix, const Extension** extension) const { |
| NN_RET_CHECK_NE(prefix, 0u) << "prefix=0 does not correspond to an extension"; |
| NN_RET_CHECK_LT(prefix, mPrefixToExtension.size()) << "Unknown extension prefix"; |
| *extension = mPrefixToExtension[prefix]; |
| return true; |
| } |
| |
| bool TypeManager::getExtensionOperandTypeInfo( |
| OperandType type, const Extension::OperandTypeInformation** info) const { |
| uint32_t operandType = static_cast<uint32_t>(type); |
| uint16_t prefix = operandType >> kLowBitsType; |
| uint16_t typeWithinExtension = operandType & ((1 << kLowBitsType) - 1); |
| const Extension* extension; |
| NN_RET_CHECK(getExtensionInfo(prefix, &extension)) |
| << "Cannot find extension corresponding to prefix " << prefix; |
| auto it = std::lower_bound( |
| extension->operandTypes.begin(), extension->operandTypes.end(), typeWithinExtension, |
| [](const Extension::OperandTypeInformation& info, uint32_t typeSought) { |
| return static_cast<uint16_t>(info.type) < typeSought; |
| }); |
| NN_RET_CHECK(it != extension->operandTypes.end() && |
| static_cast<uint16_t>(it->type) == typeWithinExtension) |
| << "Cannot find operand type " << typeWithinExtension << " in extension " |
| << extension->name; |
| *info = &*it; |
| return true; |
| } |
| |
| bool TypeManager::isTensorType(OperandType type) const { |
| if (!isExtensionOperandType(type)) { |
| return !nonExtensionOperandTypeIsScalar(static_cast<int>(type)); |
| } |
| const Extension::OperandTypeInformation* info; |
| CHECK(getExtensionOperandTypeInfo(type, &info)); |
| return info->isTensor; |
| } |
| |
| uint32_t TypeManager::getSizeOfData(OperandType type, |
| const std::vector<uint32_t>& dimensions) const { |
| if (!isExtensionOperandType(type)) { |
| return nonExtensionOperandSizeOfData(type, dimensions); |
| } |
| |
| const Extension::OperandTypeInformation* info; |
| CHECK(getExtensionOperandTypeInfo(type, &info)); |
| |
| if (!info->isTensor) { |
| return info->byteSize; |
| } |
| |
| if (dimensions.empty()) { |
| return 0; |
| } |
| |
| uint32_t size = info->byteSize; |
| for (auto dimension : dimensions) { |
| size *= dimension; |
| } |
| return size; |
| } |
| |
| } // namespace nn |
| } // namespace android |