blob: 289bce387accb179c50077c820036823cf8fa806 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "chpp/app.h"
#include "chpp/pal_api.h"
#include "chpp/services.h"
#include "chpp/clients/discovery.h"
#include "chpp/services/discovery.h"
#include "chpp/services/loopback.h"
#include "chpp/services/nonhandle.h"
/************************************************
* Prototypes
***********************************************/
static bool chppProcessPredefinedClientRequest(struct ChppAppState *context,
uint8_t *buf, size_t len);
static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context,
uint8_t *buf, size_t len);
static bool chppProcessPredefinedClientNotification(
struct ChppAppState *context, uint8_t *buf, size_t len);
static bool chppProcessPredefinedServiceNotification(
struct ChppAppState *context, uint8_t *buf, size_t len);
static bool chppDatagramLenIsOk(struct ChppAppState *context, uint8_t handle,
size_t len);
ChppDispatchFunction *chppGetDispatchFunction(struct ChppAppState *context,
uint8_t handle,
enum ChppMessageType type);
static inline const struct ChppService *chppServiceOfHandle(
struct ChppAppState *appContext, uint8_t handle);
static inline const struct ChppClient *chppClientOfHandle(
struct ChppAppState *appContext, uint8_t handle);
static inline void *chppServiceContextOfHandle(struct ChppAppState *appContext,
uint8_t handle);
static inline void *chppClientContextOfHandle(struct ChppAppState *appContext,
uint8_t handle);
static void *chppClientServiceContextOfHandle(struct ChppAppState *appContext,
uint8_t handle,
enum ChppMessageType type);
/************************************************
* Private Functions
***********************************************/
/**
* Processes a client request that is determined to be for a predefined CHPP
* service.
*
* @param context Maintains status for each app layer instance.
* @param buf Input data. Cannot be null.
* @param len Length of input data in bytes.
*
* @return False if handle is invalid. True otherwise
*/
static bool chppProcessPredefinedClientRequest(struct ChppAppState *context,
uint8_t *buf, size_t len) {
struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
bool handleValid = true;
bool dispatchResult = true;
switch (rxHeader->handle) {
case CHPP_HANDLE_LOOPBACK: {
dispatchResult = chppDispatchLoopbackClientRequest(context, buf, len);
break;
}
case CHPP_HANDLE_DISCOVERY: {
dispatchResult = chppDispatchDiscoveryClientRequest(context, buf, len);
break;
}
default: {
handleValid = false;
}
}
if (dispatchResult == false) {
LOGE("Handle = %" PRIu8
" received unknown client request command = %#x, transaction ID = "
"%" PRIu8,
rxHeader->handle, rxHeader->command, rxHeader->transaction);
}
return handleValid;
}
/**
* Processes a service response that is determined to be for a predefined CHPP
* client.
*
* @param context Maintains status for each app layer instance.
* @param buf Input data. Cannot be null.
* @param len Length of input data in bytes.
*
* @return False if handle is invalid. True otherwise
*/
static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context,
uint8_t *buf, size_t len) {
struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
bool handleValid = true;
bool dispatchResult = true;
switch (rxHeader->handle) {
case CHPP_HANDLE_LOOPBACK: {
// TODO
break;
}
case CHPP_HANDLE_DISCOVERY: {
dispatchResult = chppDispatchDiscoveryServiceResponse(context, buf, len);
break;
}
default: {
handleValid = false;
}
}
if (dispatchResult == false) {
LOGE("Handle = %" PRIu8
" received unknown server response command = %#x, transaction ID = "
"%" PRIu8,
rxHeader->handle, rxHeader->command, rxHeader->transaction);
}
return handleValid;
}
/**
* Processes a client notification that is determined to be for a predefined
* CHPP service.
*
* @param context Maintains status for each app layer instance.
* @param buf Input data. Cannot be null.
* @param len Length of input data in bytes.
*
* @return False if handle is invalid. True otherwise
*/
static bool chppProcessPredefinedClientNotification(
struct ChppAppState *context, uint8_t *buf, size_t len) {
struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
bool handleValid = true;
bool dispatchResult = true;
// No predefined services support these yet
handleValid = false;
UNUSED_VAR(context);
UNUSED_VAR(len);
UNUSED_VAR(rxHeader);
UNUSED_VAR(dispatchResult);
return handleValid;
}
/**
* Processes a service notification that is determined to be for a predefined
* CHPP client.
*
* @param context Maintains status for each app layer instance.
* @param buf Input data. Cannot be null.
* @param len Length of input data in bytes.
*
* @return False if handle is invalid. True otherwise
*/
static bool chppProcessPredefinedServiceNotification(
struct ChppAppState *context, uint8_t *buf, size_t len) {
struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
bool handleValid = true;
bool dispatchResult = true;
// No predefined clients support these yet
handleValid = false;
UNUSED_VAR(context);
UNUSED_VAR(len);
UNUSED_VAR(rxHeader);
UNUSED_VAR(dispatchResult);
return handleValid;
}
/**
* Verifies if the length of a Rx Datagram from the transport layer is
* sufficient for the associated service.
*
* @param context Maintains status for each app layer instance.
* @param handle Handle number for the service.
* @param len Length of the datagram in bytes.
*
* @return true if length is ok.
*/
static bool chppDatagramLenIsOk(struct ChppAppState *context, uint8_t handle,
size_t len) {
size_t minLen = SIZE_MAX;
if (handle < CHPP_HANDLE_NEGOTIATED_RANGE_START) {
// Predefined services
switch (handle) {
case CHPP_HANDLE_NONE:
minLen = sizeof_member(struct ChppAppHeader, handle);
break;
case CHPP_HANDLE_LOOPBACK:
minLen = sizeof_member(struct ChppAppHeader, handle) +
sizeof_member(struct ChppAppHeader, type);
break;
case CHPP_HANDLE_DISCOVERY:
minLen = sizeof(struct ChppAppHeader);
break;
default:
LOGE("Invalid predefined handle %" PRIu8, handle);
}
} else {
// Negotiated services
minLen = chppServiceOfHandle(context, handle)->minLength;
}
if (len < minLen) {
LOGE("Received datagram too short for handle=%" PRIu8 ", len=%zu", handle,
len);
}
return (len >= minLen);
}
/**
* Returns the dispatch function of a particular negotiated client/service
* handle and message type. This shall be null if it is unsupported by the
* service.
*
* @param context Maintains status for each app layer instance.
* @param handle Handle number for the client/service.
* @param type Message type
*
* @return Pointer to a function that dispatches incoming datagrams for any
* particular client/service.
*/
ChppDispatchFunction *chppGetDispatchFunction(struct ChppAppState *context,
uint8_t handle,
enum ChppMessageType type) {
switch (type) {
case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: {
return chppServiceOfHandle(context, handle)->requestDispatchFunctionPtr;
break;
}
case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: {
return chppClientOfHandle(context, handle)->responseDispatchFunctionPtr;
break;
}
case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
return chppServiceOfHandle(context, handle)
->notificationDispatchFunctionPtr;
break;
}
case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
return chppClientOfHandle(context, handle)
->notificationDispatchFunctionPtr;
break;
}
default: {
LOGE("Cannot dispatch unknown message type = %#x (handle = %" PRIu8 ")",
type, handle);
chppEnqueueTxErrorDatagram(context->transportContext,
CHPP_TRANSPORT_ERROR_APPLAYER);
}
}
}
/**
* Returns a pointer to the ChppService struct of a particular negotiated
* service handle.
*
* @param appContext Maintains status for each app layer instance.
* @param handle Handle number for the service.
*
* @return Pointer to the ChppService struct of a particular service handle.
*/
static inline const struct ChppService *chppServiceOfHandle(
struct ChppAppState *appContext, uint8_t handle) {
return appContext
->registeredServices[handle - CHPP_HANDLE_NEGOTIATED_RANGE_START];
}
/**
* Returns a pointer to the ChppClient struct of a particular negotiated
* handle. Returns null if a client doesn't exist for the handle.
*
* @param appContext Maintains status for each app layer instance.
* @param handle Handle number for the service.
*
* @return Pointer to the ChppClient struct matched to a particular handle.
*/
static inline const struct ChppClient *chppClientOfHandle(
struct ChppAppState *appContext, uint8_t handle) {
// TODO
UNUSED_VAR(appContext);
UNUSED_VAR(handle);
return NULL;
}
/**
* Returns a pointer to the service struct of a particular negotiated service
* handle.
*
* @param appContext Maintains status for each app layer instance.
* @param handle Handle number for the service.
*
* @return Pointer to the context struct of the service.
*/
static inline void *chppServiceContextOfHandle(struct ChppAppState *appContext,
uint8_t handle) {
return appContext
->registeredServiceContexts[handle - CHPP_HANDLE_NEGOTIATED_RANGE_START];
}
/**
* Returns a pointer to the client struct of a particular negotiated client
* handle.
*
* @param appContext Maintains status for each app layer instance.
* @param handle Handle number for the service.
*
* @return Pointer to the ChppService struct of the client.
*/
static inline void *chppClientContextOfHandle(struct ChppAppState *appContext,
uint8_t handle) {
// TODO
UNUSED_VAR(appContext);
UNUSED_VAR(handle);
return NULL;
}
/**
* Returns a pointer to the client/service struct of a particular negotiated
* client/service handle.
*
* @param appContext Maintains status for each app layer instance.
* @param handle Handle number for the service.
* @param type Message type (indicates if this is for a client or service).
*
* @return Pointer to the client/service struct of the service handle.
*/
static void *chppClientServiceContextOfHandle(struct ChppAppState *appContext,
uint8_t handle,
enum ChppMessageType type) {
switch (type) {
case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
return chppServiceContextOfHandle(appContext, handle);
break;
}
case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
return chppClientContextOfHandle(appContext, handle);
break;
}
default: {
LOGE(
"Cannot provide context for unknown message type = %#x (handle = "
"%" PRIu8 ")",
type, handle);
chppEnqueueTxErrorDatagram(appContext->transportContext,
CHPP_TRANSPORT_ERROR_APPLAYER);
}
}
}
/************************************************
* Public Functions
***********************************************/
void chppAppInit(struct ChppAppState *appContext,
struct ChppTransportState *transportContext) {
CHPP_NOT_NULL(appContext);
CHPP_NOT_NULL(transportContext);
memset(appContext, 0, sizeof(struct ChppAppState));
appContext->transportContext = transportContext;
chppPalSystemApiInit(appContext);
chppRegisterCommonServices(appContext);
}
void chppAppDeinit(struct ChppAppState *appContext) {
// TODO
chppPalSystemApiDeinit(appContext);
}
void chppProcessRxDatagram(struct ChppAppState *context, uint8_t *buf,
size_t len) {
struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
if (chppDatagramLenIsOk(context, rxHeader->handle, len)) {
if (rxHeader->handle >=
context->registeredServiceCount + CHPP_HANDLE_NEGOTIATED_RANGE_START) {
LOGE("Received datagram for invalid handle: %" PRIu8
", len = %zu, type = %#x, transaction ID = %" PRIu8,
rxHeader->handle, len, rxHeader->type, rxHeader->transaction);
} else if (rxHeader->handle == CHPP_HANDLE_NONE) {
// Non-handle based communication
chppDispatchNonHandle(context, buf, len);
} else if (rxHeader->handle < CHPP_HANDLE_NEGOTIATED_RANGE_START) {
// Predefined services / clients
bool success = true;
switch (rxHeader->type) {
case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: {
success = chppProcessPredefinedClientRequest(context, buf, len);
break;
}
case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
success = chppProcessPredefinedClientNotification(context, buf, len);
break;
}
case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: {
success = chppProcessPredefinedServiceResponse(context, buf, len);
break;
}
case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
success = chppProcessPredefinedServiceNotification(context, buf, len);
break;
}
default: {
LOGE(
"Received unknown message type = %#x for predefined handle = "
"%" PRIu8 " len = %zu, transaction ID = %" PRIu8,
rxHeader->type, rxHeader->handle, len, rxHeader->transaction);
chppEnqueueTxErrorDatagram(context->transportContext,
CHPP_TRANSPORT_ERROR_APPLAYER);
}
}
if (success == false) {
LOGE("Predefined client/service with handle = %" PRIu8
" does not support message type = %#x (len = %zu, transaction ID "
"= %" PRIu8 ")",
rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
chppEnqueueTxErrorDatagram(context->transportContext,
CHPP_TRANSPORT_ERROR_APPLAYER);
}
} else {
// Negotiated services / clients
ChppDispatchFunction *dispatchFunc =
chppGetDispatchFunction(context, rxHeader->handle, rxHeader->type);
void *clientServiceContext = chppClientServiceContextOfHandle(
context, rxHeader->handle, rxHeader->type);
if (dispatchFunc == NULL) {
LOGE("Negotiated handle = %" PRIu8
" does not support Rx message type = %" PRIu8,
rxHeader->handle, rxHeader->type);
} else if (clientServiceContext == NULL) {
LOGE("Client/service for negotiated handle = %" PRIu8
" and message type = %" PRIu8 " is missing context",
rxHeader->handle, rxHeader->type);
} else if (dispatchFunc(clientServiceContext, buf, len) == false) {
LOGE("Received unknown message type = %" PRIu8 " for handle = %" PRIu8
", command = %#x, transaction ID = %" PRIu8,
rxHeader->type, rxHeader->handle, rxHeader->command,
rxHeader->transaction);
// Allocate the response
struct ChppServiceBasicResponse *response =
chppAllocServiceResponseFixed(rxHeader,
struct ChppServiceBasicResponse);
// Populate the response
response->error = CHPP_APP_ERROR_INVALID_COMMAND;
// Send out response datagram
chppEnqueueTxDatagramOrFail(context->transportContext, response,
sizeof(*response));
}
}
}
chppAppProcessDoneCb(context->transportContext, buf);
}
void chppUuidToStr(const uint8_t uuid[CHPP_SERVICE_UUID_LEN],
char strOut[CHPP_SERVICE_UUID_STRING_LEN]) {
sprintf(
strOut,
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
uuid[15]);
}