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->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->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->ANeuralNetworksDevice_getName(device, &name) != ANEURALNETWORKS_NO_ERROR) {
101 LOG(ERROR) << "Failed ANeuralNetworks_getName";
102 return {};
103 }
104
105 nameToDevice.emplace(name, device);
106 }
107
108 return nameToDevice;
109 }
110
111 } // namespace
112
registerDevices(NnApiSLDriverImpl * nnapiSLImpl,const std::vector<ShimDeviceInfo> & devicesToRegister,uint32_t numberOfListenerThreads,bool registerAsLazyService,bool fallbackToMinimumSupportDevice)113 ANeuralNetworksShimResultCode registerDevices(NnApiSLDriverImpl* nnapiSLImpl,
114 const std::vector<ShimDeviceInfo>& devicesToRegister,
115 uint32_t numberOfListenerThreads,
116 bool registerAsLazyService,
117 bool fallbackToMinimumSupportDevice) {
118 if (nnapiSLImpl == nullptr) {
119 LOG(ERROR) << "Invalid arguments, nnapiSLImpl == nullptr ";
120 return ANNSHIM_INVALID_ARGUMENT;
121 }
122 if (devicesToRegister.empty()) {
123 LOG(ERROR) << "Invalid arguments, devicesToRegister is empty";
124 return ANNSHIM_INVALID_ARGUMENT;
125 }
126
127 if (nnapiSLImpl->implFeatureLevel < ANEURALNETWORKS_FEATURE_LEVEL_5) {
128 LOG(ERROR) << "Invalid implStructFeatureLevel if NnApiSLDriverImpl, has to be at least "
129 "ANEURALNETWORKS_FEATURE_LEVEL_5";
130 return ANNSHIM_FAILED_TO_LOAD_SL;
131 }
132
133 if (nnapiSLImpl->implFeatureLevel > ANEURALNETWORKS_FEATURE_LEVEL_5) {
134 LOG(ERROR) << "Invalid implStructFeatureLevel if NnApiSLDriverImpl, latest supported "
135 "version is ANEURALNETWORKS_FEATURE_LEVEL_5";
136 return ANNSHIM_FAILED_TO_LOAD_SL;
137 }
138
139 const std::shared_ptr<const NnApiSupportLibrary> nnapi =
140 std::make_shared<const NnApiSupportLibrary>(
141 *reinterpret_cast<NnApiSLDriverImplFL5*>(nnapiSLImpl), nullptr);
142
143 ABinderProcess_setThreadPoolMaxThreadCount(numberOfListenerThreads);
144
145 const auto nameToDevice = getNamedDevices(nnapi);
146 std::vector<std::shared_ptr<IDevice>> devices;
147 devices.reserve(devicesToRegister.size());
148
149 // Convert all supplied devices to AIDL IDevice interfaces.
150 for (const auto& info : devicesToRegister) {
151 const auto& name = info.deviceName;
152
153 if (const auto iter = nameToDevice.find(name); iter != nameToDevice.end()) {
154 ANeuralNetworksDevice* device = iter->second;
155
156 auto shimDevice = ndk::SharedRefBase::make<ShimDevice>(nnapi, device, info.serviceName);
157 devices.push_back(std::move(shimDevice));
158 continue;
159 }
160
161 if (!fallbackToMinimumSupportDevice) {
162 LOG(ERROR) << "NNAPI device " << name
163 << " was not found in the support library package, and falling back to a "
164 "minimum support device was not specified";
165 return ANNSHIM_FAILED_TO_REGISTER_SERVICE;
166 }
167
168 // If the device was not found in the support library package, and falling back on a
169 // minimum support device is allowed, construct a minimum support device.
170 LOG(INFO) << "NNAPI device " << name
171 << " was not found in the support library package, and falling back to a "
172 "minimal support device is allowed, so a minimal support device "
173 "is being registered instead.";
174 devices.push_back(InvalidDevice::create());
175 }
176
177 CHECK_EQ(devices.size(), devicesToRegister.size());
178
179 // Register all AIDL IDevice interfaces.
180 for (size_t i = 0; i < devicesToRegister.size(); i++) {
181 const auto& info = devicesToRegister[i];
182 const auto& device = devices[i];
183
184 const auto registrationResult =
185 registerService(device, info.serviceName, registerAsLazyService);
186 if (registrationResult != ANNSHIM_NO_ERROR) {
187 // This will only fail if there is a problem with Binder or if there is a mismatch
188 // between the service being registered and the service listed on the device manifest.
189 // Falling back to a minimum support device would not help resolve this whatever
190 // mismatch may exist, so there is no fallback path at this stage.
191 return registrationResult;
192 }
193 }
194
195 LOG(INFO) << devices.size() << " NNAPI Devices/services registered, blocking";
196 ABinderProcess_joinThreadPool();
197
198 // Shouldn't reach here.
199 LOG(ERROR) << "ABinderProcess_joinThreadPool unexpected returned in registerDevices.";
200 return ANNSHIM_GENERAL_ERROR;
201 }
202
203 } // namespace android::neuralnetworks::shim
204