Home | History | Annotate | Download | only in common
      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 
     21 #include "NeuralNetworks.h"
     22 #include "NeuralNetworksOEM.h"
     23 #include "OperationResolver.h"
     24 #include "ValidateHal.h"
     25 
     26 #include <android-base/logging.h>
     27 #include <android-base/properties.h>
     28 #include <android-base/strings.h>
     29 #include <sys/system_properties.h>
     30 #include <algorithm>
     31 #include <unordered_map>
     32 
     33 using ::android::hidl::allocator::V1_0::IAllocator;
     34 
     35 namespace android {
     36 namespace nn {
     37 
     38 const char kVLogPropKey[] = "debug.nn.vlog";
     39 int vLogMask = ~0;
     40 
     41 // Split the space separated list of tags from verbose log setting and build the
     42 // logging mask from it. note that '1' and 'all' are special cases to enable all
     43 // verbose logging.
     44 //
     45 // NN API verbose logging setting comes from system property debug.nn.vlog.
     46 // Example:
     47 // setprop debug.nn.vlog 1 : enable all logging tags.
     48 // setprop debug.nn.vlog "model compilation" : only enable logging for MODEL and
     49 //                                             COMPILATION tags.
     50 void initVLogMask() {
     51     vLogMask = 0;
     52     const std::string vLogSetting = android::base::GetProperty(kVLogPropKey, "");
     53     if (vLogSetting.empty()) {
     54         return;
     55     }
     56 
     57     std::unordered_map<std::string, int> vLogFlags = {
     58         {"1", -1},
     59         {"all", -1},
     60         {"model", MODEL},
     61         {"compilation", COMPILATION},
     62         {"execution", EXECUTION},
     63         {"cpuexe", CPUEXE},
     64         {"manager", MANAGER},
     65         {"driver", DRIVER}};
     66 
     67     std::vector<std::string> elements = android::base::Split(vLogSetting, " ,:");
     68     for (const auto& elem : elements) {
     69         const auto& flag = vLogFlags.find(elem);
     70         if (flag == vLogFlags.end()) {
     71             LOG(ERROR) << "Unknown trace flag: " << elem;
     72             continue;
     73         }
     74 
     75         if (flag->second == -1) {
     76             // -1 is used for the special values "1" and "all" that enable all
     77             // tracing.
     78             vLogMask = ~0;
     79             return;
     80         } else {
     81             vLogMask |= 1 << flag->second;
     82         }
     83     }
     84 }
     85 
     86 static bool isExtensionOperandType(int32_t type) {
     87     return static_cast<uint32_t>(type) > static_cast<uint32_t>(OperandTypeRange::BASE_MAX);
     88 }
     89 
     90 static bool isExtensionOperationType(ANeuralNetworksOperationType type) {
     91     return static_cast<uint32_t>(type) > static_cast<uint32_t>(OperationTypeRange::BASE_MAX);
     92 }
     93 
     94 bool isExtensionOperandType(OperandType type) {
     95     return isExtensionOperandType(static_cast<int32_t>(type));
     96 }
     97 
     98 bool isExtensionOperationType(OperationType type) {
     99     return isExtensionOperationType(static_cast<int32_t>(type));
    100 }
    101 
    102 namespace {
    103 
    104 template <typename EntryType, uint32_t entryCount, uint32_t entryCountOEM>
    105 EntryType tableLookup(const EntryType (&table)[entryCount],
    106                       const EntryType (&tableOEM)[entryCountOEM],
    107                       uint32_t code) {
    108     if (code < entryCount) {
    109         return table[code];
    110     } else if (code >= kOEMCodeBase && (code - kOEMCodeBase) < entryCountOEM) {
    111         return tableOEM[code - kOEMCodeBase];
    112     } else {
    113         nnAssert(!"tableLookup: bad code");
    114         return EntryType();
    115     }
    116 }
    117 
    118 class OperationValidationContext : public IOperationValidationContext {
    119     DISALLOW_IMPLICIT_CONSTRUCTORS(OperationValidationContext);
    120 
    121    public:
    122     OperationValidationContext(uint32_t inputCount, const uint32_t* inputIndexes,
    123                                uint32_t outputCount, const uint32_t* outputIndexes,
    124                                const Operand* operands, HalVersion halVersion)
    125         : inputCount(inputCount),
    126           inputIndexes(inputIndexes),
    127           outputCount(outputCount),
    128           outputIndexes(outputIndexes),
    129           operands(operands),
    130           halVersion(halVersion) {}
    131 
    132     HalVersion getHalVersion() const override;
    133 
    134     uint32_t getNumInputs() const override;
    135     OperandType getInputType(uint32_t index) const override;
    136     Shape getInputShape(uint32_t index) const override;
    137     const Operand::ExtraParams getInputExtraParams(uint32_t index) const override;
    138 
    139     uint32_t getNumOutputs() const override;
    140     OperandType getOutputType(uint32_t index) const override;
    141     Shape getOutputShape(uint32_t index) const override;
    142 
    143    private:
    144     const Operand* getInputOperand(uint32_t index) const;
    145     const Operand* getOutputOperand(uint32_t index) const;
    146 
    147     uint32_t inputCount;
    148     const uint32_t* inputIndexes;
    149     uint32_t outputCount;
    150     const uint32_t* outputIndexes;
    151     const Operand* operands;
    152     HalVersion halVersion;
    153 };
    154 
    155 HalVersion OperationValidationContext::getHalVersion() const {
    156     return halVersion;
    157 }
    158 
    159 const Operand* OperationValidationContext::getInputOperand(uint32_t index) const {
    160     CHECK(index < static_cast<uint32_t>(inputCount));
    161     return &operands[inputIndexes[index]];
    162 }
    163 
    164 const Operand* OperationValidationContext::getOutputOperand(uint32_t index) const {
    165     CHECK(index < static_cast<uint32_t>(outputCount));
    166     return &operands[outputIndexes[index]];
    167 }
    168 
    169 uint32_t OperationValidationContext::getNumInputs() const {
    170     return inputCount;
    171 }
    172 
    173 uint32_t OperationValidationContext::getNumOutputs() const {
    174     return outputCount;
    175 }
    176 
    177 OperandType OperationValidationContext::getInputType(uint32_t index) const {
    178     return getInputOperand(index)->type;
    179 }
    180 
    181 Shape OperationValidationContext::getInputShape(uint32_t index) const {
    182     const Operand* operand = getInputOperand(index);
    183     return {operand->type, operand->dimensions, operand->scale, operand->zeroPoint,
    184             operand->extraParams};
    185 }
    186 
    187 const Operand::ExtraParams OperationValidationContext::getInputExtraParams(uint32_t index) const {
    188     return getInputOperand(index)->extraParams;
    189 }
    190 
    191 OperandType OperationValidationContext::getOutputType(uint32_t index) const {
    192     return getOutputOperand(index)->type;
    193 }
    194 
    195 Shape OperationValidationContext::getOutputShape(uint32_t index) const {
    196     const Operand* operand = getOutputOperand(index);
    197     return {operand->type, operand->dimensions, operand->scale, operand->zeroPoint,
    198             operand->extraParams};
    199 }
    200 
    201 };  // anonymous namespace
    202 
    203 #define COUNT(X) (sizeof(X) / sizeof(X[0]))
    204 
    205 std::string getOperandTypeName(OperandType type) {
    206     return toString(type);
    207 }
    208 
    209 static std::string getOperationName(uint32_t code) {
    210     return getOperationName(static_cast<OperationType>(code));
    211 }
    212 
    213 std::string getOperationName(OperationType type) {
    214     return toString(type);
    215 }
    216 
    217 const uint32_t kSizeOfDataType[]{
    218         4,  // ANEURALNETWORKS_FLOAT32
    219         4,  // ANEURALNETWORKS_INT32
    220         4,  // ANEURALNETWORKS_UINT32
    221         4,  // ANEURALNETWORKS_TENSOR_FLOAT32
    222         4,  // ANEURALNETWORKS_TENSOR_INT32
    223         1,  // ANEURALNETWORKS_TENSOR_SYMMETRICAL_QUANT8
    224         1,  // ANEURALNETWORKS_BOOL
    225         2,  // ANEURALNETWORKS_TENSOR_QUANT16_SYMM
    226         2,  // ANEURALNETWORKS_TENSOR_FLOAT16
    227         1,  // ANEURALNETWORKS_TENSOR_BOOL8
    228         2,  // ANEURALNETWORKS_FLOAT16
    229         1,  // ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
    230         2,  // ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
    231         1,  // ANEURALNETWORKS_TENSOR_QUANT8_SYMM
    232 };
    233 
    234 static_assert(COUNT(kSizeOfDataType) == kNumberOfDataTypes, "kSizeOfDataType is incorrect");
    235 
    236 const bool kScalarDataType[]{
    237         true,   // ANEURALNETWORKS_FLOAT32
    238         true,   // ANEURALNETWORKS_INT32
    239         true,   // ANEURALNETWORKS_UINT32
    240         false,  // ANEURALNETWORKS_TENSOR_FLOAT32
    241         false,  // ANEURALNETWORKS_TENSOR_INT32
    242         false,  // ANEURALNETWORKS_TENSOR_SYMMETRICAL_QUANT8
    243         true,   // ANEURALNETWORKS_BOOL
    244         false,  // ANEURALNETWORKS_TENSOR_QUANT16_SYMM
    245         false,  // ANEURALNETWORKS_TENSOR_FLOAT16
    246         false,  // ANEURALNETWORKS_TENSOR_BOOL8
    247         true,   // ANEURALNETWORKS_FLOAT16
    248         false,  // ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
    249         false,  // ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
    250         false,  // ANEURALNETWORKS_TENSOR_QUANT8_SYMM
    251 };
    252 
    253 static_assert(COUNT(kScalarDataType) == kNumberOfDataTypes, "kScalarDataType is incorrect");
    254 
    255 const uint32_t kSizeOfDataTypeOEM[]{
    256         0, // ANEURALNETWORKS_OEM
    257         1, // ANEURALNETWORKS_TENSOR_OEM_BYTE
    258 };
    259 
    260 static_assert(COUNT(kSizeOfDataTypeOEM) == kNumberOfDataTypesOEM,
    261               "kSizeOfDataTypeOEM is incorrect");
    262 
    263 const bool kScalarDataTypeOEM[]{
    264         true,  // ANEURALNETWORKS_OEM
    265         false, // ANEURALNETWORKS_TENSOR_OEM_BYTE
    266 };
    267 
    268 static_assert(COUNT(kScalarDataTypeOEM) == kNumberOfDataTypesOEM,
    269               "kScalarDataTypeOEM is incorrect");
    270 
    271 bool nonExtensionOperandTypeIsScalar(int type) {
    272     CHECK(!isExtensionOperandType(type)) << "Extension operand types are not supported";
    273     return tableLookup(kScalarDataType, kScalarDataTypeOEM, type);
    274 }
    275 
    276 uint32_t nonExtensionOperandSizeOfData(OperandType type, const std::vector<uint32_t>& dimensions) {
    277     CHECK(!isExtensionOperandType(type)) << "Size of extension operand data is unknown";
    278     int n = static_cast<int>(type);
    279 
    280     uint32_t size = tableLookup(kSizeOfDataType, kSizeOfDataTypeOEM, n);
    281 
    282     if (tableLookup(kScalarDataType, kScalarDataTypeOEM, n) == true) {
    283         return size;
    284     }
    285 
    286     if (dimensions.empty()) {
    287         return 0;
    288     }
    289 
    290     for (auto d : dimensions) {
    291         size *= d;
    292     }
    293     return size;
    294 }
    295 
    296 bool tensorHasUnspecifiedDimensions(int type, const uint32_t* dim, uint32_t dimCount) {
    297     if (!isExtensionOperandType(type)) {
    298         CHECK(!nonExtensionOperandTypeIsScalar(type))
    299                 << "A scalar type can never have unspecified dimensions";
    300     }
    301     return dimCount == 0 || std::find(dim, dim + dimCount, 0) != (dim + dimCount);
    302 }
    303 
    304 bool tensorHasUnspecifiedDimensions(const ANeuralNetworksOperandType* type) {
    305     return tensorHasUnspecifiedDimensions(type->type, type->dimensions, type->dimensionCount);
    306 }
    307 
    308 bool tensorHasUnspecifiedDimensions(const Operand& operand) {
    309     return tensorHasUnspecifiedDimensions(static_cast<int>(operand.type), operand.dimensions.data(),
    310                                           operand.dimensions.size());
    311 }
    312 
    313 hidl_memory allocateSharedMemory(int64_t size) {
    314     static const std::string type = "ashmem";
    315     static sp<IAllocator> allocator = IAllocator::getService(type);
    316 
    317     hidl_memory memory;
    318 
    319     // TODO: should we align memory size to nearest page? doesn't seem necessary...
    320     allocator->allocate(size, [&](bool success, const hidl_memory& mem) {
    321         if (!success) {
    322             LOG(ERROR) << "unable to allocate " << size << " bytes of " << type;
    323         } else {
    324             memory = mem;
    325         }
    326     });
    327 
    328     return memory;
    329 }
    330 
    331 uint32_t alignBytesNeeded(uint32_t index, size_t length) {
    332     uint32_t pattern;
    333     if (length < 2) {
    334         pattern = 0; // No alignment necessary
    335     } else if (length < 4) {
    336         pattern = 1; // Align on 2-byte boundary
    337     } else {
    338         pattern = 3; // Align on 4-byte boundary
    339     }
    340     uint32_t extra = (~(index - 1)) & pattern;
    341     return extra;
    342 }
    343 
    344 void logModelToInfo(const V1_0::Model& model) {
    345     LOG(INFO) << "V1_0::Model start";
    346     LOG(INFO) << "operands" << toString(model.operands);
    347     LOG(INFO) << "operations" << toString(model.operations);
    348     LOG(INFO) << "inputIndexes" << toString(model.inputIndexes);
    349     LOG(INFO) << "outputIndexes" << toString(model.outputIndexes);
    350     LOG(INFO) << "operandValues size" << model.operandValues.size();
    351     LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools));
    352 }
    353 
    354 void logModelToInfo(const V1_1::Model& model) {
    355     LOG(INFO) << "V1_1::Model start";
    356     LOG(INFO) << "operands" << toString(model.operands);
    357     LOG(INFO) << "operations" << toString(model.operations);
    358     LOG(INFO) << "inputIndexes" << toString(model.inputIndexes);
    359     LOG(INFO) << "outputIndexes" << toString(model.outputIndexes);
    360     LOG(INFO) << "operandValues size" << model.operandValues.size();
    361     LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools));
    362 }
    363 
    364 bool validateOperandSymmPerChannelQuantParams(
    365         const Operand& halOperand, const ANeuralNetworksSymmPerChannelQuantParams& channelQuant,
    366         const char* tag) {
    367     if (halOperand.type != OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
    368         return false;
    369     }
    370 
    371     NN_RET_CHECK_LT(channelQuant.channelDim, halOperand.dimensions.size()) << tag;
    372     NN_RET_CHECK(channelQuant.scales != nullptr) << tag;
    373     NN_RET_CHECK_EQ(channelQuant.scaleCount, halOperand.dimensions[channelQuant.channelDim]) << tag;
    374     NN_RET_CHECK_NE(halOperand.dimensions[channelQuant.channelDim], 0u)
    375             << tag << " channel dimension " << channelQuant.channelDim << " is underspecified";
    376     for (uint32_t i = 0; i < halOperand.dimensions[channelQuant.channelDim]; i++) {
    377         NN_RET_CHECK_GT(channelQuant.scales[i], 0.0f) << tag << " invalid scaleArray[" << i << "]";
    378     }
    379     return true;
    380 }
    381 
    382 static bool validateScalarDimensions(const ANeuralNetworksOperandType& type, const char* tag) {
    383     NN_RET_CHECK_EQ(type.dimensionCount, 0u) << tag << " invalid dimensions for scalar type";
    384     NN_RET_CHECK(type.dimensions == nullptr) << tag << " invalid dimensions for scalar type";
    385     return true;
    386 }
    387 
    388 static bool validateQuant8AsymmParams(const ANeuralNetworksOperandType& type, const char* tag) {
    389     NN_RET_CHECK(0 <= type.zeroPoint && type.zeroPoint <= 255)
    390             << tag << " invalid zeroPoint: " << type.zeroPoint;
    391     NN_RET_CHECK_GT(type.scale, 0.f) << tag << " invalid scale";
    392     return true;
    393 }
    394 
    395 static bool validateQuant8SymmParams(const ANeuralNetworksOperandType& type, const char* tag) {
    396     NN_RET_CHECK_EQ(type.zeroPoint, 0) << tag << " invalid zeroPoint: " << type.zeroPoint;
    397     NN_RET_CHECK_GT(type.scale, 0.f) << tag << " invalid scale";
    398     return true;
    399 }
    400 
    401 static bool validateQuant16AsymmParams(const ANeuralNetworksOperandType& type, const char* tag) {
    402     NN_RET_CHECK(0 <= type.zeroPoint && type.zeroPoint <= 65535)
    403             << tag << " invalid zeroPoint: " << type.zeroPoint;
    404     NN_RET_CHECK_GT(type.scale, 0.f) << tag << " invalid scale";
    405     return true;
    406 }
    407 
    408 static bool validateQuantSymmParams(const ANeuralNetworksOperandType& type, const char* tag) {
    409     NN_RET_CHECK_EQ(type.zeroPoint, 0) << tag << " zeroPoint is not zero";
    410     NN_RET_CHECK_GT(type.scale, 0.f) << tag << " invalid scale";
    411     return true;
    412 }
    413 
    414 static bool validateNoQuantParams(const ANeuralNetworksOperandType& type, const char* tag) {
    415     NN_RET_CHECK_EQ(type.zeroPoint, 0) << tag << " zeroPoint is not zero";
    416     NN_RET_CHECK_EQ(type.scale, 0.f) << tag << " scale is not zero";
    417     return true;
    418 }
    419 
    420 static bool validateTensorDimensions(const ANeuralNetworksOperandType& type, const char* tag,
    421                                      bool allowPartial) {
    422     if (allowPartial) {
    423         return true;
    424     }
    425     NN_RET_CHECK_GT(type.dimensionCount, 0u) << tag << " invalid operand dimensions";
    426     for (uint32_t i = 0; i < type.dimensionCount; i++) {
    427         NN_RET_CHECK_NE(type.dimensions[i], 0u) << tag << " invalid operand dimensions";
    428     }
    429     return true;
    430 }
    431 
    432 static bool validateOperandTypeHelper(
    433         const ANeuralNetworksOperandType& type,
    434         const Extension::OperandTypeInformation* const extensionOperandTypeInfo, const char* tag,
    435         bool allowPartial) {
    436     NN_RET_CHECK_EQ(type.dimensionCount == 0, type.dimensions == nullptr);
    437     if (isExtensionOperandType(type.type)) {
    438         NN_RET_CHECK(extensionOperandTypeInfo != nullptr);
    439         if (extensionOperandTypeInfo->isTensor) {
    440             NN_RET_CHECK(validateTensorDimensions(type, tag, allowPartial));
    441         } else {
    442             NN_RET_CHECK(validateScalarDimensions(type, tag));
    443         }
    444         return validateNoQuantParams(type, tag);
    445     }
    446 
    447     NN_RET_CHECK(extensionOperandTypeInfo == nullptr);
    448     NN_RET_CHECK(validCode(kNumberOfDataTypes, kNumberOfDataTypesOEM, type.type))
    449             << tag << " invalid OperandType: " << type.type;
    450 
    451     bool isScalar = tableLookup(kScalarDataType, kScalarDataTypeOEM, type.type);
    452     if (isScalar) {
    453         NN_RET_CHECK(validateScalarDimensions(type, tag));
    454         if (type.type != ANEURALNETWORKS_OEM_SCALAR) {  // Historically, we have allowed OEM types
    455                                                         // to use quantization parameters.
    456             NN_RET_CHECK(validateNoQuantParams(type, tag));
    457         }
    458     } else {
    459         NN_RET_CHECK(validateTensorDimensions(type, tag, allowPartial));
    460         if (type.type == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) {
    461             NN_RET_CHECK(validateQuant8AsymmParams(type, tag));
    462         } else if (type.type == ANEURALNETWORKS_TENSOR_QUANT8_SYMM) {
    463             NN_RET_CHECK(validateQuant8SymmParams(type, tag));
    464         } else if (type.type == ANEURALNETWORKS_TENSOR_QUANT16_ASYMM) {
    465             NN_RET_CHECK(validateQuant16AsymmParams(type, tag));
    466         } else if (type.type == ANEURALNETWORKS_TENSOR_QUANT16_SYMM) {
    467             NN_RET_CHECK(validateQuantSymmParams(type, tag));
    468         } else if (type.type == ANEURALNETWORKS_TENSOR_INT32) {
    469             // TODO(b/119869082): TENSOR_INT32 should not use quantization parameters.
    470         } else if (type.type == ANEURALNETWORKS_TENSOR_OEM_BYTE) {
    471             // Historically, we have allowed OEM types to use quantization parameters.
    472         } else {
    473             NN_RET_CHECK(validateNoQuantParams(type, tag));
    474         }
    475     }
    476 
    477     return true;
    478 }
    479 
    480 int validateOperandType(const ANeuralNetworksOperandType& type,
    481                         const Extension::OperandTypeInformation* const extensionOperandTypeInfo,
    482                         const char* tag, bool allowPartial) {
    483     return validateOperandTypeHelper(type, extensionOperandTypeInfo, tag, allowPartial)
    484                    ? ANEURALNETWORKS_NO_ERROR
    485                    : ANEURALNETWORKS_BAD_DATA;
    486 }
    487 
    488 int validateOperandList(uint32_t count, const uint32_t* list, uint32_t operandCount,
    489                         const char* tag) {
    490     for (uint32_t i = 0; i < count; i++) {
    491         if (list[i] >= operandCount) {
    492             LOG(ERROR) << tag << " invalid operand index at " << i << " = " << list[i]
    493                        << ", operandCount " << operandCount;
    494             return ANEURALNETWORKS_BAD_DATA;
    495         }
    496     }
    497     return ANEURALNETWORKS_NO_ERROR;
    498 }
    499 
    500 int validateOperationOperandTypes(const std::vector<Operand>& operands,
    501                                   uint32_t inOperandCount, const uint32_t* inOperandIndexes,
    502                                   const std::vector<OperandType>& inExpectedTypes,
    503                                   uint32_t outOperandCount, const uint32_t* outOperandIndexes,
    504                                   const std::vector<OperandType>& outExpectedInTypes) {
    505     if (inOperandCount != static_cast<uint32_t>(inExpectedTypes.size()) ||
    506         outOperandCount != static_cast<uint32_t>(outExpectedInTypes.size())) {
    507         LOG(ERROR) << "Wrong operand count: expected " << inExpectedTypes.size() << " inputs and "
    508                    << outExpectedInTypes.size() << " outputs,"
    509                    << "got " << inOperandCount << " inputs and " << outOperandCount << " outputs";
    510         return ANEURALNETWORKS_BAD_DATA;
    511     }
    512     for (uint32_t i = 0; i < inOperandCount; i++) {
    513         if (operands[inOperandIndexes[i]].type != inExpectedTypes[i]) {
    514             LOG(ERROR) << "Invalid input tensor type "
    515                        << toString(operands[inOperandIndexes[i]].type)
    516                        << " for input " << i << ", expected " << toString(inExpectedTypes[i]);
    517             return ANEURALNETWORKS_BAD_DATA;
    518         }
    519     }
    520     for (uint32_t i = 0; i < outOperandCount; i++) {
    521         if (operands[outOperandIndexes[i]].type != outExpectedInTypes[i]) {
    522             LOG(ERROR) << "Invalid output tensor type "
    523                        << toString(operands[outOperandIndexes[i]].type)
    524                        << " for input " << i << ", expected " << toString(outExpectedInTypes[i]);
    525             return ANEURALNETWORKS_BAD_DATA;
    526         }
    527     }
    528 
    529     return ANEURALNETWORKS_NO_ERROR;
    530 }
    531 
    532 static int validateHalVersion(ANeuralNetworksOperationType opType, HalVersion halVersion,
    533                               HalVersion minSupportedHalVersion) {
    534     if (halVersion < minSupportedHalVersion) {
    535         LOG(ERROR) << "The given inputs and outputs for operation " << getOperationName(opType)
    536                    << " are only supported in " << toString(minSupportedHalVersion)
    537                    << " and later (validating using " << toString(halVersion) << ")";
    538         return ANEURALNETWORKS_BAD_DATA;
    539     }
    540     return ANEURALNETWORKS_NO_ERROR;
    541 }
    542 
    543 int validateOperation(ANeuralNetworksOperationType opType, uint32_t inputCount,
    544                       const uint32_t* inputIndexes, uint32_t outputCount,
    545                       const uint32_t* outputIndexes, const std::vector<Operand>& operands,
    546                       HalVersion halVersion) {
    547     NN_RETURN_IF_ERROR(validateOperandList(inputCount, inputIndexes,
    548                                            static_cast<uint32_t>(operands.size()),
    549                                            "ANeuralNetworksModel_addOperation inputs"));
    550     NN_RETURN_IF_ERROR(validateOperandList(outputCount, outputIndexes,
    551                                            static_cast<uint32_t>(operands.size()),
    552                                            "ANeuralNetworksModel_addOperation outputs"));
    553 
    554     if (isExtensionOperationType(opType)) {
    555         if (halVersion < HalVersion::V1_2) {
    556             LOG(ERROR)
    557                     << "Extension operations are supported since HAL version 1.2, validating using "
    558                     << toString(halVersion);
    559             return ANEURALNETWORKS_BAD_DATA;
    560         }
    561         // There is no other validation we can do for an extension operation.
    562         return ANEURALNETWORKS_NO_ERROR;
    563     }
    564 
    565     auto logInvalidInOutNumber = [opType, inputCount, outputCount](int expIn, int expOut) {
    566         LOG(ERROR) << "Invalid number of input operands (" << inputCount << ", expected " << expIn
    567                    << ") or output operands (" << outputCount << ", expected " << expOut
    568                    << ") for operation " << getOperationName(opType);
    569     };
    570 
    571     switch (opType) {
    572         case ANEURALNETWORKS_OEM_OPERATION: {
    573             return ANEURALNETWORKS_NO_ERROR;
    574         }
    575         case ANEURALNETWORKS_FLOOR: {
    576             if (inputCount != 1 || outputCount != 1) {
    577                 logInvalidInOutNumber(1, 1);
    578                 return ANEURALNETWORKS_BAD_DATA;
    579             }
    580             auto inputType = operands[inputIndexes[0]].type;
    581             std::vector<OperandType> inExpectedTypes;
    582             std::vector<OperandType> outExpectedTypes;
    583             if (inputType == OperandType::TENSOR_FLOAT32) {
    584                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    585                 inExpectedTypes = {OperandType::TENSOR_FLOAT32};
    586                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
    587             } else if (inputType == OperandType::TENSOR_FLOAT16) {
    588                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    589                 inExpectedTypes = {OperandType::TENSOR_FLOAT16};
    590                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
    591             } else {
    592                 LOG(ERROR) << "Unsupported input tensor type for operation "
    593                            << getOperationName(opType);
    594                 return ANEURALNETWORKS_BAD_DATA;
    595             }
    596             return validateOperationOperandTypes(operands,
    597                                                  inputCount, inputIndexes,
    598                                                  inExpectedTypes,
    599                                                  outputCount, outputIndexes,
    600                                                  outExpectedTypes);
    601         }
    602         case ANEURALNETWORKS_DEPTHWISE_CONV_2D: {
    603             if ((inputCount != 14 && inputCount != 12 && inputCount != 11 && inputCount != 9 &&
    604                  inputCount != 8) ||
    605                 outputCount != 1) {
    606                 LOG(ERROR) << "Invalid number of input operands (" << inputCount
    607                            << ", expected 14, 12, 11, 9 or 8) or output operands (" << outputCount
    608                            << ", expected 1) for operation " << getOperationName(opType);
    609                 return ANEURALNETWORKS_BAD_DATA;
    610             }
    611             auto inputType = operands[inputIndexes[0]].type;
    612             auto filterType = operands[inputIndexes[1]].type;
    613             std::vector<OperandType> inExpectedTypes;
    614             std::vector<OperandType> outExpectedTypes;
    615             if (inputType == OperandType::TENSOR_FLOAT32) {
    616                 inExpectedTypes = {
    617                         OperandType::TENSOR_FLOAT32, OperandType::TENSOR_FLOAT32,
    618                         OperandType::TENSOR_FLOAT32, OperandType::INT32,
    619                         OperandType::INT32,          OperandType::INT32,
    620                         OperandType::INT32,          OperandType::INT32,
    621                 };
    622                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
    623             } else if (inputType == OperandType::TENSOR_FLOAT16) {
    624                 inExpectedTypes = {
    625                         OperandType::TENSOR_FLOAT16, OperandType::TENSOR_FLOAT16,
    626                         OperandType::TENSOR_FLOAT16, OperandType::INT32,
    627                         OperandType::INT32,          OperandType::INT32,
    628                         OperandType::INT32,          OperandType::INT32,
    629                 };
    630                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
    631             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
    632                 if (filterType != OperandType::TENSOR_QUANT8_ASYMM &&
    633                     filterType != OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
    634                     LOG(ERROR) << "Unsupported filter tensor type for operation "
    635                                << getOperationName(opType);
    636                     return ANEURALNETWORKS_BAD_DATA;
    637                 }
    638 
    639                 inExpectedTypes = {
    640                         OperandType::TENSOR_QUANT8_ASYMM,
    641                         filterType,
    642                         OperandType::TENSOR_INT32,
    643                         OperandType::INT32,
    644                         OperandType::INT32,
    645                         OperandType::INT32,
    646                         OperandType::INT32,
    647                         OperandType::INT32,
    648                 };
    649                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
    650             } else {
    651                 LOG(ERROR) << "Unsupported input tensor type for operation "
    652                            << getOperationName(opType);
    653                 return ANEURALNETWORKS_BAD_DATA;
    654             }
    655 
    656             // NeuralNetworks.h specifies that ANEURALNETWORKS_DEPTHWISE_CONV_2D's output must
    657             // meet "outputScale > inputScale * filterScale" for the operand type
    658             // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM before API level 29. For other
    659             // operand types (e.g., ANEURALNETWORKS_TENSOR_FLOAT32), this constraint
    660             // does not apply, so by default the constraint is met.
    661             bool meetsQuantizedScaleConstraintBeforeV1_2 = true;
    662             if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
    663                 const float inputScale = operands[inputIndexes[0]].scale;
    664                 const float filterScale = operands[inputIndexes[1]].scale;
    665                 const float outputScale = operands[outputIndexes[0]].scale;
    666                 meetsQuantizedScaleConstraintBeforeV1_2 = (outputScale > inputScale * filterScale);
    667             }
    668 
    669             bool withExplicitPadding = false;
    670             bool withLayout = false;
    671             bool withDilation = false;
    672             if (inputCount >= 9) {
    673                 if (operands[inputIndexes[8]].type == OperandType::INT32 && inputCount >= 11) {
    674                     std::vector<OperandType> explicitScalarTypes(3, OperandType::INT32);
    675                     inExpectedTypes.insert(inExpectedTypes.end(), explicitScalarTypes.begin(),
    676                                            explicitScalarTypes.end());
    677                     withExplicitPadding = true;
    678                 }
    679                 int inputOffset = withExplicitPadding ? 3 : 0;
    680                 if (inputCount >= 9 + inputOffset) {
    681                     inExpectedTypes.push_back(OperandType::BOOL);
    682                     withLayout = true;
    683                 }
    684                 if (inputCount == 10 + inputOffset) {
    685                     LOG(ERROR) << "Provided only one dilation factor value, two values are requred "
    686                                   "for operation "
    687                                << getOperationName(opType);
    688                     return ANEURALNETWORKS_BAD_DATA;
    689                 }
    690                 if (inputCount == 11 + inputOffset) {
    691                     inExpectedTypes.push_back(OperandType::INT32);
    692                     inExpectedTypes.push_back(OperandType::INT32);
    693                     withDilation = true;
    694                 }
    695             }
    696 
    697             if (inputType == OperandType::TENSOR_FLOAT16 ||
    698                 filterType == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL || withLayout ||
    699                 withDilation || !meetsQuantizedScaleConstraintBeforeV1_2) {
    700                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    701             } else {
    702                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    703             }
    704             return validateOperationOperandTypes(operands,
    705                                                  inputCount, inputIndexes,
    706                                                  inExpectedTypes,
    707                                                  outputCount, outputIndexes,
    708                                                  outExpectedTypes);
    709         }
    710         case ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION: {
    711             if ((inputCount != 6 && inputCount != 5) || outputCount != 1) {
    712                 LOG(ERROR) << "Invalid number of input operands (" << inputCount
    713                            << ", expected 6 or 5) or output operands (" << outputCount
    714                            << ", expected 1) for operation " << getOperationName(opType);
    715                 return ANEURALNETWORKS_BAD_DATA;
    716             }
    717             auto inputType = operands[inputIndexes[0]].type;
    718             std::vector<OperandType> inExpectedTypes;
    719             std::vector<OperandType> outExpectedTypes;
    720             if (inputType == OperandType::TENSOR_FLOAT32) {
    721                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    722                 inExpectedTypes = {
    723                         OperandType::TENSOR_FLOAT32, OperandType::INT32,   OperandType::FLOAT32,
    724                         OperandType::FLOAT32,        OperandType::FLOAT32,
    725                 };
    726                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
    727             } else if (inputType == OperandType::TENSOR_FLOAT16) {
    728                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    729                 inExpectedTypes = {
    730                         OperandType::TENSOR_FLOAT16, OperandType::INT32,   OperandType::FLOAT16,
    731                         OperandType::FLOAT16,        OperandType::FLOAT16,
    732                 };
    733                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
    734             } else {
    735                 LOG(ERROR) << "Unsupported input tensor type for operation "
    736                            << getOperationName(opType);
    737                 return ANEURALNETWORKS_BAD_DATA;
    738             }
    739             if (inputCount == 6) {
    740                 inExpectedTypes.push_back(OperandType::INT32);
    741                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    742             } else if (operands[inputIndexes[0]].dimensions.size() != 4) {
    743                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    744             }
    745             return validateOperationOperandTypes(operands,
    746                                                  inputCount, inputIndexes,
    747                                                  inExpectedTypes,
    748                                                  outputCount, outputIndexes,
    749                                                  outExpectedTypes);
    750         }
    751         case ANEURALNETWORKS_RESHAPE: {
    752             if (inputCount != 2 || outputCount != 1) {
    753                 logInvalidInOutNumber(2, 1);
    754                 return ANEURALNETWORKS_BAD_DATA;
    755             }
    756             auto inputType = operands[inputIndexes[0]].type;
    757             std::vector<OperandType> inExpectedTypes;
    758             std::vector<OperandType> outExpectedTypes;
    759             if (inputType == OperandType::TENSOR_FLOAT32) {
    760                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    761                 inExpectedTypes = {OperandType::TENSOR_FLOAT32,
    762                                    OperandType::TENSOR_INT32};
    763                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
    764             } else if (inputType == OperandType::TENSOR_FLOAT16) {
    765                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    766                 inExpectedTypes = {OperandType::TENSOR_FLOAT16, OperandType::TENSOR_INT32};
    767                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
    768             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
    769                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    770                 inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
    771                                    OperandType::TENSOR_INT32};
    772                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
    773             } else {
    774                 LOG(ERROR) << "Unsupported input tensor type for operation "
    775                            << getOperationName(opType);
    776                 return ANEURALNETWORKS_BAD_DATA;
    777             }
    778             return validateOperationOperandTypes(operands,
    779                                                  inputCount, inputIndexes,
    780                                                  inExpectedTypes,
    781                                                  outputCount, outputIndexes,
    782                                                  outExpectedTypes);
    783         }
    784         case ANEURALNETWORKS_DEPTH_TO_SPACE: {
    785             if ((inputCount != 3 && inputCount != 2) || outputCount != 1) {
    786                 LOG(ERROR) << "Invalid number of input operands (" << inputCount
    787                            << ", expected 3 or 2) or output operands (" << outputCount
    788                            << ", expected 1) for operation " << getOperationName(opType);
    789                 return ANEURALNETWORKS_BAD_DATA;
    790             }
    791             auto inputType = operands[inputIndexes[0]].type;
    792             std::vector<OperandType> inExpectedTypes;
    793             std::vector<OperandType> outExpectedTypes;
    794             if (inputType == OperandType::TENSOR_FLOAT32) {
    795                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    796                 inExpectedTypes = {OperandType::TENSOR_FLOAT32,
    797                                    OperandType::INT32};
    798                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
    799             } else if (inputType == OperandType::TENSOR_FLOAT16) {
    800                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    801                 inExpectedTypes = {OperandType::TENSOR_FLOAT16, OperandType::INT32};
    802                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
    803             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
    804                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    805                 inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
    806                                    OperandType::INT32};
    807                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
    808             } else {
    809                 LOG(ERROR) << "Unsupported input tensor type for operation "
    810                            << getOperationName(opType);
    811                 return ANEURALNETWORKS_BAD_DATA;
    812             }
    813             if (inputCount == 3) {
    814                 inExpectedTypes.push_back(OperandType::BOOL);
    815                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    816             } else {
    817                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    818             }
    819             return validateOperationOperandTypes(operands,
    820                                                  inputCount, inputIndexes,
    821                                                  inExpectedTypes,
    822                                                  outputCount, outputIndexes,
    823                                                  outExpectedTypes);
    824         }
    825         case ANEURALNETWORKS_SPACE_TO_DEPTH: {
    826             if ((inputCount != 3 && inputCount != 2) || outputCount != 1) {
    827                 LOG(ERROR) << "Invalid number of input operands (" << inputCount
    828                            << ", expected 3 or 2) or output operands (" << outputCount
    829                            << ", expected 1) for operation " << getOperationName(opType);
    830                 return ANEURALNETWORKS_BAD_DATA;
    831             }
    832             auto inputType = operands[inputIndexes[0]].type;
    833             std::vector<OperandType> inExpectedTypes;
    834             std::vector<OperandType> outExpectedTypes;
    835             if (inputType == OperandType::TENSOR_FLOAT32) {
    836                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    837                 inExpectedTypes = {OperandType::TENSOR_FLOAT32,
    838                                    OperandType::INT32};
    839                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
    840             } else if (inputType == OperandType::TENSOR_FLOAT16) {
    841                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    842                 inExpectedTypes = {OperandType::TENSOR_FLOAT16, OperandType::INT32};
    843                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
    844             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
    845                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    846                 inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
    847                                    OperandType::INT32};
    848                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
    849             } else {
    850                 LOG(ERROR) << "Unsupported input tensor type for operation "
    851                            << getOperationName(opType);
    852                 return ANEURALNETWORKS_BAD_DATA;
    853             }
    854             if (inputCount == 3) {
    855                 inExpectedTypes.push_back(OperandType::BOOL);
    856                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    857             } else {
    858                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    859             }
    860             return validateOperationOperandTypes(operands,
    861                                                  inputCount, inputIndexes,
    862                                                  inExpectedTypes,
    863                                                  outputCount, outputIndexes,
    864                                                  outExpectedTypes);
    865         }
    866         case ANEURALNETWORKS_EMBEDDING_LOOKUP: {
    867             if (inputCount != 2 || outputCount != 1) {
    868                 logInvalidInOutNumber(2, 1);
    869                 return ANEURALNETWORKS_BAD_DATA;
    870             }
    871             auto inputType = operands[inputIndexes[1]].type;
    872             if (inputType != OperandType::TENSOR_FLOAT32 &&
    873                 inputType != OperandType::TENSOR_INT32 &&
    874                 inputType != OperandType::TENSOR_QUANT8_ASYMM) {
    875                 LOG(ERROR) << "Unsupported input tensor type for operation "
    876                            << getOperationName(opType);
    877                 return ANEURALNETWORKS_BAD_DATA;
    878             }
    879             std::vector<OperandType> inExpectedTypes = {OperandType::TENSOR_INT32,
    880                                                         inputType};
    881             std::vector<OperandType> outExpectedTypes = {inputType};
    882             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    883             return validateOperationOperandTypes(operands,
    884                                                  inputCount, inputIndexes,
    885                                                  inExpectedTypes,
    886                                                  outputCount, outputIndexes,
    887                                                  outExpectedTypes);
    888         }
    889         case ANEURALNETWORKS_HASHTABLE_LOOKUP: {
    890             if (inputCount != 3 || outputCount != 2) {
    891                 logInvalidInOutNumber(3, 2);
    892                 return ANEURALNETWORKS_BAD_DATA;
    893             }
    894             auto inputType = operands[inputIndexes[2]].type;
    895             if (inputType != OperandType::TENSOR_FLOAT32 &&
    896                 inputType != OperandType::TENSOR_INT32 &&
    897                 inputType != OperandType::TENSOR_QUANT8_ASYMM) {
    898                 LOG(ERROR) << "Unsupported input tensor type for operation "
    899                            << getOperationName(opType);
    900                 return ANEURALNETWORKS_BAD_DATA;
    901             }
    902             std::vector<OperandType> inExpectedTypes = {OperandType::TENSOR_INT32,
    903                                                         OperandType::TENSOR_INT32,
    904                                                         inputType};
    905             std::vector<OperandType> outExpectedTypes = {inputType,
    906                                                          OperandType::TENSOR_QUANT8_ASYMM};
    907             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    908             return validateOperationOperandTypes(operands,
    909                                                  inputCount, inputIndexes,
    910                                                  inExpectedTypes,
    911                                                  outputCount, outputIndexes,
    912                                                  outExpectedTypes);
    913         }
    914         case ANEURALNETWORKS_LSH_PROJECTION: {
    915             if (inputCount != 4 || outputCount != 1) {
    916                 logInvalidInOutNumber(4, 1);
    917                 return ANEURALNETWORKS_BAD_DATA;
    918             }
    919             auto inputType = operands[inputIndexes[1]].type;
    920             if (inputType != OperandType::TENSOR_FLOAT16 &&
    921                 inputType != OperandType::TENSOR_FLOAT32 &&
    922                 inputType != OperandType::TENSOR_INT32 &&
    923                 inputType != OperandType::TENSOR_QUANT8_ASYMM) {
    924                 LOG(ERROR) << "Unsupported input tensor type for operation "
    925                            << getOperationName(opType);
    926                 return ANEURALNETWORKS_BAD_DATA;
    927             }
    928             auto hashType = operands[inputIndexes[0]].type;
    929             std::vector<OperandType> inExpectedTypes;
    930             if (hashType == OperandType::TENSOR_FLOAT16) {
    931                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    932                 inExpectedTypes = {
    933                         OperandType::TENSOR_FLOAT16,
    934                         inputType,
    935                         OperandType::TENSOR_FLOAT16,
    936                         OperandType::INT32,
    937                 };
    938             } else if (hashType == OperandType::TENSOR_FLOAT32) {
    939                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
    940                 inExpectedTypes = {
    941                         OperandType::TENSOR_FLOAT32,
    942                         inputType,
    943                         OperandType::TENSOR_FLOAT32,
    944                         OperandType::INT32,
    945                 };
    946             } else {
    947                 LOG(ERROR) << "Unsupported hash tensor type for operation "
    948                            << getOperationName(opType);
    949                 return ANEURALNETWORKS_BAD_DATA;
    950             }
    951             std::vector<OperandType> outExpectedTypes = {OperandType::TENSOR_INT32};
    952             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
    953                                                  inExpectedTypes, outputCount, outputIndexes,
    954                                                  outExpectedTypes);
    955         }
    956         case ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_LSTM: {
    957             std::vector<OperandType> inExpectedTypes;
    958             auto inputType = operands[inputIndexes[0]].type;
    959             std::vector<OperandType> outExpectedTypes{inputType, inputType};
    960             std::vector<OperandType> outExpectedTypesMerged{inputType};
    961             if (inputType != OperandType::TENSOR_FLOAT32 &&
    962                 inputType != OperandType::TENSOR_FLOAT16) {
    963                 LOG(ERROR) << "Unsupported input tensor type for operation "
    964                            << getOperationName(opType);
    965                 return ANEURALNETWORKS_BAD_DATA;
    966             }
    967             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
    968 
    969             inExpectedTypes = {};
    970             for (int i = 0; i < 48; ++i) {
    971                 inExpectedTypes.push_back(inputType);
    972             }
    973             inExpectedTypes.push_back(OperandType::INT32);
    974             inExpectedTypes.push_back(inputType == OperandType::TENSOR_FLOAT32
    975                                               ? OperandType::FLOAT32
    976                                               : OperandType::FLOAT16);
    977             inExpectedTypes.push_back(inputType == OperandType::TENSOR_FLOAT32
    978                                               ? OperandType::FLOAT32
    979                                               : OperandType::FLOAT16);
    980             inExpectedTypes.push_back(OperandType::BOOL);
    981             inExpectedTypes.push_back(OperandType::BOOL);
    982             for (int i = 0; i < 8; ++i) {
    983                 inExpectedTypes.push_back(inputType);
    984             }
    985 
    986             if (inputCount != 61 || (outputCount != 1 && outputCount != 2)) {
    987                 LOG(ERROR) << "Invalid number of input operands (" << inputCount
    988                            << ", expected 61) or output operands (" << outputCount
    989                            << ", expected 1 or 2) for operation " << getOperationName(opType);
    990                 return ANEURALNETWORKS_BAD_DATA;
    991             }
    992             auto status = validateOperationOperandTypes(operands, inputCount, inputIndexes,
    993                                                         inExpectedTypes, outputCount, outputIndexes,
    994                                                         outExpectedTypes);
    995             if (status != ANEURALNETWORKS_NO_ERROR) {
    996                 status = validateOperationOperandTypes(operands, inputCount, inputIndexes,
    997                                                        inExpectedTypes, outputCount, outputIndexes,
    998                                                        outExpectedTypesMerged);
    999             }
   1000             return status;
   1001         }
   1002         case ANEURALNETWORKS_LSTM: {
   1003             std::vector<OperandType> inExpectedTypes;
   1004             std::vector<OperandType> outExpectedTypes;
   1005             auto inputType = operands[inputIndexes[0]].type;
   1006             if (inputType != OperandType::TENSOR_FLOAT32 &&
   1007                 inputType != OperandType::TENSOR_FLOAT16) {
   1008                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1009                            << getOperationName(opType);
   1010                 return ANEURALNETWORKS_BAD_DATA;
   1011             }
   1012 
   1013             inExpectedTypes = {inputType,         inputType, inputType, inputType, inputType,
   1014                                inputType,         inputType, inputType, inputType, inputType,
   1015                                inputType,         inputType, inputType, inputType, inputType,
   1016                                inputType,         inputType, inputType, inputType, inputType,
   1017                                OperandType::INT32};
   1018             if (inputType == OperandType::TENSOR_FLOAT32) {
   1019                 inExpectedTypes.push_back(OperandType::FLOAT32);
   1020                 inExpectedTypes.push_back(OperandType::FLOAT32);
   1021             } else {
   1022                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1023                 inExpectedTypes.push_back(OperandType::FLOAT16);
   1024                 inExpectedTypes.push_back(OperandType::FLOAT16);
   1025             }
   1026 
   1027             outExpectedTypes = {inputType, inputType, inputType, inputType};
   1028             if (inputCount == 23 && outputCount == 4) {
   1029                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
   1030             } else if (inputCount == 27 && outputCount == 4) {
   1031                 for (int i = 0; i < 4; ++i) {
   1032                     inExpectedTypes.push_back(inputType);
   1033                 }
   1034                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1035             } else {
   1036                 LOG(ERROR) << "Invalid number of input operands (" << inputCount
   1037                            << ", expected 23 or 27) or output operands (" << outputCount
   1038                            << ", expected 4) for operation " << getOperationName(opType);
   1039                 return ANEURALNETWORKS_BAD_DATA;
   1040             }
   1041             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1042                                                  inExpectedTypes, outputCount, outputIndexes,
   1043                                                  outExpectedTypes);
   1044         }
   1045         case ANEURALNETWORKS_QUANTIZED_16BIT_LSTM: {
   1046             if (inputCount != 15 || outputCount != 2) {
   1047                 logInvalidInOutNumber(15, 2);
   1048                 return ANEURALNETWORKS_BAD_DATA;
   1049             }
   1050             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1051             std::vector<OperandType> inExpectedTypes = {
   1052                     OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_QUANT8_ASYMM,
   1053                     OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_QUANT8_ASYMM,
   1054                     OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_QUANT8_ASYMM,
   1055                     OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_QUANT8_ASYMM,
   1056                     OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_INT32,
   1057                     OperandType::TENSOR_INT32,        OperandType::TENSOR_INT32,
   1058                     OperandType::TENSOR_INT32,        OperandType::TENSOR_QUANT16_SYMM,
   1059                     OperandType::TENSOR_QUANT8_ASYMM};
   1060             std::vector<OperandType> outExpectedTypes = {OperandType::TENSOR_QUANT16_SYMM,
   1061                                                          OperandType::TENSOR_QUANT8_ASYMM};
   1062             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1063                                                  inExpectedTypes, outputCount, outputIndexes,
   1064                                                  outExpectedTypes);
   1065         }
   1066         case ANEURALNETWORKS_RANDOM_MULTINOMIAL: {
   1067             if (inputCount != 3 || outputCount != 1) {
   1068                 logInvalidInOutNumber(3, 1);
   1069                 return ANEURALNETWORKS_BAD_DATA;
   1070             }
   1071             OperandType inputType = operands[inputIndexes[0]].type;
   1072             std::vector<OperandType> inExpectedTypes;
   1073             if (inputType == OperandType::TENSOR_FLOAT32 ||
   1074                 inputType == OperandType::TENSOR_FLOAT16) {
   1075                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1076                 inExpectedTypes = {
   1077                         inputType,
   1078                         OperandType::INT32,
   1079                         OperandType::TENSOR_INT32,
   1080                 };
   1081             } else {
   1082                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1083                            << getOperationName(opType);
   1084                 return ANEURALNETWORKS_BAD_DATA;
   1085             }
   1086             std::vector<OperandType> outExpectedTypes = {OperandType::TENSOR_INT32};
   1087             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1088                                                  inExpectedTypes, outputCount, outputIndexes,
   1089                                                  outExpectedTypes);
   1090         }
   1091         case ANEURALNETWORKS_RNN: {
   1092             if (inputCount != 6 || outputCount != 2) {
   1093                 logInvalidInOutNumber(6, 2);
   1094                 return ANEURALNETWORKS_BAD_DATA;
   1095             }
   1096             OperandType inputType = operands[inputIndexes[0]].type;
   1097             std::vector<OperandType> inExpectedTypes;
   1098             std::vector<OperandType> outExpectedTypes;
   1099             if (inputType == OperandType::TENSOR_FLOAT32) {
   1100                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
   1101                 inExpectedTypes = {
   1102                         OperandType::TENSOR_FLOAT32, OperandType::TENSOR_FLOAT32,
   1103                         OperandType::TENSOR_FLOAT32, OperandType::TENSOR_FLOAT32,
   1104                         OperandType::TENSOR_FLOAT32, OperandType::INT32,
   1105                 };
   1106                 outExpectedTypes = {
   1107                         OperandType::TENSOR_FLOAT32,
   1108                         OperandType::TENSOR_FLOAT32,
   1109                 };
   1110             } else if (inputType == OperandType::TENSOR_FLOAT16) {
   1111                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1112                 inExpectedTypes = {
   1113                         OperandType::TENSOR_FLOAT16, OperandType::TENSOR_FLOAT16,
   1114                         OperandType::TENSOR_FLOAT16, OperandType::TENSOR_FLOAT16,
   1115                         OperandType::TENSOR_FLOAT16, OperandType::INT32,
   1116                 };
   1117                 outExpectedTypes = {
   1118                         OperandType::TENSOR_FLOAT16,
   1119                         OperandType::TENSOR_FLOAT16,
   1120                 };
   1121             } else {
   1122                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1123                            << getOperationName(opType);
   1124                 return ANEURALNETWORKS_BAD_DATA;
   1125             }
   1126             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1127                                                  inExpectedTypes, outputCount, outputIndexes,
   1128                                                  outExpectedTypes);
   1129         }
   1130         case ANEURALNETWORKS_SVDF: {
   1131             if (inputCount != 7 || outputCount != 2) {
   1132                 logInvalidInOutNumber(7, 2);
   1133                 return ANEURALNETWORKS_BAD_DATA;
   1134             }
   1135             OperandType inputType = operands[inputIndexes[0]].type;
   1136             if (inputType == OperandType::TENSOR_FLOAT32) {
   1137                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0));
   1138 
   1139             } else if (inputType == OperandType::TENSOR_FLOAT16) {
   1140                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1141             } else {
   1142                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1143                            << getOperationName(opType);
   1144                 return ANEURALNETWORKS_BAD_DATA;
   1145             }
   1146             std::vector<OperandType> inExpectedTypes = {
   1147                     inputType, inputType,          inputType,          inputType,
   1148                     inputType, OperandType::INT32, OperandType::INT32,
   1149             };
   1150             std::vector<OperandType> outExpectedTypes = {inputType, inputType};
   1151             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1152                                                  inExpectedTypes, outputCount, outputIndexes,
   1153                                                  outExpectedTypes);
   1154         }
   1155         case ANEURALNETWORKS_BATCH_TO_SPACE_ND: {
   1156             if ((inputCount != 3 && inputCount != 2) || outputCount != 1) {
   1157                 LOG(ERROR) << "Invalid number of input operands (" << inputCount
   1158                            << ", expected 3 or 2) or output operands (" << outputCount
   1159                            << ", expected 1) for operation " << getOperationName(opType);
   1160                 return ANEURALNETWORKS_BAD_DATA;
   1161             }
   1162             auto inputType = operands[inputIndexes[0]].type;
   1163             std::vector<OperandType> inExpectedTypes;
   1164             std::vector<OperandType> outExpectedTypes;
   1165             if (inputType == OperandType::TENSOR_FLOAT32) {
   1166                 inExpectedTypes = {
   1167                         OperandType::TENSOR_FLOAT32,
   1168                         OperandType::TENSOR_INT32,
   1169                 };
   1170                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
   1171             } else if (inputType == OperandType::TENSOR_FLOAT16) {
   1172                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1173                 inExpectedTypes = {
   1174                         OperandType::TENSOR_FLOAT16,
   1175                         OperandType::TENSOR_INT32,
   1176                 };
   1177                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
   1178             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1179                 inExpectedTypes = {
   1180                         OperandType::TENSOR_QUANT8_ASYMM,
   1181                         OperandType::TENSOR_INT32,
   1182                 };
   1183                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
   1184             } else {
   1185                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1186                            << getOperationName(opType);
   1187                 return ANEURALNETWORKS_BAD_DATA;
   1188             }
   1189             if (inputCount == 3) {
   1190                 inExpectedTypes.push_back(OperandType::BOOL);
   1191                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1192             } else {
   1193                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1));
   1194             }
   1195             return validateOperationOperandTypes(operands,
   1196                                                  inputCount, inputIndexes,
   1197                                                  inExpectedTypes,
   1198                                                  outputCount, outputIndexes,
   1199                                                  outExpectedTypes);
   1200         }
   1201         case ANEURALNETWORKS_SPACE_TO_BATCH_ND: {
   1202             if ((inputCount != 4 && inputCount != 3) || outputCount != 1) {
   1203                 LOG(ERROR) << "Invalid number of input operands (" << inputCount
   1204                            << ", expected 4 or 3) or output operands (" << outputCount
   1205                            << ", expected 1) for operation " << getOperationName(opType);
   1206                 return ANEURALNETWORKS_BAD_DATA;
   1207             }
   1208             auto inputType = operands[inputIndexes[0]].type;
   1209             std::vector<OperandType> inExpectedTypes;
   1210             std::vector<OperandType> outExpectedTypes;
   1211             if (inputType == OperandType::TENSOR_FLOAT32) {
   1212                 inExpectedTypes = {
   1213                         OperandType::TENSOR_FLOAT32,
   1214                         OperandType::TENSOR_INT32,
   1215                         OperandType::TENSOR_INT32,
   1216                 };
   1217                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
   1218             } else if (inputType == OperandType::TENSOR_FLOAT16) {
   1219                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1220                 inExpectedTypes = {
   1221                         OperandType::TENSOR_FLOAT16,
   1222                         OperandType::TENSOR_INT32,
   1223                         OperandType::TENSOR_INT32,
   1224                 };
   1225                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
   1226             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1227                 if (operands[inputIndexes[0]].zeroPoint != 0) {
   1228                     NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1229                 }
   1230                 inExpectedTypes = {
   1231                         OperandType::TENSOR_QUANT8_ASYMM,
   1232                         OperandType::TENSOR_INT32,
   1233                         OperandType::TENSOR_INT32,
   1234                 };
   1235                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
   1236             } else {
   1237                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1238                            << getOperationName(opType);
   1239                 return ANEURALNETWORKS_BAD_DATA;
   1240             }
   1241             if (inputCount == 4) {
   1242                 inExpectedTypes.push_back(OperandType::BOOL);
   1243                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1244             } else {
   1245                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1));
   1246             }
   1247             return validateOperationOperandTypes(operands,
   1248                                                  inputCount, inputIndexes,
   1249                                                  inExpectedTypes,
   1250                                                  outputCount, outputIndexes,
   1251                                                  outExpectedTypes);
   1252         }
   1253         case ANEURALNETWORKS_PAD: {
   1254             if (inputCount != 2 || outputCount != 1) {
   1255                 logInvalidInOutNumber(2, 1);
   1256                 return ANEURALNETWORKS_BAD_DATA;
   1257             }
   1258             auto inputType = operands[inputIndexes[0]].type;
   1259             std::vector<OperandType> inExpectedTypes;
   1260             std::vector<OperandType> outExpectedTypes;
   1261             if (inputType == OperandType::TENSOR_FLOAT32) {
   1262                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1));
   1263                 inExpectedTypes = {
   1264                         OperandType::TENSOR_FLOAT32,
   1265                         OperandType::TENSOR_INT32,
   1266                 };
   1267                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
   1268             } else if (inputType == OperandType::TENSOR_FLOAT16) {
   1269                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1270                 inExpectedTypes = {
   1271                         OperandType::TENSOR_FLOAT16,
   1272                         OperandType::TENSOR_INT32,
   1273                 };
   1274                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
   1275             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1276                 if (operands[inputIndexes[0]].zeroPoint == 0) {
   1277                     NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1));
   1278                 } else {
   1279                     NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1280                 }
   1281                 inExpectedTypes = {
   1282                         OperandType::TENSOR_QUANT8_ASYMM,
   1283                         OperandType::TENSOR_INT32,
   1284                 };
   1285                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
   1286             } else {
   1287                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1288                            << getOperationName(opType);
   1289                 return ANEURALNETWORKS_BAD_DATA;
   1290             }
   1291             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1292                                                  inExpectedTypes, outputCount, outputIndexes,
   1293                                                  outExpectedTypes);
   1294         }
   1295         case ANEURALNETWORKS_PAD_V2: {
   1296             if (inputCount != 3 || outputCount != 1) {
   1297                 logInvalidInOutNumber(3, 1);
   1298                 return ANEURALNETWORKS_BAD_DATA;
   1299             }
   1300             auto inputType = operands[inputIndexes[0]].type;
   1301             std::vector<OperandType> inExpectedTypes;
   1302             std::vector<OperandType> outExpectedTypes;
   1303             if (inputType == OperandType::TENSOR_FLOAT32) {
   1304                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1305                 inExpectedTypes = {
   1306                         OperandType::TENSOR_FLOAT32,
   1307                         OperandType::TENSOR_INT32,
   1308                         OperandType::FLOAT32,
   1309                 };
   1310                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
   1311             } else if (inputType == OperandType::TENSOR_FLOAT16) {
   1312                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1313                 inExpectedTypes = {
   1314                         OperandType::TENSOR_FLOAT16,
   1315                         OperandType::TENSOR_INT32,
   1316                         OperandType::FLOAT16,
   1317                 };
   1318                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
   1319             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1320                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1321                 inExpectedTypes = {
   1322                         OperandType::TENSOR_QUANT8_ASYMM,
   1323                         OperandType::TENSOR_INT32,
   1324                         OperandType::INT32,
   1325                 };  // TODO(b/116699425): Make it UINT8.
   1326                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
   1327             } else {
   1328                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1329                            << getOperationName(opType);
   1330                 return ANEURALNETWORKS_BAD_DATA;
   1331             }
   1332             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1333                                                  inExpectedTypes, outputCount, outputIndexes,
   1334                                                  outExpectedTypes);
   1335         }
   1336         case ANEURALNETWORKS_CAST: {
   1337             if (inputCount != 1 || outputCount != 1) {
   1338                 logInvalidInOutNumber(1, 1);
   1339                 return ANEURALNETWORKS_BAD_DATA;
   1340             }
   1341             auto inputType = operands[inputIndexes[0]].type;
   1342             auto outputType = operands[outputIndexes[0]].type;
   1343             std::vector<OperandType> inExpectedTypes;
   1344             if (inputType == OperandType::TENSOR_FLOAT16 ||
   1345                 inputType == OperandType::TENSOR_FLOAT32 ||
   1346                 inputType == OperandType::TENSOR_INT32 ||
   1347                 inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1348                 inExpectedTypes = {inputType};
   1349             } else {
   1350                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1351                            << getOperationName(opType);
   1352                 return ANEURALNETWORKS_BAD_DATA;
   1353             }
   1354             std::vector<OperandType> outExpectedTypes;
   1355             if (outputType == OperandType::TENSOR_FLOAT16 ||
   1356                 outputType == OperandType::TENSOR_FLOAT32 ||
   1357                 outputType == OperandType::TENSOR_INT32 ||
   1358                 outputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1359                 outExpectedTypes = {outputType};
   1360             } else {
   1361                 LOG(ERROR) << "Unsupported output tensor type for operation "
   1362                            << getOperationName(opType);
   1363                 return ANEURALNETWORKS_BAD_DATA;
   1364             }
   1365             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1366             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1367                                                  inExpectedTypes, outputCount, outputIndexes,
   1368                                                  outExpectedTypes);
   1369         }
   1370         case ANEURALNETWORKS_SQUEEZE: {
   1371             if (inputCount != 2 || outputCount != 1) {
   1372                 logInvalidInOutNumber(2, 1);
   1373                 return ANEURALNETWORKS_BAD_DATA;
   1374             }
   1375             auto inputType = operands[inputIndexes[0]].type;
   1376             std::vector<OperandType> inExpectedTypes;
   1377             std::vector<OperandType> outExpectedTypes;
   1378             if (inputType == OperandType::TENSOR_FLOAT32) {
   1379                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1));
   1380                 inExpectedTypes = {OperandType::TENSOR_FLOAT32,
   1381                                    OperandType::TENSOR_INT32};
   1382                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
   1383             } else if (inputType == OperandType::TENSOR_FLOAT16) {
   1384                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1385                 inExpectedTypes = {OperandType::TENSOR_FLOAT16, OperandType::TENSOR_INT32};
   1386                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
   1387             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1388                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1));
   1389                 inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
   1390                                    OperandType::TENSOR_INT32};
   1391                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
   1392             } else {
   1393                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1394                            << getOperationName(opType);
   1395                 return ANEURALNETWORKS_BAD_DATA;
   1396             }
   1397             return validateOperationOperandTypes(operands,
   1398                                                  inputCount, inputIndexes,
   1399                                                  inExpectedTypes,
   1400                                                  outputCount, outputIndexes,
   1401                                                  outExpectedTypes);
   1402         }
   1403         case ANEURALNETWORKS_STRIDED_SLICE: {
   1404             if (inputCount != 7 || outputCount != 1) {
   1405                 logInvalidInOutNumber(7, 1);
   1406                 return ANEURALNETWORKS_BAD_DATA;
   1407             }
   1408             auto inputType = operands[inputIndexes[0]].type;
   1409             std::vector<OperandType> inExpectedTypes;
   1410             std::vector<OperandType> outExpectedTypes;
   1411             if (inputType == OperandType::TENSOR_FLOAT32) {
   1412                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1));
   1413                 inExpectedTypes = {
   1414                         OperandType::TENSOR_FLOAT32, OperandType::TENSOR_INT32,
   1415                         OperandType::TENSOR_INT32,   OperandType::TENSOR_INT32,
   1416                         OperandType::INT32,          OperandType::INT32,
   1417                         OperandType::INT32,
   1418                 };
   1419                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
   1420             } else if (inputType == OperandType::TENSOR_FLOAT16) {
   1421                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1422                 inExpectedTypes = {
   1423                         OperandType::TENSOR_FLOAT16, OperandType::TENSOR_INT32,
   1424                         OperandType::TENSOR_INT32,   OperandType::TENSOR_INT32,
   1425                         OperandType::INT32,          OperandType::INT32,
   1426                         OperandType::INT32,
   1427                 };
   1428                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
   1429             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1430                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1));
   1431                 inExpectedTypes = {
   1432                         OperandType::TENSOR_QUANT8_ASYMM,
   1433                         OperandType::TENSOR_INT32,
   1434                         OperandType::TENSOR_INT32,
   1435                         OperandType::TENSOR_INT32,
   1436                         OperandType::INT32,
   1437                         OperandType::INT32,
   1438                         OperandType::INT32,
   1439                 };
   1440                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
   1441             } else {
   1442                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1443                            << getOperationName(opType);
   1444                 return ANEURALNETWORKS_BAD_DATA;
   1445             }
   1446             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1447                                                  inExpectedTypes, outputCount, outputIndexes,
   1448                                                  outExpectedTypes);
   1449         }
   1450         case ANEURALNETWORKS_MEAN: {
   1451             if (inputCount != 3 || outputCount != 1) {
   1452                 logInvalidInOutNumber(3, 1);
   1453                 return ANEURALNETWORKS_BAD_DATA;
   1454             }
   1455             auto inputType = operands[inputIndexes[0]].type;
   1456             std::vector<OperandType> inExpectedTypes;
   1457             std::vector<OperandType> outExpectedTypes;
   1458             if (inputType == OperandType::TENSOR_FLOAT32) {
   1459                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1));
   1460                 inExpectedTypes = {OperandType::TENSOR_FLOAT32,
   1461                                    OperandType::TENSOR_INT32,
   1462                                    OperandType::INT32};
   1463                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
   1464             } else if (inputType == OperandType::TENSOR_FLOAT16) {
   1465                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1466                 inExpectedTypes = {OperandType::TENSOR_FLOAT16, OperandType::TENSOR_INT32,
   1467                                    OperandType::INT32};
   1468                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
   1469             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1470                 NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1));
   1471                 inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
   1472                                    OperandType::TENSOR_INT32,
   1473                                    OperandType::INT32};
   1474                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
   1475             } else {
   1476                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1477                            << getOperationName(opType);
   1478                 return ANEURALNETWORKS_BAD_DATA;
   1479             }
   1480             return validateOperationOperandTypes(operands,
   1481                                                  inputCount, inputIndexes,
   1482                                                  inExpectedTypes,
   1483                                                  outputCount, outputIndexes,
   1484                                                  outExpectedTypes);
   1485         }
   1486         case ANEURALNETWORKS_ARGMAX:
   1487         case ANEURALNETWORKS_ARGMIN: {
   1488             if (inputCount != 2 || outputCount != 1) {
   1489                 logInvalidInOutNumber(2, 1);
   1490                 return ANEURALNETWORKS_BAD_DATA;
   1491             }
   1492             auto inputType = operands[inputIndexes[0]].type;
   1493             std::vector<OperandType> inExpectedTypes;
   1494             std::vector<OperandType> outExpectedTypes;
   1495             if (inputType == OperandType::TENSOR_FLOAT16 ||
   1496                 inputType == OperandType::TENSOR_FLOAT32 ||
   1497                 inputType == OperandType::TENSOR_INT32 ||
   1498                 inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1499                 inExpectedTypes = {inputType, OperandType::INT32};
   1500                 outExpectedTypes = {OperandType::TENSOR_INT32};
   1501             } else {
   1502                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1503                            << getOperationName(opType);
   1504                 return ANEURALNETWORKS_BAD_DATA;
   1505             }
   1506             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1507             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1508                                                  inExpectedTypes, outputCount, outputIndexes,
   1509                                                  outExpectedTypes);
   1510         }
   1511         case ANEURALNETWORKS_EXPAND_DIMS: {
   1512             if (inputCount != 2 || outputCount != 1) {
   1513                 logInvalidInOutNumber(2, 1);
   1514                 return ANEURALNETWORKS_BAD_DATA;
   1515             }
   1516             auto inputType = operands[inputIndexes[0]].type;
   1517             std::vector<OperandType> inExpectedTypes;
   1518             std::vector<OperandType> outExpectedTypes;
   1519             if (inputType == OperandType::TENSOR_FLOAT16 ||
   1520                 inputType == OperandType::TENSOR_FLOAT32 ||
   1521                 inputType == OperandType::TENSOR_INT32 ||
   1522                 inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1523                 inExpectedTypes = {inputType, OperandType::INT32};
   1524                 outExpectedTypes = {inputType};
   1525             } else {
   1526                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1527                            << getOperationName(opType);
   1528                 return ANEURALNETWORKS_BAD_DATA;
   1529             }
   1530             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1531             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1532                                                  inExpectedTypes, outputCount, outputIndexes,
   1533                                                  outExpectedTypes);
   1534         }
   1535         case ANEURALNETWORKS_SPLIT: {
   1536             if (inputCount != 3) {
   1537                 LOG(ERROR) << "Invalid number of input operands (" << inputCount << ", expected 3)"
   1538                            << getOperationName(opType);
   1539                 return ANEURALNETWORKS_BAD_DATA;
   1540             }
   1541             auto inputType = operands[inputIndexes[0]].type;
   1542             if (inputType != OperandType::TENSOR_FLOAT16 &&
   1543                 inputType != OperandType::TENSOR_FLOAT32 &&
   1544                 inputType != OperandType::TENSOR_INT32 &&
   1545                 inputType != OperandType::TENSOR_QUANT8_ASYMM) {
   1546                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1547                            << getOperationName(opType);
   1548                 return ANEURALNETWORKS_BAD_DATA;
   1549             }
   1550             std::vector<OperandType> inExpectedTypes = {inputType, OperandType::INT32,
   1551                                                         OperandType::INT32};
   1552             std::vector<OperandType> outExpectedTypes(outputCount, inputType);
   1553             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1554             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1555                                                  inExpectedTypes, outputCount, outputIndexes,
   1556                                                  outExpectedTypes);
   1557         }
   1558         case ANEURALNETWORKS_MAXIMUM:
   1559         case ANEURALNETWORKS_MINIMUM: {
   1560             if (inputCount != 2 || outputCount != 1) {
   1561                 logInvalidInOutNumber(2, 1);
   1562                 return ANEURALNETWORKS_BAD_DATA;
   1563             }
   1564             std::vector<OperandType> inExpectedTypes;
   1565             std::vector<OperandType> outExpectedTypes;
   1566             OperandType inputType = operands[inputIndexes[0]].type;
   1567             if (inputType == OperandType::TENSOR_FLOAT16 ||
   1568                 inputType == OperandType::TENSOR_FLOAT32 ||
   1569                 inputType == OperandType::TENSOR_INT32 ||
   1570                 inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1571                 inExpectedTypes = {inputType, inputType};
   1572                 outExpectedTypes = {inputType};
   1573             } else {
   1574                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1575                            << getOperationName(opType);
   1576                 return ANEURALNETWORKS_BAD_DATA;
   1577             }
   1578             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1579             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1580                                                  inExpectedTypes, outputCount, outputIndexes,
   1581                                                  outExpectedTypes);
   1582         }
   1583         case ANEURALNETWORKS_GROUPED_CONV_2D: {
   1584             if ((inputCount != 12 && inputCount != 9) || outputCount != 1) {
   1585                 LOG(ERROR) << "Invalid number of input operands (" << inputCount
   1586                            << ", expected 12 or 9) or output operands (" << outputCount
   1587                            << ", expected 1) for operation " << getOperationName(opType);
   1588                 return ANEURALNETWORKS_BAD_DATA;
   1589             }
   1590             auto inputType = operands[inputIndexes[0]].type;
   1591             auto filterType = operands[inputIndexes[1]].type;
   1592             std::vector<OperandType> inExpectedTypes;
   1593             std::vector<OperandType> outExpectedTypes;
   1594             if (inputType == OperandType::TENSOR_FLOAT32) {
   1595                 inExpectedTypes = {OperandType::TENSOR_FLOAT32, OperandType::TENSOR_FLOAT32,
   1596                                    OperandType::TENSOR_FLOAT32, OperandType::INT32,
   1597                                    OperandType::INT32,          OperandType::INT32,
   1598                                    OperandType::INT32,          OperandType::INT32};
   1599                 outExpectedTypes = {OperandType::TENSOR_FLOAT32};
   1600             } else if (inputType == OperandType::TENSOR_FLOAT16) {
   1601                 inExpectedTypes = {OperandType::TENSOR_FLOAT16, OperandType::TENSOR_FLOAT16,
   1602                                    OperandType::TENSOR_FLOAT16, OperandType::INT32,
   1603                                    OperandType::INT32,          OperandType::INT32,
   1604                                    OperandType::INT32,          OperandType::INT32};
   1605                 outExpectedTypes = {OperandType::TENSOR_FLOAT16};
   1606             } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1607                 if (filterType != OperandType::TENSOR_QUANT8_ASYMM &&
   1608                     filterType != OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
   1609                     LOG(ERROR) << "Unsupported filter tensor type for operation "
   1610                                << getOperationName(opType);
   1611                     return ANEURALNETWORKS_BAD_DATA;
   1612                 }
   1613 
   1614                 if (filterType == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL &&
   1615                     operands[inputIndexes[1]].extraParams.channelQuant().channelDim != 0) {
   1616                     LOG(ERROR) << "Unsupported filter tensor channel dimension for operation "
   1617                                << getOperationName(opType);
   1618                     return ANEURALNETWORKS_BAD_DATA;
   1619                 }
   1620 
   1621                 inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM,
   1622                                    filterType,
   1623                                    OperandType::TENSOR_INT32,
   1624                                    OperandType::INT32,
   1625                                    OperandType::INT32,
   1626                                    OperandType::INT32,
   1627                                    OperandType::INT32,
   1628                                    OperandType::INT32};
   1629                 outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM};
   1630             } else {
   1631                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1632                            << getOperationName(opType);
   1633                 return ANEURALNETWORKS_BAD_DATA;
   1634             }
   1635 
   1636             if (inputCount == 12) {
   1637                 std::vector<OperandType> explicitScalarTypes(3, OperandType::INT32);
   1638                 inExpectedTypes.insert(inExpectedTypes.end(), explicitScalarTypes.begin(),
   1639                                        explicitScalarTypes.end());
   1640             }
   1641             inExpectedTypes.push_back(OperandType::BOOL);
   1642             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1643             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1644                                                  inExpectedTypes, outputCount, outputIndexes,
   1645                                                  outExpectedTypes);
   1646         }
   1647         case ANEURALNETWORKS_TILE: {
   1648             if (inputCount != 2 || outputCount != 1) {
   1649                 logInvalidInOutNumber(2, 1);
   1650                 return ANEURALNETWORKS_BAD_DATA;
   1651             }
   1652             auto inputType = operands[inputIndexes[0]].type;
   1653             std::vector<OperandType> inExpectedTypes;
   1654             std::vector<OperandType> outExpectedTypes;
   1655             if (inputType == OperandType::TENSOR_FLOAT16 ||
   1656                 inputType == OperandType::TENSOR_FLOAT32 ||
   1657                 inputType == OperandType::TENSOR_INT32 ||
   1658                 inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1659                 inExpectedTypes = {inputType, OperandType::TENSOR_INT32};
   1660                 outExpectedTypes = {inputType};
   1661             } else {
   1662                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1663                            << getOperationName(opType);
   1664                 return ANEURALNETWORKS_BAD_DATA;
   1665             }
   1666             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1667             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1668                                                  inExpectedTypes, outputCount, outputIndexes,
   1669                                                  outExpectedTypes);
   1670         }
   1671         case ANEURALNETWORKS_POW: {
   1672             if (inputCount != 2 || outputCount != 1) {
   1673                 logInvalidInOutNumber(2, 1);
   1674                 return ANEURALNETWORKS_BAD_DATA;
   1675             }
   1676             auto inputType = operands[inputIndexes[0]].type;
   1677             std::vector<OperandType> inExpectedTypes;
   1678             std::vector<OperandType> outExpectedTypes;
   1679             if (inputType == OperandType::TENSOR_FLOAT16 ||
   1680                 inputType == OperandType::TENSOR_FLOAT32) {
   1681                 inExpectedTypes = {inputType, inputType};
   1682                 outExpectedTypes = {inputType};
   1683             } else {
   1684                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1685                            << getOperationName(opType);
   1686                 return ANEURALNETWORKS_BAD_DATA;
   1687             }
   1688             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1689             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1690                                                  inExpectedTypes, outputCount, outputIndexes,
   1691                                                  outExpectedTypes);
   1692         }
   1693         case ANEURALNETWORKS_TOPK_V2: {
   1694             if (inputCount != 2 || outputCount != 2) {
   1695                 logInvalidInOutNumber(2, 1);
   1696                 return ANEURALNETWORKS_BAD_DATA;
   1697             }
   1698             std::vector<OperandType> inExpectedTypes;
   1699             std::vector<OperandType> outExpectedTypes;
   1700             OperandType inputType = operands[inputIndexes[0]].type;
   1701             if (inputType == OperandType::TENSOR_FLOAT16 ||
   1702                 inputType == OperandType::TENSOR_FLOAT32 ||
   1703                 inputType == OperandType::TENSOR_INT32 ||
   1704                 inputType == OperandType::TENSOR_QUANT8_ASYMM) {
   1705                 inExpectedTypes = {inputType, OperandType::INT32};
   1706                 outExpectedTypes = {inputType, OperandType::TENSOR_INT32};
   1707             } else {
   1708                 LOG(ERROR) << "Unsupported input tensor type for operation "
   1709                            << getOperationName(opType);
   1710                 return ANEURALNETWORKS_BAD_DATA;
   1711             }
   1712             NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2));
   1713             return validateOperationOperandTypes(operands, inputCount, inputIndexes,
   1714                                                  inExpectedTypes, outputCount, outputIndexes,
   1715                                                  outExpectedTypes);
   1716         }
   1717         default: {
   1718             const OperationRegistration* operationRegistration =
   1719                     BuiltinOperationResolver::get()->findOperation(
   1720                             static_cast<OperationType>(opType));
   1721             if (operationRegistration == nullptr) {
   1722                 if (0 <= opType && opType < kNumberOfOperationTypes) {
   1723                     LOG(ERROR) << getOperationName(opType) << " not registered";
   1724                 } else {
   1725                     LOG(ERROR) << "Operation type " << opType << " out of the range [0, "
   1726                                << kNumberOfOperationTypes << ")";
   1727                 }
   1728                 return ANEURALNETWORKS_UNEXPECTED_NULL;
   1729             }
   1730             if (operationRegistration->validate == nullptr) {
   1731                 LOG(ERROR) << "Incomplete operation registration: " << getOperationName(opType);
   1732                 return ANEURALNETWORKS_UNEXPECTED_NULL;
   1733             }
   1734             OperationValidationContext context(inputCount, inputIndexes, outputCount, outputIndexes,
   1735                                                operands.data(), halVersion);
   1736             if (!operationRegistration->validate(&context)) {
   1737                 LOG(ERROR) << "Validation failed for operation " << getOperationName(opType);
   1738                 return ANEURALNETWORKS_BAD_DATA;
   1739             }
   1740             return ANEURALNETWORKS_NO_ERROR;
   1741         }
   1742     }
   1743 }
   1744 
   1745 ErrorStatus convertResultCodeToErrorStatus(int resultCode) {
   1746     switch (resultCode) {
   1747         case ANEURALNETWORKS_NO_ERROR:
   1748             return ErrorStatus::NONE;
   1749 
   1750         case ANEURALNETWORKS_BAD_DATA:
   1751         case ANEURALNETWORKS_UNEXPECTED_NULL:
   1752             return ErrorStatus::INVALID_ARGUMENT;
   1753 
   1754         case ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE:
   1755             return ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
   1756 
   1757         case ANEURALNETWORKS_UNAVAILABLE_DEVICE:
   1758             return ErrorStatus::DEVICE_UNAVAILABLE;
   1759 
   1760         default:
   1761             LOG(ERROR) << "Unknown result code " << resultCode
   1762                        << " mapped to ErrorStatus::GENERAL_FAILURE";
   1763             return ErrorStatus::GENERAL_FAILURE;
   1764         case ANEURALNETWORKS_BAD_STATE:
   1765         case ANEURALNETWORKS_INCOMPLETE:
   1766         case ANEURALNETWORKS_OP_FAILED:
   1767         case ANEURALNETWORKS_OUT_OF_MEMORY:
   1768         case ANEURALNETWORKS_UNMAPPABLE:
   1769             return ErrorStatus::GENERAL_FAILURE;
   1770     }
   1771 }
   1772 
   1773 int convertErrorStatusToResultCode(ErrorStatus status) {
   1774     switch (status) {
   1775         case ErrorStatus::NONE:
   1776             return ANEURALNETWORKS_NO_ERROR;
   1777 
   1778         case ErrorStatus::INVALID_ARGUMENT:
   1779             return ANEURALNETWORKS_BAD_DATA;
   1780 
   1781         case ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
   1782             return ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE;
   1783 
   1784         case ErrorStatus::DEVICE_UNAVAILABLE:
   1785             return ANEURALNETWORKS_UNAVAILABLE_DEVICE;
   1786 
   1787         default:
   1788             LOG(ERROR) << "Unknown ErrorStatus " << toString(status)
   1789                        << " mapped to ANEURALNETWORKS_OP_FAILED";
   1790             return ANEURALNETWORKS_OP_FAILED;
   1791         case ErrorStatus::GENERAL_FAILURE:
   1792             return ANEURALNETWORKS_OP_FAILED;
   1793     }
   1794 }
   1795 
   1796 // V1_2::Capabilities::operandPerformance utilities.
   1797 // The field V1_2::Capabilities::operandPerformance is a vector sorted by the
   1798 // field V1_2::Capabilities::OperandPerformance::type.
   1799 
   1800 hidl_vec<Capabilities::OperandPerformance> nonExtensionOperandPerformance(PerformanceInfo perf) {
   1801     using OpPerf = Capabilities::OperandPerformance;
   1802 
   1803     // Note: range presents enumerators in declaration order, not in numerical order.
   1804     static constexpr ::android::hardware::hidl_enum_range<OperandType> kOperandTypeRange;
   1805 
   1806     hidl_vec<OpPerf> ret(kOperandTypeRange.end() - kOperandTypeRange.begin());
   1807 
   1808     std::transform(kOperandTypeRange.begin(), kOperandTypeRange.end(), ret.begin(),
   1809                    [perf](OperandType type) {
   1810                        return Capabilities::OperandPerformance{type, perf};
   1811                    });
   1812     std::sort(ret.begin(), ret.end(),
   1813               [](const OpPerf& a, const OpPerf& b) { return a.type < b.type; });
   1814 
   1815     return ret;
   1816 }
   1817 
   1818 void update(hidl_vec<Capabilities::OperandPerformance>* operandPerformance, OperandType type,
   1819             PerformanceInfo perf) {
   1820     CHECK(operandPerformance != nullptr);
   1821     const auto it = std::lower_bound(operandPerformance->begin(), operandPerformance->end(), type,
   1822                                      [](const Capabilities::OperandPerformance& perf,
   1823                                         OperandType type) { return perf.type < type; });
   1824     CHECK(it != operandPerformance->end())
   1825             << toString(type) << " not in " << toString(*operandPerformance);
   1826     it->info = perf;
   1827 }
   1828 
   1829 PerformanceInfo lookup(const hidl_vec<Capabilities::OperandPerformance>& operandPerformance,
   1830                        OperandType type) {
   1831     const auto it = std::lower_bound(operandPerformance.begin(), operandPerformance.end(), type,
   1832                                      [](const Capabilities::OperandPerformance& perf,
   1833                                         OperandType type) { return perf.type < type; });
   1834     if (it == operandPerformance.end()) {
   1835         LOG(WARNING) << "No PerformanceInfo for " << toString(type);
   1836         return {.execTime = FLT_MAX, .powerUsage = FLT_MAX};
   1837     } else {
   1838         return it->info;
   1839     }
   1840 }
   1841 
   1842 // Versioning
   1843 
   1844 // In Android P, most data types are treated as having the same performance as TENSOR_QUANT8_ASYMM.
   1845 // This array must be in sorted order.
   1846 static const OperandType kQuantized8PerformanceConsistentWithP[] = {
   1847         OperandType::INT32, OperandType::UINT32, OperandType::TENSOR_INT32, OperandType::OEM,
   1848         OperandType::TENSOR_OEM_BYTE};
   1849 
   1850 static bool isQuantized8PerformanceConsistentWithP(const V1_2::Capabilities& capabilities) {
   1851     const PerformanceInfo quantized8Performance =
   1852             lookup(capabilities.operandPerformance, OperandType::TENSOR_QUANT8_ASYMM);
   1853     return std::all_of(std::begin(kQuantized8PerformanceConsistentWithP),
   1854                        std::end(kQuantized8PerformanceConsistentWithP),
   1855                        [quantized8Performance, &capabilities](OperandType type) {
   1856                            return quantized8Performance ==
   1857                                   lookup(capabilities.operandPerformance, type);
   1858                        });
   1859 }
   1860 
   1861 static hidl_vec<V1_2::Capabilities::OperandPerformance> makeQuantized8PerformanceConsistentWithP(
   1862         PerformanceInfo quantized8Performance) {
   1863     hidl_vec<V1_2::Capabilities::OperandPerformance> ret(
   1864             sizeof(kQuantized8PerformanceConsistentWithP) /
   1865             sizeof(kQuantized8PerformanceConsistentWithP[0]));
   1866     std::transform(
   1867             std::begin(kQuantized8PerformanceConsistentWithP),
   1868             std::end(kQuantized8PerformanceConsistentWithP), ret.begin(),
   1869             [quantized8Performance](OperandType type) -> V1_2::Capabilities::OperandPerformance {
   1870                 return {type, quantized8Performance};
   1871             });
   1872     return ret;
   1873 }
   1874 
   1875 bool compliantWithV1_0(const V1_0::Capabilities&) {
   1876     return true;
   1877 }
   1878 
   1879 bool compliantWithV1_0(const V1_1::Capabilities& capabilities) {
   1880     return capabilities.relaxedFloat32toFloat16Performance == capabilities.float32Performance;
   1881 }
   1882 
   1883 bool compliantWithV1_0(const V1_2::Capabilities& capabilities) {
   1884     const PerformanceInfo perfTensorFloat32 =
   1885             lookup(capabilities.operandPerformance, OperandType::TENSOR_FLOAT32);
   1886     const PerformanceInfo perfFloat32 =
   1887             lookup(capabilities.operandPerformance, OperandType::FLOAT32);
   1888     if (perfTensorFloat32 != perfFloat32 ||
   1889         perfTensorFloat32 != capabilities.relaxedFloat32toFloat16PerformanceTensor ||
   1890         perfFloat32 != capabilities.relaxedFloat32toFloat16PerformanceScalar) {
   1891         return false;
   1892     }
   1893 
   1894     return isQuantized8PerformanceConsistentWithP(capabilities);
   1895 }
   1896 
   1897 bool compliantWithV1_1(const V1_0::Capabilities&) {
   1898     return true;
   1899 }
   1900 
   1901 bool compliantWithV1_1(const V1_1::Capabilities&) {
   1902     return true;
   1903 }
   1904 
   1905 bool compliantWithV1_1(const V1_2::Capabilities& capabilities) {
   1906     if ((capabilities.relaxedFloat32toFloat16PerformanceTensor !=
   1907          capabilities.relaxedFloat32toFloat16PerformanceScalar) ||
   1908         (lookup(capabilities.operandPerformance, OperandType::TENSOR_FLOAT32) !=
   1909          lookup(capabilities.operandPerformance, OperandType::FLOAT32))) {
   1910         return false;
   1911     }
   1912 
   1913     return isQuantized8PerformanceConsistentWithP(capabilities);
   1914 }
   1915 
   1916 bool compliantWithV1_2(const V1_0::Capabilities&) {
   1917     return true;
   1918 }
   1919 
   1920 bool compliantWithV1_2(const V1_1::Capabilities&) {
   1921     return true;
   1922 }
   1923 
   1924 bool compliantWithV1_2(const V1_0::Model&) {
   1925     return true;
   1926 }
   1927 
   1928 bool compliantWithV1_0(const V1_1::Model& model) {
   1929     // In addition to new enumeration values being introduced in V1_1::Model, a
   1930     // new flag was introduced to indicate whether or not float32 data can be
   1931     // calculated using float16 units. This 'relaxComputationFloat32toFloat16'
   1932     // flag is not relevant in whether a V1_1::Model is compliant with a
   1933     // V1_0::Model because all 1.0 drivers require strict calculation by default
   1934     // in the P NN runtime. Even if fp16 calculations are allowed, they can
   1935     // still be computed by a strict fp32 driver.
   1936     return std::all_of(
   1937             model.operations.begin(), model.operations.end(), [&model](const V1_1::Operation& op) {
   1938                 int error = validateOperation(static_cast<int32_t>(op.type), op.inputs.size(),
   1939                                               op.inputs.size() > 0 ? op.inputs.data() : nullptr,
   1940                                               op.outputs.size(),
   1941                                               op.outputs.size() > 0 ? op.outputs.data() : nullptr,
   1942                                               convertToV1_2(model.operands), HalVersion::V1_0);
   1943                 return error == ANEURALNETWORKS_NO_ERROR;
   1944             });
   1945 }
   1946 
   1947 bool compliantWithV1_1(const V1_0::Model&) {
   1948     return true;
   1949 }
   1950 
   1951 bool compliantWithV1_1(const V1_1::Model&) {
   1952     return true;
   1953 }
   1954 
   1955 static V1_0::OperationType uncheckedConvertToV1_0(V1_1::OperationType type) {
   1956     return static_cast<V1_0::OperationType>(type);
   1957 }
   1958 
   1959 static V1_1::OperationType convertToV1_1(V1_0::OperationType type) {
   1960     return static_cast<V1_1::OperationType>(type);
   1961 }
   1962 
   1963 V1_0::Capabilities convertToV1_0(const V1_0::Capabilities& capabilities) {
   1964     return capabilities;
   1965 }
   1966 
   1967 V1_0::Capabilities convertToV1_0(const V1_1::Capabilities& capabilities) {
   1968     if (!compliantWithV1_0(capabilities)) {
   1969         LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities)
   1970                    << " from V1_1::Capabilities to V1_0::Capabilities";
   1971     }
   1972     return { .float32Performance = capabilities.float32Performance,
   1973              .quantized8Performance = capabilities.quantized8Performance };
   1974 }
   1975 
   1976 V1_0::Capabilities convertToV1_0(const V1_2::Capabilities& capabilities) {
   1977     if (!compliantWithV1_0(capabilities)) {
   1978         LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities)
   1979                    << " from V1_2::Capabilities to V1_0::Capabilities";
   1980     }
   1981     return {.float32Performance =
   1982                     lookup(capabilities.operandPerformance, OperandType::TENSOR_FLOAT32),
   1983             .quantized8Performance =
   1984                     lookup(capabilities.operandPerformance, OperandType::TENSOR_QUANT8_ASYMM)};
   1985 }
   1986 
   1987 V1_1::Capabilities convertToV1_1(const V1_0::Capabilities& capabilities) {
   1988     return { .float32Performance = capabilities.float32Performance,
   1989              .quantized8Performance = capabilities.quantized8Performance,
   1990              .relaxedFloat32toFloat16Performance = capabilities.float32Performance };
   1991 }
   1992 
   1993 V1_1::Capabilities convertToV1_1(const V1_1::Capabilities& capabilities) {
   1994     return capabilities;
   1995 }
   1996 
   1997 V1_1::Capabilities convertToV1_1(const V1_2::Capabilities& capabilities) {
   1998     if (!compliantWithV1_1(capabilities)) {
   1999         LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities)
   2000                    << " from V1_2::Capabilities to V1_1::Capabilities";
   2001     }
   2002     return {.float32Performance =
   2003                     lookup(capabilities.operandPerformance, OperandType::TENSOR_FLOAT32),
   2004             .quantized8Performance =
   2005                     lookup(capabilities.operandPerformance, OperandType::TENSOR_QUANT8_ASYMM),
   2006             .relaxedFloat32toFloat16Performance =
   2007                     capabilities.relaxedFloat32toFloat16PerformanceTensor};
   2008 }
   2009 
   2010 V1_2::Capabilities convertToV1_2(const V1_0::Capabilities& capabilities) {
   2011     V1_2::Capabilities ret = {
   2012             .relaxedFloat32toFloat16PerformanceScalar = capabilities.float32Performance,
   2013             .relaxedFloat32toFloat16PerformanceTensor = capabilities.float32Performance,
   2014             .operandPerformance =
   2015                     makeQuantized8PerformanceConsistentWithP(capabilities.quantized8Performance)};
   2016     auto& opPerf = ret.operandPerformance;
   2017     opPerf.resize(opPerf.size() + 2);
   2018     opPerf[opPerf.size() - 2] = {OperandType::TENSOR_FLOAT32, capabilities.float32Performance};
   2019     opPerf[opPerf.size() - 1] = {OperandType::FLOAT32, capabilities.float32Performance};
   2020     using OperandPerformance = V1_2::Capabilities::OperandPerformance;
   2021     std::sort(opPerf.begin(), opPerf.end(),
   2022               [](const OperandPerformance& a, const OperandPerformance& b) {
   2023                   return a.type < b.type;
   2024               });
   2025     return ret;
   2026 }
   2027 
   2028 V1_2::Capabilities convertToV1_2(const V1_1::Capabilities& capabilities) {
   2029     V1_2::Capabilities ret = {.relaxedFloat32toFloat16PerformanceScalar =
   2030                                       capabilities.relaxedFloat32toFloat16Performance,
   2031                               .relaxedFloat32toFloat16PerformanceTensor =
   2032                                       capabilities.relaxedFloat32toFloat16Performance,
   2033                               .operandPerformance = makeQuantized8PerformanceConsistentWithP(
   2034                                       capabilities.quantized8Performance)};
   2035     auto& opPerf = ret.operandPerformance;
   2036     opPerf.resize(opPerf.size() + 2);
   2037     opPerf[opPerf.size() - 2] = {OperandType::TENSOR_FLOAT32, capabilities.float32Performance};
   2038     opPerf[opPerf.size() - 1] = {OperandType::FLOAT32, capabilities.float32Performance};
   2039     using OperandPerformance = V1_2::Capabilities::OperandPerformance;
   2040     std::sort(opPerf.begin(), opPerf.end(),
   2041               [](const OperandPerformance& a, const OperandPerformance& b) {
   2042                   return a.type < b.type;
   2043               });
   2044     return ret;
   2045 }
   2046 
   2047 V1_2::Capabilities convertToV1_2(const V1_2::Capabilities& capabilities) {
   2048     return capabilities;
   2049 }
   2050 
   2051 static V1_0::Operation uncheckedConvertToV1_0(const V1_1::Operation& operation) {
   2052     return {.type = uncheckedConvertToV1_0(operation.type),
   2053             .inputs = operation.inputs,
   2054             .outputs = operation.outputs};
   2055 }
   2056 
   2057 static V1_1::Operation convertToV1_1(const V1_0::Operation& operation) {
   2058     return {.type = convertToV1_1(operation.type),
   2059             .inputs = operation.inputs,
   2060             .outputs = operation.outputs};
   2061 }
   2062 
   2063 static hidl_vec<V1_0::Operation> uncheckedConvertToV1_0(
   2064         const hidl_vec<V1_1::Operation>& operations) {
   2065     hidl_vec<V1_0::Operation> result(operations.size());
   2066     std::transform(
   2067             operations.begin(), operations.end(), result.begin(),
   2068             [](const V1_1::Operation& operation) { return uncheckedConvertToV1_0(operation); });
   2069     return result;
   2070 }
   2071 
   2072 static hidl_vec<V1_1::Operation> convertToV1_1(const hidl_vec<V1_0::Operation>& operations) {
   2073     hidl_vec<V1_1::Operation> result(operations.size());
   2074     std::transform(operations.begin(), operations.end(), result.begin(),
   2075                    [](const V1_0::Operation& operation) { return convertToV1_1(operation); });
   2076     return result;
   2077 }
   2078 
   2079 bool compliantWithV1_0(const V1_2::Operand& operand) {
   2080     return validOperandType(static_cast<V1_0::OperandType>(operand.type)) &&
   2081            (nonExtensionOperandTypeIsScalar(static_cast<int>(operand.type)) ||
   2082             operand.dimensions.size() != 0);
   2083 }
   2084 
   2085 V1_0::Model convertToV1_0(const V1_0::Model& model) {
   2086     return model;
   2087 }
   2088 
   2089 V1_0::Model convertToV1_0(const V1_1::Model& model) {
   2090     if (!compliantWithV1_0(model)) {
   2091         LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model))
   2092                    << " from V1_1::Model to V1_0::Model";
   2093     }
   2094     return {.operands = model.operands,
   2095             .operations = uncheckedConvertToV1_0(model.operations),
   2096             .inputIndexes = model.inputIndexes,
   2097             .outputIndexes = model.outputIndexes,
   2098             .operandValues = model.operandValues,
   2099             .pools = model.pools};
   2100 }
   2101 
   2102 V1_1::Model convertToV1_1(const V1_0::Model& model) {
   2103     return {.operands = model.operands,
   2104             .operations = convertToV1_1(model.operations),
   2105             .inputIndexes = model.inputIndexes,
   2106             .outputIndexes = model.outputIndexes,
   2107             .operandValues = model.operandValues,
   2108             .pools = model.pools,
   2109             .relaxComputationFloat32toFloat16 = false};
   2110 }
   2111 
   2112 V1_1::Model convertToV1_1(const V1_1::Model& model) {
   2113     return model;
   2114 }
   2115 
   2116 void logModelToInfo(const V1_2::Model& model) {
   2117     LOG(INFO) << "V1_2::Model start";
   2118     LOG(INFO) << "operands" << toString(model.operands);
   2119     LOG(INFO) << "operations" << toString(model.operations);
   2120     LOG(INFO) << "inputIndexes" << toString(model.inputIndexes);
   2121     LOG(INFO) << "outputIndexes" << toString(model.outputIndexes);
   2122     LOG(INFO) << "operandValues size" << model.operandValues.size();
   2123     LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools));
   2124 }
   2125 
   2126 static bool compliantWith(HalVersion version, const V1_2::Model& model,
   2127                           std::set<uint32_t>* noncompliantOperations) {
   2128     if (version >= HalVersion::V1_2) return true;
   2129 
   2130     // A boolean vector indicating whether each pool is compliant with the target HAL version.
   2131     std::vector<bool> isPoolCompliant(model.pools.size(), false);
   2132     std::transform(model.pools.begin(), model.pools.end(), isPoolCompliant.begin(),
   2133                    [version](const hidl_memory& pool) { return validatePool(pool, version); });
   2134 
   2135     // A boolean vector indicating whether each operand is compliant with the target HAL version.
   2136     std::vector<bool> isOperandCompliant(model.operands.size(), false);
   2137     std::transform(model.operands.begin(), model.operands.end(), isOperandCompliant.begin(),
   2138                    [&isPoolCompliant](const V1_2::Operand& op) {
   2139                        // There is no V1_1::Operand -- both V1_0::Model and V1_1::Model use
   2140                        // V1_0::Operand.
   2141                        return compliantWithV1_0(op) &&
   2142                               !(op.lifetime == OperandLifeTime::CONSTANT_REFERENCE &&
   2143                                 !isPoolCompliant[op.location.poolIndex]);
   2144                    });
   2145 
   2146     auto allOperandsCompliant = [&isOperandCompliant](const hidl_vec<uint32_t>& indices) {
   2147         return std::all_of(
   2148                 indices.begin(), indices.end(),
   2149                 [&isOperandCompliant](const uint32_t ind) { return isOperandCompliant[ind]; });
   2150     };
   2151 
   2152     auto localValidateOperation = [&model, version,
   2153                                    &allOperandsCompliant](const V1_2::Operation& op) {
   2154         if (!allOperandsCompliant(op.inputs) || !allOperandsCompliant(op.outputs)) return false;
   2155         int error = validateOperation(
   2156                 static_cast<int32_t>(op.type), op.inputs.size(),
   2157                 op.inputs.size() > 0 ? op.inputs.data() : nullptr, op.outputs.size(),
   2158                 op.outputs.size() > 0 ? op.outputs.data() : nullptr, model.operands, version);
   2159         return error == ANEURALNETWORKS_NO_ERROR;
   2160     };
   2161 
   2162     if (noncompliantOperations) {
   2163         CHECK(noncompliantOperations->empty());
   2164         for (uint32_t idx = 0; idx < model.operations.size(); ++idx) {
   2165             if (!localValidateOperation(model.operations[idx])) {
   2166                 noncompliantOperations->insert(idx);
   2167             }
   2168         }
   2169         return noncompliantOperations->empty();
   2170     } else {
   2171         return std::all_of(model.operations.begin(), model.operations.end(),
   2172                            localValidateOperation);
   2173     }
   2174 }
   2175 
   2176 bool compliantWithV1_0(const V1_2::Model& model, std::set<uint32_t>* noncompliantOperations) {
   2177     return compliantWith(HalVersion::V1_0, model, noncompliantOperations);
   2178 }
   2179 
   2180 bool compliantWithV1_1(const V1_2::Model& model, std::set<uint32_t>* noncompliantOperations) {
   2181     return compliantWith(HalVersion::V1_1, model, noncompliantOperations);
   2182 }
   2183 
   2184 V1_0::OperationType uncheckedConvertToV1_0(V1_2::OperationType type) {
   2185     return static_cast<V1_0::OperationType>(type);
   2186 }
   2187 
   2188 V1_1::OperationType uncheckedConvertToV1_1(V1_2::OperationType type) {
   2189     return static_cast<V1_1::OperationType>(type);
   2190 }
   2191 
   2192 static V1_2::OperationType convertToV1_2(V1_0::OperationType type) {
   2193     return static_cast<V1_2::OperationType>(type);
   2194 }
   2195 
   2196 static V1_2::OperationType convertToV1_2(V1_1::OperationType type) {
   2197     return static_cast<V1_2::OperationType>(type);
   2198 }
   2199 
   2200 static V1_0::Operation uncheckedConvertToV1_0(const V1_2::Operation& operation) {
   2201     return {.type = uncheckedConvertToV1_0(operation.type),
   2202             .inputs = operation.inputs,
   2203             .outputs = operation.outputs};
   2204 }
   2205 
   2206 static V1_1::Operation uncheckedConvertToV1_1(const V1_2::Operation& operation) {
   2207     return {.type = uncheckedConvertToV1_1(operation.type),
   2208             .inputs = operation.inputs,
   2209             .outputs = operation.outputs};
   2210 }
   2211 
   2212 static V1_2::Operation convertToV1_2(const V1_0::Operation& operation) {
   2213     return {.type = convertToV1_2(operation.type),
   2214             .inputs = operation.inputs,
   2215             .outputs = operation.outputs};
   2216 }
   2217 
   2218 static V1_2::Operation convertToV1_2(const V1_1::Operation& operation) {
   2219     return {.type = convertToV1_2(operation.type),
   2220             .inputs = operation.inputs,
   2221             .outputs = operation.outputs};
   2222 }
   2223 
   2224 static hidl_vec<V1_0::Operation> uncheckedConvertToV1_0(
   2225         const hidl_vec<V1_2::Operation>& operations) {
   2226     hidl_vec<V1_0::Operation> result(operations.size());
   2227     std::transform(
   2228             operations.begin(), operations.end(), result.begin(),
   2229             [](const V1_2::Operation& operation) { return uncheckedConvertToV1_0(operation); });
   2230     return result;
   2231 }
   2232 
   2233 static hidl_vec<V1_1::Operation> uncheckedConvertToV1_1(
   2234         const hidl_vec<V1_2::Operation>& operations) {
   2235     hidl_vec<V1_1::Operation> result(operations.size());
   2236     std::transform(
   2237             operations.begin(), operations.end(), result.begin(),
   2238             [](const V1_2::Operation& operation) { return uncheckedConvertToV1_1(operation); });
   2239     return result;
   2240 }
   2241 
   2242 static hidl_vec<V1_2::Operation> convertToV1_2(const hidl_vec<V1_0::Operation>& operations) {
   2243     hidl_vec<V1_2::Operation> result(operations.size());
   2244     std::transform(operations.begin(), operations.end(), result.begin(),
   2245                    [](const V1_0::Operation& operation) { return convertToV1_2(operation); });
   2246     return result;
   2247 }
   2248 
   2249 static hidl_vec<V1_2::Operation> convertToV1_2(const hidl_vec<V1_1::Operation>& operations) {
   2250     hidl_vec<V1_2::Operation> result(operations.size());
   2251     std::transform(operations.begin(), operations.end(), result.begin(),
   2252                    [](const V1_1::Operation& operation) { return convertToV1_2(operation); });
   2253     return result;
   2254 }
   2255 
   2256 // We only need to convert from 1.0 and back since there wasn't any changes to
   2257 // Operand in 1.1
   2258 V1_2::OperandType convertToV1_2(const V1_0::OperandType& operandType) {
   2259     return static_cast<V1_2::OperandType>(operandType);
   2260 }
   2261 
   2262 static bool compliantWithV1_0(const V1_2::OperandType& operandType) {
   2263     return validOperandType(static_cast<V1_0::OperandType>(operandType));
   2264 }
   2265 
   2266 V1_0::OperandType convertToV1_0(const V1_2::OperandType& operandType) {
   2267     if (!compliantWithV1_0(operandType)) {
   2268         LOG(ERROR) << "Upcasting non-compliant operand type " << toString(operandType)
   2269                    << " from V1_2::Operand to V1_0::Operand";
   2270     }
   2271     return static_cast<V1_0::OperandType>(operandType);
   2272 }
   2273 
   2274 // We only need to convert from 1.0 and back since there wasn't any changes to
   2275 // Operand in 1.1
   2276 V1_2::Operand convertToV1_2(const V1_0::Operand& operand) {
   2277     return {.type = convertToV1_2(operand.type),
   2278             .dimensions = operand.dimensions,
   2279             .numberOfConsumers = operand.numberOfConsumers,
   2280             .scale = operand.scale,
   2281             .zeroPoint = operand.zeroPoint,
   2282             .lifetime = operand.lifetime,
   2283             .location = operand.location};
   2284 }
   2285 
   2286 V1_2::Operand convertToV1_2(const V1_2::Operand& operand) {
   2287     return operand;
   2288 }
   2289 
   2290 V1_0::Operand convertToV1_0(const V1_2::Operand& operand) {
   2291     return {.type = convertToV1_0(operand.type),
   2292             .dimensions = operand.dimensions,
   2293             .numberOfConsumers = operand.numberOfConsumers,
   2294             .scale = operand.scale,
   2295             .zeroPoint = operand.zeroPoint,
   2296             .lifetime = operand.lifetime,
   2297             .location = operand.location};
   2298 }
   2299 
   2300 // We only need to convert from 1.0 and back since there wasn't any changes to
   2301 // Operand in 1.1
   2302 hidl_vec<V1_2::Operand> convertToV1_2(const hidl_vec<V1_0::Operand>& operands) {
   2303     hidl_vec<V1_2::Operand> result(operands.size());
   2304     std::transform(operands.begin(), operands.end(), result.begin(),
   2305                    [](const V1_0::Operand& operand) { return convertToV1_2(operand); });
   2306     return result;
   2307 }
   2308 
   2309 hidl_vec<V1_2::Operand> convertToV1_2(const hidl_vec<V1_2::Operand>& operands) {
   2310     return operands;
   2311 }
   2312 
   2313 hidl_vec<V1_0::Operand> convertToV1_0(const hidl_vec<V1_2::Operand>& operands) {
   2314     hidl_vec<V1_0::Operand> result(operands.size());
   2315     std::transform(operands.begin(), operands.end(), result.begin(),
   2316                    [](const V1_2::Operand& operand) { return convertToV1_0(operand); });
   2317     return result;
   2318 }
   2319 
   2320 V1_0::Model convertToV1_0(const V1_2::Model& model) {
   2321     if (!compliantWithV1_0(model)) {
   2322         LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model))
   2323                    << " from V1_2::Model to V1_0::Model";
   2324     }
   2325     return {.operands = convertToV1_0(model.operands),
   2326             .operations = uncheckedConvertToV1_0(model.operations),
   2327             .inputIndexes = model.inputIndexes,
   2328             .outputIndexes = model.outputIndexes,
   2329             .operandValues = model.operandValues,
   2330             .pools = model.pools};
   2331 }
   2332 
   2333 V1_1::Model convertToV1_1(const V1_2::Model& model) {
   2334     if (!compliantWithV1_1(model)) {
   2335         LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model))
   2336                    << " from V1_2::Model to V1_1::Model";
   2337     }
   2338     return {.operands = convertToV1_0(model.operands),  // Operands in 1.1 and 1.0 are identical.
   2339             .operations = uncheckedConvertToV1_1(model.operations),
   2340             .inputIndexes = model.inputIndexes,
   2341             .outputIndexes = model.outputIndexes,
   2342             .operandValues = model.operandValues,
   2343             .pools = model.pools,
   2344             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16};
   2345 }
   2346 
   2347 V1_2::Model convertToV1_2(const V1_0::Model& model) {
   2348     return {.operands = convertToV1_2(model.operands),
   2349             .operations = convertToV1_2(model.operations),
   2350             .inputIndexes = model.inputIndexes,
   2351             .outputIndexes = model.outputIndexes,
   2352             .operandValues = model.operandValues,
   2353             .pools = model.pools,
   2354             .relaxComputationFloat32toFloat16 = false};
   2355 }
   2356 
   2357 V1_2::Model convertToV1_2(const V1_1::Model& model) {
   2358     return {.operands = convertToV1_2(model.operands),
   2359             .operations = convertToV1_2(model.operations),
   2360             .inputIndexes = model.inputIndexes,
   2361             .outputIndexes = model.outputIndexes,
   2362             .operandValues = model.operandValues,
   2363             .pools = model.pools,
   2364             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16};
   2365 }
   2366 
   2367 V1_2::Model convertToV1_2(const V1_2::Model& model) {
   2368     return model;
   2369 }
   2370 
   2371 #ifdef NN_DEBUGGABLE
   2372 uint32_t getProp(const char* str, uint32_t defaultValue) {
   2373     const std::string propStr = android::base::GetProperty(str, "");
   2374     if (propStr.size() > 0) {
   2375         return std::stoi(propStr);
   2376     } else {
   2377         return defaultValue;
   2378     }
   2379 }
   2380 #endif  // NN_DEBUGGABLE
   2381 
   2382 } // namespace nn
   2383 } // namespace android
   2384