ExecutionBuilder.cpp revision 105807d963d969197fe78185ed588bfad3dc0ea5
1707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet/*
2707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet * Copyright (C) 2017 The Android Open Source Project
3707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet *
4707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet * Licensed under the Apache License, Version 2.0 (the "License");
5707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet * you may not use this file except in compliance with the License.
6707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet * You may obtain a copy of the License at
7707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet *
8707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet *      http://www.apache.org/licenses/LICENSE-2.0
9707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet *
10707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet * Unless required by applicable law or agreed to in writing, software
11707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet * distributed under the License is distributed on an "AS IS" BASIS,
12707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet * See the License for the specific language governing permissions and
14707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet * limitations under the License.
15707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet */
16707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
17707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet#define LOG_TAG "RequestBuilder"
18707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
19707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet#include "RequestBuilder.h"
20707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
21707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet#include "CpuExecutor.h"
22707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet#include "HalInterfaces.h"
23707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet#include "Manager.h"
24707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet#include "ModelBuilder.h"
25707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
26689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler#include <mutex>
27389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet#include <thread>
28689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler#include <vector>
29689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler
30707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouilletnamespace android {
31707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouilletnamespace nn {
32707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
33389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouilletint ModelArgumentInfo::setFromPointer(const Operand& operand,
34389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet                                      const ANeuralNetworksOperandType* type, void* data,
35389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet                                      uint32_t length) {
36389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    int n = updateDimensionInfo(operand, type);
37389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    if (n != ANEURALNETWORKS_NO_ERROR) {
38389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        return n;
39389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    }
40389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    state = ModelArgumentInfo::POINTER;
41389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    locationAndDimension.location = {.poolIndex = RUN_TIME, .offset = 0, .length = length};
42389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    buffer = data;
43389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    return ANEURALNETWORKS_NO_ERROR;
44389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet}
45389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet
46389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouilletint ModelArgumentInfo::setFromMemory(const Operand& operand, const ANeuralNetworksOperandType* type,
47389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet                                     uint32_t poolIndex, uint32_t offset, uint32_t length) {
48389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    int n = updateDimensionInfo(operand, type);
49389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    if (n != ANEURALNETWORKS_NO_ERROR) {
50389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        return n;
51389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    }
52389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    state = ModelArgumentInfo::MEMORY;
53389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    locationAndDimension.location = {.poolIndex = poolIndex, .offset = offset, .length = length};
54389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    buffer = nullptr;
55389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    return ANEURALNETWORKS_NO_ERROR;
56389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet}
57389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet
58389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouilletint ModelArgumentInfo::updateDimensionInfo(const Operand& operand,
59389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet                                           const ANeuralNetworksOperandType* newType) {
60389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    if (newType == nullptr) {
61389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        locationAndDimension.dimensions = hidl_vec<uint32_t>();
62389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    } else {
63389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        uint32_t count = newType->dimensions.count;
64389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        if (static_cast<OperandType>(newType->type) != operand.type ||
65389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet            count != operand.dimensions.size()) {
66389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet            LOG(ERROR) << "ANeuralNetworksRequest_setInput/Output incompatible types";
67389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet            return ANEURALNETWORKS_BAD_DATA;
68389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        }
69389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        for (uint32_t i = 0; i < count; i++) {
70389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet            locationAndDimension.dimensions[i] = newType->dimensions.data[i];
71389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        }
72389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    }
73389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    return ANEURALNETWORKS_NO_ERROR;
74389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet}
75389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet
768b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc BrouilletRequestBuilder::RequestBuilder(const ModelBuilder* model)
77389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    : mModel(model),
78389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet      mInputs(model->inputCount()),
79389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet      mOutputs(model->outputCount()),
80389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet      mMemories(model->getMemories()) {
81707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    LOG(DEBUG) << "RequestBuilder::RequestBuilder";
828b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    for (auto& p : mInputs) {
83389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        p.state = ModelArgumentInfo::UNSPECIFIED;
848b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    }
858b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    for (auto& p : mOutputs) {
86389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        p.state = ModelArgumentInfo::UNSPECIFIED;
878b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    }
88707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet}
89707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
90707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouilletint RequestBuilder::setInput(uint32_t index, const ANeuralNetworksOperandType* type,
91707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet                             const void* buffer, uint32_t length) {
92707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    uint32_t count = static_cast<uint32_t>(mInputs.size());
93707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    if (index >= count) {
94707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        LOG(ERROR) << "ANeuralNetworksRequest_setInput bad index " << index << " " << count;
95707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        return ANEURALNETWORKS_BAD_DATA;
96707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    }
97389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    return mInputs[index].setFromPointer(mModel->getInputOperand(index), type,
98389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet                                         const_cast<void*>(buffer), length);
99707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet}
100707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
1018b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouilletint RequestBuilder::setInputFromMemory(uint32_t index, const ANeuralNetworksOperandType* type,
1028b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet                                       const Memory* memory, uint32_t offset, uint32_t length) {
1038b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    uint32_t count = static_cast<uint32_t>(mInputs.size());
1048b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    if (index >= count) {
1058b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        LOG(ERROR) << "ANeuralNetworksRequest_setInputFromMemory bad index " << index << " "
1068b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet                   << count;
1078b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        return ANEURALNETWORKS_BAD_DATA;
1088b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    }
109105807d963d969197fe78185ed588bfad3dc0ea5Miao Wang    if (!memory->validateSize(offset, length)) {
110105807d963d969197fe78185ed588bfad3dc0ea5Miao Wang        return ANEURALNETWORKS_BAD_DATA;
111105807d963d969197fe78185ed588bfad3dc0ea5Miao Wang    }
112389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    uint32_t poolIndex = mMemories.add(memory);
113389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    return mInputs[index].setFromMemory(mModel->getInputOperand(index), type, poolIndex, offset,
114389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet                                        length);
115707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet}
116707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
117707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouilletint RequestBuilder::setOutput(uint32_t index, const ANeuralNetworksOperandType* type, void* buffer,
118707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet                              uint32_t length) {
119707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    uint32_t count = static_cast<uint32_t>(mOutputs.size());
120707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    if (index >= count) {
121707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        LOG(ERROR) << "ANeuralNetworksRequest_setOutput bad index " << index << " " << count;
122707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        return ANEURALNETWORKS_BAD_DATA;
123707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    }
124389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    return mOutputs[index].setFromPointer(mModel->getOutputOperand(index), type, buffer, length);
125707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet}
126707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
1278b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouilletint RequestBuilder::setOutputFromMemory(uint32_t index, const ANeuralNetworksOperandType* type,
1288b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet                                        const Memory* memory, uint32_t offset, uint32_t length) {
1298b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    uint32_t count = static_cast<uint32_t>(mOutputs.size());
1308b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    if (index >= count) {
1318b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        LOG(ERROR) << "ANeuralNetworksRequest_setOutputFromMemory bad index " << index << " "
1328b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet                   << count;
1338b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        return ANEURALNETWORKS_BAD_DATA;
1348b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    }
135105807d963d969197fe78185ed588bfad3dc0ea5Miao Wang    if (!memory->validateSize(offset, length)) {
136105807d963d969197fe78185ed588bfad3dc0ea5Miao Wang        return ANEURALNETWORKS_BAD_DATA;
137105807d963d969197fe78185ed588bfad3dc0ea5Miao Wang    }
138389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    uint32_t poolIndex = mMemories.add(memory);
139389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet    return mOutputs[index].setFromMemory(mModel->getOutputOperand(index), type, poolIndex, offset,
140389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet                                         length);
141707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet}
142707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
143689d892203c06c66c7bb2e374462a8434e40b75fMichael Butlerint RequestBuilder::startCompute(sp<Event>* event) {
144689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    *event = nullptr;
145689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler
146707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    // TODO validate that we have full types for all inputs and outputs,
147707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    // that the graph is not cyclic,
148f1817c663af4f22bc089ef82cd50df4186422c42Yang Ni    /*
149f1817c663af4f22bc089ef82cd50df4186422c42Yang Ni       TODO: For non-optional inputs, also verify that buffers are not null.
150f1817c663af4f22bc089ef82cd50df4186422c42Yang Ni
1518b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    for (auto& p : mInputs) {
152389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        if (p.state == ModelArgumentInfo::UNSPECIFIED) {
153707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet            LOG(ERROR) << "ANeuralNetworksRequest_startCompute not all inputs specified";
154707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet            return ANEURALNETWORKS_BAD_DATA;
155707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        }
156707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    }
157f1817c663af4f22bc089ef82cd50df4186422c42Yang Ni    */
1588b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    for (auto& p : mOutputs) {
159389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        if (p.state == ModelArgumentInfo::UNSPECIFIED) {
160707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet            LOG(ERROR) << "ANeuralNetworksRequest_startCompute not all outputs specified";
161707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet            return ANEURALNETWORKS_BAD_DATA;
162707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        }
163707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    }
164707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    LOG(DEBUG) << "RequestBuilder::startCompute";
165707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
166707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    std::shared_ptr<Device> device = DeviceManager::get()->getAvailableDriver();
167707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    Model model;
168707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    mModel->setHidlModel(&model);
169707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
170689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    return device == nullptr ? startComputeOnCpu(model, event)
171707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet                             : startComputeOnDevice(device->getInterface(), model, event);
172707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet}
173707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
174707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet// Figures out how to place each of the input or outputs in a buffer. This just does the layout,
1758b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet// it does not copy data.  Aligns each input a bit.
1768b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouilletint RequestBuilder::allocatePointerArgumentsToPool(std::vector<ModelArgumentInfo>* args,
1778b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet                                                   Memory* memory) {
1788b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    uint32_t nextPoolIndex = mMemories.size();
179707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    int64_t total = 0;
1808b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    for (auto& info : *args) {
1818b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        if (info.state == ModelArgumentInfo::POINTER) {
1828b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            DataLocation& loc = info.locationAndDimension.location;
1838b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            // TODO Good enough alignment?
1848b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            total += alignBytesNeeded(static_cast<uint32_t>(total), loc.length);
1858b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            loc.poolIndex = nextPoolIndex;
1868b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            loc.offset = static_cast<uint32_t>(total);
1878b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            total += loc.length;
1888b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        }
1898b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    };
190707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    if (total > 0xFFFFFFFF) {
191707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        LOG(ERROR) << "ANeuralNetworksRequest_startCompute Size of all inputs or outputs exceeds "
192707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet                      "2^32.";
193707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        return ANEURALNETWORKS_BAD_DATA;
194707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    }
1958b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    hidl_memory hidlMemory;
1968b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    if (total > 0) {
197389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet        memory->create(total);  // TODO check error
1988b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        mMemories.add(memory);
1998b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    }
200707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    return ANEURALNETWORKS_NO_ERROR;
201707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet}
202707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
2038b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouilletstatic void copyLocationAndDimension(const std::vector<ModelArgumentInfo>& argumentInfos,
2048b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet                                     hidl_vec<InputOutputInfo>* ioInfos) {
2058b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    size_t count = argumentInfos.size();
2068b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    ioInfos->resize(count);
2078b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    for (size_t i = 0; i < count; i++) {
2088b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        (*ioInfos)[i] = argumentInfos[i].locationAndDimension;
2098b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    }
2108b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet}
2118b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet
212389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouilletint RequestBuilder::startComputeOnDevice(sp<IDevice> driver, const Model& model, sp<Event>* event) {
213689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    *event = nullptr;
214689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler
2158b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    LOG(DEBUG) << "RequestBuilder::startComputeOnDevice1";
216707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    // TODO Dangerous!  In async, the model will outlive it here. Safe for now
217707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    sp<IPreparedModel> preparedModel = driver->prepareModel(model);
218707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    if (preparedModel == nullptr) {
219707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        return ANEURALNETWORKS_OP_FAILED;
220707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    }
221707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
2228b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    // Layout the input and output data
2238b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    int n = allocatePointerArgumentsToPool(&mInputs, &mInputPointerArguments);
224707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    if (n != ANEURALNETWORKS_NO_ERROR) {
225707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        return n;
226707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    }
2278b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    n = allocatePointerArgumentsToPool(&mOutputs, &mOutputPointerArguments);
228707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    if (n != ANEURALNETWORKS_NO_ERROR) {
229707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        return n;
230707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    }
231707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
2328b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    // Copy the input data that was specified via a pointer.
2338b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    // mInputPointerArguments.update();
2348b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    for (auto& info : mInputs) {
2358b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        if (info.state == ModelArgumentInfo::POINTER) {
2368b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            DataLocation& loc = info.locationAndDimension.location;
2372150f1d186b2854fb5aa609594be12a667f845f0Jean-Luc Brouillet            uint8_t* data = nullptr;
2382150f1d186b2854fb5aa609594be12a667f845f0Jean-Luc Brouillet            int n = mInputPointerArguments.getPointer(&data);
2392150f1d186b2854fb5aa609594be12a667f845f0Jean-Luc Brouillet            if (n != ANEURALNETWORKS_NO_ERROR) {
2402150f1d186b2854fb5aa609594be12a667f845f0Jean-Luc Brouillet                return n;
2412150f1d186b2854fb5aa609594be12a667f845f0Jean-Luc Brouillet            }
2428b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            memcpy(data + loc.offset, info.buffer, loc.length);
2438b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        }
244707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    }
2458b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    // TODO: Add mInputPointerArguments.commit() and .update() at all the right places
246707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
247707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    Request request;
2488b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    copyLocationAndDimension(mInputs, &request.inputs);
2498b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    copyLocationAndDimension(mOutputs, &request.outputs);
2508b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    uint32_t count = mMemories.size();
2518b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    request.pools.resize(count);
2528b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    for (uint32_t i = 0; i < count; i++) {
2538b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        request.pools[i] = mMemories[i]->getHidlMemory();
2548b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    }
255707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
256689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // Prepare the event for asynchronous execution. The sp<Event> object is
257689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // returned when the request has been successfully launched, otherwise a
258689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // nullptr is returned. The sp is used for ref-counting purposes. Without
259689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // it, the HIDL service could attempt to communicate with a dead event
260689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // object.
261689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    //
262689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // TODO: Explain the "dead event" problem further, either here or
263689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // in the design document.
264689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    sp<Event> eventSp = new Event();
265689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler
2668b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    LOG(DEBUG) << "Before preparedModel->execute() " << toString(request);
267707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    // Execute the request.
268689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // TODO: What happens to the Event if the service dies abnormally
269689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // -- won't that keep the Event live forever, because the service
270689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // never has the opportunity to bump the reference count down? Or
271689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // maybe the HIDL infrastructure handles this magically? At worst,
272689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // it seems like this is a small memory leak, if the Event stays
273689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // alive forever.
274689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    if (!preparedModel->execute(request, eventSp)) {
275707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        LOG(DEBUG) << "**Execute failed**";
276707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet        return ANEURALNETWORKS_OP_FAILED;
277707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    }
278707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
279689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // TODO: Remove this synchronization point when the block of code below is
280689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // removed.
281689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    Event::Status status = eventSp->wait();
282689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    if (status != Event::Status::SUCCESS) {
283689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler        LOG(DEBUG) << "**Execute async failed**";
284689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler        return ANEURALNETWORKS_OP_FAILED;
285689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    }
286689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler
287707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    // Copy the output data from shared memory to the output buffers.
288689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // TODO: Move this block of code somewhere else. It should not be in the
289689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // startCompute function.
290689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // TODO: outputMemory->update(); outputMemory->commit()
2918b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    for (auto& info : mOutputs) {
2928b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        if (info.state == ModelArgumentInfo::POINTER) {
2938b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            DataLocation& loc = info.locationAndDimension.location;
2942150f1d186b2854fb5aa609594be12a667f845f0Jean-Luc Brouillet            uint8_t* data = nullptr;
2952150f1d186b2854fb5aa609594be12a667f845f0Jean-Luc Brouillet            int n = mOutputPointerArguments.getPointer(&data);
2962150f1d186b2854fb5aa609594be12a667f845f0Jean-Luc Brouillet            if (n != ANEURALNETWORKS_NO_ERROR) {
2972150f1d186b2854fb5aa609594be12a667f845f0Jean-Luc Brouillet                return n;
2982150f1d186b2854fb5aa609594be12a667f845f0Jean-Luc Brouillet            }
2998b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            memcpy(info.buffer, data + loc.offset, loc.length);
3008b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        }
301707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    }
302707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    LOG(DEBUG) << "RequestBuilder::startComputeOnDevice completed";
303707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
304689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    *event = eventSp;
305707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    return ANEURALNETWORKS_NO_ERROR;
306707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet}
307707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
308689d892203c06c66c7bb2e374462a8434e40b75fMichael Butlerstatic void asyncStartComputeOnCpu(const Model& model, const Request& request,
309689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler                                   const std::vector<RunTimePoolInfo>& runTimePoolInfos,
310689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler                                   const sp<IEvent>& event) {
311689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    CpuExecutor executor;
312689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    int err = executor.run(model, request, runTimePoolInfos);
313689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    Status executionStatus = err == ANEURALNETWORKS_NO_ERROR ? Status::SUCCESS : Status::ERROR;
314689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    event->notify(executionStatus);
315689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler}
316689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler
317689d892203c06c66c7bb2e374462a8434e40b75fMichael Butlerint RequestBuilder::startComputeOnCpu([[maybe_unused]] const Model& model, sp<Event>* event) {
318707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    // TODO: use a thread pool
319689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler
320689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // Prepare the event for asynchronous execution. The sp<Event> object is
321689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // returned when the request has been successfully launched, otherwise a
322689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // nullptr is returned.
323689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    sp<Event> eventSp = new Event();
324689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    *event = nullptr;
325707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
326707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet    std::vector<RunTimePoolInfo> runTimePoolInfos;
3278b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    uint32_t count = mMemories.size();
3288b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    runTimePoolInfos.resize(count);
3298b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    for (uint32_t i = 0; i < count; i++) {
3308b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        const Memory* mem = mMemories[i];
3318b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        runTimePoolInfos[i].set(mem->getHidlMemory());
3328b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    }
3338b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    // Create as many pools as there are input / output.
3348b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    auto fixPointerArguments = [&runTimePoolInfos](std::vector<ModelArgumentInfo>& argumentInfos) {
3358b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        for (ModelArgumentInfo& argumentInfo : argumentInfos) {
3368b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            if (argumentInfo.state == ModelArgumentInfo::POINTER) {
337389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet                RunTimePoolInfo runTimeInfo = {
338389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet                            .buffer = static_cast<uint8_t*>(argumentInfo.buffer)};
3398b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet                argumentInfo.locationAndDimension.location.poolIndex =
340389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet                            static_cast<uint32_t>(runTimePoolInfos.size());
3418b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet                argumentInfo.locationAndDimension.location.offset = 0;
3428b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet                runTimePoolInfos.push_back(runTimeInfo);
3438b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet            }
3448b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet        }
3458b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    };
3468b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    fixPointerArguments(mInputs);
3478b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    fixPointerArguments(mOutputs);
348707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
3498b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    Request request;
3508b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    copyLocationAndDimension(mInputs, &request.inputs);
3518b99bb1d98a42b67ba1c00e12c7abb3708cf7c05Jean-Luc Brouillet    copyLocationAndDimension(mOutputs, &request.outputs);
352707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
353689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    // TODO: should model be moved with a std::cref?
354689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    std::thread thread(asyncStartComputeOnCpu, model, std::move(request),
355689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler                       std::move(runTimePoolInfos), eventSp);
356689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    eventSp->bind_thread(std::move(thread));
357689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler
358689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    *event = eventSp;
359689d892203c06c66c7bb2e374462a8434e40b75fMichael Butler    return ANEURALNETWORKS_NO_ERROR;
360707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet}
361707dbd2d55f5dacf78ffb3ad7c8b3f37c2e9d758Jean-Luc Brouillet
362389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet}  // namespace nn
363389f26c7c442c37994db9f43d013fe3953c9353cJean-Luc Brouillet}  // namespace android
364