1 /*
2  * Copyright (C) 2020 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 #include "Device.h"
18 
19 #include "Callbacks.h"
20 #include "Conversions.h"
21 #include "Utils.h"
22 
23 #include <android/hardware/neuralnetworks/1.0/types.h>
24 #include <android/hardware/neuralnetworks/1.1/types.h>
25 #include <android/hardware/neuralnetworks/1.2/IDevice.h>
26 #include <android/hardware/neuralnetworks/1.2/types.h>
27 #include <nnapi/IBuffer.h>
28 #include <nnapi/IDevice.h>
29 #include <nnapi/IPreparedModel.h>
30 #include <nnapi/OperandTypes.h>
31 #include <nnapi/Result.h>
32 #include <nnapi/Types.h>
33 #include <nnapi/hal/1.0/HandleError.h>
34 #include <nnapi/hal/1.0/ProtectCallback.h>
35 #include <nnapi/hal/1.1/Conversions.h>
36 #include <nnapi/hal/CommonUtils.h>
37 
38 #include <functional>
39 #include <memory>
40 #include <optional>
41 #include <string>
42 #include <vector>
43 
44 // See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
45 // lifetimes across processes and for protecting asynchronous calls across HIDL.
46 
47 namespace android::hardware::neuralnetworks::V1_2::utils {
48 namespace {
49 
capabilitiesCallback(V1_0::ErrorStatus status,const Capabilities & capabilities)50 nn::GeneralResult<nn::Capabilities> capabilitiesCallback(V1_0::ErrorStatus status,
51                                                          const Capabilities& capabilities) {
52     HANDLE_STATUS_HIDL(status) << "getting capabilities failed with " << toString(status);
53     return nn::convert(capabilities);
54 }
55 
versionStringCallback(V1_0::ErrorStatus status,const hidl_string & versionString)56 nn::GeneralResult<std::string> versionStringCallback(V1_0::ErrorStatus status,
57                                                      const hidl_string& versionString) {
58     HANDLE_STATUS_HIDL(status) << "getVersionString failed with " << toString(status);
59     return versionString;
60 }
61 
deviceTypeCallback(V1_0::ErrorStatus status,DeviceType deviceType)62 nn::GeneralResult<nn::DeviceType> deviceTypeCallback(V1_0::ErrorStatus status,
63                                                      DeviceType deviceType) {
64     HANDLE_STATUS_HIDL(status) << "getDeviceType failed with " << toString(status);
65     return nn::convert(deviceType);
66 }
67 
supportedExtensionsCallback(V1_0::ErrorStatus status,const hidl_vec<Extension> & extensions)68 nn::GeneralResult<std::vector<nn::Extension>> supportedExtensionsCallback(
69         V1_0::ErrorStatus status, const hidl_vec<Extension>& extensions) {
70     HANDLE_STATUS_HIDL(status) << "getExtensions failed with " << toString(status);
71     return nn::convert(extensions);
72 }
73 
numberOfCacheFilesNeededCallback(V1_0::ErrorStatus status,uint32_t numModelCache,uint32_t numDataCache)74 nn::GeneralResult<std::pair<uint32_t, uint32_t>> numberOfCacheFilesNeededCallback(
75         V1_0::ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
76     HANDLE_STATUS_HIDL(status) << "getNumberOfCacheFilesNeeded failed with " << toString(status);
77     if (numModelCache > nn::kMaxNumberOfCacheFiles) {
78         return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numModelCache files greater "
79                              "than allowed max ("
80                           << numModelCache << " vs " << nn::kMaxNumberOfCacheFiles << ")";
81     }
82     if (numDataCache > nn::kMaxNumberOfCacheFiles) {
83         return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numDataCache files greater "
84                              "than allowed max ("
85                           << numDataCache << " vs " << nn::kMaxNumberOfCacheFiles << ")";
86     }
87     return std::make_pair(numModelCache, numDataCache);
88 }
89 
getCapabilitiesFrom(V1_2::IDevice * device)90 nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(V1_2::IDevice* device) {
91     CHECK(device != nullptr);
92 
93     auto cb = hal::utils::CallbackValue(capabilitiesCallback);
94 
95     const auto ret = device->getCapabilities_1_2(cb);
96     HANDLE_TRANSPORT_FAILURE(ret);
97 
98     return cb.take();
99 }
100 
101 }  // namespace
102 
getVersionStringFrom(V1_2::IDevice * device)103 nn::GeneralResult<std::string> getVersionStringFrom(V1_2::IDevice* device) {
104     CHECK(device != nullptr);
105 
106     auto cb = hal::utils::CallbackValue(versionStringCallback);
107 
108     const auto ret = device->getVersionString(cb);
109     HANDLE_TRANSPORT_FAILURE(ret);
110 
111     return cb.take();
112 }
113 
getDeviceTypeFrom(V1_2::IDevice * device)114 nn::GeneralResult<nn::DeviceType> getDeviceTypeFrom(V1_2::IDevice* device) {
115     CHECK(device != nullptr);
116 
117     auto cb = hal::utils::CallbackValue(deviceTypeCallback);
118 
119     const auto ret = device->getType(cb);
120     HANDLE_TRANSPORT_FAILURE(ret);
121 
122     return cb.take();
123 }
124 
getSupportedExtensionsFrom(V1_2::IDevice * device)125 nn::GeneralResult<std::vector<nn::Extension>> getSupportedExtensionsFrom(V1_2::IDevice* device) {
126     CHECK(device != nullptr);
127 
128     auto cb = hal::utils::CallbackValue(supportedExtensionsCallback);
129 
130     const auto ret = device->getSupportedExtensions(cb);
131     HANDLE_TRANSPORT_FAILURE(ret);
132 
133     return cb.take();
134 }
135 
getNumberOfCacheFilesNeededFrom(V1_2::IDevice * device)136 nn::GeneralResult<std::pair<uint32_t, uint32_t>> getNumberOfCacheFilesNeededFrom(
137         V1_2::IDevice* device) {
138     CHECK(device != nullptr);
139 
140     auto cb = hal::utils::CallbackValue(numberOfCacheFilesNeededCallback);
141 
142     const auto ret = device->getNumberOfCacheFilesNeeded(cb);
143     HANDLE_TRANSPORT_FAILURE(ret);
144 
145     return cb.take();
146 }
147 
create(std::string name,sp<V1_2::IDevice> device)148 nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
149                                                                 sp<V1_2::IDevice> device) {
150     if (name.empty()) {
151         return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
152                << "V1_2::utils::Device::create must have non-empty name";
153     }
154     if (device == nullptr) {
155         return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
156                << "V1_2::utils::Device::create must have non-null device";
157     }
158 
159     auto versionString = NN_TRY(getVersionStringFrom(device.get()));
160     const auto deviceType = NN_TRY(getDeviceTypeFrom(device.get()));
161     auto extensions = NN_TRY(getSupportedExtensionsFrom(device.get()));
162     auto capabilities = NN_TRY(getCapabilitiesFrom(device.get()));
163     const auto numberOfCacheFilesNeeded = NN_TRY(getNumberOfCacheFilesNeededFrom(device.get()));
164 
165     auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
166     return std::make_shared<const Device>(
167             PrivateConstructorTag{}, std::move(name), std::move(versionString), deviceType,
168             std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
169             std::move(device), std::move(deathHandler));
170 }
171 
Device(PrivateConstructorTag,std::string name,std::string versionString,nn::DeviceType deviceType,std::vector<nn::Extension> extensions,nn::Capabilities capabilities,std::pair<uint32_t,uint32_t> numberOfCacheFilesNeeded,sp<V1_2::IDevice> device,hal::utils::DeathHandler deathHandler)172 Device::Device(PrivateConstructorTag /*tag*/, std::string name, std::string versionString,
173                nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
174                nn::Capabilities capabilities,
175                std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded, sp<V1_2::IDevice> device,
176                hal::utils::DeathHandler deathHandler)
177     : kName(std::move(name)),
178       kVersionString(std::move(versionString)),
179       kDeviceType(deviceType),
180       kExtensions(std::move(extensions)),
181       kCapabilities(std::move(capabilities)),
182       kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded),
183       kDevice(std::move(device)),
184       kDeathHandler(std::move(deathHandler)) {}
185 
getName() const186 const std::string& Device::getName() const {
187     return kName;
188 }
189 
getVersionString() const190 const std::string& Device::getVersionString() const {
191     return kVersionString;
192 }
193 
getFeatureLevel() const194 nn::Version Device::getFeatureLevel() const {
195     return kVersion;
196 }
197 
getType() const198 nn::DeviceType Device::getType() const {
199     return kDeviceType;
200 }
201 
getSupportedExtensions() const202 const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
203     return kExtensions;
204 }
205 
getCapabilities() const206 const nn::Capabilities& Device::getCapabilities() const {
207     return kCapabilities;
208 }
209 
getNumberOfCacheFilesNeeded() const210 std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
211     return kNumberOfCacheFilesNeeded;
212 }
213 
wait() const214 nn::GeneralResult<void> Device::wait() const {
215     const auto ret = kDevice->ping();
216     HANDLE_TRANSPORT_FAILURE(ret);
217     return {};
218 }
219 
getSupportedOperations(const nn::Model & model) const220 nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
221     // Ensure that model is ready for IPC.
222     std::optional<nn::Model> maybeModelInShared;
223     const nn::Model& modelInShared =
224             NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
225 
226     const auto hidlModel = NN_TRY(convert(modelInShared));
227 
228     auto cb = hal::utils::CallbackValue(V1_0::utils::supportedOperationsCallback);
229 
230     const auto ret = kDevice->getSupportedOperations_1_2(hidlModel, cb);
231     HANDLE_TRANSPORT_FAILURE(ret);
232 
233     return cb.take();
234 }
235 
prepareModel(const nn::Model & model,nn::ExecutionPreference preference,nn::Priority,nn::OptionalTimePoint,const std::vector<nn::SharedHandle> & modelCache,const std::vector<nn::SharedHandle> & dataCache,const nn::CacheToken & token,const std::vector<nn::TokenValuePair> &,const std::vector<nn::ExtensionNameAndPrefix> &) const236 nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
237         const nn::Model& model, nn::ExecutionPreference preference, nn::Priority /*priority*/,
238         nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& modelCache,
239         const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token,
240         const std::vector<nn::TokenValuePair>& /*hints*/,
241         const std::vector<nn::ExtensionNameAndPrefix>& /*extensionNameToPrefix*/) const {
242     // Ensure that model is ready for IPC.
243     std::optional<nn::Model> maybeModelInShared;
244     const nn::Model& modelInShared =
245             NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
246 
247     const auto hidlModel = NN_TRY(convert(modelInShared));
248     const auto hidlPreference = NN_TRY(convert(preference));
249     const auto hidlModelCache = NN_TRY(convert(modelCache));
250     const auto hidlDataCache = NN_TRY(convert(dataCache));
251     const auto hidlToken = CacheToken{token};
252 
253     const auto cb = sp<PreparedModelCallback>::make();
254     const auto scoped = kDeathHandler.protectCallback(cb.get());
255 
256     const auto ret = kDevice->prepareModel_1_2(hidlModel, hidlPreference, hidlModelCache,
257                                                hidlDataCache, hidlToken, cb);
258     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
259     HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
260 
261     return cb->get();
262 }
263 
prepareModelFromCache(nn::OptionalTimePoint,const std::vector<nn::SharedHandle> & modelCache,const std::vector<nn::SharedHandle> & dataCache,const nn::CacheToken & token) const264 nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
265         nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& modelCache,
266         const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
267     const auto hidlModelCache = NN_TRY(convert(modelCache));
268     const auto hidlDataCache = NN_TRY(convert(dataCache));
269     const auto hidlToken = CacheToken{token};
270 
271     const auto cb = sp<PreparedModelCallback>::make();
272     const auto scoped = kDeathHandler.protectCallback(cb.get());
273 
274     const auto ret = kDevice->prepareModelFromCache(hidlModelCache, hidlDataCache, hidlToken, cb);
275     const auto status = HANDLE_TRANSPORT_FAILURE(ret);
276     HANDLE_STATUS_HIDL(status) << "model preparation from cache failed with " << toString(status);
277 
278     return cb->get();
279 }
280 
allocate(const nn::BufferDesc &,const std::vector<nn::SharedPreparedModel> &,const std::vector<nn::BufferRole> &,const std::vector<nn::BufferRole> &) const281 nn::GeneralResult<nn::SharedBuffer> Device::allocate(
282         const nn::BufferDesc& /*desc*/,
283         const std::vector<nn::SharedPreparedModel>& /*preparedModels*/,
284         const std::vector<nn::BufferRole>& /*inputRoles*/,
285         const std::vector<nn::BufferRole>& /*outputRoles*/) const {
286     return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
287            << "IDevice::allocate not supported on 1.2 HAL service";
288 }
289 
290 }  // namespace android::hardware::neuralnetworks::V1_2::utils
291