153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol/* 253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * Copyright (C) 2017 The Android Open Source Project 353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * 453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * Licensed under the Apache License, Version 2.0 (the "License"); 553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * you may not use this file except in compliance with the License. 653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * You may obtain a copy of the License at 753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * 853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * http://www.apache.org/licenses/LICENSE-2.0 953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * 1053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * Unless required by applicable law or agreed to in writing, software 1153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * distributed under the License is distributed on an "AS IS" BASIS, 1253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * See the License for the specific language governing permissions and 1453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * limitations under the License. 1553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol */ 1653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 1753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol#include "chre/platform/android/host_link.h" 1853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 1953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol#include "chre/core/event_loop_manager.h" 2053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol#include "chre/platform/shared/host_protocol_common.h" 2153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol#include "chre/util/macros.h" 2253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol#include "chre_api/chre/version.h" 2353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol#include "chre_host/host_messages_generated.h" 2453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 2553b98346214effa969c45e95ae417c08fe492779Andrew Rossignolnamespace chre { 2653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 273f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol//! Used to pass the client ID through the user data pointer in deferCallback 283f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignolunion HostClientIdCallbackData { 293f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol uint16_t hostClientId; 303f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol void *ptr; 313f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol}; 323f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol 333f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignolstatic_assert(sizeof(uint16_t) <= sizeof(void*), 343f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol "Pointer must at least fit a u16 for passing the host client ID"); 353f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol 3653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol/** 3753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * Assigns a vector the contents of a C-style, null-terminated string. 3853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * 3953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * @param vector The vector to assign with the contents of a string. 4053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * @param str The string to assign. 4153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol */ 4253b98346214effa969c45e95ae417c08fe492779Andrew Rossignolvoid setVectorToString(std::vector<int8_t> *vector, const char *str) { 4353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol *vector = std::vector<int8_t>(str, str + strlen(str)); 4453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol} 4553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 4653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol/** 473f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol * Sends a message to the host given a hostClientId. 483f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol * 493f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol * @param message The message to send to the host. 503f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol * @param hostClientId The host who made the original request for which this is 513f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol * a reply. 523f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol */ 533f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignoltemplate<typename T> 543f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignolvoid sendFlatbufferToHost(T& message, uint16_t hostClientId) { 553f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol static_assert( 563f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol fbs::ChreMessageTraits<typename T::TableType>::enum_value 573f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol != fbs::ChreMessage::NONE, 583f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol "Only works for message types supported by ChreMessageUnion"); 593f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol 603f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol fbs::MessageContainerT container; 613f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol container.message.Set(std::move(message)); 623f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol container.host_addr.reset(new fbs::HostAddress(hostClientId)); 633f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol 643f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol flatbuffers::FlatBufferBuilder builder; 653f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol auto containerOffset = CreateMessageContainer(builder, &container, nullptr); 663f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol builder.Finish(containerOffset); 673f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol 683f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol SocketServerSingleton::get()->sendToClientById( 693f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol builder.GetBufferPointer(), builder.GetSize(), hostClientId); 703f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol} 713f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol 723f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol/** 7353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * Handles a message directed to a nanoapp from the system. 7453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * 7553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * @param message The message to deliver to a nanoapp. 7653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol */ 7753b98346214effa969c45e95ae417c08fe492779Andrew Rossignolvoid handleNanoappMessage(const fbs::NanoappMessageT& message) { 7853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol LOGD("handleNanoappMessage"); 79b10afb27a73ed62371ea56c9cf96f02aa45354a6Andrew Rossignol HostCommsManager& manager = 80b10afb27a73ed62371ea56c9cf96f02aa45354a6Andrew Rossignol EventLoopManagerSingleton::get()->getHostCommsManager(); 81b10afb27a73ed62371ea56c9cf96f02aa45354a6Andrew Rossignol manager.sendMessageToNanoappFromHost( 82b10afb27a73ed62371ea56c9cf96f02aa45354a6Andrew Rossignol message.app_id, message.message_type, message.host_endpoint, 83b10afb27a73ed62371ea56c9cf96f02aa45354a6Andrew Rossignol message.message.data(), message.message.size()); 8453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol} 8553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 8653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol/** 8753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * Handles a request for information about this context hub instance. 8853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * 8953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * @param hostClientId The client ID on the host making the request. 9053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol */ 9153b98346214effa969c45e95ae417c08fe492779Andrew Rossignolvoid handleHubInfoRequest(uint16_t hostClientId) { 9253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol LOGD("handleHubInfoRequest"); 9353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol fbs::HubInfoResponseT response; 9453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol setVectorToString(&response.name, "CHRE on Android"); 9553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol setVectorToString(&response.vendor, "Google"); 9653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol setVectorToString(&response.toolchain, "Android NDK API 26 (clang " 9753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol STRINGIFY(__clang_major__) "." 9853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol STRINGIFY(__clang_minor__) "." 9953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol STRINGIFY(__clang_patchlevel__) ")"); 10053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol response.platform_version = 0; 10153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol response.toolchain_version = ((__clang_major__ & 0xFF) << 24) | 10253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol ((__clang_minor__ & 0xFF) << 16) | 10353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol (__clang_patchlevel__ & 0xFFFF); 10453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol response.peak_mips = 1000; 10553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol response.stopped_power = 1000; 10653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol response.sleep_power = 1000; 10753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol response.peak_power = 10000; 10853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol response.max_msg_len = CHRE_MESSAGE_TO_HOST_MAX_SIZE; 10953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol response.platform_id = chreGetPlatformId(); 11053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol response.chre_platform_version = chreGetVersion(); 11153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 1123f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol sendFlatbufferToHost(response, hostClientId); 1133f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol} 11453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 1153f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignolvoid constructNanoappListCallback(uint16_t /*eventType*/, void *cookie) { 1163f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol HostClientIdCallbackData clientIdCbData; 1173f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol clientIdCbData.ptr = cookie; 11853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 1193f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol auto nanoappAddCallback = [](const Nanoapp *nanoapp, void *data) { 1203f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol auto response = static_cast<fbs::NanoappListResponseT *>(data); 1213f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol auto nanoappListEntry = 1223f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol std::unique_ptr<fbs::NanoappListEntryT>(new fbs::NanoappListEntryT()); 1233f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol nanoappListEntry->app_id = nanoapp->getAppId(); 1243f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol nanoappListEntry->version = nanoapp->getAppVersion(); 1253f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol nanoappListEntry->enabled = true; 1263f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol nanoappListEntry->is_system = nanoapp->isSystemNanoapp(); 1273f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol response->nanoapps.push_back(std::move(nanoappListEntry)); 1283f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol }; 1293f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol 1303f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol fbs::NanoappListResponseT response; 1313f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol EventLoop& eventLoop = EventLoopManagerSingleton::get()->getEventLoop(); 1323f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol eventLoop.forEachNanoapp(nanoappAddCallback, &response); 1333f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol 1343f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol sendFlatbufferToHost(response, clientIdCbData.hostClientId); 13553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol} 13653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 13753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol/** 13853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * Handles a request from the host for a list of nanoapps. 13953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * 14053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * @param hostClientId The client ID on the host making the request. 14153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol */ 14253b98346214effa969c45e95ae417c08fe492779Andrew Rossignolvoid handleNanoappListRequest(uint16_t hostClientId) { 14353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol LOGD("handleNanoappListRequest"); 1443f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol HostClientIdCallbackData cbData = {}; 1453f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol cbData.hostClientId = hostClientId; 1463f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol EventLoopManagerSingleton::get()->deferCallback( 1473f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol SystemCallbackType::NanoappListResponse, cbData.ptr, 1483f346add1e161ee5e1b1f8580a7c024095e867b2Andrew Rossignol constructNanoappListCallback); 14953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol} 15053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 15153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol/** 15253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * Handles a request to load a nanoapp. 15353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * 15453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * @param hostClientId The client ID on the host making the request. 15553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * @param loadRequest The details of the nanoapp load request. 15653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol */ 15753b98346214effa969c45e95ae417c08fe492779Andrew Rossignolvoid handleLoadNanoappRequest( 15853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol uint16_t hostClientId, const fbs::LoadNanoappRequestT& loadRequest) { 15953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol LOGD("handleLoadNanoappRequest"); 16053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol} 16153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 16253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol/** 16353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * Handles a request to unload a nanoapp. 16453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * 16553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * @param hostClientId The client ID on the host making the request. 16653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * @param unloadRequest The details of the nanoapp unload request. 16753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol */ 16853b98346214effa969c45e95ae417c08fe492779Andrew Rossignolvoid handleUnloadNanoappRequest( 16953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol uint16_t hostClientId, const fbs::UnloadNanoappRequestT& unloadRequest) { 17053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol LOGD("handleUnloadNanoappRequest"); 17153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol} 17253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 17353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol/** 17453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * Handles a request for a debug dump. 17553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * 17653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol * @param hostClientId The client OD on the host making the request. 17753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol */ 17853b98346214effa969c45e95ae417c08fe492779Andrew Rossignolvoid handleDebugDumpRequest(uint16_t hostClientId) { 17953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol LOGD("handleDebugDumpRequest"); 18053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol} 18153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 18253b98346214effa969c45e95ae417c08fe492779Andrew Rossignolbool handleMessageFromHost(void *message, size_t length) { 18353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol bool success = HostProtocolCommon::verifyMessage(message, length); 18453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol if (success) { 18553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol fbs::MessageContainerT container; 18653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol fbs::GetMessageContainer(message)->UnPackTo(&container); 18753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol uint16_t hostClientId = container.host_addr->client_id(); 18853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol switch (container.message.type) { 18953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol case fbs::ChreMessage::NanoappMessage: 19053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol handleNanoappMessage(*container.message.AsNanoappMessage()); 19153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol break; 19253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 19353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol case fbs::ChreMessage::HubInfoRequest: 19453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol handleHubInfoRequest(hostClientId); 19553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol break; 19653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 19753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol case fbs::ChreMessage::NanoappListRequest: 19853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol handleNanoappListRequest(hostClientId); 19953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol break; 20053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 20153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol case fbs::ChreMessage::LoadNanoappRequest: 20253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol handleLoadNanoappRequest(hostClientId, 20353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol *container.message.AsLoadNanoappRequest()); 20453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol break; 20553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 20653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol case fbs::ChreMessage::UnloadNanoappRequest: 20753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol handleUnloadNanoappRequest(hostClientId, 20853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol *container.message.AsUnloadNanoappRequest()); 20953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol break; 21053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 21153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol case fbs::ChreMessage::DebugDumpRequest: 21253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol handleDebugDumpRequest(hostClientId); 21353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol break; 21453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 21553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol default: 21653b98346214effa969c45e95ae417c08fe492779Andrew Rossignol LOGW("Got invalid/unexpected message type %" PRIu8, 21753b98346214effa969c45e95ae417c08fe492779Andrew Rossignol static_cast<uint8_t>(container.message.type)); 21853b98346214effa969c45e95ae417c08fe492779Andrew Rossignol success = false; 21953b98346214effa969c45e95ae417c08fe492779Andrew Rossignol } 22053b98346214effa969c45e95ae417c08fe492779Andrew Rossignol } 22153b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 22253b98346214effa969c45e95ae417c08fe492779Andrew Rossignol return success; 22353b98346214effa969c45e95ae417c08fe492779Andrew Rossignol} 22453b98346214effa969c45e95ae417c08fe492779Andrew Rossignol 22553b98346214effa969c45e95ae417c08fe492779Andrew Rossignol} // namespace chre 226