/* * 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 "chre/core/telemetry_manager.h" #include #include "chre/core/event_loop_manager.h" #include "chre/platform/fatal_error.h" #include "chre/platform/shared/host_protocol_chre.h" #include "chre/util/macros.h" #include "chre/util/nested_data_ptr.h" #include "chre/util/time.h" #include "pixelatoms.nanopb.h" namespace chre { namespace { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!! DISCLAIMER !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // The metrics implemented in this class makes use of open-sourced PixelAtoms, // but they are not Pixel-specific, and can be extended to OEM use. If you // would like to use this code for telemetry purposes, please contact us for // details. //! Helper define macros for nanopb types. #define PIXELATOMS_GET(x) android_hardware_google_pixel_PixelAtoms_##x #define PIXELATOMS_GET_PAL_TYPE(x) \ _android_hardware_google_pixel_PixelAtoms_ChrePalType:: \ android_hardware_google_pixel_PixelAtoms_ChrePalType_CHRE_PAL_TYPE_##x void sendMetricToHost(uint32_t atomId, const pb_field_t fields[], const void *data) { size_t size; if (!pb_get_encoded_size(&size, PIXELATOMS_GET(ChrePalOpenFailed_fields), data)) { LOGE("Failed to get message size"); } else { pb_byte_t *bytes = static_cast(memoryAlloc(size)); if (bytes == nullptr) { LOG_OOM(); } else { pb_ostream_t stream = pb_ostream_from_buffer(bytes, size); if (!pb_encode(&stream, PIXELATOMS_GET(ChrePalOpenFailed_fields), data)) { LOGE("Failed to metric error %s", PB_GET_ERROR(&stream)); } else { HostCommsManager &manager = EventLoopManagerSingleton::get()->getHostCommsManager(); if (!manager.sendMetricLog( PIXELATOMS_GET(Atom_chre_pal_open_failed_tag), bytes, size)) { LOGE("Failed to send metric message"); } } memoryFree(bytes); } } } void sendPalOpenFailedMetric( _android_hardware_google_pixel_PixelAtoms_ChrePalType pal) { _android_hardware_google_pixel_PixelAtoms_ChrePalOpenFailed result = PIXELATOMS_GET(ChrePalOpenFailed_init_default); result.has_pal = true; result.pal = pal; result.has_type = true; result .type = _android_hardware_google_pixel_PixelAtoms_ChrePalOpenFailed_Type:: android_hardware_google_pixel_PixelAtoms_ChrePalOpenFailed_Type_INITIAL_OPEN; sendMetricToHost(PIXELATOMS_GET(Atom_chre_pal_open_failed_tag), PIXELATOMS_GET(ChrePalOpenFailed_fields), &result); } void sendEventLoopStats(uint32_t maxQueueSize, uint32_t meanQueueSize, uint32_t numDroppedEvents) { _android_hardware_google_pixel_PixelAtoms_ChreEventQueueSnapshotReported result = PIXELATOMS_GET(ChreEventQueueSnapshotReported_init_default); result.has_snapshot_chre_get_time_ms = true; result.snapshot_chre_get_time_ms = SystemTime::getMonotonicTime().toRawNanoseconds() / kOneMillisecondInNanoseconds; result.has_max_event_queue_size = true; result.max_event_queue_size = maxQueueSize; result.has_mean_event_queue_size = true; result.mean_event_queue_size = meanQueueSize; result.has_num_dropped_events = true; result.num_dropped_events = numDroppedEvents; sendMetricToHost(PIXELATOMS_GET(Atom_chre_event_queue_snapshot_reported_tag), PIXELATOMS_GET(ChreEventQueueSnapshotReported_fields), &result); } _android_hardware_google_pixel_PixelAtoms_ChrePalType toAtomPalType( TelemetryManager::PalType type) { switch (type) { case TelemetryManager::PalType::SENSOR: return PIXELATOMS_GET_PAL_TYPE(SENSOR); case TelemetryManager::PalType::WIFI: return PIXELATOMS_GET_PAL_TYPE(WIFI); case TelemetryManager::PalType::GNSS: return PIXELATOMS_GET_PAL_TYPE(GNSS); case TelemetryManager::PalType::WWAN: return PIXELATOMS_GET_PAL_TYPE(WWAN); case TelemetryManager::PalType::AUDIO: return PIXELATOMS_GET_PAL_TYPE(AUDIO); case TelemetryManager::PalType::BLE: return PIXELATOMS_GET_PAL_TYPE(BLE); case TelemetryManager::PalType::UNKNOWN: default: LOGW("Unknown PAL type %" PRIu8, type); return PIXELATOMS_GET_PAL_TYPE(UNKNOWN); } } } // anonymous namespace TelemetryManager::TelemetryManager() { scheduleMetricTimer(); } void TelemetryManager::onPalOpenFailure(PalType type) { auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) { _android_hardware_google_pixel_PixelAtoms_ChrePalType palType = toAtomPalType(NestedDataPtr(data)); if (palType != PIXELATOMS_GET_PAL_TYPE(UNKNOWN)) { sendPalOpenFailedMetric(palType); } }; // Defer the metric sending callback to better ensure that the host can // receive this message, as this method may be called prior to chre::init() // completion. EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::DeferredMetricPostEvent, NestedDataPtr(type), callback); } void TelemetryManager::collectSystemMetrics() { EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop(); sendEventLoopStats(eventLoop.getMaxEventQueueSize(), eventLoop.getMeanEventQueueSize(), eventLoop.getNumEventsDropped()); scheduleMetricTimer(); } void TelemetryManager::scheduleMetricTimer() { constexpr Seconds kDelay = Seconds(60 * 60 * 24); // 24 hours auto callback = [](uint16_t /* eventType */, void * /* data */, void * /* extraData */) { EventLoopManagerSingleton::get() ->getTelemetryManager() .collectSystemMetrics(); }; TimerHandle handle = EventLoopManagerSingleton::get()->setDelayedCallback( SystemCallbackType::DeferredMetricPostEvent, nullptr /* data */, callback, kDelay); if (handle == CHRE_TIMER_INVALID) { LOGE("Failed to set daily metric timer"); } } } // namespace chre