/* * Copyright (C) 2022 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 #include "chre.h" #include "chre/util/macros.h" #include "chre/util/memory.h" #include "chre/util/nanoapp/log.h" #include "chre/util/time.h" #include "chre/util/unique_ptr.h" #ifdef CHRE_NANOAPP_INTERNAL namespace chre { namespace { #endif // CHRE_NANOAPP_INTERNAL bool gAsyncResultReceived = false; uint32_t gTimerHandle = 0; //! A fake/unused cookie to pass into the session async and timer request. const uint32_t kBleCookie = 0x1337; //! The interval in seconds between updates. const chreBleScanMode kScanModes[] = {CHRE_BLE_SCAN_MODE_BACKGROUND, CHRE_BLE_SCAN_MODE_FOREGROUND, CHRE_BLE_SCAN_MODE_AGGRESSIVE}; enum ScanRequestType { NO_FILTER = 0, SERVICE_DATA_16 = 1, STOP_SCAN = 2, }; chreBleScanFilter *getBleScanFilter(ScanRequestType &scanRequestType) { chre::UniquePtr filter = chre::MakeUniqueZeroFill(); filter->rssiThreshold = CHRE_BLE_RSSI_THRESHOLD_NONE; filter->scanFilterCount = 1; chre::UniquePtr scanFilter = chre::MakeUniqueZeroFill(); switch (scanRequestType) { case NO_FILTER: filter = nullptr; scanRequestType = SERVICE_DATA_16; break; case SERVICE_DATA_16: scanFilter->type = CHRE_BLE_AD_TYPE_SERVICE_DATA_WITH_UUID_16; scanFilter->len = 2; filter->scanFilters = scanFilter.release(); scanRequestType = STOP_SCAN; break; case STOP_SCAN: break; } return filter.release(); } void makeBleScanRequest() { static uint8_t scanModeIndex = 0; static ScanRequestType scanRequestType = NO_FILTER; if (scanRequestType != STOP_SCAN) { chreBleScanMode mode = kScanModes[scanModeIndex]; uint32_t reportDelayMs = 0; chreBleScanFilter *filter = getBleScanFilter(scanRequestType); LOGI("Sending BLE start scan request to PAL with parameters:"); LOGI(" mode=%" PRIu8, kScanModes[scanModeIndex]); LOGI(" reportDelayMs=%" PRIu32, reportDelayMs); if (filter != nullptr) { LOGI(" rssiThreshold=%" PRIu32, filter->rssiThreshold); LOGI(" scanFilterType=%" PRIx8, filter->scanFilters[0].type); LOGI(" scanFilterLen=%" PRIu8, filter->scanFilters[0].len); LOGI(" scanFilterData=%s", filter->scanFilters[0].data); LOGI(" scanFilterDataMask=%s", filter->scanFilters[0].dataMask); } if (chreBleStartScanAsync(mode, 0, nullptr)) { LOGI("BLE start scan request sent to PAL"); } else { LOGE("Error sending BLE start scan request sent to PAL"); } if (filter != nullptr) { if (filter->scanFilters != nullptr) { chre::memoryFree( const_cast(filter->scanFilters)); } chre::memoryFree(filter); } } else { if (chreBleStopScanAsync()) { LOGI("BLE stop scan request sent to PAL"); } else { LOGE("Error sending BLE stop scan request sent to PAL"); } scanRequestType = NO_FILTER; scanModeIndex = (scanModeIndex + 1) % ARRAY_SIZE(kScanModes); } gTimerHandle = chreTimerSet(CHRE_ASYNC_RESULT_TIMEOUT_NS, /* 5 sec */ &kBleCookie, true /* oneShot */); } void handleAdvertismentEvent(const chreBleAdvertisementEvent *event) { for (uint8_t i = 0; i < event->numReports; i++) { LOGI("BLE Report %" PRIu8, i + 1); LOGI("Scan data:"); const uint8_t *data = event->reports[i].data; for (uint8_t j = 0; j < event->reports[i].dataLength; j++) { LOGI(" %" PRIx8, data[j]); } } } void handleAsyncResultEvent(const chreAsyncResult *result) { gAsyncResultReceived = true; const char *requestType = result->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN ? "start" : "stop"; if (result->success) { LOGI("BLE %s scan success", requestType); } else { LOGE("BLE %s scan failure: %" PRIu8, requestType, result->errorCode); } } void handleTimerEvent(const void *eventData) { static uint32_t timerCount = 1; if (eventData == &kBleCookie) { LOGI("BLE timer event received, count %" PRIu32, timerCount++); if (!gAsyncResultReceived) { LOGE("BLE async result not received"); } gAsyncResultReceived = false; makeBleScanRequest(); } else { LOGE("Invalid timer cookie"); } } bool nanoappStart(void) { LOGI("nanoapp started"); makeBleScanRequest(); return true; } void nanoappEnd(void) { if (!chreBleStopScanAsync()) { LOGE("Error sending BLE stop scan request sent to PAL"); } if (!chreTimerCancel(gTimerHandle)) { LOGE("Error canceling timer"); } LOGI("nanoapp stopped"); } void nanoappHandleEvent(uint32_t /* sender_instance_id */, uint16_t event_type, const void *event_data) { if (event_type == CHRE_EVENT_BLE_ADVERTISEMENT) { handleAdvertismentEvent( static_cast(event_data)); } else if (event_type == CHRE_EVENT_BLE_ASYNC_RESULT) { handleAsyncResultEvent(static_cast(event_data)); } else if (event_type == CHRE_EVENT_TIMER) { handleTimerEvent(event_data); } } #ifdef CHRE_NANOAPP_INTERNAL } // anonymous namespace } // namespace chre #include "chre/platform/static_nanoapp_init.h" #include "chre/util/nanoapp/app_id.h" #include "chre/util/system/napp_permissions.h" CHRE_STATIC_NANOAPP_INIT(BleWorld, kBleWorldAppId, 0, NanoappPermissions::CHRE_PERMS_BLE); #endif // CHRE_NANOAPP_INTERNAL