1 /*
2  * Copyright (C) 2017 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 // This file contains pre-canonical-types utility code and includes HAL
17 // utilities. LegacyUtils.h is the subset of these utilities that do not touch
18 // HAL.
19 
20 #ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_COMMON_LEGACY_HAL_UTILS_H
21 #define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_COMMON_LEGACY_HAL_UTILS_H
22 
23 #include <android-base/logging.h>
24 #include <nnapi/TypeUtils.h>
25 #include <nnapi/Types.h>
26 
27 #include <set>
28 #include <string>
29 #include <tuple>
30 #include <utility>
31 #include <vector>
32 
33 #include "HalInterfaces.h"
34 #include "LegacyUtils.h"
35 #include "NeuralNetworks.h"
36 #include "ValidateHal.h"
37 
38 namespace android {
39 namespace nn {
40 
41 using LegacyClock = std::chrono::steady_clock;
42 using LegacyDuration = std::chrono::duration<uint64_t, std::nano>;
43 using LegacyOptionalDuration = std::optional<LegacyDuration>;
44 using LegacyTimePoint = std::chrono::time_point<std::chrono::steady_clock, LegacyDuration>;
45 using LegacyOptionalTimePoint = std::optional<LegacyTimePoint>;
46 
47 // Make an optional deadline from an V1_3::OptionalTimePoint.
48 LegacyOptionalTimePoint makeDeadline(const V1_3::OptionalTimePoint& timePoint);
49 
50 // Make an optional deadline from an V1_3::OptionalDuration.
51 LegacyOptionalTimePoint makeDeadline(const V1_3::OptionalTimeoutDuration& optionalDuration);
52 
53 // Returns true if the deadline has passed. Returns false if either the deadline
54 // has not been exceeded or if the deadline is not present.
55 bool hasDeadlinePassed(const LegacyOptionalTimePoint& deadline);
56 
57 // Ensure that every user of FalseyErrorStream is linked to the
58 // correct instance, using the correct LOG_TAG
59 namespace {
60 
61 template <HalVersion version>
62 struct VersionedType {};
63 
64 template <>
65 struct VersionedType<HalVersion::V1_2> {
66     using OperandPerformance = V1_2::Capabilities::OperandPerformance;
67     using OperandType = V1_2::OperandType;
68 };
69 
70 template <>
71 struct VersionedType<HalVersion::V1_3> {
72     using OperandPerformance = V1_3::Capabilities::OperandPerformance;
73     using OperandType = V1_3::OperandType;
74 };
75 
76 template <HalVersion version>
77 using VersionedOperandPerformance = typename VersionedType<version>::OperandPerformance;
78 template <HalVersion version>
79 using VersionedOperandType = typename VersionedType<version>::OperandType;
80 
81 }  // namespace
82 
83 // Return a vector with one entry for each non-extension OperandType except
84 // SUBGRAPH, set to the specified PerformanceInfo value.  The vector will be
85 // sorted by OperandType.
86 //
87 // Control flow (OperandType::SUBGRAPH) operation performance is specified
88 // separately using Capabilities::ifPerformance and
89 // Capabilities::whilePerformance.
90 template <HalVersion version>
91 hardware::hidl_vec<VersionedOperandPerformance<version>> nonExtensionOperandPerformance(
92         V1_0::PerformanceInfo perf);
93 
94 // Update the vector entry corresponding to the specified OperandType with the
95 // specified PerformanceInfo value.  The vector must already have an entry for
96 // that OperandType, and must be sorted by OperandType.
97 void update(hardware::hidl_vec<V1_2::Capabilities::OperandPerformance>* operandPerformance,
98             V1_2::OperandType type, V1_0::PerformanceInfo perf);
99 void update(hardware::hidl_vec<V1_3::Capabilities::OperandPerformance>* operandPerformance,
100             V1_3::OperandType type, V1_0::PerformanceInfo perf);
101 
102 // Look for a vector entry corresponding to the specified OperandType.  If
103 // found, return the associated PerformanceInfo.  If not, return a pessimistic
104 // PerformanceInfo (FLT_MAX).  The vector must be sorted by OperandType.
105 V1_0::PerformanceInfo lookup(
106         const hardware::hidl_vec<V1_2::Capabilities::OperandPerformance>& operandPerformance,
107         V1_2::OperandType type);
108 V1_0::PerformanceInfo lookup(
109         const hardware::hidl_vec<V1_3::Capabilities::OperandPerformance>& operandPerformance,
110         V1_3::OperandType type);
111 
112 // Returns true if an operand type is an extension type.
113 bool isExtensionOperandType(V1_3::OperandType type);
114 
115 // Returns true if an operation type is an extension type.
116 bool isExtensionOperationType(V1_3::OperationType type);
117 
118 // Returns the amount of space needed to store a value of the specified
119 // dimensions and type. For a tensor with unspecified rank or at least one
120 // unspecified dimension, returns zero.
121 //
122 // Aborts if the specified type is an extension type.
123 // Aborts if the size would overflow the return type.
124 //
125 // See also TypeManager::getSizeOfData(OperandType, const std::vector<uint32_t>&).
126 uint32_t nonExtensionOperandSizeOfData(V1_3::OperandType type,
127                                        const std::vector<uint32_t>& dimensions);
128 
129 // Returns the amount of space needed to store a value of the dimensions and
130 // type of this operand. For a tensor with unspecified rank or at least one
131 // unspecified dimension, returns zero.
132 //
133 // Aborts if the specified type is an extension type.
134 // Aborts if the size would overflow the return type.
135 //
136 // See also TypeManager::getSizeOfData(const Operand&).
137 inline uint32_t nonExtensionOperandSizeOfData(const V1_3::Operand& operand) {
138     return nonExtensionOperandSizeOfData(operand.type, operand.dimensions);
139 }
140 
141 // Returns true if the amount of space needed to store a value of the specified
142 // dimensions and element size overflows the uint32_t type.
143 //
144 // Aborts if the specified type is an extension type.
145 //
146 // See also TypeManager::sizeOfDataOverflowsUInt32(OperandType, const std::vector<uint32_t>&).
147 bool nonExtensionOperandSizeOfDataOverflowsUInt32(V1_3::OperandType type,
148                                                   const std::vector<uint32_t>& dimensions);
149 
150 // Returns the name of the operation type in ASCII.
151 std::string getOperationName(V1_3::OperationType opCode);
152 
153 // Returns the name of the operand type in ASCII.
154 std::string getOperandTypeName(V1_3::OperandType type);
155 
156 // Whether an operand of tensor type has unspecified dimensions.
157 //
158 // Undefined behavior if the operand type is a scalar type.
159 bool tensorHasUnspecifiedDimensions(V1_3::OperandType type,
160                                     const std::vector<uint32_t>& dimensions);
161 bool tensorHasUnspecifiedDimensions(const V1_3::Operand& operand);
162 
163 // Does a detailed LOG(INFO) of the model
164 void logModelToInfo(const V1_0::Model& model);
165 void logModelToInfo(const V1_1::Model& model);
166 void logModelToInfo(const V1_2::Model& model);
167 void logModelToInfo(const V1_3::Model& model);
168 
169 bool validateOperandSymmPerChannelQuantParams(
170         const V1_3::Operand& halOperand,
171         const ANeuralNetworksSymmPerChannelQuantParams& channelQuant, const char* tag);
172 
173 // Convert ANEURALNETWORKS_* result code to ErrorStatus.
174 // Not guaranteed to be a 1-to-1 mapping.
175 V1_3::ErrorStatus convertResultCodeToHalErrorStatus(int resultCode);
176 
177 // Convert ErrorStatus to ANEURALNETWORKS_* result code.
178 // Not guaranteed to be a 1-to-1 mapping.
179 int convertErrorStatusToResultCode(V1_3::ErrorStatus status);
180 
181 // Convert execution results to runtime format. Additionally checks that the
182 // returned results abide by the HAL specification, and logs an error if the
183 // result violates the specification.
184 std::tuple<int, std::vector<OutputShape>, Timing> getExecutionResult(
185         V1_3::ErrorStatus status, const hardware::hidl_vec<V1_2::OutputShape>& outputShapes,
186         const V1_2::Timing& timing);
187 
188 // Forward declaration for type defined in CpuExecutor.h.
189 class RunTimePoolInfo;
190 
191 bool setRunTimePoolInfosFromHidlMemories(std::vector<RunTimePoolInfo>* poolInfos,
192                                          const hardware::hidl_vec<hardware::hidl_memory>& pools);
193 
194 // Versioning
195 
196 bool compliantWithV1_0(const V1_0::Capabilities& capabilities);
197 bool compliantWithV1_0(const V1_1::Capabilities& capabilities);
198 bool compliantWithV1_0(const V1_2::Capabilities& capabilities);
199 bool compliantWithV1_0(const V1_3::Capabilities& capabilities);
200 bool compliantWithV1_1(const V1_0::Capabilities& capabilities);
201 bool compliantWithV1_1(const V1_1::Capabilities& capabilities);
202 bool compliantWithV1_1(const V1_2::Capabilities& capabilities);
203 bool compliantWithV1_1(const V1_3::Capabilities& capabilities);
204 bool compliantWithV1_2(const V1_0::Capabilities& capabilities);
205 bool compliantWithV1_2(const V1_1::Capabilities& capabilities);
206 bool compliantWithV1_2(const V1_2::Capabilities& capabilities);
207 bool compliantWithV1_2(const V1_3::Capabilities& capabilities);
208 bool compliantWithV1_3(const V1_0::Capabilities& capabilities);
209 bool compliantWithV1_3(const V1_1::Capabilities& capabilities);
210 bool compliantWithV1_3(const V1_2::Capabilities& capabilities);
211 bool compliantWithV1_3(const V1_3::Capabilities& capabilities);
212 
213 // If noncompliantOperations != nullptr, then
214 //     precondition: noncompliantOperations->empty()
215 //     postcondition: *noncompliantOperations consists of the indices of the noncompliant
216 //                    operations; if the compliance check fails for some reason
217 //                    other than a noncompliant operation,
218 //                    *noncompliantOperations consists of the indices of all operations
219 bool compliantWithV1_0(const V1_0::Model& model);
220 bool compliantWithV1_0(const V1_1::Model& model);
221 bool compliantWithV1_0(const V1_2::Model& model,
222                        std::set<uint32_t>* noncompliantOperations = nullptr);
223 bool compliantWithV1_0(const V1_3::Model& model,
224                        std::set<uint32_t>* noncompliantOperations = nullptr);
225 bool compliantWithV1_1(const V1_0::Model& model);
226 bool compliantWithV1_1(const V1_1::Model& model);
227 bool compliantWithV1_1(const V1_2::Model& model,
228                        std::set<uint32_t>* noncompliantOperations = nullptr);
229 bool compliantWithV1_1(const V1_3::Model& model,
230                        std::set<uint32_t>* noncompliantOperations = nullptr);
231 bool compliantWithV1_2(const V1_0::Model& model);
232 bool compliantWithV1_2(const V1_1::Model& model);
233 bool compliantWithV1_2(const V1_2::Model& model,
234                        std::set<uint32_t>* noncompliantOperations = nullptr);
235 bool compliantWithV1_2(const V1_3::Model& model,
236                        std::set<uint32_t>* noncompliantOperations = nullptr);
237 
238 V1_0::ErrorStatus convertToV1_0(V1_0::ErrorStatus status);
239 V1_0::ErrorStatus convertToV1_0(V1_3::ErrorStatus status);
240 V1_3::ErrorStatus convertToV1_3(V1_0::ErrorStatus status);
241 V1_3::ErrorStatus convertToV1_3(V1_3::ErrorStatus status);
242 
243 V1_0::Capabilities convertToV1_0(const V1_0::Capabilities& capabilities);
244 V1_0::Capabilities convertToV1_0(const V1_1::Capabilities& capabilities);
245 V1_0::Capabilities convertToV1_0(const V1_2::Capabilities& capabilities);
246 V1_0::Capabilities convertToV1_0(const V1_3::Capabilities& capabilities);
247 V1_1::Capabilities convertToV1_1(const V1_0::Capabilities& capabilities);
248 V1_1::Capabilities convertToV1_1(const V1_1::Capabilities& capabilities);
249 V1_1::Capabilities convertToV1_1(const V1_2::Capabilities& capabilities);
250 V1_1::Capabilities convertToV1_1(const V1_3::Capabilities& capabilities);
251 V1_2::Capabilities convertToV1_2(const V1_0::Capabilities& capabilities);
252 V1_2::Capabilities convertToV1_2(const V1_1::Capabilities& capabilities);
253 V1_2::Capabilities convertToV1_2(const V1_2::Capabilities& capabilities);
254 V1_2::Capabilities convertToV1_2(const V1_3::Capabilities& capabilities);
255 V1_3::Capabilities convertToV1_3(const V1_0::Capabilities& capabilities);
256 V1_3::Capabilities convertToV1_3(const V1_1::Capabilities& capabilities);
257 V1_3::Capabilities convertToV1_3(const V1_2::Capabilities& capabilities);
258 V1_3::Capabilities convertToV1_3(const V1_3::Capabilities& capabilities);
259 
260 V1_0::Model convertToV1_0(const V1_0::Model& model);
261 V1_0::Model convertToV1_0(const V1_1::Model& model);
262 V1_0::Model convertToV1_0(const V1_2::Model& model);
263 V1_0::Model convertToV1_0(const V1_3::Model& model);
264 V1_1::Model convertToV1_1(const V1_0::Model& model);
265 V1_1::Model convertToV1_1(const V1_1::Model& model);
266 V1_1::Model convertToV1_1(const V1_2::Model& model);
267 V1_1::Model convertToV1_1(const V1_3::Model& model);
268 V1_2::Model convertToV1_2(const V1_0::Model& model);
269 V1_2::Model convertToV1_2(const V1_1::Model& model);
270 V1_2::Model convertToV1_2(const V1_2::Model& model);
271 V1_2::Model convertToV1_2(const V1_3::Model& model);
272 V1_3::Model convertToV1_3(const V1_0::Model& model);
273 V1_3::Model convertToV1_3(const V1_1::Model& model);
274 V1_3::Model convertToV1_3(const V1_2::Model& model);
275 V1_3::Model convertToV1_3(const V1_3::Model& model);
276 
277 V1_0::OperationType uncheckedConvertToV1_0(V1_3::OperationType type);
278 V1_1::OperationType uncheckedConvertToV1_1(V1_3::OperationType type);
279 V1_2::OperationType uncheckedConvertToV1_2(V1_3::OperationType type);
280 
281 V1_0::Operand convertToV1_0(const V1_2::Operand& operand);
282 V1_0::Operand convertToV1_0(const V1_3::Operand& operand);
283 V1_2::Operand convertToV1_2(const V1_0::Operand& operand);
284 V1_2::Operand convertToV1_2(const V1_3::Operand& operand);
285 V1_3::Operand convertToV1_3(const V1_0::Operand& operand);
286 V1_3::Operand convertToV1_3(const V1_2::Operand& operand);
287 V1_3::Operand convertToV1_3(const V1_3::Operand& operand);
288 
289 hardware::hidl_vec<V1_0::Operand> convertToV1_0(const hardware::hidl_vec<V1_0::Operand>& operands);
290 hardware::hidl_vec<V1_0::Operand> convertToV1_0(const hardware::hidl_vec<V1_2::Operand>& operands);
291 hardware::hidl_vec<V1_0::Operand> convertToV1_0(const hardware::hidl_vec<V1_3::Operand>& operands);
292 hardware::hidl_vec<V1_2::Operand> convertToV1_2(const hardware::hidl_vec<V1_0::Operand>& operands);
293 hardware::hidl_vec<V1_2::Operand> convertToV1_2(const hardware::hidl_vec<V1_2::Operand>& operands);
294 hardware::hidl_vec<V1_2::Operand> convertToV1_2(const hardware::hidl_vec<V1_3::Operand>& operands);
295 hardware::hidl_vec<V1_3::Operand> convertToV1_3(const hardware::hidl_vec<V1_0::Operand>& operands);
296 hardware::hidl_vec<V1_3::Operand> convertToV1_3(const hardware::hidl_vec<V1_2::Operand>& operands);
297 hardware::hidl_vec<V1_3::Operand> convertToV1_3(const hardware::hidl_vec<V1_3::Operand>& operands);
298 
299 bool compliantWithV1_0(const V1_0::Request& request);
300 bool compliantWithV1_0(const V1_3::Request& request);
301 bool compliantWithV1_2(const V1_3::Request& request);
302 
303 V1_0::Request convertToV1_0(const V1_0::Request& request);
304 V1_0::Request convertToV1_0(const V1_3::Request& request);
305 V1_0::Request convertToV1_2(const V1_3::Request& request);
306 V1_3::Request convertToV1_3(const V1_0::Request& request);
307 V1_3::Request convertToV1_3(const V1_3::Request& request);
308 
309 bool compliantWithV1_0(V1_0::OperandLifeTime lifetime);
310 bool compliantWithV1_0(V1_3::OperandLifeTime lifetime);
311 bool compliantWithV1_3(V1_0::OperandLifeTime lifetime);
312 bool compliantWithV1_3(V1_3::OperandLifeTime lifetime);
313 
314 V1_0::OperandLifeTime convertToV1_0(V1_0::OperandLifeTime lifetime);
315 V1_0::OperandLifeTime convertToV1_0(V1_3::OperandLifeTime lifetime);
316 V1_3::OperandLifeTime convertToV1_3(V1_0::OperandLifeTime lifetime);
317 V1_3::OperandLifeTime convertToV1_3(V1_3::OperandLifeTime lifetime);
318 
319 constexpr V1_3::Priority convertToHalPriority(int32_t priority) {
320     switch (priority) {
321         case ANEURALNETWORKS_PRIORITY_LOW:
322             return V1_3::Priority::LOW;
323         case ANEURALNETWORKS_PRIORITY_MEDIUM:
324             return V1_3::Priority::MEDIUM;
325         case ANEURALNETWORKS_PRIORITY_HIGH:
326             return V1_3::Priority::HIGH;
327     }
328     LOG(FATAL) << "unrecognized priority: " << priority;
329     return {};
330 }
331 
332 // DEPRECATED. Use checked conversions from nnapi/hal/1.X/Conversions.h.
333 Capabilities::OperandPerformance uncheckedConvert(
334         const V1_3::Capabilities::OperandPerformance& operandPerformance);
335 Capabilities::PerformanceInfo uncheckedConvert(const V1_0::PerformanceInfo& performanceInfo);
336 Capabilities uncheckedConvert(const V1_3::Capabilities& capabilities);
337 DataLocation uncheckedConvert(const V1_0::DataLocation& location);
338 ErrorStatus uncheckedConvert(V1_0::ErrorStatus status);
339 ErrorStatus uncheckedConvert(V1_3::ErrorStatus status);
340 Extension::OperandTypeInformation uncheckedConvert(const V1_2::Extension::OperandTypeInformation&);
341 Extension uncheckedConvert(const V1_2::Extension& extension);
342 hardware::hidl_vec<uint8_t> uncheckedConvert(const Operand::ExtensionParams& params);
343 MeasureTiming uncheckedConvert(V1_2::MeasureTiming measure);
344 SharedMemory uncheckedConvert(const hardware::hidl_memory& memory);
345 ExtensionNameAndPrefix uncheckedConvert(const V1_2::Model::ExtensionNameAndPrefix&);
346 Model::Subgraph uncheckedConvert(const V1_3::Subgraph& subgraph);
347 Model uncheckedConvert(const V1_3::Model& model);
348 Operand::ExtensionParams uncheckedConvert(const hardware::hidl_vec<uint8_t>& params);
349 Operand::ExtraParams uncheckedConvert(const V1_2::Operand::ExtraParams& params);
350 Operand::LifeTime uncheckedConvert(V1_3::OperandLifeTime lifetime);
351 Operand::SymmPerChannelQuantParams uncheckedConvert(const V1_2::SymmPerChannelQuantParams& params);
352 OperandType uncheckedConvert(V1_3::OperandType operandType);
353 Operand uncheckedConvert(const V1_3::Operand& operand);
354 OperationType uncheckedConvert(V1_3::OperationType operationType);
355 Operation uncheckedConvert(const V1_3::Operation& operation);
356 OptionalDuration uncheckedConvert(const V1_3::OptionalTimeoutDuration& timeoutDuration);
357 OutputShape uncheckedConvert(const V1_2::OutputShape& outputShape);
358 Request::Argument uncheckedConvert(const V1_0::RequestArgument& requestArgument);
359 Request::MemoryPool uncheckedConvert(const V1_3::Request::MemoryPool& memoryPool);
360 Request uncheckedConvert(const V1_3::Request& request);
361 std::vector<Extension> uncheckedConvert(const hardware::hidl_vec<V1_2::Extension>& extensions);
362 std::vector<SharedMemory> uncheckedConvert(
363         const hardware::hidl_vec<hardware::hidl_memory>& memories);
364 std::vector<Model::Subgraph> uncheckedConvert(const hardware::hidl_vec<V1_3::Subgraph>& subgraphs);
365 std::vector<Operand> uncheckedConvert(const hardware::hidl_vec<V1_3::Operand>& operands);
366 std::vector<OutputShape> uncheckedConvert(
367         const hardware::hidl_vec<V1_2::OutputShape>& outputShapes);
368 std::vector<Request::MemoryPool> uncheckedConvert(
369         const hardware::hidl_vec<V1_3::Request::MemoryPool>& memoryPools);
370 Timing uncheckedConvert(const V1_2::Timing& timing);
371 
372 // DEPRECATED. Use conversions from nnapi/hal/1.X/Conversions.h.
373 hardware::hidl_memory convertToV1_0(const SharedMemory& memory);
374 hardware::hidl_vec<hardware::hidl_memory> convertToV1_0(const std::vector<SharedMemory>& memories);
375 hardware::hidl_vec<uint8_t> convertToV1_0(const Model::OperandValues& operandValues);
376 hardware::hidl_vec<V1_2::OutputShape> convertToV1_2(const std::vector<OutputShape>& outputShapes);
377 hardware::hidl_vec<V1_3::BufferRole> convertToV1_3(const std::vector<BufferRole>& bufferRoles);
378 V1_0::DataLocation convertToV1_0(const DataLocation& location);
379 V1_0::ErrorStatus convertToV1_0(ErrorStatus status);
380 V1_0::RequestArgument convertToV1_0(const Request::Argument& requestArgument);
381 V1_1::ExecutionPreference convertToV1_1(ExecutionPreference preference);
382 V1_2::MeasureTiming convertToV1_2(MeasureTiming measure);
383 V1_2::Model::ExtensionNameAndPrefix convertToV1_2(const ExtensionNameAndPrefix&);
384 V1_2::Operand::ExtraParams convertToV1_2(const Operand::ExtraParams& params);
385 V1_2::OutputShape convertToV1_2(const OutputShape& outputShape);
386 V1_2::SymmPerChannelQuantParams convertToV1_2(const Operand::SymmPerChannelQuantParams& params);
387 V1_2::Timing convertToV1_2(const Timing& timing);
388 V1_3::BufferRole convertToV1_3(const BufferRole& bufferRole);
389 V1_3::ErrorStatus convertToV1_3(ErrorStatus status);
390 V1_3::Model convertToV1_3(const Model& model);
391 V1_3::Operand convertToV1_3(const Operand& operand);
392 V1_3::OperandLifeTime convertToV1_3(Operand::LifeTime lifetime);
393 V1_3::OperandType convertToV1_3(OperandType operandType);
394 V1_3::Operation convertToV1_3(const Operation& operation);
395 V1_3::OperationType convertToV1_3(OperationType operationType);
396 V1_3::OptionalTimeoutDuration convertToV1_3(const OptionalDuration& timeoutDuration);
397 V1_3::OptionalTimePoint convertToV1_3(const OptionalTimePoint& timePoint);
398 V1_3::Priority convertToV1_3(Priority priority);
399 V1_3::Request convertToV1_3(const Request& request);
400 V1_3::Request::MemoryPool convertToV1_3(const Request::MemoryPool& memoryPool);
401 V1_3::Subgraph convertToV1_3(const Model::Subgraph& model);
402 
403 }  // namespace nn
404 }  // namespace android
405 
406 #endif  // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_COMMON_LEGACY_HAL_UTILS_H
407