• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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