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