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
17#define LOG_TAG "Utils"
18
19#include "Utils.h"
20#include "NeuralNetworks.h"
21#include "NeuralNetworksOEM.h"
22
23#include <android-base/logging.h>
24#include <android-base/properties.h>
25#include <android-base/strings.h>
26#include <sys/system_properties.h>
27#include <unordered_map>
28
29using ::android::hidl::allocator::V1_0::IAllocator;
30
31namespace android {
32namespace nn {
33
34const char kVLogPropKey[] = "debug.nn.vlog";
35int vLogMask = ~0;
36
37// Split the space separated list of tags from verbose log setting and build the
38// logging mask from it. note that '1' and 'all' are special cases to enable all
39// verbose logging.
40//
41// NN API verbose logging setting comes from system property debug.nn.vlog.
42// Example:
43// setprop debug.nn.vlog 1 : enable all logging tags.
44// setprop debug.nn.vlog "model compilation" : only enable logging for MODEL and
45//                                             COMPILATION tags.
46void initVLogMask() {
47    vLogMask = 0;
48    const std::string vLogSetting = android::base::GetProperty(kVLogPropKey, "");
49    if (vLogSetting.empty()) {
50        return;
51    }
52
53    std::unordered_map<std::string, int> vLogFlags = {
54        {"1", -1},
55        {"all", -1},
56        {"model", MODEL},
57        {"compilation", COMPILATION},
58        {"execution", EXECUTION},
59        {"cpuexe", CPUEXE},
60        {"manager", MANAGER},
61        {"driver", DRIVER}};
62
63    std::vector<std::string> elements = android::base::Split(vLogSetting, " ,:");
64    for (const auto& elem : elements) {
65        const auto& flag = vLogFlags.find(elem);
66        if (flag == vLogFlags.end()) {
67            LOG(ERROR) << "Unknown trace flag: " << elem;
68            continue;
69        }
70
71        if (flag->second == -1) {
72            // -1 is used for the special values "1" and "all" that enable all
73            // tracing.
74            vLogMask = ~0;
75            return;
76        } else {
77            vLogMask |= 1 << flag->second;
78        }
79    }
80}
81
82namespace {
83
84template <typename EntryType, uint32_t entryCount, uint32_t entryCountOEM>
85EntryType tableLookup(const EntryType (&table)[entryCount],
86                      const EntryType (&tableOEM)[entryCountOEM],
87                      uint32_t code) {
88    if (code < entryCount) {
89        return table[code];
90    } else if (code >= kOEMCodeBase && (code - kOEMCodeBase) < entryCountOEM) {
91        return tableOEM[code - kOEMCodeBase];
92    } else {
93        nnAssert(!"tableLookup: bad code");
94        return EntryType();
95    }
96}
97
98};  // anonymous namespace
99
100#define COUNT(X) (sizeof(X) / sizeof(X[0]))
101
102const char* kTypeNames[kNumberOfDataTypes] = {
103        "FLOAT32",        "INT32",        "UINT32",
104        "TENSOR_FLOAT32", "TENSOR_INT32", "TENSOR_QUANT8_ASYMM",
105};
106
107static_assert(COUNT(kTypeNames) == kNumberOfDataTypes, "kTypeNames is incorrect");
108
109const char* kTypeNamesOEM[kNumberOfDataTypesOEM] = {
110        "OEM",            "TENSOR_OEM_BYTE",
111};
112
113static_assert(COUNT(kTypeNamesOEM) == kNumberOfDataTypesOEM, "kTypeNamesOEM is incorrect");
114
115const char* getOperandTypeName(OperandType type) {
116    uint32_t n = static_cast<uint32_t>(type);
117    return tableLookup(kTypeNames, kTypeNamesOEM, n);
118}
119
120// TODO Check if this useful
121const char* kErrorNames[] = {
122        "NO_ERROR", "OUT_OF_MEMORY", "INCOMPLETE", "NULL", "BAD_DATA",
123};
124
125const char* kOperationNames[kNumberOfOperationTypes] = {
126        "ADD",
127        "AVERAGE_POOL",
128        "CONCATENATION",
129        "CONV",
130        "DEPTHWISE_CONV",
131        "DEPTH_TO_SPACE",
132        "DEQUANTIZE",
133        "EMBEDDING_LOOKUP",
134        "FLOOR",
135        "FULLY_CONNECTED",
136        "HASHTABLE_LOOKUP",
137        "L2_NORMALIZATION",
138        "L2_POOL",
139        "LOCAL_RESPONSE_NORMALIZATION",
140        "LOGISTIC",
141        "LSH_PROJECTION",
142        "LSTM",
143        "MAX_POOL",
144        "MUL",
145        "RELU",
146        "RELU1",
147        "RELU6",
148        "RESHAPE",
149        "RESIZE_BILINEAR",
150        "RNN",
151        "SOFTMAX",
152        "SPACE_TO_DEPTH",
153        "SVDF",
154        "TANH",
155        "BATCH_TO_SPACE_ND",
156        "DIV",
157        "MEAN",
158        "PAD",
159        "SPACE_TO_BATCH_ND",
160        "SQUEEZE",
161        "STRIDED_SLICE",
162        "SUB",
163        "TRANSPOSE",
164};
165
166static_assert(COUNT(kOperationNames) == kNumberOfOperationTypes, "kOperationNames is incorrect");
167
168const char* kOperationNamesOEM[kNumberOfOperationTypesOEM] = {
169        "OEM_OPERATION",
170};
171
172static_assert(COUNT(kOperationNamesOEM) == kNumberOfOperationTypesOEM,
173              "kOperationNamesOEM is incorrect");
174
175const char* getOperationName(OperationType type) {
176    uint32_t n = static_cast<uint32_t>(type);
177    return tableLookup(kOperationNames, kOperationNamesOEM, n);
178}
179
180const uint32_t kSizeOfDataType[]{
181        4, // ANEURALNETWORKS_FLOAT32
182        4, // ANEURALNETWORKS_INT32
183        4, // ANEURALNETWORKS_UINT32
184        4, // ANEURALNETWORKS_TENSOR_FLOAT32
185        4, // ANEURALNETWORKS_TENSOR_INT32
186        1  // ANEURALNETWORKS_TENSOR_SYMMETRICAL_QUANT8
187};
188
189static_assert(COUNT(kSizeOfDataType) == kNumberOfDataTypes, "kSizeOfDataType is incorrect");
190
191const bool kScalarDataType[]{
192        true,  // ANEURALNETWORKS_FLOAT32
193        true,  // ANEURALNETWORKS_INT32
194        true,  // ANEURALNETWORKS_UINT32
195        false, // ANEURALNETWORKS_TENSOR_FLOAT32
196        false, // ANEURALNETWORKS_TENSOR_INT32
197        false, // ANEURALNETWORKS_TENSOR_SYMMETRICAL_QUANT8
198};
199
200static_assert(COUNT(kScalarDataType) == kNumberOfDataTypes, "kScalarDataType is incorrect");
201
202const uint32_t kSizeOfDataTypeOEM[]{
203        0, // ANEURALNETWORKS_OEM
204        1, // ANEURALNETWORKS_TENSOR_OEM_BYTE
205};
206
207static_assert(COUNT(kSizeOfDataTypeOEM) == kNumberOfDataTypesOEM,
208              "kSizeOfDataTypeOEM is incorrect");
209
210const bool kScalarDataTypeOEM[]{
211        true,  // ANEURALNETWORKS_OEM
212        false, // ANEURALNETWORKS_TENSOR_OEM_BYTE
213};
214
215static_assert(COUNT(kScalarDataTypeOEM) == kNumberOfDataTypesOEM,
216              "kScalarDataTypeOEM is incorrect");
217
218uint32_t sizeOfData(OperandType type, const std::vector<uint32_t>& dimensions) {
219    int n = static_cast<int>(type);
220
221    uint32_t size = tableLookup(kSizeOfDataType, kSizeOfDataTypeOEM, n);
222
223    if (tableLookup(kScalarDataType, kScalarDataTypeOEM, n) == true) {
224        return size;
225    }
226
227    for (auto d : dimensions) {
228        size *= d;
229    }
230    return size;
231}
232
233hidl_memory allocateSharedMemory(int64_t size) {
234    static const std::string type = "ashmem";
235    static sp<IAllocator> allocator = IAllocator::getService(type);
236
237    hidl_memory memory;
238
239    // TODO: should we align memory size to nearest page? doesn't seem necessary...
240    allocator->allocate(size, [&](bool success, const hidl_memory& mem) {
241        if (!success) {
242            LOG(ERROR) << "unable to allocate " << size << " bytes of " << type;
243        } else {
244            memory = mem;
245        }
246    });
247
248    return memory;
249}
250
251uint32_t alignBytesNeeded(uint32_t index, size_t length) {
252    uint32_t pattern;
253    if (length < 2) {
254        pattern = 0; // No alignment necessary
255    } else if (length < 4) {
256        pattern = 1; // Align on 2-byte boundary
257    } else {
258        pattern = 3; // Align on 4-byte boundary
259    }
260    uint32_t extra = (~(index - 1)) & pattern;
261    return extra;
262}
263
264void logModelToInfo(const V1_0::Model& model) {
265    LOG(INFO) << "V1_0::Model start";
266    LOG(INFO) << "operands" << toString(model.operands);
267    LOG(INFO) << "operations" << toString(model.operations);
268    LOG(INFO) << "inputIndexes" << toString(model.inputIndexes);
269    LOG(INFO) << "outputIndexes" << toString(model.outputIndexes);
270    LOG(INFO) << "operandValues size" << model.operandValues.size();
271    LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools));
272}
273
274void logModelToInfo(const V1_1::Model& model) {
275    LOG(INFO) << "V1_1::Model start";
276    LOG(INFO) << "operands" << toString(model.operands);
277    LOG(INFO) << "operations" << toString(model.operations);
278    LOG(INFO) << "inputIndexes" << toString(model.inputIndexes);
279    LOG(INFO) << "outputIndexes" << toString(model.outputIndexes);
280    LOG(INFO) << "operandValues size" << model.operandValues.size();
281    LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools));
282}
283
284// Validates the type. The used dimensions can be underspecified.
285int validateOperandType(const ANeuralNetworksOperandType& type, const char* tag,
286                        bool allowPartial) {
287    if (!allowPartial) {
288        for (uint32_t i = 0; i < type.dimensionCount; i++) {
289            if (type.dimensions[i] == 0) {
290                LOG(ERROR) << tag << " OperandType invalid dimensions[" << i
291                           << "] = " << type.dimensions[i];
292                return ANEURALNETWORKS_BAD_DATA;
293            }
294        }
295    }
296    if (!validCode(kNumberOfDataTypes, kNumberOfDataTypesOEM, type.type)) {
297        LOG(ERROR) << tag << " OperandType invalid type " << type.type;
298        return ANEURALNETWORKS_BAD_DATA;
299    }
300    if (type.type == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
301        if (type.zeroPoint < 0 || type.zeroPoint > 255) {
302            LOG(ERROR) << tag << " OperandType invalid zeroPoint " << type.zeroPoint;
303            return ANEURALNETWORKS_BAD_DATA;
304        }
305        if (type.scale <= 0.f) {
306            LOG(ERROR) << tag << " OperandType invalid scale " << type.scale;
307            return ANEURALNETWORKS_BAD_DATA;
308        }
309    }
310    if (type.type == ANEURALNETWORKS_FLOAT32 ||
311        type.type == ANEURALNETWORKS_INT32 ||
312        type.type == ANEURALNETWORKS_UINT32 ||
313        type.type == ANEURALNETWORKS_OEM_SCALAR) {
314        if (type.dimensionCount != 0 || type.dimensions != nullptr) {
315            LOG(ERROR) << tag << " Invalid dimensions for scalar type";
316            return ANEURALNETWORKS_BAD_DATA;
317        }
318    }
319
320    return ANEURALNETWORKS_NO_ERROR;
321}
322
323int validateOperandList(uint32_t count, const uint32_t* list, uint32_t operandCount,
324                        const char* tag) {
325    for (uint32_t i = 0; i < count; i++) {
326        if (list[i] >= operandCount) {
327            LOG(ERROR) << tag << " invalid operand index at " << i << " = " << list[i]
328                       << ", operandCount " << operandCount;
329            return ANEURALNETWORKS_BAD_DATA;
330        }
331    }
332    return ANEURALNETWORKS_NO_ERROR;
333}
334
335int validateOperationOperandTypes(const std::vector<Operand>& operands,
336                                  uint32_t inOperandCount, const uint32_t* inOperandIndexes,
337                                  const std::vector<OperandType>& inExpectedTypes,
338                                  uint32_t outOperandCount, const uint32_t* outOperandIndexes,
339                                  const std::vector<OperandType>& outExpectedInTypes) {
340    if (inOperandCount > static_cast<uint32_t>(inExpectedTypes.size()) ||
341        outOperandCount > static_cast<uint32_t>(outExpectedInTypes.size())) {
342        return ANEURALNETWORKS_BAD_DATA;
343    }
344    for (uint32_t i = 0; i < inOperandCount; i++) {
345        if (operands[inOperandIndexes[i]].type != inExpectedTypes[i]) {
346            LOG(ERROR) << "Invalid input tensor type "
347                       << toString(operands[inOperandIndexes[i]].type)
348                       << " for input " << i << ", expected " << toString(inExpectedTypes[i]);
349            return ANEURALNETWORKS_BAD_DATA;
350        }
351    }
352    for (uint32_t i = 0; i < outOperandCount; i++) {
353        if (operands[outOperandIndexes[i]].type != outExpectedInTypes[i]) {
354            LOG(ERROR) << "Invalid output tensor type "
355                       << toString(operands[outOperandIndexes[i]].type)
356                       << " for input " << i << ", expected " << toString(outExpectedInTypes[i]);
357            return ANEURALNETWORKS_BAD_DATA;
358        }
359    }
360
361    return ANEURALNETWORKS_NO_ERROR;
362}
363
364int validateOperation(ANeuralNetworksOperationType opType,
365                      uint32_t inputCount, const uint32_t* inputIndexes,
366                      uint32_t outputCount, const uint32_t* outputIndexes,
367                      const std::vector<Operand>& operands) {
368    int n = validateOperandList(inputCount, inputIndexes, static_cast<uint32_t>(operands.size()),
369                                "ANeuralNetworksModel_addOperation inputs");
370    if (n != ANEURALNETWORKS_NO_ERROR) {
371        return n;
372    }
373    n = validateOperandList(outputCount, outputIndexes, static_cast<uint32_t>(operands.size()),
374                            "ANeuralNetworksModel_addOperation outputs");
375    if (n != ANEURALNETWORKS_NO_ERROR) {
376        return n;
377    }
378
379    auto logInvalidInOutNumber = [opType, inputCount, outputCount](int expIn, int expOut) {
380        LOG(ERROR) << "Invalid number of input operands ("
381                   << inputCount << ", expected " << expIn << ") or output operands ("
382                   << outputCount << ", expected " << expOut << ") for operation "
383                   << kOperationNames[opType];
384    };
385
386    switch (opType) {
387        case ANEURALNETWORKS_OEM_OPERATION: {
388            return ANEURALNETWORKS_NO_ERROR;
389        }
390        case ANEURALNETWORKS_ADD: {
391            if (inputCount != 3 || outputCount != 1) {
392                logInvalidInOutNumber(3, 1);
393                return ANEURALNETWORKS_BAD_DATA;
394            }
395            auto inputType = operands[inputIndexes[0]].type;
396            std::vector<OperandType> inExpectedTypes;
397            std::vector<OperandType> outExpectedTypes;
398            if (inputType == OperandType::TENSOR_FLOAT32) {
399                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
400                                   OperandType::TENSOR_FLOAT32,
401                                   OperandType::INT32};
402                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
403            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
404                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
405                                   OperandType::TENSOR_QUANT8_ASYMM,
406                                   OperandType::INT32};
407                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
408            } else {
409                LOG(ERROR) << "Unsupported input tensor type for operation "
410                           << kOperationNames[opType];
411                return ANEURALNETWORKS_BAD_DATA;
412            }
413            return validateOperationOperandTypes(operands,
414                                                 inputCount, inputIndexes,
415                                                 inExpectedTypes,
416                                                 outputCount, outputIndexes,
417                                                 outExpectedTypes);
418        }
419        case ANEURALNETWORKS_MUL: {
420            if (inputCount != 3 || outputCount != 1) {
421                logInvalidInOutNumber(3, 1);
422                return ANEURALNETWORKS_BAD_DATA;
423            }
424            auto inputType = operands[inputIndexes[0]].type;
425            std::vector<OperandType> inExpectedTypes;
426            std::vector<OperandType> outExpectedTypes;
427            if (inputType == OperandType::TENSOR_FLOAT32) {
428                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
429                                   OperandType::TENSOR_FLOAT32,
430                                   OperandType::INT32};
431                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
432            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
433                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
434                                   OperandType::TENSOR_QUANT8_ASYMM,
435                                   OperandType::INT32};
436                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
437            } else {
438                LOG(ERROR) << "Unsupported input tensor type for operation "
439                           << kOperationNames[opType];
440                return ANEURALNETWORKS_BAD_DATA;
441            }
442            return validateOperationOperandTypes(operands,
443                                                 inputCount, inputIndexes,
444                                                 inExpectedTypes,
445                                                 outputCount, outputIndexes,
446                                                 outExpectedTypes);
447        }
448        case ANEURALNETWORKS_FLOOR: {
449            if (inputCount != 1 || outputCount != 1) {
450                logInvalidInOutNumber(1, 1);
451                return ANEURALNETWORKS_BAD_DATA;
452            }
453            auto inputType = operands[inputIndexes[0]].type;
454            std::vector<OperandType> inExpectedTypes;
455            std::vector<OperandType> outExpectedTypes;
456            if (inputType == OperandType::TENSOR_FLOAT32) {
457                inExpectedTypes = {OperandType::TENSOR_FLOAT32};
458                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
459            } else {
460                LOG(ERROR) << "Unsupported input tensor type for operation "
461                           << kOperationNames[opType];
462                return ANEURALNETWORKS_BAD_DATA;
463            }
464            return validateOperationOperandTypes(operands,
465                                                 inputCount, inputIndexes,
466                                                 inExpectedTypes,
467                                                 outputCount, outputIndexes,
468                                                 outExpectedTypes);
469        }
470        case ANEURALNETWORKS_DEQUANTIZE: {
471            if (inputCount != 1 || outputCount != 1) {
472                logInvalidInOutNumber(1, 1);
473                return ANEURALNETWORKS_BAD_DATA;
474            }
475            auto inputType = operands[inputIndexes[0]].type;
476            std::vector<OperandType> inExpectedTypes;
477            std::vector<OperandType> outExpectedTypes;
478            if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
479                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
480                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
481            } else {
482                LOG(ERROR) << "Unsupported input tensor type for operation "
483                           << kOperationNames[opType];
484                return ANEURALNETWORKS_BAD_DATA;
485            }
486            return validateOperationOperandTypes(operands,
487                                                 inputCount, inputIndexes,
488                                                 inExpectedTypes,
489                                                 outputCount, outputIndexes,
490                                                 outExpectedTypes);
491        }
492        case ANEURALNETWORKS_DEPTHWISE_CONV_2D: {
493            if ((inputCount != 11 && inputCount != 8) || outputCount != 1) {
494                LOG(ERROR) << "Invalid number of input operands ("
495                           << inputCount << ", expected 11 or 8) or output operands ("
496                           << outputCount << ", expected 1) for operation "
497                           << kOperationNames[opType];
498                return ANEURALNETWORKS_BAD_DATA;
499            }
500            auto inputType = operands[inputIndexes[0]].type;
501            std::vector<OperandType> inExpectedTypes;
502            std::vector<OperandType> outExpectedTypes;
503            if (inputType == OperandType::TENSOR_FLOAT32) {
504                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
505                                   OperandType::TENSOR_FLOAT32,
506                                   OperandType::TENSOR_FLOAT32,
507                                   OperandType::INT32,
508                                   OperandType::INT32,
509                                   OperandType::INT32,
510                                   OperandType::INT32,
511                                   OperandType::INT32};
512                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
513            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
514                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
515                                   OperandType::TENSOR_QUANT8_ASYMM,
516                                   OperandType::TENSOR_INT32,
517                                   OperandType::INT32,
518                                   OperandType::INT32,
519                                   OperandType::INT32,
520                                   OperandType::INT32,
521                                   OperandType::INT32};
522                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
523            } else {
524                LOG(ERROR) << "Unsupported input tensor type for operation "
525                           << kOperationNames[opType];
526                return ANEURALNETWORKS_BAD_DATA;
527            }
528
529            if (inputCount == 11) {
530                std::vector<OperandType> explicitScalarTypes(3, OperandType::INT32);
531                inExpectedTypes.insert(inExpectedTypes.end(),
532                                       explicitScalarTypes.begin(),
533                                       explicitScalarTypes.end());
534            }
535            return validateOperationOperandTypes(operands,
536                                                 inputCount, inputIndexes,
537                                                 inExpectedTypes,
538                                                 outputCount, outputIndexes,
539                                                 outExpectedTypes);
540        }
541        case ANEURALNETWORKS_CONV_2D: {
542            if ((inputCount != 10 && inputCount != 7) || outputCount != 1) {
543                LOG(ERROR) << "Invalid number of input operands ("
544                           << inputCount << ", expected 10 or 7) or output operands ("
545                           << outputCount << ", expected 1) for operation "
546                           << kOperationNames[opType];
547                return ANEURALNETWORKS_BAD_DATA;
548            }
549            auto inputType = operands[inputIndexes[0]].type;
550            std::vector<OperandType> inExpectedTypes;
551            std::vector<OperandType> outExpectedTypes;
552            if (inputType == OperandType::TENSOR_FLOAT32) {
553                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
554                                   OperandType::TENSOR_FLOAT32,
555                                   OperandType::TENSOR_FLOAT32,
556                                   OperandType::INT32,
557                                   OperandType::INT32,
558                                   OperandType::INT32,
559                                   OperandType::INT32};
560                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
561            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
562                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
563                                   OperandType::TENSOR_QUANT8_ASYMM,
564                                   OperandType::TENSOR_INT32,
565                                   OperandType::INT32,
566                                   OperandType::INT32,
567                                   OperandType::INT32,
568                                   OperandType::INT32};
569                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
570            } else {
571                LOG(ERROR) << "Unsupported input tensor type for operation "
572                           << kOperationNames[opType];
573                return ANEURALNETWORKS_BAD_DATA;
574            }
575
576            if (inputCount == 10) {
577                std::vector<OperandType> explicitScalarTypes(3, OperandType::INT32);
578                inExpectedTypes.insert(inExpectedTypes.end(),
579                                       explicitScalarTypes.begin(),
580                                       explicitScalarTypes.end());
581            }
582            return validateOperationOperandTypes(operands,
583                                                 inputCount, inputIndexes,
584                                                 inExpectedTypes,
585                                                 outputCount, outputIndexes,
586                                                 outExpectedTypes);
587        }
588        case ANEURALNETWORKS_AVERAGE_POOL_2D: {
589            if ((inputCount != 10 && inputCount != 7) || outputCount != 1) {
590                LOG(ERROR) << "Invalid number of input operands ("
591                           << inputCount << ", expected 10 or 7) or output operands ("
592                           << outputCount << ", expected 1) for operation "
593                           << kOperationNames[opType];
594                return ANEURALNETWORKS_BAD_DATA;
595            }
596            auto inputType = operands[inputIndexes[0]].type;
597            std::vector<OperandType> inExpectedTypes;
598            std::vector<OperandType> outExpectedTypes;
599            if (inputType == OperandType::TENSOR_FLOAT32) {
600                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
601                                   OperandType::INT32,
602                                   OperandType::INT32,
603                                   OperandType::INT32,
604                                   OperandType::INT32,
605                                   OperandType::INT32,
606                                   OperandType::INT32};
607                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
608            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
609                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
610                                   OperandType::INT32,
611                                   OperandType::INT32,
612                                   OperandType::INT32,
613                                   OperandType::INT32,
614                                   OperandType::INT32,
615                                   OperandType::INT32};
616                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
617            } else {
618                LOG(ERROR) << "Unsupported input tensor type for operation "
619                           << kOperationNames[opType];
620                return ANEURALNETWORKS_BAD_DATA;
621            }
622
623            if (inputCount == 10) {
624                std::vector<OperandType> explicitScalarTypes(3, OperandType::INT32);
625                inExpectedTypes.insert(inExpectedTypes.end(),
626                                       explicitScalarTypes.begin(),
627                                       explicitScalarTypes.end());
628            }
629            return validateOperationOperandTypes(operands,
630                                                 inputCount, inputIndexes,
631                                                 inExpectedTypes,
632                                                 outputCount, outputIndexes,
633                                                 outExpectedTypes);
634        }
635        case ANEURALNETWORKS_L2_POOL_2D: {
636            if ((inputCount != 10 && inputCount != 7) || outputCount != 1) {
637                LOG(ERROR) << "Invalid number of input operands ("
638                           << inputCount << ", expected 10 or 7) or output operands ("
639                           << outputCount << ", expected 1) for operation "
640                           << kOperationNames[opType];
641                return ANEURALNETWORKS_BAD_DATA;
642            }
643            auto inputType = operands[inputIndexes[0]].type;
644            std::vector<OperandType> inExpectedTypes;
645            std::vector<OperandType> outExpectedTypes;
646            if (inputType == OperandType::TENSOR_FLOAT32) {
647                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
648                                   OperandType::INT32,
649                                   OperandType::INT32,
650                                   OperandType::INT32,
651                                   OperandType::INT32,
652                                   OperandType::INT32,
653                                   OperandType::INT32};
654                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
655            } else {
656                LOG(ERROR) << "Unsupported input tensor type for operation "
657                           << kOperationNames[opType];
658                return ANEURALNETWORKS_BAD_DATA;
659            }
660
661            if (inputCount == 10) {
662                std::vector<OperandType> explicitScalarTypes(3, OperandType::INT32);
663                inExpectedTypes.insert(inExpectedTypes.end(),
664                                       explicitScalarTypes.begin(),
665                                       explicitScalarTypes.end());
666            }
667            return validateOperationOperandTypes(operands,
668                                                 inputCount, inputIndexes,
669                                                 inExpectedTypes,
670                                                 outputCount, outputIndexes,
671                                                 outExpectedTypes);
672        }
673        case ANEURALNETWORKS_MAX_POOL_2D: {
674            if ((inputCount != 10 && inputCount != 7) || outputCount != 1) {
675                LOG(ERROR) << "Invalid number of input operands ("
676                           << inputCount << ", expected 10 or 7) or output operands ("
677                           << outputCount << ", expected 1) for operation "
678                           << kOperationNames[opType];
679                return ANEURALNETWORKS_BAD_DATA;
680            }
681            auto inputType = operands[inputIndexes[0]].type;
682            std::vector<OperandType> inExpectedTypes;
683            std::vector<OperandType> outExpectedTypes;
684            if (inputType == OperandType::TENSOR_FLOAT32) {
685                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
686                                   OperandType::INT32,
687                                   OperandType::INT32,
688                                   OperandType::INT32,
689                                   OperandType::INT32,
690                                   OperandType::INT32,
691                                   OperandType::INT32};
692                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
693            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
694                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
695                                   OperandType::INT32,
696                                   OperandType::INT32,
697                                   OperandType::INT32,
698                                   OperandType::INT32,
699                                   OperandType::INT32,
700                                   OperandType::INT32};
701                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
702            } else {
703                LOG(ERROR) << "Unsupported input tensor type for operation "
704                           << kOperationNames[opType];
705                return ANEURALNETWORKS_BAD_DATA;
706            }
707
708            if (inputCount == 10) {
709                std::vector<OperandType> explicitScalarTypes(3, OperandType::INT32);
710                inExpectedTypes.insert(inExpectedTypes.end(),
711                                       explicitScalarTypes.begin(),
712                                       explicitScalarTypes.end());
713            }
714            return validateOperationOperandTypes(operands,
715                                                 inputCount, inputIndexes,
716                                                 inExpectedTypes,
717                                                 outputCount, outputIndexes,
718                                                 outExpectedTypes);
719        }
720        case ANEURALNETWORKS_RELU: {
721            if (inputCount != 1 || outputCount != 1) {
722                logInvalidInOutNumber(1, 1);
723                return ANEURALNETWORKS_BAD_DATA;
724            }
725            auto inputType = operands[inputIndexes[0]].type;
726            std::vector<OperandType> inExpectedTypes;
727            std::vector<OperandType> outExpectedTypes;
728            if (inputType == OperandType::TENSOR_FLOAT32) {
729                inExpectedTypes = {OperandType::TENSOR_FLOAT32};
730                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
731            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
732                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
733                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
734            } else {
735                LOG(ERROR) << "Unsupported input tensor type for operation "
736                           << kOperationNames[opType];
737                return ANEURALNETWORKS_BAD_DATA;
738            }
739            return validateOperationOperandTypes(operands,
740                                                 inputCount, inputIndexes,
741                                                 inExpectedTypes,
742                                                 outputCount, outputIndexes,
743                                                 outExpectedTypes);
744        }
745        case ANEURALNETWORKS_RELU1: {
746            if (inputCount != 1 || outputCount != 1) {
747                logInvalidInOutNumber(1, 1);
748                return ANEURALNETWORKS_BAD_DATA;
749            }
750            auto inputType = operands[inputIndexes[0]].type;
751            std::vector<OperandType> inExpectedTypes;
752            std::vector<OperandType> outExpectedTypes;
753            if (inputType == OperandType::TENSOR_FLOAT32) {
754                inExpectedTypes = {OperandType::TENSOR_FLOAT32};
755                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
756            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
757                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
758                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
759            } else {
760                LOG(ERROR) << "Unsupported input tensor type for operation "
761                           << kOperationNames[opType];
762                return ANEURALNETWORKS_BAD_DATA;
763            }
764            return validateOperationOperandTypes(operands,
765                                                 inputCount, inputIndexes,
766                                                 inExpectedTypes,
767                                                 outputCount, outputIndexes,
768                                                 outExpectedTypes);
769        }
770        case ANEURALNETWORKS_RELU6: {
771            if (inputCount != 1 || outputCount != 1) {
772                logInvalidInOutNumber(1, 1);
773                return ANEURALNETWORKS_BAD_DATA;
774            }
775            auto inputType = operands[inputIndexes[0]].type;
776            std::vector<OperandType> inExpectedTypes;
777            std::vector<OperandType> outExpectedTypes;
778            if (inputType == OperandType::TENSOR_FLOAT32) {
779                inExpectedTypes = {OperandType::TENSOR_FLOAT32};
780                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
781            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
782                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
783                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
784            } else {
785                LOG(ERROR) << "Unsupported input tensor type for operation "
786                           << kOperationNames[opType];
787                return ANEURALNETWORKS_BAD_DATA;
788            }
789            return validateOperationOperandTypes(operands,
790                                                 inputCount, inputIndexes,
791                                                 inExpectedTypes,
792                                                 outputCount, outputIndexes,
793                                                 outExpectedTypes);
794        }
795        case ANEURALNETWORKS_TANH: {
796            if (inputCount != 1 || outputCount != 1) {
797                logInvalidInOutNumber(1, 1);
798                return ANEURALNETWORKS_BAD_DATA;
799            }
800            auto inputType = operands[inputIndexes[0]].type;
801            std::vector<OperandType> inExpectedTypes;
802            std::vector<OperandType> outExpectedTypes;
803            if (inputType == OperandType::TENSOR_FLOAT32) {
804                inExpectedTypes = {OperandType::TENSOR_FLOAT32};
805                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
806            } else {
807                LOG(ERROR) << "Unsupported input tensor type for operation "
808                           << kOperationNames[opType];
809                return ANEURALNETWORKS_BAD_DATA;
810            }
811            return validateOperationOperandTypes(operands,
812                                                 inputCount, inputIndexes,
813                                                 inExpectedTypes,
814                                                 outputCount, outputIndexes,
815                                                 outExpectedTypes);
816        }
817        case ANEURALNETWORKS_LOGISTIC: {
818            if (inputCount != 1 || outputCount != 1) {
819                logInvalidInOutNumber(1, 1);
820                return ANEURALNETWORKS_BAD_DATA;
821            }
822            auto inputType = operands[inputIndexes[0]].type;
823            std::vector<OperandType> inExpectedTypes;
824            std::vector<OperandType> outExpectedTypes;
825            if (inputType == OperandType::TENSOR_FLOAT32) {
826                inExpectedTypes = {OperandType::TENSOR_FLOAT32};
827                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
828            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
829                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
830                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
831            } else {
832                LOG(ERROR) << "Unsupported input tensor type for operation "
833                           << kOperationNames[opType];
834                return ANEURALNETWORKS_BAD_DATA;
835            }
836            return validateOperationOperandTypes(operands,
837                                                 inputCount, inputIndexes,
838                                                 inExpectedTypes,
839                                                 outputCount, outputIndexes,
840                                                 outExpectedTypes);
841        }
842        case ANEURALNETWORKS_SOFTMAX: {
843            if (inputCount != 2 || outputCount != 1) {
844                logInvalidInOutNumber(2, 1);
845                return ANEURALNETWORKS_BAD_DATA;
846            }
847            auto inputType = operands[inputIndexes[0]].type;
848            std::vector<OperandType> inExpectedTypes;
849            std::vector<OperandType> outExpectedTypes;
850            if (inputType == OperandType::TENSOR_FLOAT32) {
851                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
852                                   OperandType::FLOAT32};
853                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
854            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
855                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
856                                   OperandType::FLOAT32};
857                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
858            } else {
859                LOG(ERROR) << "Unsupported input tensor type for operation "
860                           << kOperationNames[opType];
861                return ANEURALNETWORKS_BAD_DATA;
862            }
863            return validateOperationOperandTypes(operands,
864                                                 inputCount, inputIndexes,
865                                                 inExpectedTypes,
866                                                 outputCount, outputIndexes,
867                                                 outExpectedTypes);
868        }
869        case ANEURALNETWORKS_FULLY_CONNECTED: {
870            if (inputCount != 4 || outputCount != 1) {
871                logInvalidInOutNumber(4, 1);
872                return ANEURALNETWORKS_BAD_DATA;
873            }
874            auto inputType = operands[inputIndexes[0]].type;
875            std::vector<OperandType> inExpectedTypes;
876            std::vector<OperandType> outExpectedTypes;
877            if (inputType == OperandType::TENSOR_FLOAT32) {
878                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
879                                   OperandType::TENSOR_FLOAT32,
880                                   OperandType::TENSOR_FLOAT32,
881                                   OperandType::INT32};
882                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
883            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
884                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
885                                   OperandType::TENSOR_QUANT8_ASYMM,
886                                   OperandType::TENSOR_INT32,
887                                   OperandType::INT32};
888                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
889            } else {
890                LOG(ERROR) << "Unsupported input tensor type for operation "
891                           << kOperationNames[opType];
892                return ANEURALNETWORKS_BAD_DATA;
893            }
894            return validateOperationOperandTypes(operands,
895                                                 inputCount, inputIndexes,
896                                                 inExpectedTypes,
897                                                 outputCount, outputIndexes,
898                                                 outExpectedTypes);
899        }
900        case ANEURALNETWORKS_CONCATENATION: {
901            if (inputCount < 2 || outputCount != 1) {
902                LOG(ERROR) << "Invalid number of input operands ("
903                           << inputCount << ", expected at least 2) or output operands ("
904                           << outputCount << ", expected 1) for operation "
905                           << kOperationNames[opType];
906                return ANEURALNETWORKS_BAD_DATA;
907            }
908            auto inputType = operands[inputIndexes[0]].type;
909            std::vector<OperandType> inExpectedTypes(inputCount, inputType);
910            std::vector<OperandType> outExpectedTypes = {inputType};
911            // The last one is the activation function.
912            inExpectedTypes.back() = OperandType::INT32;
913            return validateOperationOperandTypes(operands,
914                                                 inputCount, inputIndexes,
915                                                 inExpectedTypes,
916                                                 outputCount, outputIndexes,
917                                                 outExpectedTypes);
918        }
919        case ANEURALNETWORKS_L2_NORMALIZATION: {
920            if (inputCount != 1 || outputCount != 1) {
921                logInvalidInOutNumber(1, 1);
922                return ANEURALNETWORKS_BAD_DATA;
923            }
924            auto inputType = operands[inputIndexes[0]].type;
925            std::vector<OperandType> inExpectedTypes;
926            std::vector<OperandType> outExpectedTypes;
927            if (inputType == OperandType::TENSOR_FLOAT32) {
928                inExpectedTypes = {OperandType::TENSOR_FLOAT32};
929                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
930            } else {
931                LOG(ERROR) << "Unsupported input tensor type for operation "
932                           << kOperationNames[opType];
933                return ANEURALNETWORKS_BAD_DATA;
934            }
935            return validateOperationOperandTypes(operands,
936                                                 inputCount, inputIndexes,
937                                                 inExpectedTypes,
938                                                 outputCount, outputIndexes,
939                                                 outExpectedTypes);
940        }
941        case ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION: {
942            if (inputCount != 5 || outputCount != 1) {
943                logInvalidInOutNumber(5, 1);
944                return ANEURALNETWORKS_BAD_DATA;
945            }
946            auto inputType = operands[inputIndexes[0]].type;
947            std::vector<OperandType> inExpectedTypes;
948            std::vector<OperandType> outExpectedTypes;
949            if (inputType == OperandType::TENSOR_FLOAT32) {
950                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
951                                   OperandType::INT32,
952                                   OperandType::FLOAT32,
953                                   OperandType::FLOAT32,
954                                   OperandType::FLOAT32};
955                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
956            } else {
957                LOG(ERROR) << "Unsupported input tensor type for operation "
958                           << kOperationNames[opType];
959                return ANEURALNETWORKS_BAD_DATA;
960            }
961            return validateOperationOperandTypes(operands,
962                                                 inputCount, inputIndexes,
963                                                 inExpectedTypes,
964                                                 outputCount, outputIndexes,
965                                                 outExpectedTypes);
966        }
967        case ANEURALNETWORKS_RESHAPE: {
968            if (inputCount != 2 || outputCount != 1) {
969                logInvalidInOutNumber(2, 1);
970                return ANEURALNETWORKS_BAD_DATA;
971            }
972            auto inputType = operands[inputIndexes[0]].type;
973            std::vector<OperandType> inExpectedTypes;
974            std::vector<OperandType> outExpectedTypes;
975            if (inputType == OperandType::TENSOR_FLOAT32) {
976                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
977                                   OperandType::TENSOR_INT32};
978                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
979            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
980                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
981                                   OperandType::TENSOR_INT32};
982                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
983            } else {
984                LOG(ERROR) << "Unsupported input tensor type for operation "
985                           << kOperationNames[opType];
986                return ANEURALNETWORKS_BAD_DATA;
987            }
988            return validateOperationOperandTypes(operands,
989                                                 inputCount, inputIndexes,
990                                                 inExpectedTypes,
991                                                 outputCount, outputIndexes,
992                                                 outExpectedTypes);
993        }
994        case ANEURALNETWORKS_RESIZE_BILINEAR: {
995            if (inputCount != 3 || outputCount != 1) {
996                logInvalidInOutNumber(3, 1);
997                return ANEURALNETWORKS_BAD_DATA;
998            }
999            auto inputType = operands[inputIndexes[0]].type;
1000            std::vector<OperandType> inExpectedTypes;
1001            std::vector<OperandType> outExpectedTypes;
1002            if (inputType == OperandType::TENSOR_FLOAT32) {
1003                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1004                                   OperandType::INT32,
1005                                   OperandType::INT32};
1006                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1007            } else {
1008                LOG(ERROR) << "Unsupported input tensor type for operation "
1009                           << kOperationNames[opType];
1010                return ANEURALNETWORKS_BAD_DATA;
1011            }
1012            return validateOperationOperandTypes(operands,
1013                                                 inputCount, inputIndexes,
1014                                                 inExpectedTypes,
1015                                                 outputCount, outputIndexes,
1016                                                 outExpectedTypes);
1017        }
1018        case ANEURALNETWORKS_DEPTH_TO_SPACE: {
1019            if (inputCount != 2 || outputCount != 1) {
1020                logInvalidInOutNumber(2, 1);
1021                return ANEURALNETWORKS_BAD_DATA;
1022            }
1023            auto inputType = operands[inputIndexes[0]].type;
1024            std::vector<OperandType> inExpectedTypes;
1025            std::vector<OperandType> outExpectedTypes;
1026            if (inputType == OperandType::TENSOR_FLOAT32) {
1027                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1028                                   OperandType::INT32};
1029                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1030            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
1031                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
1032                                   OperandType::INT32};
1033                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
1034            } else {
1035                LOG(ERROR) << "Unsupported input tensor type for operation "
1036                           << kOperationNames[opType];
1037                return ANEURALNETWORKS_BAD_DATA;
1038            }
1039            return validateOperationOperandTypes(operands,
1040                                                 inputCount, inputIndexes,
1041                                                 inExpectedTypes,
1042                                                 outputCount, outputIndexes,
1043                                                 outExpectedTypes);
1044        }
1045        case ANEURALNETWORKS_SPACE_TO_DEPTH: {
1046            if (inputCount != 2 || outputCount != 1) {
1047                logInvalidInOutNumber(2, 1);
1048                return ANEURALNETWORKS_BAD_DATA;
1049            }
1050            auto inputType = operands[inputIndexes[0]].type;
1051            std::vector<OperandType> inExpectedTypes;
1052            std::vector<OperandType> outExpectedTypes;
1053            if (inputType == OperandType::TENSOR_FLOAT32) {
1054                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1055                                   OperandType::INT32};
1056                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1057            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
1058                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
1059                                   OperandType::INT32};
1060                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
1061            } else {
1062                LOG(ERROR) << "Unsupported input tensor type for operation "
1063                           << kOperationNames[opType];
1064                return ANEURALNETWORKS_BAD_DATA;
1065            }
1066            return validateOperationOperandTypes(operands,
1067                                                 inputCount, inputIndexes,
1068                                                 inExpectedTypes,
1069                                                 outputCount, outputIndexes,
1070                                                 outExpectedTypes);
1071        }
1072        case ANEURALNETWORKS_EMBEDDING_LOOKUP: {
1073            if (inputCount != 2 || outputCount != 1) {
1074                logInvalidInOutNumber(2, 1);
1075                return ANEURALNETWORKS_BAD_DATA;
1076            }
1077            auto inputType = operands[inputIndexes[1]].type;
1078            std::vector<OperandType> inExpectedTypes = {OperandType::TENSOR_INT32,
1079                                                        inputType};
1080            std::vector<OperandType> outExpectedTypes = {inputType};
1081            return validateOperationOperandTypes(operands,
1082                                                 inputCount, inputIndexes,
1083                                                 inExpectedTypes,
1084                                                 outputCount, outputIndexes,
1085                                                 outExpectedTypes);
1086        }
1087        case ANEURALNETWORKS_HASHTABLE_LOOKUP: {
1088            if (inputCount != 3 || outputCount != 2) {
1089                logInvalidInOutNumber(3, 2);
1090                return ANEURALNETWORKS_BAD_DATA;
1091            }
1092            auto inputType = operands[inputIndexes[2]].type;
1093            std::vector<OperandType> inExpectedTypes = {OperandType::TENSOR_INT32,
1094                                                        OperandType::TENSOR_INT32,
1095                                                        inputType};
1096            std::vector<OperandType> outExpectedTypes = {inputType,
1097                                                         OperandType::TENSOR_QUANT8_ASYMM};
1098            return validateOperationOperandTypes(operands,
1099                                                 inputCount, inputIndexes,
1100                                                 inExpectedTypes,
1101                                                 outputCount, outputIndexes,
1102                                                 outExpectedTypes);
1103        }
1104        case ANEURALNETWORKS_LSH_PROJECTION: {
1105            if (inputCount != 4 || outputCount != 1) {
1106                logInvalidInOutNumber(4, 1);
1107                return ANEURALNETWORKS_BAD_DATA;
1108            }
1109            auto inputType = operands[inputIndexes[1]].type;
1110            std::vector<OperandType> inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1111                                                        inputType,
1112                                                        OperandType::TENSOR_FLOAT32,
1113                                                        OperandType::INT32};
1114            std::vector<OperandType> outExpectedTypes = {OperandType::TENSOR_INT32};
1115            return validateOperationOperandTypes(operands,
1116                                                 inputCount, inputIndexes,
1117                                                 inExpectedTypes,
1118                                                 outputCount, outputIndexes,
1119                                                 outExpectedTypes);
1120        }
1121        case ANEURALNETWORKS_LSTM: {
1122            if (inputCount != 23 || outputCount != 4) {
1123                logInvalidInOutNumber(23, 4);
1124                return ANEURALNETWORKS_BAD_DATA;
1125            }
1126            std::vector<OperandType> inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1127                                                        OperandType::TENSOR_FLOAT32,
1128                                                        OperandType::TENSOR_FLOAT32,
1129                                                        OperandType::TENSOR_FLOAT32,
1130                                                        OperandType::TENSOR_FLOAT32,
1131                                                        OperandType::TENSOR_FLOAT32,
1132                                                        OperandType::TENSOR_FLOAT32,
1133                                                        OperandType::TENSOR_FLOAT32,
1134                                                        OperandType::TENSOR_FLOAT32,
1135                                                        OperandType::TENSOR_FLOAT32,
1136                                                        OperandType::TENSOR_FLOAT32,
1137                                                        OperandType::TENSOR_FLOAT32,
1138                                                        OperandType::TENSOR_FLOAT32,
1139                                                        OperandType::TENSOR_FLOAT32,
1140                                                        OperandType::TENSOR_FLOAT32,
1141                                                        OperandType::TENSOR_FLOAT32,
1142                                                        OperandType::TENSOR_FLOAT32,
1143                                                        OperandType::TENSOR_FLOAT32,
1144                                                        OperandType::TENSOR_FLOAT32,
1145                                                        OperandType::TENSOR_FLOAT32,
1146                                                        OperandType::INT32,
1147                                                        OperandType::FLOAT32,
1148                                                        OperandType::FLOAT32};
1149            std::vector<OperandType> outExpectedTypes = {OperandType::TENSOR_FLOAT32,
1150                                                         OperandType::TENSOR_FLOAT32,
1151                                                         OperandType::TENSOR_FLOAT32,
1152                                                         OperandType::TENSOR_FLOAT32};
1153            return validateOperationOperandTypes(operands,
1154                                                 inputCount, inputIndexes,
1155                                                 inExpectedTypes,
1156                                                 outputCount, outputIndexes,
1157                                                 outExpectedTypes);
1158        }
1159        case ANEURALNETWORKS_RNN: {
1160            if (inputCount != 6 || outputCount != 2) {
1161                logInvalidInOutNumber(6, 2);
1162                return ANEURALNETWORKS_BAD_DATA;
1163            }
1164            std::vector<OperandType> inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1165                                                        OperandType::TENSOR_FLOAT32,
1166                                                        OperandType::TENSOR_FLOAT32,
1167                                                        OperandType::TENSOR_FLOAT32,
1168                                                        OperandType::TENSOR_FLOAT32,
1169                                                        OperandType::INT32};
1170            std::vector<OperandType> outExpectedTypes = {OperandType::TENSOR_FLOAT32,
1171                                                         OperandType::TENSOR_FLOAT32};
1172            return validateOperationOperandTypes(operands,
1173                                                 inputCount, inputIndexes,
1174                                                 inExpectedTypes,
1175                                                 outputCount, outputIndexes,
1176                                                 outExpectedTypes);
1177        }
1178        case ANEURALNETWORKS_SVDF: {
1179            if (inputCount != 7 || outputCount != 2) {
1180                logInvalidInOutNumber(7, 2);
1181                return ANEURALNETWORKS_BAD_DATA;
1182            }
1183            std::vector<OperandType> inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1184                                                        OperandType::TENSOR_FLOAT32,
1185                                                        OperandType::TENSOR_FLOAT32,
1186                                                        OperandType::TENSOR_FLOAT32,
1187                                                        OperandType::TENSOR_FLOAT32,
1188                                                        OperandType::INT32,
1189                                                        OperandType::INT32};
1190            std::vector<OperandType> outExpectedTypes = {OperandType::TENSOR_FLOAT32,
1191                                                         OperandType::TENSOR_FLOAT32};
1192            return validateOperationOperandTypes(operands,
1193                                                 inputCount, inputIndexes,
1194                                                 inExpectedTypes,
1195                                                 outputCount, outputIndexes,
1196                                                 outExpectedTypes);
1197        }
1198        case ANEURALNETWORKS_BATCH_TO_SPACE_ND: {
1199            if (inputCount != 2 || outputCount != 1) {
1200                logInvalidInOutNumber(2, 1);
1201                return ANEURALNETWORKS_BAD_DATA;
1202            }
1203            auto inputType = operands[inputIndexes[0]].type;
1204            std::vector<OperandType> inExpectedTypes;
1205            std::vector<OperandType> outExpectedTypes;
1206            if (inputType == OperandType::TENSOR_FLOAT32) {
1207                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1208                                   OperandType::TENSOR_INT32};
1209                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1210            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
1211                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
1212                                   OperandType::TENSOR_INT32};
1213                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
1214            } else {
1215                LOG(ERROR) << "Unsupported input tensor type for operation "
1216                           << kOperationNames[opType];
1217                return ANEURALNETWORKS_BAD_DATA;
1218            }
1219            return validateOperationOperandTypes(operands,
1220                                                 inputCount, inputIndexes,
1221                                                 inExpectedTypes,
1222                                                 outputCount, outputIndexes,
1223                                                 outExpectedTypes);
1224        }
1225        case ANEURALNETWORKS_SPACE_TO_BATCH_ND: {
1226            if (inputCount != 3 || outputCount != 1) {
1227                logInvalidInOutNumber(3, 1);
1228                return ANEURALNETWORKS_BAD_DATA;
1229            }
1230            auto inputType = operands[inputIndexes[0]].type;
1231            std::vector<OperandType> inExpectedTypes;
1232            std::vector<OperandType> outExpectedTypes;
1233            if (inputType == OperandType::TENSOR_FLOAT32) {
1234                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1235                                   OperandType::TENSOR_INT32,
1236                                   OperandType::TENSOR_INT32};
1237                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1238            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
1239                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
1240                                   OperandType::TENSOR_INT32,
1241                                   OperandType::TENSOR_INT32};
1242                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
1243            } else {
1244                LOG(ERROR) << "Unsupported input tensor type for operation "
1245                           << kOperationNames[opType];
1246                return ANEURALNETWORKS_BAD_DATA;
1247            }
1248            return validateOperationOperandTypes(operands,
1249                                                 inputCount, inputIndexes,
1250                                                 inExpectedTypes,
1251                                                 outputCount, outputIndexes,
1252                                                 outExpectedTypes);
1253        }
1254        case ANEURALNETWORKS_PAD: {
1255            if (inputCount != 2 || outputCount != 1) {
1256                logInvalidInOutNumber(2, 1);
1257                return ANEURALNETWORKS_BAD_DATA;
1258            }
1259            auto inputType = operands[inputIndexes[0]].type;
1260            std::vector<OperandType> inExpectedTypes;
1261            std::vector<OperandType> outExpectedTypes;
1262            if (inputType == OperandType::TENSOR_FLOAT32) {
1263                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1264                                   OperandType::TENSOR_INT32};
1265                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1266            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
1267                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
1268                                   OperandType::TENSOR_INT32};
1269                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
1270            } else {
1271                LOG(ERROR) << "Unsupported input tensor type for operation "
1272                           << kOperationNames[opType];
1273                return ANEURALNETWORKS_BAD_DATA;
1274            }
1275            return validateOperationOperandTypes(operands,
1276                                                 inputCount, inputIndexes,
1277                                                 inExpectedTypes,
1278                                                 outputCount, outputIndexes,
1279                                                 outExpectedTypes);
1280        }
1281        case ANEURALNETWORKS_SQUEEZE: {
1282            if (inputCount != 2 || outputCount != 1) {
1283                logInvalidInOutNumber(2, 1);
1284                return ANEURALNETWORKS_BAD_DATA;
1285            }
1286            auto inputType = operands[inputIndexes[0]].type;
1287            std::vector<OperandType> inExpectedTypes;
1288            std::vector<OperandType> outExpectedTypes;
1289            if (inputType == OperandType::TENSOR_FLOAT32) {
1290                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1291                                   OperandType::TENSOR_INT32};
1292                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1293            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
1294                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
1295                                   OperandType::TENSOR_INT32};
1296                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
1297            } else {
1298                LOG(ERROR) << "Unsupported input tensor type for operation "
1299                           << kOperationNames[opType];
1300                return ANEURALNETWORKS_BAD_DATA;
1301            }
1302            return validateOperationOperandTypes(operands,
1303                                                 inputCount, inputIndexes,
1304                                                 inExpectedTypes,
1305                                                 outputCount, outputIndexes,
1306                                                 outExpectedTypes);
1307        }
1308        case ANEURALNETWORKS_TRANSPOSE: {
1309            if (inputCount != 2 || outputCount != 1) {
1310                logInvalidInOutNumber(2, 1);
1311                return ANEURALNETWORKS_BAD_DATA;
1312            }
1313            auto inputType = operands[inputIndexes[0]].type;
1314            std::vector<OperandType> inExpectedTypes;
1315            std::vector<OperandType> outExpectedTypes;
1316            if (inputType == OperandType::TENSOR_FLOAT32) {
1317                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1318                                   OperandType::TENSOR_INT32};
1319                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1320            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
1321                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
1322                                   OperandType::TENSOR_INT32};
1323                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
1324            } else {
1325                LOG(ERROR) << "Unsupported input tensor type for operation "
1326                           << kOperationNames[opType];
1327                return ANEURALNETWORKS_BAD_DATA;
1328            }
1329            return validateOperationOperandTypes(operands,
1330                                                 inputCount, inputIndexes,
1331                                                 inExpectedTypes,
1332                                                 outputCount, outputIndexes,
1333                                                 outExpectedTypes);
1334        }
1335        case ANEURALNETWORKS_STRIDED_SLICE: {
1336            if (inputCount != 7 || outputCount != 1) {
1337                logInvalidInOutNumber(7, 1);
1338                return ANEURALNETWORKS_BAD_DATA;
1339            }
1340            auto inputType = operands[inputIndexes[0]].type;
1341            std::vector<OperandType> inExpectedTypes;
1342            std::vector<OperandType> outExpectedTypes;
1343            if (inputType == OperandType::TENSOR_FLOAT32) {
1344                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1345                                   OperandType::TENSOR_INT32,
1346                                   OperandType::TENSOR_INT32,
1347                                   OperandType::TENSOR_INT32,
1348                                   OperandType::INT32,
1349                                   OperandType::INT32,
1350                                   OperandType::INT32};
1351                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1352            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
1353                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
1354                                   OperandType::TENSOR_INT32,
1355                                   OperandType::TENSOR_INT32,
1356                                   OperandType::TENSOR_INT32,
1357                                   OperandType::INT32,
1358                                   OperandType::INT32,
1359                                   OperandType::INT32};
1360                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
1361            } else {
1362                LOG(ERROR) << "Unsupported input tensor type for operation "
1363                           << kOperationNames[opType];
1364                return ANEURALNETWORKS_BAD_DATA;
1365            }
1366            return validateOperationOperandTypes(operands,
1367                                                 inputCount, inputIndexes,
1368                                                 inExpectedTypes,
1369                                                 outputCount, outputIndexes,
1370                                                 outExpectedTypes);
1371        }
1372        case ANEURALNETWORKS_DIV: {
1373            if (inputCount != 3 || outputCount != 1) {
1374                logInvalidInOutNumber(3, 1);
1375                return ANEURALNETWORKS_BAD_DATA;
1376            }
1377            auto inputType = operands[inputIndexes[0]].type;
1378            std::vector<OperandType> inExpectedTypes;
1379            std::vector<OperandType> outExpectedTypes;
1380            if (inputType == OperandType::TENSOR_FLOAT32) {
1381                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1382                                   OperandType::TENSOR_FLOAT32,
1383                                   OperandType::INT32};
1384                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1385            } else {
1386                LOG(ERROR) << "Unsupported input tensor type for operation "
1387                           << kOperationNames[opType];
1388                return ANEURALNETWORKS_BAD_DATA;
1389            }
1390            return validateOperationOperandTypes(operands,
1391                                                 inputCount, inputIndexes,
1392                                                 inExpectedTypes,
1393                                                 outputCount, outputIndexes,
1394                                                 outExpectedTypes);
1395        }
1396        case ANEURALNETWORKS_SUB: {
1397            if (inputCount != 3 || outputCount != 1) {
1398                logInvalidInOutNumber(3, 1);
1399                return ANEURALNETWORKS_BAD_DATA;
1400            }
1401            auto inputType = operands[inputIndexes[0]].type;
1402            std::vector<OperandType> inExpectedTypes;
1403            std::vector<OperandType> outExpectedTypes;
1404            if (inputType == OperandType::TENSOR_FLOAT32) {
1405                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1406                                   OperandType::TENSOR_FLOAT32,
1407                                   OperandType::INT32};
1408                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1409            } else {
1410                LOG(ERROR) << "Unsupported input tensor type for operation "
1411                           << kOperationNames[opType];
1412                return ANEURALNETWORKS_BAD_DATA;
1413            }
1414            return validateOperationOperandTypes(operands,
1415                                                 inputCount, inputIndexes,
1416                                                 inExpectedTypes,
1417                                                 outputCount, outputIndexes,
1418                                                 outExpectedTypes);
1419        }
1420        case ANEURALNETWORKS_MEAN: {
1421            if (inputCount != 3 || outputCount != 1) {
1422                logInvalidInOutNumber(3, 1);
1423                return ANEURALNETWORKS_BAD_DATA;
1424            }
1425            auto inputType = operands[inputIndexes[0]].type;
1426            std::vector<OperandType> inExpectedTypes;
1427            std::vector<OperandType> outExpectedTypes;
1428            if (inputType == OperandType::TENSOR_FLOAT32) {
1429                inExpectedTypes = {OperandType::TENSOR_FLOAT32,
1430                                   OperandType::TENSOR_INT32,
1431                                   OperandType::INT32};
1432                outExpectedTypes = {OperandType::TENSOR_FLOAT32};
1433            } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
1434                inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
1435                                   OperandType::TENSOR_INT32,
1436                                   OperandType::INT32};
1437                outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
1438            } else {
1439                LOG(ERROR) << "Unsupported input tensor type for operation "
1440                           << kOperationNames[opType];
1441                return ANEURALNETWORKS_BAD_DATA;
1442            }
1443            return validateOperationOperandTypes(operands,
1444                                                 inputCount, inputIndexes,
1445                                                 inExpectedTypes,
1446                                                 outputCount, outputIndexes,
1447                                                 outExpectedTypes);
1448        }
1449        default:
1450            return ANEURALNETWORKS_BAD_DATA;
1451    }
1452}
1453
1454ErrorStatus convertResultCodeToErrorStatus(int resultCode) {
1455    switch (resultCode) {
1456        case ANEURALNETWORKS_NO_ERROR:
1457            return ErrorStatus::NONE;
1458
1459        case ANEURALNETWORKS_BAD_DATA:
1460        case ANEURALNETWORKS_UNEXPECTED_NULL:
1461            return ErrorStatus::INVALID_ARGUMENT;
1462
1463        default:
1464            LOG(ERROR) << "Unknown result code " << resultCode
1465                       << " mapped to ErrorStatus::GENERAL_FAILURE";
1466        case ANEURALNETWORKS_BAD_STATE:
1467        case ANEURALNETWORKS_INCOMPLETE:
1468        case ANEURALNETWORKS_OP_FAILED:
1469        case ANEURALNETWORKS_OUT_OF_MEMORY:
1470        case ANEURALNETWORKS_UNMAPPABLE:
1471            return ErrorStatus::GENERAL_FAILURE;
1472    }
1473}
1474
1475int convertErrorStatusToResultCode(ErrorStatus status) {
1476    switch (status) {
1477        case ErrorStatus::NONE:
1478            return ANEURALNETWORKS_NO_ERROR;
1479
1480        case ErrorStatus::INVALID_ARGUMENT:
1481            return ANEURALNETWORKS_BAD_DATA;
1482
1483        default:
1484            LOG(ERROR) << "Unknown ErrorStatus " << toString(status)
1485                       << " mapped to ANEURALNETWORKS_OP_FAILED";
1486        case ErrorStatus::DEVICE_UNAVAILABLE:
1487        case ErrorStatus::GENERAL_FAILURE:
1488        case ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
1489            return ANEURALNETWORKS_OP_FAILED;
1490    }
1491}
1492
1493// Versioning
1494
1495bool compliantWithV1_0(V1_0::OperationType) {
1496    return true;
1497}
1498
1499bool compliantWithV1_0(V1_1::OperationType operation) {
1500    switch (static_cast<V1_0::OperationType>(operation)) {
1501        case V1_0::OperationType::ADD:
1502        case V1_0::OperationType::AVERAGE_POOL_2D:
1503        case V1_0::OperationType::CONCATENATION:
1504        case V1_0::OperationType::CONV_2D:
1505        case V1_0::OperationType::DEPTHWISE_CONV_2D:
1506        case V1_0::OperationType::DEPTH_TO_SPACE:
1507        case V1_0::OperationType::DEQUANTIZE:
1508        case V1_0::OperationType::EMBEDDING_LOOKUP:
1509        case V1_0::OperationType::FLOOR:
1510        case V1_0::OperationType::FULLY_CONNECTED:
1511        case V1_0::OperationType::HASHTABLE_LOOKUP:
1512        case V1_0::OperationType::L2_NORMALIZATION:
1513        case V1_0::OperationType::L2_POOL_2D:
1514        case V1_0::OperationType::LOCAL_RESPONSE_NORMALIZATION:
1515        case V1_0::OperationType::LOGISTIC:
1516        case V1_0::OperationType::LSH_PROJECTION:
1517        case V1_0::OperationType::LSTM:
1518        case V1_0::OperationType::MAX_POOL_2D:
1519        case V1_0::OperationType::MUL:
1520        case V1_0::OperationType::RELU:
1521        case V1_0::OperationType::RELU1:
1522        case V1_0::OperationType::RELU6:
1523        case V1_0::OperationType::RESHAPE:
1524        case V1_0::OperationType::RESIZE_BILINEAR:
1525        case V1_0::OperationType::RNN:
1526        case V1_0::OperationType::SOFTMAX:
1527        case V1_0::OperationType::SPACE_TO_DEPTH:
1528        case V1_0::OperationType::SVDF:
1529        case V1_0::OperationType::TANH:
1530        case V1_0::OperationType::OEM_OPERATION:
1531            return true;
1532        default:
1533            return false;
1534    }
1535}
1536
1537bool compliantWithV1_1(V1_0::OperationType) {
1538    return true;
1539}
1540
1541bool compliantWithV1_1(V1_1::OperationType) {
1542    return true;
1543}
1544
1545bool compliantWithV1_0(V1_0::Capabilities) {
1546    return true;
1547}
1548
1549bool compliantWithV1_0(const V1_1::Capabilities& capabilities) {
1550    return capabilities.relaxedFloat32toFloat16Performance.execTime ==
1551           capabilities.float32Performance.execTime
1552           &&
1553           capabilities.relaxedFloat32toFloat16Performance.powerUsage ==
1554           capabilities.float32Performance.powerUsage;
1555}
1556
1557bool compliantWithV1_1(const V1_0::Capabilities&) {
1558    return true;
1559}
1560
1561bool compliantWithV1_1(const V1_1::Capabilities&) {
1562    return true;
1563}
1564
1565bool compliantWithV1_0(const V1_0::Operation&) {
1566    return true;
1567}
1568
1569bool compliantWithV1_0(const V1_1::Operation& operation) {
1570    return compliantWithV1_0(operation.type);
1571}
1572
1573bool compliantWithV1_1(const V1_0::Operation&) {
1574    return true;
1575}
1576
1577bool compliantWithV1_1(const V1_1::Operation&) {
1578    return true;
1579}
1580
1581static bool compliantWithV1_0(const hidl_vec<V1_1::Operation>& operations) {
1582    return std::all_of(operations.begin(), operations.end(),
1583                       [](const V1_1::Operation& operation) {
1584                           return compliantWithV1_0(operation);
1585                       });
1586}
1587
1588bool compliantWithV1_0(const V1_0::Model&) {
1589    return true;
1590}
1591
1592bool compliantWithV1_0(const V1_1::Model& model) {
1593    // In addition to new enumeration values being introduced in V1_1::Model, a
1594    // new flag was introduced to indicate whether or not float32 data can be
1595    // calculated using float16 units. This 'relaxComputationFloat32toFloat16'
1596    // flag is not relevant in whether a V1_1::Model is compliant with a
1597    // V1_0::Model because all 1.0 drivers require strict calculation by default
1598    // in the P NN runtime. Even if fp16 calculations are allowed, they can
1599    // still be computed by a strict fp32 driver.
1600    return compliantWithV1_0(model.operations);
1601}
1602
1603bool compliantWithV1_1(const V1_0::Model&) {
1604    return true;
1605}
1606
1607bool compliantWithV1_1(const V1_1::Model&) {
1608    return true;
1609}
1610
1611V1_0::OperationType convertToV1_0(V1_0::OperationType type) {
1612    return type;
1613}
1614
1615V1_0::OperationType convertToV1_0(V1_1::OperationType type) {
1616    if (!compliantWithV1_0(type)) {
1617        LOG(ERROR) << "Upcasting non-compliant type " << toString(type)
1618                   << " from V1_1::OperationType to V1_0::OperationType";
1619    }
1620    return static_cast<V1_0::OperationType>(type);
1621}
1622
1623V1_1::OperationType convertToV1_1(V1_0::OperationType type) {
1624    return static_cast<V1_1::OperationType>(type);
1625}
1626
1627V1_1::OperationType convertToV1_1(V1_1::OperationType type) {
1628    return type;
1629}
1630
1631V1_0::Capabilities convertToV1_0(const V1_0::Capabilities& capabilities) {
1632    return capabilities;
1633}
1634
1635V1_0::Capabilities convertToV1_0(const V1_1::Capabilities& capabilities) {
1636    if (!compliantWithV1_0(capabilities)) {
1637        LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities)
1638                   << " from V1_1::Capabilities to V1_0::Capabilities";
1639    }
1640    return { .float32Performance = capabilities.float32Performance,
1641             .quantized8Performance = capabilities.quantized8Performance };
1642}
1643
1644V1_1::Capabilities convertToV1_1(const V1_0::Capabilities& capabilities) {
1645    return { .float32Performance = capabilities.float32Performance,
1646             .quantized8Performance = capabilities.quantized8Performance,
1647             .relaxedFloat32toFloat16Performance = capabilities.float32Performance };
1648}
1649
1650V1_1::Capabilities convertToV1_1(const V1_1::Capabilities& capabilities) {
1651    return capabilities;
1652}
1653
1654V1_0::Operation convertToV1_0(const V1_0::Operation& operation) {
1655    return operation;
1656}
1657
1658V1_0::Operation convertToV1_0(const V1_1::Operation& operation) {
1659    if (!compliantWithV1_0(operation)) {
1660        LOG(ERROR) << "Upcasting non-compliant operation " << toString(operation)
1661                   << " from V1_1::Operation to V1_0::Operation";
1662    }
1663    return {.type = convertToV1_0(operation.type),
1664            .inputs = operation.inputs,
1665            .outputs = operation.outputs};
1666}
1667
1668V1_1::Operation convertToV1_1(const V1_0::Operation& operation) {
1669    return {.type = convertToV1_1(operation.type),
1670            .inputs = operation.inputs,
1671            .outputs = operation.outputs};
1672}
1673
1674V1_1::Operation convertToV1_1(const V1_1::Operation& operation) {
1675    return operation;
1676}
1677
1678static hidl_vec<V1_0::Operation> convertToV1_0(const hidl_vec<V1_1::Operation>& operations) {
1679    hidl_vec<V1_0::Operation> result(operations.size());
1680    std::transform(operations.begin(), operations.end(), result.begin(),
1681                   [](const V1_1::Operation& operation) { return convertToV1_0(operation); });
1682    return result;
1683}
1684
1685static hidl_vec<V1_1::Operation> convertToV1_1(const hidl_vec<V1_0::Operation>& operations) {
1686    hidl_vec<V1_1::Operation> result(operations.size());
1687    std::transform(operations.begin(), operations.end(), result.begin(),
1688                   [](const V1_0::Operation& operation) { return convertToV1_1(operation); });
1689    return result;
1690}
1691
1692V1_0::Model convertToV1_0(const V1_0::Model& model) {
1693    return model;
1694}
1695
1696V1_0::Model convertToV1_0(const V1_1::Model& model) {
1697    if (!compliantWithV1_0(model)) {
1698        LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model))
1699                   << " from V1_1::Model to V1_0::Model";
1700    }
1701    return {.operands = model.operands,
1702            .operations = convertToV1_0(model.operations),
1703            .inputIndexes = model.inputIndexes,
1704            .outputIndexes = model.outputIndexes,
1705            .operandValues = model.operandValues,
1706            .pools = model.pools};
1707}
1708
1709V1_1::Model convertToV1_1(const V1_0::Model& model) {
1710    return {.operands = model.operands,
1711            .operations = convertToV1_1(model.operations),
1712            .inputIndexes = model.inputIndexes,
1713            .outputIndexes = model.outputIndexes,
1714            .operandValues = model.operandValues,
1715            .pools = model.pools,
1716            .relaxComputationFloat32toFloat16 = false};
1717}
1718
1719V1_1::Model convertToV1_1(const V1_1::Model& model) {
1720    return model;
1721}
1722
1723#ifdef NN_DEBUGGABLE
1724uint32_t getProp(const char* str, uint32_t defaultValue) {
1725    const std::string propStr = android::base::GetProperty(str, "");
1726    if (propStr.size() > 0) {
1727        return std::stoi(propStr);
1728    } else {
1729        return defaultValue;
1730    }
1731}
1732#endif  // NN_DEBUGGABLE
1733
1734} // namespace nn
1735} // namespace android
1736