You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
7.1 KiB

# Vendor Extensions
Neural Networks API (NNAPI) vendor extensions, introduced in Android 10, are
collections of vendor-defined operations and data types. On devices running NN
HAL 1.2 or higher, drivers can provide custom hardware-accelerated operations by
supporting corresponding vendor extensions. Vendor extensions don't modify the
behavior of existing operations.
Vendor extensions provide a more structured alternative to OEM operation and
data types, which were deprecated in Android 10.
For more information, see the last section.
## Extensions usage allowlist
Vendor extensions can only be used by explicitly specified Android apps and
native binaries on the `/product`, `/vendor`, `/odm`, and `/data` partitions.
Apps and native binaries located on the `/system` partition can't use vendor
extensions.
A list of Android apps and binaries permitted to use NNAPI vendor extensions is
stored in `/vendor/etc/nnapi_extensions_app_allowlist`. Each line of the file
contains a new entry. An entry can be a native binary path that is prefixed with
a slash (/), for example, `/data/foo`, or a name of an Android app package, for
example, `com.foo.bar`.
The allowlist is enforced from the NNAPI runtime shared library. This library
protects against accidental usage but not against deliberate circumvention by
an app directly using the NNAPI driver HAL interface.
## Vendor extension definition
The vendor creates and maintains a header file with the extension definition. A
complete example of an extension definition can be found in
`example/fibonacci/FibonacciExtension.h`.
Each extension must have a unique name that starts with the reverse domain name
of the vendor.
Note: Names of extension operation and operand types must be qualified with a
vendor name. The vendor name is represented by `EXAMPLE` in the code samples on
this page.
```
const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";
```
The name acts as a namespace for operations and data types. The NNAPI uses this
name to distinguish between vendor extensions.
Operations and data types are declared in a way similar to those in
`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,
};
```
An extension operation can use any operand type, including nonextension operand
types and operand types from other extensions. When using an operand type from
another extension, the driver must support the other extension.
Extensions can also declare custom structures to accompany extension operands.
```
/**
* Quantization parameters for {@link EXAMPLE_TENSOR}.
*/
typedef struct ExampleTensorParams {
double scale;
int64_t zeroPoint;
} ExampleTensorParams;
```
## Using extensions in NNAPI clients
The `runtime/include/NeuralNetworksExtensions.h` (C API) file provides runtime
extension support. This section provides an overview of the C API.
To check whether a device supports an extension, use
`ANeuralNetworksDevice_getExtensionSupport`.
```
bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME, &isExtensionSupported),
ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
// The device supports the extension.
...
}
```
To build a model with an extension operand, use `ANeuralNetworksModel_getExtensionOperandType`
to obtain the operand type and call `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);
```
Optionally, use
`ANeuralNetworksModel_setOperandExtensionData`
to associate additional data with an extension operand.
```
ExampleTensorParams params{
.scale = 0.5,
.zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, &params, sizeof(params)),
ANEURALNETWORKS_NO_ERROR);
```
To build a model with an extension operation, use
`ANeuralNetworksModel_getExtensionOperationType`
to obtain the operation type and call
`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);
```
## Adding extension support to an NNAPI driver
Drivers report supported extensions through the `IDevice::getSupportedExtensions`
method. The returned list must contain an entry describing each supported
extension.
```
Extension {
.name = EXAMPLE_EXTENSION_NAME,
.operandTypes = {
{
.type = EXAMPLE_SCALAR,
.isTensor = false,
.byteSize = 8,
},
{
.type = EXAMPLE_TENSOR,
.isTensor = true,
.byteSize = 8,
},
},
}
```
Of the 32 bits used to identify types and operations, the high
`ExtensionTypeEncoding::HIGH_BITS_PREFIX` bits is the extension
_prefix_ and the low `ExtensionTypeEncoding::LOW_BITS_TYPE`
bits represents the type or operation of the extension.
When handling an operation or operand type, the driver must check the extension
prefix. If the extension prefix has a nonzero value, the operation or operand
type is an extension type. If the value is `0`, the operation or operand type
isn't an extension type.
To map the prefix to an extension name, look it up in `model.extensionNameToPrefix`.
The mapping from the prefix to the extension name is a one-to-one correspondence
(bijection) for a given model. Different prefix values might correspond to the
same extension name in different models.
The driver must validate extension operations and data types because the NNAPI
runtime can't validate particular extension operations and data types.
Extension operands can have associated data in `operand.extraParams.extension`,
which the runtime treats as a raw data blob of arbitrary size.
## OEM operation and data types
Note: OEM operation and data types have been deprecated. For devices running
Android 10 or higher, use vendor extensions instead.
NNAPI has an OEM operation and OEM data types to allow
device manufacturers to provide custom, driver-specific functionality. These
operation and data types are only used by OEM applications. The semantics of OEM
operation and data types are OEM-specific and can change at any time. The OEM
operation and data types are encoded using `OperationType::OEM_OPERATION`,
`OperandType::OEM`, and `OperandType::TENSOR_OEM_BYTE`.