As extensões do fornecedor Neural Networks API (NNAPI), introduzidas no Android 10, são coleções de operações definidas pelo fornecedor e tipos de dados. Em dispositivos que executam o NN HAL 1.2 ou superior, os drivers podem fornecer operações personalizadas aceleradas por hardware, oferecendo suporte às extensões correspondentes do fornecedor. As extensões do fornecedor não modificam o comportamento das operações existentes.
As extensões do fornecedor fornecem uma alternativa mais estruturada para a operação e tipos de dados do OEM, que foram descontinuados no Android 10. Para obter mais informações, consulte Operação e tipos de dados do OEM .
Lista de permissões de uso de extensões
As extensões do fornecedor só podem ser usadas por aplicativos Android explicitamente especificados e binários nativos nas partições /product
, /vendor
, /odm
e /data
. Aplicativos e binários nativos localizados na partição /system
não podem usar extensões do fornecedor.
Uma lista de aplicativos Android e binários com permissão para usar extensões de fornecedores NNAPI é armazenada em /vendor/etc/nnapi_extensions_app_allowlist
. Cada linha do arquivo contém uma nova entrada. Uma entrada pode ser um caminho binário nativo prefixado com uma barra (/), por exemplo, /data/foo
, ou um nome de um pacote de aplicativo Android, por exemplo, com.foo.bar
.
A lista de permissões é aplicada a partir da biblioteca compartilhada de tempo de execução NNAPI. Esta biblioteca protege contra o uso acidental, mas não contra a violação deliberada por um aplicativo diretamente usando a interface HAL do driver NNAPI.
Definição de extensão de fornecedor
O fornecedor cria e mantém um arquivo de cabeçalho com a definição de extensão. Um exemplo completo de uma definição de extensão pode ser encontrado em example/fibonacci/FibonacciExtension.h
.
Cada extensão deve ter um nome exclusivo que começa com o nome de domínio reverso do fornecedor.
const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";
O nome atua como um namespace para operações e tipos de dados. O NNAPI usa esse nome para distinguir entre as extensões do fornecedor.
Operações e tipos de dados são declarados de maneira semelhante àqueles em runtime/include/NeuralNetworks.h
.
enum {
/**
* A custom scalar type.
*/
EXAMPLE_SCALAR = 0,
/**
* A custom tensor type.
*
* Attached to this tensor is {@link ExampleTensorParams}.
*/
EXAMPLE_TENSOR = 1,
};
enum {
/**
* Computes example function.
*
* Inputs:
* * 0: A scalar of {@link EXAMPLE_SCALAR}.
*
* Outputs:
* * 0: A tensor of {@link EXAMPLE_TENSOR}.
*/
EXAMPLE_FUNCTION = 0,
};
Uma operação de extensão pode usar qualquer tipo de operando, incluindo tipos de operando de não extensão e tipos de operando de outras extensões. Ao usar um tipo de operando de outra extensão, o driver deve suportar a outra extensão.
As extensões também podem declarar estruturas personalizadas para acompanhar operandos de extensão.
/**
* Quantization parameters for {@link EXAMPLE_TENSOR}.
*/
typedef struct ExampleTensorParams {
double scale;
int64_t zeroPoint;
} ExampleTensorParams;
Usando extensões em clientes NNAPI
O arquivo runtime/include/NeuralNetworksExtensions.h
(API C) fornece suporte à extensão de tempo de execução. Esta seção fornece uma visão geral da API C.
Para verificar se um dispositivo oferece suporte a uma extensão, use ANeuralNetworksDevice_getExtensionSupport
.
bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME,
&isExtensionSupported),
ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
// The device supports the extension.
...
}
Para construir um modelo com um operando de extensão, use ANeuralNetworksModel_getExtensionOperandType
para obter o tipo de operando e chame ANeuralNetworksModel_addOperand
.
int32_t type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperandType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_TENSOR, &type),
ANEURALNETWORKS_NO_ERROR);
ANeuralNetworksOperandType operandType{
.type = type,
.dimensionCount = dimensionCount,
.dimensions = dimensions,
};
CHECK_EQ(ANeuralNetworksModel_addOperand(model, &operandType), ANEURALNETWORKS_NO_ERROR);
Opcionalmente, use ANeuralNetworksModel_setOperandExtensionData
para associar dados adicionais a um operando de extensão.
ExampleTensorParams params{
.scale = 0.5,
.zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, ¶ms, sizeof(params)),
ANEURALNETWORKS_NO_ERROR);
Para construir um modelo com uma operação de extensão, use ANeuralNetworksModel_getExtensionOperationType
para obter o tipo de operação e chame ANeuralNetworksModel_addOperation
.
ANeuralNetworksOperationType type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperationType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_FUNCTION,
&type),
ANEURALNETWORKS_NO_ERROR);
CHECK_EQ(ANeuralNetworksModel_addOperation(model, type, inputCount, inputs, outputCount, outputs),
ANEURALNETWORKS_NO_ERROR);
Adicionando suporte de extensão a um driver NNAPI
Os drivers relatam extensões com suporte por meio do método IDevice::getSupportedExtensions
. A lista retornada deve conter uma entrada que descreve cada extensão com suporte.
Extension {
.name = EXAMPLE_EXTENSION_NAME,
.operandTypes = {
{
.type = EXAMPLE_SCALAR,
.isTensor = false,
.byteSize = 8,
},
{
.type = EXAMPLE_TENSOR,
.isTensor = true,
.byteSize = 8,
},
},
}
Dos 32 bits usados para identificar tipos e operações, os bits Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX
são o prefixo da extensão e os bits Model::ExtensionTypeEncoding::LOW_BITS_TYPE
representam o tipo ou operação da extensão.
Ao lidar com uma operação ou tipo de operando, o driver deve verificar o prefixo da extensão. Se o prefixo de extensão tiver um valor diferente de zero, a operação ou tipo de operando é um tipo de extensão. Se o valor for 0
, a operação ou tipo de operando não é um tipo de extensão.
Para mapear o prefixo para um nome de extensão, procure em model.extensionNameToPrefix
. O mapeamento do prefixo para o nome da extensão é uma correspondência um-a-um (bijeção) para um determinado modelo. Valores de prefixo diferentes podem corresponder ao mesmo nome de extensão em modelos diferentes.
O driver deve validar operações de extensão e tipos de dados porque o tempo de execução NNAPI não pode validar operações de extensão e tipos de dados específicos.
Os operandos de extensão podem ter dados associados em operand.extraParams.extension
, que o tempo de execução trata como um blob de dados brutos de tamanho arbitrário.
Operação OEM e tipos de dados
A NNAPI tem uma operação e tipos de dados OEM para permitir que os fabricantes de dispositivos forneçam funcionalidades personalizadas e específicas do driver. Esses tipos de operação e dados são usados apenas por aplicativos OEM. A semântica da operação e dos tipos de dados do OEM são específicos do OEM e podem mudar a qualquer momento. A operação OEM e os tipos de dados são codificados usando OperationType::OEM_OPERATION
, OperandType::OEM
e OperandType::TENSOR_OEM_BYTE
.