As extensões de fornecedor da API Neural Networks (NNAPI), introduzidas no Android 10, são coleções de operações e tipos de dados definidos pelo fornecedor. Em dispositivos com a NN HAL 1.2 ou mais recente, os drivers podem fornecer operações personalizadas aceleradas por hardware, oferecendo suporte às extensões de fornecedor correspondentes. As extensões de fornecedor não modificam o comportamento das operações atuais.
As extensões de fornecedor oferecem uma alternativa mais estruturada à operação de OEM e aos tipos de dados, que foram descontinuados no Android 10. Para mais informações, consulte OEM e tipos de dados.
Lista de permissões de uso de extensões
As extensões do fornecedor só podem ser usadas por apps Android especificados explicitamente e
binários nativos nas partições /product
, /vendor
, /odm
e /data
.
Apps e binários nativos localizados na partição /system
não podem usar extensões de
fornecedor.
Uma lista de apps e binários do Android permitidos a usar extensões de fornecedor da 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 com prefixo de
uma barra (/), por exemplo, /data/foo
, ou um nome de um pacote de app Android, por
exemplo, com.foo.bar
.
A lista de permissões é aplicada na biblioteca compartilhada do ambiente de execução da NNAPI. Essa biblioteca protege contra o uso acidental, mas não contra a evasão deliberada por um app que usa diretamente a interface HAL do driver da NNAPI.
Definição da extensão do fornecedor
O fornecedor cria e mantém um arquivo principal com a definição da extensão. Um
exemplo completo de uma definição de extensão pode ser encontrado em
example/fibonacci/FibonacciExtension.h
.
Cada extensão precisa ter um nome exclusivo que comece com o nome de domínio inverso do fornecedor.
const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";
O nome atua como um namespace para operações e tipos de dados. A NNAPI usa esse nome para distinguir entre extensões do fornecedor.
As operações e os tipos de dados são declarados de maneira semelhante aos de
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 sem extensão e tipos de operando de outras extensões. Ao usar um tipo de operando de outra extensão, o driver precisa oferecer suporte a ela.
As extensões também podem declarar estruturas personalizadas para acompanhar os operandos de extensão.
/**
* Quantization parameters for {@link EXAMPLE_TENSOR}.
*/
typedef struct ExampleTensorParams {
double scale;
int64_t zeroPoint;
} ExampleTensorParams;
Usar extensões em clientes NNAPI
O arquivo
runtime/include/NeuralNetworksExtensions.h
(API C) oferece suporte à extensão de ambiente 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 criar um modelo com um operando de extensão, use
ANeuralNetworksModel_getExtensionOperandType
para acessar o tipo de operando e chamar
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 outros dados 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 criar um modelo com uma operação de extensão, use
ANeuralNetworksModel_getExtensionOperationType
para saber o tipo de operação e chamar
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);
Adicionar suporte de extensão a um driver de NNAPI
Os drivers informam as extensões compatíveis pelo
método
IDevice::getSupportedExtensions
. A lista retornada precisa conter uma entrada que descreva cada extensão
compatível.
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
altos são o prefixo
da extensão, e os bits
Model::ExtensionTypeEncoding::LOW_BITS_TYPE
baixos representam o tipo ou a operação
da extensão.
Ao processar um tipo de operação ou operando, o driver precisa verificar o prefixo
da extensão. Se o prefixo de extensão tiver um valor diferente de zero, a operação ou o tipo de operando
será um tipo de extensão. Se o valor for 0
, o tipo de operação ou de operando
não é um tipo de extensão.
Para mapear o prefixo para um nome de extensão, pesquise-o 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 precisa validar operações de extensão e tipos de dados porque o ambiente de execução da 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 ambiente de execução trata como um blob de dados brutos de tamanho arbitrário.
Operação do OEM e tipos de dados
A NNAPI tem uma operação de OEM e tipos de dados de OEM para permitir
que os fabricantes de dispositivos forneçam funcionalidades personalizadas e específicas ao driver. Esses
tipos de operação e de dados são usados apenas por aplicativos OEM. A semântica da operação
e dos tipos de dados do OEM são específicas do OEM e podem mudar a qualquer momento. A operação
e os tipos de dados do OEM são codificados usando OperationType::OEM_OPERATION
,
OperandType::OEM
e OperandType::TENSOR_OEM_BYTE
.