blob: 55b58403750da6db5725b6f141fc21bc10bafe25 [file] [log] [blame]
/*
* Copyright (C) 2016 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.
*/
// Note that to avoid always polluting the include paths of nanoapps, we use
// symlinks under the chre_nsl_internal include path to the "real" files, e.g.
// chre_nsl_internal/platform/shared maps to the same files that would normally
// be included via chre/platform/shared
#include "chre_nsl_internal/platform/shared/nanoapp_support_lib_dso.h"
#include <algorithm>
#include "chre/util/nanoapp/log.h"
#include "chre_api/chre.h"
#include "chre_nsl_internal/platform/shared/debug_dump.h"
#include "chre_nsl_internal/util/macros.h"
#include "chre_nsl_internal/util/system/napp_permissions.h"
#ifdef CHRE_NANOAPP_USES_WIFI
#include "chre_nsl_internal/util/system/wifi_util.h"
#endif
#ifndef LOG_TAG
#define LOG_TAG "[NSL]"
#endif // LOG_TAG
/**
* @file
* The Nanoapp Support Library (NSL) that gets built with nanoapps to act as an
* intermediary to the reference CHRE implementation. It provides hooks so the
* app can be registered with the system, and also provides a layer where we can
* implement cross-version compatibility features as needed.
*/
namespace {
constexpr uint32_t kNanoappPermissions = 0
// DO NOT USE this macro outside of specific CHQTS nanoapps. This is only used
// to allow testing of invalid permission declarations.
#ifdef CHRE_TEST_NANOAPP_PERMS
| CHRE_TEST_NANOAPP_PERMS
#else
#ifdef CHRE_NANOAPP_USES_AUDIO
| static_cast<uint32_t>(chre::NanoappPermissions::CHRE_PERMS_AUDIO)
#endif
#ifdef CHRE_NANOAPP_USES_BLE
| static_cast<uint32_t>(chre::NanoappPermissions::CHRE_PERMS_BLE)
#endif
#ifdef CHRE_NANOAPP_USES_GNSS
| static_cast<uint32_t>(chre::NanoappPermissions::CHRE_PERMS_GNSS)
#endif
#ifdef CHRE_NANOAPP_USES_WIFI
| static_cast<uint32_t>(chre::NanoappPermissions::CHRE_PERMS_WIFI)
#endif
#ifdef CHRE_NANOAPP_USES_WWAN
| static_cast<uint32_t>(chre::NanoappPermissions::CHRE_PERMS_WWAN)
#endif
#endif // CHRE_TEST_NANOAPP_PERMS
;
#if defined(CHRE_SLPI_UIMG_ENABLED) || defined(CHRE_TCM_ENABLED)
constexpr int kIsTcmNanoapp = 1;
#else
constexpr int kIsTcmNanoapp = 0;
#endif // CHRE_SLPI_UIMG_ENABLED
#if !defined(CHRE_NANOAPP_DISABLE_BACKCOMPAT) && defined(CHRE_NANOAPP_USES_GNSS)
// Return a v1.3+ GnssLocationEvent for the nanoapp when running on a v1.2-
// platform.
chreGnssLocationEvent translateLegacyGnssLocation(
const chreGnssLocationEvent &legacyEvent) {
// Copy v1.2- fields over to a v1.3+ event.
chreGnssLocationEvent newEvent = {};
newEvent.timestamp = legacyEvent.timestamp;
newEvent.latitude_deg_e7 = legacyEvent.latitude_deg_e7;
newEvent.longitude_deg_e7 = legacyEvent.longitude_deg_e7;
newEvent.altitude = legacyEvent.altitude;
newEvent.speed = legacyEvent.speed;
newEvent.bearing = legacyEvent.bearing;
newEvent.accuracy = legacyEvent.accuracy;
newEvent.flags = legacyEvent.flags;
// Unset flags that are defined in v1.3+ but not in v1.2-.
newEvent.flags &= ~(CHRE_GPS_LOCATION_HAS_ALTITUDE_ACCURACY |
CHRE_GPS_LOCATION_HAS_SPEED_ACCURACY |
CHRE_GPS_LOCATION_HAS_BEARING_ACCURACY);
return newEvent;
}
void nanoappHandleEventCompat(uint32_t senderInstanceId, uint16_t eventType,
const void *eventData) {
if (eventType == CHRE_EVENT_GNSS_LOCATION &&
chreGetApiVersion() < CHRE_API_VERSION_1_3) {
chreGnssLocationEvent event = translateLegacyGnssLocation(
*static_cast<const chreGnssLocationEvent *>(eventData));
nanoappHandleEvent(senderInstanceId, eventType, &event);
} else {
nanoappHandleEvent(senderInstanceId, eventType, eventData);
}
}
#endif
#if !defined(CHRE_NANOAPP_DISABLE_BACKCOMPAT) && defined(CHRE_NANOAPP_USES_BLE)
void reverseServiceDataUuid(struct chreBleGenericFilter *filter) {
if (filter->type != CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE ||
filter->len == 0) {
return;
}
std::swap(filter->data[0], filter->data[1]);
std::swap(filter->dataMask[0], filter->dataMask[1]);
if (filter->len == 1) {
filter->data[0] = 0x0;
filter->dataMask[0] = 0x0;
filter->len = 2;
}
}
bool serviceDataFilterEndianSwapRequired(
const struct chreBleScanFilter *filter) {
if (chreGetApiVersion() >= CHRE_API_VERSION_1_8 || filter == nullptr) {
return false;
}
for (size_t i = 0; i < filter->scanFilterCount; i++) {
if (filter->scanFilters[i].type ==
CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16_LE &&
filter->scanFilters[i].len > 0) {
return true;
}
}
return false;
}
#endif
} // anonymous namespace
//! Used to determine the given unstable ID that was provided when building this
//! nanoapp, if any. The symbol is placed in its own section so it can be
//! stripped to determine if the nanoapp changed compared to a previous version.
//! We also align the variable to match the minimum alignment of the surrounding
//! sections, since for compilers with a default size-1 alignment, there might
//! be a spill-over from the previous segment if not zero-padded, when we
//! attempt to read the string.
extern "C" DLL_EXPORT const char _chreNanoappUnstableId[]
__attribute__((section(".unstable_id"))) __attribute__((aligned(8))) =
NANOAPP_UNSTABLE_ID;
extern "C" DLL_EXPORT const struct chreNslNanoappInfo _chreNslDsoNanoappInfo = {
/* magic */ CHRE_NSL_NANOAPP_INFO_MAGIC,
/* structMinorVersion */ CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION,
/* isSystemNanoapp */ NANOAPP_IS_SYSTEM_NANOAPP,
/* isTcmNanoapp */ kIsTcmNanoapp,
/* reservedFlags */ 0,
/* reserved */ 0,
/* targetApiVersion */ CHRE_API_VERSION,
// These values are supplied by the build environment.
/* vendor */ NANOAPP_VENDOR_STRING,
/* name */ NANOAPP_NAME_STRING,
/* appId */ NANOAPP_ID,
/* appVersion */ NANOAPP_VERSION,
/* entryPoints */
{
/* start */ nanoappStart,
#if !defined(CHRE_NANOAPP_DISABLE_BACKCOMPAT) && defined(CHRE_NANOAPP_USES_GNSS)
/* handleEvent */ nanoappHandleEventCompat,
#else
/* handleEvent */ nanoappHandleEvent,
#endif
/* end */ nanoappEnd,
},
/* appVersionString */ _chreNanoappUnstableId,
/* appPermissions */ kNanoappPermissions,
};
const struct chreNslNanoappInfo *getChreNslDsoNanoappInfo() {
return &_chreNslDsoNanoappInfo;
}
// The code section below provides default implementations for new symbols
// introduced in CHRE API v1.2+ to provide binary compatibility with previous
// CHRE implementations. Note that we don't presently include symbols for v1.1,
// as the current known set of CHRE platforms that use this NSL implementation
// are all v1.1+.
// If a nanoapp knows that it is only targeting the latest platform version, it
// can define the CHRE_NANOAPP_DISABLE_BACKCOMPAT flag, so this indirection will
// be avoided at the expense of a nanoapp not being able to load at all on prior
// implementations.
#ifndef CHRE_NANOAPP_DISABLE_BACKCOMPAT
#include <dlfcn.h>
namespace {
// Populate chreNanoappInfo for CHRE API pre v1.8.
void populateChreNanoappInfoPre18(struct chreNanoappInfo *info) {
info->rpcServiceCount = 0;
info->rpcServices = nullptr;
memset(&info->reserved, 0, sizeof(info->reserved));
}
} // namespace
/**
* Lazily calls dlsym to find the function pointer for a given function
* (provided without quotes) in another library (i.e. the CHRE platform DSO),
* caching and returning the result.
*/
#define CHRE_NSL_LAZY_LOOKUP(functionName) \
({ \
static bool lookupPerformed = false; \
static decltype(functionName) *funcPtr = nullptr; \
if (!lookupPerformed) { \
funcPtr = reinterpret_cast<decltype(funcPtr)>( \
dlsym(RTLD_NEXT, STRINGIFY(functionName))); \
lookupPerformed = true; \
} \
funcPtr; \
})
#ifdef CHRE_NANOAPP_USES_AUDIO
WEAK_SYMBOL
bool chreAudioGetSource(uint32_t handle, struct chreAudioSource *audioSource) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreAudioGetSource);
return (fptr != nullptr) ? fptr(handle, audioSource) : false;
}
WEAK_SYMBOL
bool chreAudioConfigureSource(uint32_t handle, bool enable,
uint64_t bufferDuration,
uint64_t deliveryInterval) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreAudioConfigureSource);
return (fptr != nullptr)
? fptr(handle, enable, bufferDuration, deliveryInterval)
: false;
}
WEAK_SYMBOL
bool chreAudioGetStatus(uint32_t handle, struct chreAudioSourceStatus *status) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreAudioGetStatus);
return (fptr != nullptr) ? fptr(handle, status) : false;
}
#endif /* CHRE_NANOAPP_USES_AUDIO */
#ifdef CHRE_NANOAPP_USES_BLE
WEAK_SYMBOL
uint32_t chreBleGetCapabilities() {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleGetCapabilities);
return (fptr != nullptr) ? fptr() : CHRE_BLE_CAPABILITIES_NONE;
}
WEAK_SYMBOL
uint32_t chreBleGetFilterCapabilities() {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleGetFilterCapabilities);
return (fptr != nullptr) ? fptr() : CHRE_BLE_FILTER_CAPABILITIES_NONE;
}
WEAK_SYMBOL
bool chreBleFlushAsync(const void *cookie) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleFlushAsync);
return (fptr != nullptr) ? fptr(cookie) : false;
}
WEAK_SYMBOL
bool chreBleStartScanAsync(chreBleScanMode mode, uint32_t reportDelayMs,
const struct chreBleScanFilter *filter) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleStartScanAsync);
if (fptr == nullptr) {
return false;
} else if (!serviceDataFilterEndianSwapRequired(filter)) {
return fptr(mode, reportDelayMs, filter);
}
// For nanoapps compiled against v1.8+ working with earlier versions of CHRE,
// convert service data filters to big-endian format.
chreBleScanFilter convertedFilter = *filter;
auto genericFilters = static_cast<chreBleGenericFilter *>(
chreHeapAlloc(sizeof(chreBleGenericFilter) * filter->scanFilterCount));
if (genericFilters == nullptr) {
LOG_OOM();
return false;
}
memcpy(genericFilters, filter->scanFilters,
filter->scanFilterCount * sizeof(chreBleGenericFilter));
for (size_t i = 0; i < filter->scanFilterCount; i++) {
reverseServiceDataUuid(&genericFilters[i]);
}
convertedFilter.scanFilters = genericFilters;
bool success = fptr(mode, reportDelayMs, &convertedFilter);
chreHeapFree(const_cast<chreBleGenericFilter *>(convertedFilter.scanFilters));
return success;
}
WEAK_SYMBOL
bool chreBleStartScanAsyncV1_9(chreBleScanMode mode, uint32_t reportDelayMs,
const struct chreBleScanFilterV1_9 *filter,
const void *cookie) {
if (chreGetApiVersion() < CHRE_API_VERSION_1_9) {
return false;
}
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleStartScanAsyncV1_9);
if (fptr == nullptr) {
return false;
}
return fptr(mode, reportDelayMs, filter, cookie);
}
WEAK_SYMBOL
bool chreBleStopScanAsync() {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleStopScanAsync);
return (fptr != nullptr) ? fptr() : false;
}
WEAK_SYMBOL
bool chreBleStopScanAsyncV1_9(const void *cookie) {
if (chreGetApiVersion() < CHRE_API_VERSION_1_9) {
return false;
}
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleStopScanAsyncV1_9);
if (fptr == nullptr) {
return false;
}
return fptr(cookie);
}
WEAK_SYMBOL
bool chreBleReadRssiAsync(uint16_t connectionHandle, const void *cookie) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleReadRssiAsync);
return (fptr != nullptr) ? fptr(connectionHandle, cookie) : false;
}
WEAK_SYMBOL
bool chreBleGetScanStatus(struct chreBleScanStatus *status) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreBleGetScanStatus);
return (fptr != nullptr) ? fptr(status) : false;
}
#endif /* CHRE_NANOAPP_USES_BLE */
WEAK_SYMBOL
void chreConfigureHostSleepStateEvents(bool enable) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreConfigureHostSleepStateEvents);
if (fptr != nullptr) {
fptr(enable);
}
}
WEAK_SYMBOL
bool chreIsHostAwake(void) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreIsHostAwake);
return (fptr != nullptr) ? fptr() : false;
}
#ifdef CHRE_NANOAPP_USES_GNSS
WEAK_SYMBOL
bool chreGnssConfigurePassiveLocationListener(bool enable) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreGnssConfigurePassiveLocationListener);
return (fptr != nullptr) ? fptr(enable) : false;
}
#endif /* CHRE_NANOAPP_USES_GNSS */
#ifdef CHRE_NANOAPP_USES_WIFI
WEAK_SYMBOL
bool chreWifiRequestScanAsync(const struct chreWifiScanParams *params,
const void *cookie) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreWifiRequestScanAsync);
if (fptr == nullptr) {
// Should never happen
return false;
} else if (chreGetApiVersion() < CHRE_API_VERSION_1_5) {
const struct chreWifiScanParams legacyParams =
chre::translateToLegacyWifiScanParams(params);
return fptr(&legacyParams, cookie);
} else {
return fptr(params, cookie);
}
}
WEAK_SYMBOL
bool chreWifiRequestRangingAsync(const struct chreWifiRangingParams *params,
const void *cookie) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreWifiRequestRangingAsync);
return (fptr != nullptr) ? fptr(params, cookie) : false;
}
WEAK_SYMBOL
bool chreWifiNanRequestRangingAsync(
const struct chreWifiNanRangingParams *params, const void *cookie) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreWifiNanRequestRangingAsync);
return (fptr != nullptr) ? fptr(params, cookie) : false;
}
WEAK_SYMBOL
bool chreWifiNanSubscribe(struct chreWifiNanSubscribeConfig *config,
const void *cookie) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreWifiNanSubscribe);
return (fptr != nullptr) ? fptr(config, cookie) : false;
}
WEAK_SYMBOL
bool chreWifiNanSubscribeCancel(uint32_t subscriptionID) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreWifiNanSubscribeCancel);
return (fptr != nullptr) ? fptr(subscriptionID) : false;
}
#endif /* CHRE_NANOAPP_USES_WIFI */
WEAK_SYMBOL
bool chreSensorFind(uint8_t sensorType, uint8_t sensorIndex, uint32_t *handle) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreSensorFind);
if (fptr != nullptr) {
return fptr(sensorType, sensorIndex, handle);
} else if (sensorIndex == 0) {
return chreSensorFindDefault(sensorType, handle);
} else {
return false;
}
}
WEAK_SYMBOL
bool chreSensorConfigureBiasEvents(uint32_t sensorHandle, bool enable) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreSensorConfigureBiasEvents);
return (fptr != nullptr) ? fptr(sensorHandle, enable) : false;
}
WEAK_SYMBOL
bool chreSensorGetThreeAxisBias(uint32_t sensorHandle,
struct chreSensorThreeAxisData *bias) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreSensorGetThreeAxisBias);
return (fptr != nullptr) ? fptr(sensorHandle, bias) : false;
}
WEAK_SYMBOL
bool chreSensorFlushAsync(uint32_t sensorHandle, const void *cookie) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreSensorFlushAsync);
return (fptr != nullptr) ? fptr(sensorHandle, cookie) : false;
}
WEAK_SYMBOL
void chreConfigureDebugDumpEvent(bool enable) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreConfigureDebugDumpEvent);
if (fptr != nullptr) {
fptr(enable);
}
}
WEAK_SYMBOL
void chreDebugDumpLog(const char *formatStr, ...) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(platform_chreDebugDumpVaLog);
if (fptr != nullptr) {
va_list args;
va_start(args, formatStr);
fptr(formatStr, args);
va_end(args);
}
}
WEAK_SYMBOL
bool chreSendMessageWithPermissions(void *message, size_t messageSize,
uint32_t messageType, uint16_t hostEndpoint,
uint32_t messagePermissions,
chreMessageFreeFunction *freeCallback) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreSendMessageWithPermissions);
if (fptr != nullptr) {
return fptr(message, messageSize, messageType, hostEndpoint,
messagePermissions, freeCallback);
} else {
return chreSendMessageToHostEndpoint(message, messageSize, messageType,
hostEndpoint, freeCallback);
}
}
WEAK_SYMBOL
int8_t chreUserSettingGetState(uint8_t setting) {
int8_t settingState = CHRE_USER_SETTING_STATE_UNKNOWN;
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreUserSettingGetState);
if (fptr != nullptr) {
settingState = fptr(setting);
}
return settingState;
}
WEAK_SYMBOL
void chreUserSettingConfigureEvents(uint8_t setting, bool enable) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreUserSettingConfigureEvents);
if (fptr != nullptr) {
fptr(setting, enable);
}
}
WEAK_SYMBOL
bool chreConfigureHostEndpointNotifications(uint16_t hostEndpointId,
bool enable) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreConfigureHostEndpointNotifications);
return (fptr != nullptr) ? fptr(hostEndpointId, enable) : false;
}
WEAK_SYMBOL
bool chrePublishRpcServices(struct chreNanoappRpcService *services,
size_t numServices) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chrePublishRpcServices);
return (fptr != nullptr) ? fptr(services, numServices) : false;
}
WEAK_SYMBOL
bool chreGetHostEndpointInfo(uint16_t hostEndpointId,
struct chreHostEndpointInfo *info) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreGetHostEndpointInfo);
return (fptr != nullptr) ? fptr(hostEndpointId, info) : false;
}
bool chreGetNanoappInfoByAppId(uint64_t appId, struct chreNanoappInfo *info) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreGetNanoappInfoByAppId);
bool success = (fptr != nullptr) ? fptr(appId, info) : false;
if (success && chreGetApiVersion() < CHRE_API_VERSION_1_8) {
populateChreNanoappInfoPre18(info);
}
return success;
}
bool chreGetNanoappInfoByInstanceId(uint32_t instanceId,
struct chreNanoappInfo *info) {
auto *fptr = CHRE_NSL_LAZY_LOOKUP(chreGetNanoappInfoByInstanceId);
bool success = (fptr != nullptr) ? fptr(instanceId, info) : false;
if (success && chreGetApiVersion() < CHRE_API_VERSION_1_8) {
populateChreNanoappInfoPre18(info);
}
return success;
}
#endif // CHRE_NANOAPP_DISABLE_BACKCOMPAT