blob: 6ea58de33ad0b1c9c00954b78acdafd6ae583a35 [file] [log] [blame]
/*
* Copyright (C) 2021 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 "ShimDeviceManager"
#include "ShimDeviceManager.h"
#include <AndroidVersionUtil.h>
#include <aidl/android/hardware/neuralnetworks/IDevice.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <nnapi/hal/aidl/InvalidDevice.h>
#include <algorithm>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "NeuralNetworksShim.h"
#include "ShimDevice.h"
#include "ShimUtils.h"
#include "SupportLibrary.h"
namespace android::neuralnetworks::shim {
namespace {
using aidl::android::hardware::neuralnetworks::IDevice;
using aidl::android::hardware::neuralnetworks::InvalidDevice;
using aidl::android::hardware::neuralnetworks::ShimDevice;
ANeuralNetworksShimResultCode registerEagerService(const std::shared_ptr<IDevice>& device,
const std::string& name) {
const binder_exception_t status =
AServiceManager_addService(device->asBinder().get(), name.c_str());
if (status != EX_NONE) {
LOG(ERROR) << "AServiceManager_addService failed for " << name << ", error code " << status;
return ANNSHIM_FAILED_TO_REGISTER_SERVICE;
}
return ANNSHIM_NO_ERROR;
}
ANeuralNetworksShimResultCode registerLazyService(const std::shared_ptr<IDevice>& device,
const std::string& name) {
if (__builtin_available(android __NNAPI_AIDL_MIN_ANDROID_API__, *)) {
const binder_status_t status =
AServiceManager_registerLazyService(device->asBinder().get(), name.c_str());
if (status != STATUS_OK) {
LOG(ERROR) << "Service registration failed for " << name << ", error code " << status;
return ANNSHIM_FAILED_TO_REGISTER_SERVICE;
}
return ANNSHIM_NO_ERROR;
}
LOG(ERROR) << "Service registration failed for " << name
<< " because AServiceManager_registerLazyService is not supported until API "
"level 31";
return ANNSHIM_FAILED_TO_REGISTER_SERVICE;
}
ANeuralNetworksShimResultCode registerService(const std::shared_ptr<IDevice>& device,
const std::string& name, bool registerAsLazy) {
const std::string instance = std::string(ShimDevice::descriptor) + "/" + name;
LOG(INFO) << "Attempting service registration for " << instance;
return registerAsLazy ? registerLazyService(device, instance)
: registerEagerService(device, instance);
}
std::unordered_map<std::string, ANeuralNetworksDevice*> getNamedDevices(
const std::shared_ptr<const NnApiSupportLibrary>& nnapi) {
uint32_t numDevices;
if (nnapi->ANeuralNetworks_getDeviceCount(&numDevices) != ANEURALNETWORKS_NO_ERROR) {
LOG(ERROR) << "Failed ANeuralNetworks_getDeviceCount";
return {};
}
std::unordered_map<std::string, ANeuralNetworksDevice*> nameToDevice;
for (uint32_t i = 0; i < numDevices; ++i) {
ANeuralNetworksDevice* device;
if (nnapi->ANeuralNetworks_getDevice(i, &device) != ANEURALNETWORKS_NO_ERROR) {
LOG(ERROR) << "Failed ANeuralNetworks_getDevice";
return {};
}
const char* name = nullptr;
if (nnapi->ANeuralNetworksDevice_getName(device, &name) != ANEURALNETWORKS_NO_ERROR) {
LOG(ERROR) << "Failed ANeuralNetworks_getName";
return {};
}
nameToDevice.emplace(name, device);
}
return nameToDevice;
}
} // namespace
ANeuralNetworksShimResultCode registerDevices(NnApiSLDriverImpl* nnapiSLImpl,
const std::vector<ShimDeviceInfo>& devicesToRegister,
uint32_t numberOfListenerThreads,
bool registerAsLazyService,
bool fallbackToMinimumSupportDevice) {
if (nnapiSLImpl == nullptr) {
LOG(ERROR) << "Invalid arguments, nnapiSLImpl == nullptr ";
return ANNSHIM_INVALID_ARGUMENT;
}
if (devicesToRegister.empty()) {
LOG(ERROR) << "Invalid arguments, devicesToRegister is empty";
return ANNSHIM_INVALID_ARGUMENT;
}
if (nnapiSLImpl->implFeatureLevel < ANEURALNETWORKS_FEATURE_LEVEL_5) {
LOG(ERROR) << "Invalid implStructFeatureLevel if NnApiSLDriverImpl, has to be at least "
"ANEURALNETWORKS_FEATURE_LEVEL_5";
return ANNSHIM_FAILED_TO_LOAD_SL;
}
if (nnapiSLImpl->implFeatureLevel > ANEURALNETWORKS_FEATURE_LEVEL_5) {
LOG(ERROR) << "Invalid implStructFeatureLevel if NnApiSLDriverImpl, latest supported "
"version is ANEURALNETWORKS_FEATURE_LEVEL_5";
return ANNSHIM_FAILED_TO_LOAD_SL;
}
const std::shared_ptr<const NnApiSupportLibrary> nnapi =
std::make_shared<const NnApiSupportLibrary>(
*reinterpret_cast<NnApiSLDriverImplFL5*>(nnapiSLImpl), nullptr);
ABinderProcess_setThreadPoolMaxThreadCount(numberOfListenerThreads);
const auto nameToDevice = getNamedDevices(nnapi);
std::vector<std::shared_ptr<IDevice>> devices;
devices.reserve(devicesToRegister.size());
// Convert all supplied devices to AIDL IDevice interfaces.
for (const auto& info : devicesToRegister) {
const auto& name = info.deviceName;
if (const auto iter = nameToDevice.find(name); iter != nameToDevice.end()) {
ANeuralNetworksDevice* device = iter->second;
auto shimDevice = ndk::SharedRefBase::make<ShimDevice>(nnapi, device, info.serviceName);
devices.push_back(std::move(shimDevice));
continue;
}
if (!fallbackToMinimumSupportDevice) {
LOG(ERROR) << "NNAPI device " << name
<< " was not found in the support library package, and falling back to a "
"minimum support device was not specified";
return ANNSHIM_FAILED_TO_REGISTER_SERVICE;
}
// If the device was not found in the support library package, and falling back on a
// minimum support device is allowed, construct a minimum support device.
LOG(INFO) << "NNAPI device " << name
<< " was not found in the support library package, and falling back to a "
"minimal support device is allowed, so a minimal support device "
"is being registered instead.";
devices.push_back(InvalidDevice::create());
}
CHECK_EQ(devices.size(), devicesToRegister.size());
// Register all AIDL IDevice interfaces.
for (size_t i = 0; i < devicesToRegister.size(); i++) {
const auto& info = devicesToRegister[i];
const auto& device = devices[i];
const auto registrationResult =
registerService(device, info.serviceName, registerAsLazyService);
if (registrationResult != ANNSHIM_NO_ERROR) {
// This will only fail if there is a problem with Binder or if there is a mismatch
// between the service being registered and the service listed on the device manifest.
// Falling back to a minimum support device would not help resolve this whatever
// mismatch may exist, so there is no fallback path at this stage.
return registrationResult;
}
}
LOG(INFO) << devices.size() << " NNAPI Devices/services registered, blocking";
ABinderProcess_joinThreadPool();
// Shouldn't reach here.
LOG(ERROR) << "ABinderProcess_joinThreadPool unexpected returned in registerDevices.";
return ANNSHIM_GENERAL_ERROR;
}
} // namespace android::neuralnetworks::shim