1 /*
2 * Copyright (C) 2021 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 #define LOG_TAG "ShimDeviceManager"
18
19 #include "ShimDeviceManager.h"
20
21 #include <AndroidVersionUtil.h>
22 #include <aidl/android/hardware/neuralnetworks/IDevice.h>
23 #include <android-base/logging.h>
24 #include <android/binder_manager.h>
25 #include <android/binder_process.h>
26 #include <nnapi/hal/aidl/InvalidDevice.h>
27
28 #include <algorithm>
29 #include <memory>
30 #include <string>
31 #include <unordered_map>
32 #include <utility>
33 #include <vector>
34
35 #include "NeuralNetworksShim.h"
36 #include "ShimDevice.h"
37 #include "ShimUtils.h"
38 #include "SupportLibrary.h"
39
40 namespace android::neuralnetworks::shim {
41 namespace {
42
43 using aidl::android::hardware::neuralnetworks::IDevice;
44 using aidl::android::hardware::neuralnetworks::InvalidDevice;
45 using aidl::android::hardware::neuralnetworks::ShimDevice;
46
registerEagerService(const std::shared_ptr<IDevice> & device,const std::string & name)47 ANeuralNetworksShimResultCode registerEagerService(const std::shared_ptr<IDevice>& device,
48 const std::string& name) {
49 const binder_exception_t status =
50 AServiceManager_addService(device->asBinder().get(), name.c_str());
51 if (status != EX_NONE) {
52 LOG(ERROR) << "AServiceManager_addService failed for " << name << ", error code " << status;
53 return ANNSHIM_FAILED_TO_REGISTER_SERVICE;
54 }
55 return ANNSHIM_NO_ERROR;
56 }
57
registerLazyService(const std::shared_ptr<IDevice> & device,const std::string & name)58 ANeuralNetworksShimResultCode registerLazyService(const std::shared_ptr<IDevice>& device,
59 const std::string& name) {
60 if (__builtin_available(android __NNAPI_AIDL_MIN_ANDROID_API__, *)) {
61 const binder_status_t status =
62 AServiceManager_registerLazyService(device->asBinder().get(), name.c_str());
63 if (status != STATUS_OK) {
64 LOG(ERROR) << "Service registration failed for " << name << ", error code " << status;
65 return ANNSHIM_FAILED_TO_REGISTER_SERVICE;
66 }
67 return ANNSHIM_NO_ERROR;
68 }
69 LOG(ERROR) << "Service registration failed for " << name
70 << " because AServiceManager_registerLazyService is not supported until API "
71 "level 31";
72 return ANNSHIM_FAILED_TO_REGISTER_SERVICE;
73 }
74
registerService(const std::shared_ptr<IDevice> & device,const std::string & name,bool registerAsLazy)75 ANeuralNetworksShimResultCode registerService(const std::shared_ptr<IDevice>& device,
76 const std::string& name, bool registerAsLazy) {
77 const std::string instance = std::string(ShimDevice::descriptor) + "/" + name;
78 LOG(INFO) << "Attempting service registration for " << instance;
79 return registerAsLazy ? registerLazyService(device, instance)
80 : registerEagerService(device, instance);
81 }
82
getNamedDevices(const std::shared_ptr<const NnApiSupportLibrary> & nnapi)83 std::unordered_map<std::string, ANeuralNetworksDevice*> getNamedDevices(
84 const std::shared_ptr<const NnApiSupportLibrary>& nnapi) {
85 uint32_t numDevices;
86 if (nnapi->getFL5()->ANeuralNetworks_getDeviceCount(&numDevices) != ANEURALNETWORKS_NO_ERROR) {
87 LOG(ERROR) << "Failed ANeuralNetworks_getDeviceCount";
88 return {};
89 }
90
91 std::unordered_map<std::string, ANeuralNetworksDevice*> nameToDevice;
92 for (uint32_t i = 0; i < numDevices; ++i) {
93 ANeuralNetworksDevice* device;
94 if (nnapi->getFL5()->ANeuralNetworks_getDevice(i, &device) != ANEURALNETWORKS_NO_ERROR) {
95 LOG(ERROR) << "Failed ANeuralNetworks_getDevice";
96 return {};
97 }
98
99 const char* name = nullptr;
100 if (nnapi->getFL5()->ANeuralNetworksDevice_getName(device, &name) !=
101 ANEURALNETWORKS_NO_ERROR) {
102 LOG(ERROR) << "Failed ANeuralNetworks_getName";
103 return {};
104 }
105
106 nameToDevice.emplace(name, device);
107 }
108
109 return nameToDevice;
110 }
111
112 } // namespace
113
registerDevices(NnApiSLDriverImpl * nnapiSLImpl,const std::vector<ShimDeviceInfo> & devicesToRegister,uint32_t numberOfListenerThreads,bool registerAsLazyService,bool fallbackToMinimumSupportDevice)114 ANeuralNetworksShimResultCode registerDevices(NnApiSLDriverImpl* nnapiSLImpl,
115 const std::vector<ShimDeviceInfo>& devicesToRegister,
116 uint32_t numberOfListenerThreads,
117 bool registerAsLazyService,
118 bool fallbackToMinimumSupportDevice) {
119 if (nnapiSLImpl == nullptr) {
120 LOG(ERROR) << "Invalid arguments, nnapiSLImpl == nullptr ";
121 return ANNSHIM_INVALID_ARGUMENT;
122 }
123 if (devicesToRegister.empty()) {
124 LOG(ERROR) << "Invalid arguments, devicesToRegister is empty";
125 return ANNSHIM_INVALID_ARGUMENT;
126 }
127
128 if (nnapiSLImpl->implFeatureLevel < ANEURALNETWORKS_FEATURE_LEVEL_5) {
129 LOG(ERROR) << "Invalid implStructFeatureLevel if NnApiSLDriverImpl, has to be at least "
130 "ANEURALNETWORKS_FEATURE_LEVEL_5";
131 return ANNSHIM_FAILED_TO_LOAD_SL;
132 }
133
134 // NnApiSLDriverImplFL[5-6] are identical, hence we can just cast to latest one.
135 std::shared_ptr<const NnApiSupportLibrary> nnapi;
136
137 if (nnapiSLImpl->implFeatureLevel == ANEURALNETWORKS_FEATURE_LEVEL_5) {
138 nnapi = std::make_unique<NnApiSupportLibrary>(
139 *reinterpret_cast<NnApiSLDriverImplFL5*>(nnapiSLImpl), nullptr);
140 }
141 if (nnapiSLImpl->implFeatureLevel == ANEURALNETWORKS_FEATURE_LEVEL_6) {
142 nnapi = std::make_unique<NnApiSupportLibrary>(
143 *reinterpret_cast<NnApiSLDriverImplFL6*>(nnapiSLImpl), nullptr);
144 }
145 if (nnapiSLImpl->implFeatureLevel == ANEURALNETWORKS_FEATURE_LEVEL_7) {
146 nnapi = std::make_unique<NnApiSupportLibrary>(
147 *reinterpret_cast<NnApiSLDriverImplFL7*>(nnapiSLImpl), nullptr);
148 }
149 if (nnapiSLImpl->implFeatureLevel >= ANEURALNETWORKS_FEATURE_LEVEL_8) {
150 nnapi = std::make_unique<NnApiSupportLibrary>(
151 *reinterpret_cast<NnApiSLDriverImplFL8*>(nnapiSLImpl), nullptr);
152 }
153
154 CHECK_NE(nnapi, nullptr);
155
156 ABinderProcess_setThreadPoolMaxThreadCount(numberOfListenerThreads);
157
158 const auto nameToDevice = getNamedDevices(nnapi);
159 std::vector<std::shared_ptr<IDevice>> devices;
160 devices.reserve(devicesToRegister.size());
161
162 // Convert all supplied devices to AIDL IDevice interfaces.
163 for (const auto& info : devicesToRegister) {
164 const auto& name = info.deviceName;
165
166 if (const auto iter = nameToDevice.find(name); iter != nameToDevice.end()) {
167 ANeuralNetworksDevice* device = iter->second;
168
169 auto shimDevice = ndk::SharedRefBase::make<ShimDevice>(nnapi, device, info.serviceName);
170 devices.push_back(std::move(shimDevice));
171 continue;
172 }
173
174 if (!fallbackToMinimumSupportDevice) {
175 LOG(ERROR) << "NNAPI device " << name
176 << " was not found in the support library package, and falling back to a "
177 "minimum support device was not specified";
178 return ANNSHIM_FAILED_TO_REGISTER_SERVICE;
179 }
180
181 // If the device was not found in the support library package, and falling back on a
182 // minimum support device is allowed, construct a minimum support device.
183 LOG(INFO) << "NNAPI device " << name
184 << " was not found in the support library package, and falling back to a "
185 "minimal support device is allowed, so a minimal support device "
186 "is being registered instead.";
187 devices.push_back(InvalidDevice::create());
188 }
189
190 CHECK_EQ(devices.size(), devicesToRegister.size());
191
192 // Register all AIDL IDevice interfaces.
193 for (size_t i = 0; i < devicesToRegister.size(); i++) {
194 const auto& info = devicesToRegister[i];
195 const auto& device = devices[i];
196
197 const auto registrationResult =
198 registerService(device, info.serviceName, registerAsLazyService);
199 if (registrationResult != ANNSHIM_NO_ERROR) {
200 // This will only fail if there is a problem with Binder or if there is a mismatch
201 // between the service being registered and the service listed on the device manifest.
202 // Falling back to a minimum support device would not help resolve this whatever
203 // mismatch may exist, so there is no fallback path at this stage.
204 return registrationResult;
205 }
206 }
207
208 LOG(INFO) << devices.size() << " NNAPI Devices/services registered, blocking";
209 ABinderProcess_joinThreadPool();
210
211 // Shouldn't reach here.
212 LOG(ERROR) << "ABinderProcess_joinThreadPool unexpected returned in registerDevices.";
213 return ANNSHIM_GENERAL_ERROR;
214 }
215
216 } // namespace android::neuralnetworks::shim
217