From 51924568fef2d59596d57bb839b1835bfcdf1fad Mon Sep 17 00:00:00 2001 From: jLynx Date: Sun, 8 Feb 2026 15:41:02 +1300 Subject: [PATCH] fw/syscall: add MediaService API for volume control and music management Signed-off-by: Joel Herbert --- .../process_management/pebble_process_info.h | 3 +- src/fw/syscall/music_syscalls.c | 85 ++++++++++ src/fw/syscall/syscall.h | 19 +++ .../generate_native_sdk/exported_symbols.json | 152 +++++++++++++++++- 4 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 src/fw/syscall/music_syscalls.c diff --git a/src/fw/process_management/pebble_process_info.h b/src/fw/process_management/pebble_process_info.h index cc608d1b4..18416b2d0 100644 --- a/src/fw/process_management/pebble_process_info.h +++ b/src/fw/process_management/pebble_process_info.h @@ -149,9 +149,10 @@ typedef enum { // sdk.major:0x5 .minor:0x54 -- Add PlatformType enum and defines (rev 87) // sdk.major:0x5 .minor:0x55 -- Preferred Content Size (rev 88) // sdk.major:0x5 .minor:0x56 -- Add PlatformType enum and defines (rev 89) +// sdk.major:0x5 .minor:0x57 -- Add MediaService API for volume control (rev 90) #define PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR 0x5 -#define PROCESS_INFO_CURRENT_SDK_VERSION_MINOR 0x56 +#define PROCESS_INFO_CURRENT_SDK_VERSION_MINOR 0x57 // The first SDK to ship with 2.x APIs #define PROCESS_INFO_FIRST_2X_SDK_VERSION_MAJOR 0x4 diff --git a/src/fw/syscall/music_syscalls.c b/src/fw/syscall/music_syscalls.c new file mode 100644 index 000000000..1959d2fb5 --- /dev/null +++ b/src/fw/syscall/music_syscalls.c @@ -0,0 +1,85 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "services/normal/music.h" +#include "syscall/syscall_internal.h" + +DEFINE_SYSCALL(void, sys_music_get_now_playing, char *title, char *artist, char *album) { + if (PRIVILEGE_WAS_ELEVATED) { + if (title) syscall_assert_userspace_buffer(title, MUSIC_BUFFER_LENGTH); + if (artist) syscall_assert_userspace_buffer(artist, MUSIC_BUFFER_LENGTH); + if (album) syscall_assert_userspace_buffer(album, MUSIC_BUFFER_LENGTH); + } + music_get_now_playing(title, artist, album); +} + +DEFINE_SYSCALL(bool, sys_music_has_now_playing, void) { + return music_has_now_playing(); +} + +DEFINE_SYSCALL(bool, sys_music_get_player_name, char *player_name_out) { + if (PRIVILEGE_WAS_ELEVATED) { + if (player_name_out) syscall_assert_userspace_buffer(player_name_out, MUSIC_BUFFER_LENGTH); + } + return music_get_player_name(player_name_out); +} + +DEFINE_SYSCALL(uint32_t, sys_music_get_ms_since_pos_last_updated, void) { + return music_get_ms_since_pos_last_updated(); +} + +DEFINE_SYSCALL(void, sys_music_get_pos, uint32_t *track_pos_ms, uint32_t *track_length_ms) { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(track_pos_ms, sizeof(*track_pos_ms)); + syscall_assert_userspace_buffer(track_length_ms, sizeof(*track_length_ms)); + } + music_get_pos(track_pos_ms, track_length_ms); +} + +DEFINE_SYSCALL(int32_t, sys_music_get_playback_rate_percent, void) { + return music_get_playback_rate_percent(); +} + +DEFINE_SYSCALL(uint8_t, sys_music_get_volume_percent, void) { + return music_get_volume_percent(); +} + +DEFINE_SYSCALL(MusicPlayState, sys_music_get_playback_state, void) { + return music_get_playback_state(); +} + +DEFINE_SYSCALL(bool, sys_music_is_playback_state_reporting_supported, void) { + return music_is_playback_state_reporting_supported(); +} + +DEFINE_SYSCALL(bool, sys_music_is_progress_reporting_supported, void) { + return music_is_progress_reporting_supported(); +} + +DEFINE_SYSCALL(bool, sys_music_is_volume_reporting_supported, void) { + return music_is_volume_reporting_supported(); +} + +DEFINE_SYSCALL(void, sys_music_command_send, MusicCommand command) { + music_command_send(command); +} + +DEFINE_SYSCALL(bool, sys_music_is_command_supported, MusicCommand command) { + return music_is_command_supported(command); +} + +DEFINE_SYSCALL(bool, sys_music_needs_user_to_start_playback_on_phone, void) { + return music_needs_user_to_start_playback_on_phone(); +} + +DEFINE_SYSCALL(void, sys_music_request_reduced_latency, bool reduced_latency) { + music_request_reduced_latency(reduced_latency); +} + +DEFINE_SYSCALL(void, sys_music_request_low_latency_for_period, uint32_t period_seconds) { + music_request_low_latency_for_period(period_seconds); +} + +DEFINE_SYSCALL(const char *, sys_music_get_connected_server_debug_name, void) { + return music_get_connected_server_debug_name(); +} diff --git a/src/fw/syscall/syscall.h b/src/fw/syscall/syscall.h index 53d45b238..0c29d241e 100644 --- a/src/fw/syscall/syscall.h +++ b/src/fw/syscall/syscall.h @@ -25,6 +25,7 @@ #include "kernel/events.h" #include "kernel/logging_private.h" #include "services/normal/wakeup.h" +#include "services/normal/music.h" #include "services/common/analytics/analytics.h" #include "services/common/analytics/analytics_event.h" #include "services/common/comm_session/session.h" @@ -180,6 +181,24 @@ int sys_strftime(char* s, size_t maxsize, const char* format, const struct tm* t BatteryChargeState sys_battery_get_charge_state(void); +void sys_music_get_now_playing(char *title, char *artist, char *album); +bool sys_music_has_now_playing(void); +bool sys_music_get_player_name(char *player_name_out); +uint32_t sys_music_get_ms_since_pos_last_updated(void); +void sys_music_get_pos(uint32_t *track_pos_ms, uint32_t *track_length_ms); +int32_t sys_music_get_playback_rate_percent(void); +uint8_t sys_music_get_volume_percent(void); +MusicPlayState sys_music_get_playback_state(void); +bool sys_music_is_playback_state_reporting_supported(void); +bool sys_music_is_progress_reporting_supported(void); +bool sys_music_is_volume_reporting_supported(void); +void sys_music_command_send(MusicCommand command); +bool sys_music_is_command_supported(MusicCommand command); +bool sys_music_needs_user_to_start_playback_on_phone(void); +void sys_music_request_reduced_latency(bool reduced_latency); +void sys_music_request_low_latency_for_period(uint32_t period_seconds); +const char * sys_music_get_connected_server_debug_name(void); + bool sys_activity_get_metric(ActivityMetric metric, uint32_t history_len, int32_t *history); bool sys_activity_get_minute_history(HealthMinuteData *minute_data, uint32_t *num_records, time_t *utc_start); diff --git a/tools/generate_native_sdk/exported_symbols.json b/tools/generate_native_sdk/exported_symbols.json index b2b4be42c..f335a01f2 100644 --- a/tools/generate_native_sdk/exported_symbols.json +++ b/tools/generate_native_sdk/exported_symbols.json @@ -4,7 +4,7 @@ "You should also make sure you are obeying our API design guidelines:", "https://pebbletechnology.atlassian.net/wiki/display/DEV/SDK+API+Design+Guidelines" ], - "revision" : "89", + "revision" : "90", "version" : "2.0", "files": [ "fw/drivers/ambient_light.h", @@ -50,6 +50,7 @@ "fw/applib/app_exit_reason.h", "fw/applib/app_launch_reason.h", "fw/applib/cpu_cache.h", + "fw/applib/event_service_client.h", "fw/applib/health_service.h", "fw/applib/platform.h", "fw/applib/tick_timer_service.h", @@ -84,6 +85,7 @@ "fw/services/common/ecompass.h", "fw/services/common/new_timer/new_timer.h", "fw/services/normal/wakeup.h", + "fw/services/normal/music.h", "fw/syscall/syscall.h", "fw/kernel/events.h", "fw/applib/preferred_content_size.h", @@ -685,7 +687,153 @@ "name": "health_service_get_measurement_system_for_display", "addedRevision": "80" } - ]}] + ]}, { + "type": "group", + "name": "MediaService", + "exports": [ + { + "type": "type", + "name": "MusicPlayState", + "addedRevision": "90" + }, { + "type": "type", + "name": "MusicCommand", + "addedRevision": "90" + }, { + "type": "type", + "name": "PebbleMediaEventType", + "addedRevision": "90" + }, { + "type": "type", + "name": "PebbleMediaEvent", + "addedRevision": "90" + }, { + "type": "define", + "name": "MUSIC_BUFFER_LENGTH", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_get_now_playing", + "implName": "sys_music_get_now_playing", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_has_now_playing", + "implName": "sys_music_has_now_playing", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_get_player_name", + "implName": "sys_music_get_player_name", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_get_ms_since_pos_last_updated", + "implName": "sys_music_get_ms_since_pos_last_updated", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_get_pos", + "implName": "sys_music_get_pos", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_get_playback_rate_percent", + "implName": "sys_music_get_playback_rate_percent", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_get_volume_percent", + "implName": "sys_music_get_volume_percent", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_get_playback_state", + "implName": "sys_music_get_playback_state", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_is_playback_state_reporting_supported", + "implName": "sys_music_is_playback_state_reporting_supported", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_is_progress_reporting_supported", + "implName": "sys_music_is_progress_reporting_supported", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_is_volume_reporting_supported", + "implName": "sys_music_is_volume_reporting_supported", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_command_send", + "implName": "sys_music_command_send", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_is_command_supported", + "implName": "sys_music_is_command_supported", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_needs_user_to_start_playback_on_phone", + "implName": "sys_music_needs_user_to_start_playback_on_phone", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_request_reduced_latency", + "implName": "sys_music_request_reduced_latency", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_request_low_latency_for_period", + "implName": "sys_music_request_low_latency_for_period", + "addedRevision": "90" + }, { + "type": "function", + "name": "music_get_connected_server_debug_name", + "implName": "sys_music_get_connected_server_debug_name", + "addedRevision": "90" + } + ] + }, { + "type": "group", + "name": "EventServiceClient", + "appOnly": true, + "exports": [ + { + "type": "forward_struct", + "name": "PebbleEvent", + "addedRevision": "90" + }, { + "type": "type", + "name": "PebbleEventType", + "addedRevision": "90" + }, { + "type": "type", + "name": "ListNode", + "addedRevision": "90" + }, { + "type": "type", + "name": "EventServiceEventHandler", + "addedRevision": "90" + }, { + "type": "type", + "name": "EventServiceInfo", + "addedRevision": "90" + }, { + "type": "function", + "name": "event_service_client_subscribe", + "addedRevision": "90" + }, { + "type": "function", + "name": "event_service_client_unsubscribe", + "addedRevision": "90" + } + ] + }] }, { "type": "group", "name": "DataLogging",