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