aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTai Vuong <tvuong@audiokinetic.com>2017-10-27 21:40:20 -0400
committerTai Vuong <tvuong@audiokinetic.com>2017-10-27 21:40:20 -0400
commit8a584f01b46d251fdc5de8b071eff755d99f0090 (patch)
tree625bfb4d66933b8fd18428027aa38c8a708368ce
parent9b7e1d0361d1a5eee415e453ae79925084552c68 (diff)
Add JSON object parameters for policy functions call, fix various bug and code stabilisation
-rw-r--r--conf.d/project/.asoundrc164
-rw-r--r--conf.d/project/CMakeLists.txt24
-rw-r--r--conf.d/project/agl-ahl-config.json128
-rw-r--r--conf.d/project/alsa.d/asoundrc.sample146
-rw-r--r--conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf6
-rw-r--r--conf.d/project/alsa.d/ucm.sample/HiFi.conf84
-rw-r--r--conf.d/project/alsa.d/ucm.sample/README2
-rw-r--r--conf.d/project/json.d/ahl-binding.json127
-rw-r--r--conf.d/project/json.d/onload-audio-control.json137
-rw-r--r--htdocs/audio-control.html46
-rw-r--r--htdocs/audio-logic.html9
-rw-r--r--htdocs/audiohl-demo.html47
-rw-r--r--htdocs/audiohl.html100
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/ahl-apidef.h261
-rw-r--r--src/ahl-apidef.json60
-rw-r--r--src/ahl-binding.c1245
-rw-r--r--src/ahl-binding.h180
-rw-r--r--src/ahl-config.c126
-rw-r--r--src/ahl-deviceenum.c350
-rw-r--r--src/ahl-interface.h115
-rw-r--r--src/ahl-policy-utils.c312
-rw-r--r--src/ahl-policy-utils.h33
-rw-r--r--src/ahl-policy.c1424
-rw-r--r--src/ahl-policy.h90
25 files changed, 2801 insertions, 2417 deletions
diff --git a/conf.d/project/.asoundrc b/conf.d/project/.asoundrc
new file mode 100644
index 0000000..cba88e5
--- /dev/null
+++ b/conf.d/project/.asoundrc
@@ -0,0 +1,164 @@
+
+
+##### AGL Conf #####
+pcm.SoftMixer {
+ type dmix
+ ipc_key 1024
+ ipc_key_add_uid false
+ ipc_perm 0666 # mixing for all users
+
+ # Define target effective sound card (cannot be a plugin)
+ slave {
+ pcm "hw:0" # Main sound card
+ # periods 8
+ period_size 1024
+ buffer_size 4096
+ }
+
+ # DMIX can only map two channels
+ bindings {
+ 0 0
+ 1 1
+ }
+}
+
+pcm.SoftMixer_DriverHR {
+ type dmix
+ ipc_key 1024
+ ipc_key_add_uid false
+ ipc_perm 0666 # mixing for all users
+
+ # Define target effective sound card (cannot be a plugin)
+ slave {
+ pcm "hw:1" # Alternate sound card / dummy
+ # periods 8
+ period_size 1024
+ buffer_size 4096
+ }
+
+ # DMIX can only map two channels
+ bindings {
+ 0 0
+ 1 1
+ }
+}
+
+pcm.SoftMixer_RSE {
+ type dmix
+ ipc_key 1024
+ ipc_key_add_uid false
+ ipc_perm 0666 # mixing for all users
+
+ # Define target effective sound card (cannot be a plugin)
+ slave {
+ pcm "hw:2" # Alternate sound card / dummy
+ # periods 8
+ period_size 1024
+ buffer_size 4096
+ }
+
+ # DMIX can only map two channels
+ bindings {
+ 0 0
+ 1 1
+ }
+}
+
+pcm.Entertainment_Main {
+ type softvol
+ slave.pcm "SoftMixer"
+ control{
+ name "Entertainment_Vol"
+ card 0
+ }
+}
+
+pcm.Guidance_Main {
+ type softvol
+ slave.pcm "SoftMixer"
+ control{
+ name "Guidance_Vol"
+ card 0
+ }
+}
+
+pcm.Communications_Main {
+ type softvol
+ slave.pcm "SoftMixer"
+ control{
+ name "Communications_Vol"
+ card 0
+ }
+}
+
+pcm.Notification_Main {
+ type softvol
+ slave.pcm "SoftMixer"
+ control{
+ name "Notification_Vol"
+ card 0
+ }
+}
+
+pcm.Warning_Main {
+ type softvol
+ slave.pcm "SoftMixer"
+ control{
+ name "Warning_Vol"
+ card 0
+ }
+}
+
+pcm.Entertainment_DriverHR {
+ type softvol
+ slave.pcm "SoftMixer_DriverHR"
+ control{
+ name "Entertainment_Vol"
+ card 1
+ }
+}
+
+pcm.Guidance_DriverHR {
+ type softvol
+ slave.pcm "SoftMixer_DriverHR"
+ control{
+ name "Guidance_Vol"
+ card 1
+ }
+}
+
+pcm.Communications_DriverHR {
+ type softvol
+ slave.pcm "SoftMixer_DriverHR"
+ control{
+ name "Communications_Vol"
+ card 1
+ }
+}
+
+pcm.Notification_DriverHR {
+ type softvol
+ slave.pcm "SoftMixer_DriverHR"
+ control{
+ name "Notification_Vol"
+ card 1
+ }
+}
+
+pcm.Warning_DriverHR {
+ type softvol
+ slave.pcm "SoftMixer_DriverHR"
+ control{
+ name "Warning_Vol"
+ card 1
+ }
+}
+
+pcm.Entertainment_RSE {
+ type softvol
+ slave.pcm "SoftMixer_RSE"
+ control{
+ name "Entertainment_Vol"
+ card 2
+ }
+}
diff --git a/conf.d/project/CMakeLists.txt b/conf.d/project/CMakeLists.txt
deleted file mode 100644
index fd4d454..0000000
--- a/conf.d/project/CMakeLists.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-###########################################################################
-# Copyright 2015, 2016, 2017 IoT.bzh
-#
-# author: Fulup Ar Foll <fulup@iot.bzh>
-#
-# 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 anything not starting with _
-PROJECT_SUBDIRS_ADD(${PROJECT_SRC_DIR_PATTERN})
-
-
diff --git a/conf.d/project/agl-ahl-config.json b/conf.d/project/agl-ahl-config.json
new file mode 100644
index 0000000..f49c0be
--- /dev/null
+++ b/conf.d/project/agl-ahl-config.json
@@ -0,0 +1,128 @@
+{
+ "version": "0.2.0",
+ "policy_module": "AudioPolicy_v1",
+ "description": "High-level binding configuration file",
+ "note": "Devices and routings are always listed in order of priority (for device selection rules)",
+ "hal_list": ["ensoniq","usbaudio"],
+ "audio_roles": [
+ {
+ "name": "Warning",
+ "id": 0,
+ "description": "Safety-relevant or critical alerts/alarms",
+ "priority": 100,
+ "output": [
+ "alsa.plug:Warning_Main",
+ "alsa.plug:Warning_DriverHR"
+ ],
+ "actions": [
+ "emergency_brake",
+ "collision_warning",
+ "blind_spot_warning"
+ ],
+ "interupt_behavior": "pause"
+ },
+ {
+ "name": "Guidance",
+ "id": 1,
+ "description": "Important user information where user action is expected (e.g. navigation instruction)",
+ "priority": 25,
+ "output": [
+ "alsa.plug:Guidance_Main",
+ "alsa.plug:Guidance_DriverHR"
+ ],
+ "actions": [
+ "lane_guidance_left",
+ "lane_guidance_right",
+ "destination_reached"
+ ],
+ "interupt_behavior": "continue"
+ },
+ {
+ "name": "Notification",
+ "id": 2,
+ "description": "HMI or else notifications (e.g. touchscreen events, speech recognition on/off,...)",
+ "priority": 0,
+ "output": [
+ "alsa.plug:Notification_Main",
+ "alsa.plug:Notification_DriverHR"
+ ],
+ "actions": [
+ "home",
+ "context_switch",
+ "accept",
+ "cancel",
+ "selection_change"
+ ],
+ "interupt_behavior": "cancel"
+ },
+ {
+ "name": "Communication",
+ "id": 3,
+ "description": "Voice communications (e.g. handsfree, speech recognition)",
+ "priority": 50,
+ "output": [
+ "alsa.plug:Communications_Main",
+ "alsa.plug:Communications_DriverHR",
+ ],
+ "input": [
+ "alsa.hw:0",
+ ],
+ "actions": [
+ "bt_device_connected",
+ "bt_device_disconnected",
+ "sms_received"
+ ],
+ "interupt_behavior": "continue"
+ },
+ {
+ "name": "Entertainment",
+ "id": 4,
+ "description": "Multimedia content (e.g. tuner, media player, etc.)",
+ "priority": 0,
+ "output": [
+ "alsa.plug:Entertainment_Main",
+ "alsa.plug:Entertainment_DriverHR",
+ ],
+ "interupt_behavior": "pause"
+ },
+ {
+ "name": "System",
+ "id": 5,
+ "description": "System level content or development",
+ "priority": 100,
+ "output": [
+ "alsa.hw:0"
+ ],
+ "input": [
+ "alsa.hw:0"
+ ],
+ "interupt_behavior": "continue"
+ },
+ {
+ "name": "Startup",
+ "id": 6,
+ "description": "Early (startup) sound",
+ "priority": 100,
+ "output": [
+ "alsa.hw:0"
+ ],
+ "actions": [
+ "welcome_sound"
+ ],
+ "interupt_behavior": "pause"
+ },
+ {
+ "name": "Shutdown",
+ "id": 7,
+ "description": "Late (shutdown) sound",
+ "priority": 100,
+ "output": [
+ "alsa.hw:0"
+ ],
+ "actions": [
+ "goodbye_sound"
+ ],
+ "interupt_behavior": "cancel"
+ }
+ ]
+}
diff --git a/conf.d/project/alsa.d/asoundrc.sample b/conf.d/project/alsa.d/asoundrc.sample
deleted file mode 100644
index 8976077..0000000
--- a/conf.d/project/alsa.d/asoundrc.sample
+++ /dev/null
@@ -1,146 +0,0 @@
-#
-# Author: Fulup Ar Foll
-# Object: PCM hook type
-#
-# Test : Note: Jabra_USB=hw:v1340
-# Check SoundCard speaker-test -Dhw:v1340 -c2 -twav
-# Check MixerPCM speaker-test -DMyMixerPCM -c2 -twav
-# Check HookPCM speaker-test -DMyNavigationHook -c2 -twav
-# Check NavPCM speaker-test -DMyNavPCM -c2 -twav
-# MultiMedia aplay -DDMyNavPCM /usr/share/sounds/alsa/test.wav
-#
-# Bug/Feature: when softvol control is initialised from plugin and not
-# from AGL binding. At 1st run ctl has invalid TLV and cannot be
-# use. Bypass Solution:
-# * start audio-binder before playing sound (binding create control before softvol plugin)
-# * run a dummy aplay -DMyNavPCM "" to get a clean control
-#
-# References: https://www.spinics.net/lists/alsa-devel/msg54235.html
-# --------------------------------------------------------------------
-
-# Mixer PCM allow to play multiple stream simultaneously
-# ------------------------------------------------------
-pcm.MyMixerPCM {
- type dmix
- ipc_key 1024
- ipc_key_add_uid false
- ipc_perm 0666 # mixing for all users
-
- # Define target effective sound card (cannot be a plugin)
- slave {
- pcm "hw:v1340" #Jabra Solmate
- period_time 0
- period_size 1024
- buffer_size 8192
- rate 44100
- }
-
- # DMIX can only map two channels
- bindings {
- 0 0
- 1 1
- }
-}
-
-# Define a Hook_type with a private sharelib
-# -------------------------------------------
-pcm_hook_type.MyHookPlugin {
- install "AlsaInstallHook"
- lib "/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build/Alsa-Plugin/Alsa-Policy-Hook/policy_hook_cb.so"
-}
-
-
-# Define a HookedPCM that point to Hook_type sharelib
-# ----------------------------------------------------
-pcm.MyNavigationHook {
- type hooks
- slave.pcm "MyMixerPCM"
- # Defined used hook sharelib and provide arguments/config to install func
- hooks.0 {
- type "MyHookPlugin"
- hook_args {
- verbose true # print few log messages (default false);
-
- # Every Call should return OK in order PCM to open (default timeout 100ms)
- uri "ws://localhost:1234/api?token=audio-agent-token&uuid=audio-agent-session"
- request {
- # Request autorisation to write on navigation
- RequestNavigation {
- api "control"
- verb "dispatch"
- query "{'target':'navigation', 'args':{'device':'Jabra SOLEMATE v1.34.0'}}"
- }
- }
- # map event reception to self generated signal
- event {
- pause 30
- resume 31
- stop 3
- }
- }
- }
-}
-
-# If hardware does not support mixer emulate it with softvol
-# -----------------------------------------------------------
-pcm.MyMultimediaPCM {
- type softvol
-
- # Point Slave on HOOK for policies control
- slave.pcm "MyNavigationHook"
-
- # resolution=HAL(valMax+1) (default=256)
- resolution 256
-
- # name should match with HAL but do not set card=xx
- control.name "Playback Navigation"
-
- # Make this plugin visible from aplay -L
- hint {
- show on
- description "Navigation SolftVol PCM"
- }
-}
-
-# If hardware does not support mixer emulate it with softvol
-# -----------------------------------------------------------
-pcm.MyNavPCM {
- type softvol
-
- # Point Slave on HOOK for policies control
- slave.pcm "MyNavigationHook"
-
- # resolution=HAL(valMax+1) (default=256)
- resolution 256
-
- # name should match with HAL but do not set card=xx
- control.name "Playback Navigation"
-
- # Make this plugin visible from aplay -L
- hint {
- show on
- description "Navigation SolftVol PCM"
- }
-}
-
-# If hardware does not support mixer emulate it with softvol
-# -----------------------------------------------------------
-pcm.MyAlarmPCM {
- type softvol
-
- # Point Slave on HOOK for policies control
- slave.pcm "MyNavigationHook"
-
- # resolution=HAL(valMax+1) (default=256)
- resolution 256
-
- # name should match with HAL but do not set card=xx
- control.name "Playback Navigation"
-
- # Make this plugin visible from aplay -L
- hint {
- show on
- description "Navigation SolftVol PCM"
- }
-}
-
diff --git a/conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf b/conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf
deleted file mode 100644
index f6608a0..0000000
--- a/conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-Comment "Leon internal card"
-
-SectionUseCase."HiFi" {
- File "HiFi.conf"
- Comment "Default"
-}
diff --git a/conf.d/project/alsa.d/ucm.sample/HiFi.conf b/conf.d/project/alsa.d/ucm.sample/HiFi.conf
deleted file mode 100644
index 9a53c8c..0000000
--- a/conf.d/project/alsa.d/ucm.sample/HiFi.conf
+++ /dev/null
@@ -1,84 +0,0 @@
-SectionVerb {
- EnableSequence [
- cdev "hw:PCH"
-
- cset "name='Master Playback Switch' on"
- cset "name='Headphone Playback Switch' off"
- cset "name='Speaker Playback Switch' on"
-
- cset "name='Capture Switch' on"
- cset "name='Capture Volume' 39"
- cset "name='Mic Boost Volume' 2"
- cset "name='Internal Mic Boost Volume' 0"
- #cset "name='Capture Source' 0"
- ]
- DisableSequence [
- ]
- Value {
- TQ "Music"
- OutputDspName "speaker_eq"
- PlaybackPCM "hw:PCH,0"
- }
-}
-
-SectionDevice."Headphone".0 {
- Value {
- JackName "Headphone Jack"
- OutputDspName "Jheadphone"
- }
- EnableSequence [
- cdev "hw:PCH"
-
- cset "name='Speaker Playback Switch' off"
- cset "name='Headphone Playback Switch' on"
- ]
- DisableSequence [
- cdev "hw:PCH"
-
- cset "name='Headphone Playback Switch' off"
- cset "name='Speaker Playback Switch' on"
- ]
-}
-
-SectionDevice."Mic".0 {
- Value {
- JackName "Mic Jack"
- }
- EnableSequence [
- cdev "hw:PCH"
-
- #cset "name='Capture Source' 1"
- ]
- DisableSequence [
- cdev "hw:PCH"
-
- cset "name='Capture Source' 0"
- ]
-}
-
-SectionModifier."RecordMedia".0 {
- SupportedDevice [
- "Headphone"
- ]
- EnableSequence [
- cdev "hw:PCH"
- ]
-
- DisableSequence [
- cdev "hw:PCH"
- ]
-
- TransitionSequence."ToModifierName" [
- cdev "hw:PCH"
- ]
-
- # Optional TQ and ALSA PCMs
- Value {
- TQ Voice
- CapturePCM "hw:1"
- PlaybackVolume "name='Master Playback Volume',index=2"
- PlaybackSwitch "name='Master Playback Switch',index=2"
- }
-
-}
-
diff --git a/conf.d/project/alsa.d/ucm.sample/README b/conf.d/project/alsa.d/ucm.sample/README
deleted file mode 100644
index e7f08ae..0000000
--- a/conf.d/project/alsa.d/ucm.sample/README
+++ /dev/null
@@ -1,2 +0,0 @@
-Should match sound card name ex: "HDA Intel PCH"
-cp -r . /usr/share/alsa/ucm
diff --git a/conf.d/project/json.d/ahl-binding.json b/conf.d/project/json.d/ahl-binding.json
deleted file mode 100644
index d53a9aa..0000000
--- a/conf.d/project/json.d/ahl-binding.json
+++ /dev/null
@@ -1,127 +0,0 @@
-{
- "description" : "Audio Plugin configuration files",
- "Audioroles" : {
- "Description" : "Specify configuration and default value for Audio",
- "Warning" :{
- "Attributes" :{
- "Interruptible" : "No",
- "Routing" : "None"
- },
- "devices" :{
- "output" : ["Speaker", "Lineout", "HDMI", "Default"]
- }
- },
- "Guidance" : {
- "Attributes" :{
- "Interruptible" : "Yes",
- "Routing" : "None"
- },
- "devices" :{
- "output" : ["Speaker", "Lineout", "Default"]
- }
- },
- "Notification" : {
- "Attributes" :{
- "Interruptible" : "Yes",
- "Routing" : "None"
- },
- "devices" :{
- "output" : ["Speaker", "Lineout", "HDMI", "Default"]
- }
- },
- "Communications" :{
- "Attributes" :{
- "Interruptible" : "Yes",
- "Routing" : "Custom"
- },
- "devices" :{
- "output" : [ "Speaker", "Lineout", "Default"],
- "input" : [ "BT", "USB", "Headset", "Linein", "Default"]
- },
- "Routing" :{
- "phone":{
- "Input" : "BT",
- "Output" : "Speaker"
- },
- "usb":{
- "Input" : "USB",
- "Output" : "Speaker"
- },
- "headset":{
- "Input" : "Headset",
- "Output" : "Speaker"
- },
- "phone_aux":{
- "Input" : "BT",
- "Output" : "Lineout"
- },
- "usb_aux":{
- "Input" : "USB",
- "Output" : "Lineout"
- }
-
- }
-
-
- },
- "Entertainment" : {
- "Attributes" :{
- "Interruptible" : "Yes",
- "Routing" : "Custom"
- },
- "devices" :{
- "output" : [ "Headphone", "Speaker", "Lineout", "Default"],
- "input" : [ "BT", "USB", "Linein", "Default"]
- },
- "Routing" : {
- "phone":{
- "Input" : "BT",
- "Output" : "Speaker"
- },
- "phone_headphone":{
- "Input" : "BT",
- "Output" : "Headphone"
- },
- "phone_aux":{
- "Input" : "BT",
- "Output" : "Lineout"
- },
- "usb":{
- "Input" : "USB",
- "Output" : "Speaker"
- },
- "usb_headphone":{
- "Input" : "USB",
- "Output" : "Headphone"
- },
- "usb_aux":{
- "Input" : "USB",
- "Output" : "Lineout"
- },
- "aux":{
- "Input" : "Linein",
- "Output" : "Speaker"
- },
- "aux_headphone":{
- "Input" : "Linein",
- "Output" : "Headphone"
- },
- "aux_aux":{
- "Input" : "Linein",
- "Output" : "Lineout"
- }
-
- }
- },
- "System" : {
- "Attributes" :{
- "Interruptible" : "Yes",
- "Routing" : "All"
- },
- "devices" :{
- "output" : [ "Speaker", "Headset", "Headphone", "Handset", "BT", "HDMI", "Lineout", "USB", "Default"],
- "input" : [ "BT", "Linein", "USB", "Voice", "Default"]
- }
- }
- }
-} \ No newline at end of file
diff --git a/conf.d/project/json.d/onload-audio-control.json b/conf.d/project/json.d/onload-audio-control.json
deleted file mode 100644
index 14d6970..0000000
--- a/conf.d/project/json.d/onload-audio-control.json
+++ /dev/null
@@ -1,137 +0,0 @@
-{
- "$schema": "ToBeDone",
- "metadata": {
- "label": "audio-policy-control-configuration",
- "info": "Provide Default Audio Policy Control configuration",
- "version": "1.0"
- },
- "onload": [{
- "label": "onload-default",
- "info": "onload initialisation config",
- "plugin": {
- "label" : "_MyPlug",
- "sharelib": "ctl-audio-plugin-sample.ctlso",
- "lua2c": ["Lua2cHelloWorld1", "Lua2cHelloWorld2"]
- },
- "require": ["intel-hda"],
- "actions": [
- {
- "label": "onload-sample-cb",
- "info": "Call control sharelib install entrypoint",
- "callback": "SamplePolicyInit",
- "args": {
- "arg1": "first_arg",
- "nextarg": "second arg value"
- }
- }, {
- "label": "onload-sample-api",
- "info": "Assert AlsaCore Presence",
- "api": "alsacore",
- "verb": "ping",
- "args": {"data": "none"}
- }, {
- "label": "onload-hal-lua",
- "info": "Load avaliable HALs",
- "lua": "_Alsa_Get_Hal"
- }
- ]
- }],
- "controls":
- [
- {
- "label": "duckentertainment_lowprioritypriority",
- "api": "intel-hda",
- "verb": "SetRTPC",
- "args": {
- "value": "-30"
- }
- },
-
- {
- "label": "multimedia",
- "actions": {
- "label": "multimedia-control-lua",
- "info": "Call Lua Script function Test_Lua_Engin",
- "lua": "_Audio_Set_Multimedia"
- }
- }, {
- "label": "navigation",
- "actions": {
- "label": "navigation-control-lua",
- "info": "Call Lua Script to set Navigation",
- "lua": "_Audio_Set_Navigation"
- }
- }, {
- "label": "emergency",
- "actions": {
- "label": "emergency-control-ucm",
- "lua": "_Audio_Set_Emergency"
- }
- }, {
- "label": "multi-step-sample",
- "info" : "all actions must succeed for control to be accepted",
- "actions": [{
- "label": "multimedia-control-cb",
- "info": "Call Sharelib Sample Callback",
- "callback": "sampleControlNavigation",
- "args": {
- "arg1": "snoopy",
- "arg2": "toto"
- }
- }, {
- "label": "duckentertainment_lowprioritypriority",
- "api": "hal",
- "verb": "SetRTPC",
- "args": {
- "value": "-30"
- }
- }, {
- "label": "navigation-control-lua",
- "info": "Call Lua Script to set Navigation",
- "lua": "_Audio_Set_Navigation"
- }]
- }
- ],
- "events":
- [
- {
- "label": "SampleEvent1",
- "info": "define action when receiving a given event",
- "actions": [
- {
- "label": "Event Callback-1",
- "callback": "SampleControlEvent",
- "args": {
- "arg": "action-1"
- }
- }, {
- "label": "Event Callback-2",
- "callback": "SampleControlEvent",
- "args": {
- "arg": "action-2"
- }
- }
- ]
- },
- {
- "label": "SampleEvent2",
- "info": "define action when receiving a given event",
- "actions": [
- {
- "label": "Event Callback-1",
- "callback": "SampleControlEvent",
- "args": {
- "arg": "action-1"
- }
- }, {
- "label": "Event Callback-2",
- "callback": "SampleControlEvent",
- "args": {
- "arg": "action-2"
- }
- }
- ]
- }
- ]
-}
-
diff --git a/htdocs/audio-control.html b/htdocs/audio-control.html
deleted file mode 100644
index 6b7872f..0000000
--- a/htdocs/audio-control.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<html>
-<head>
- <title>Basic Audio Hardware Abstraction Layer Test</title>
- <link rel="stylesheet" href="AudioBinding.css">
- <script type="text/javascript" src="AFB-websock.js"></script>
- <script type="text/javascript" src="AudioBinding.js"></script>
-</head>
-
-<body onload="init('hal_registry','alsacore', 'hallist')">
-
- <h1>Simple Audio Control Test</h1>
- <button id="connected" onclick="init('hal_registry','alsacore', 'hallist')">Binder WS Fail</button>
- <br><br>
- <b>Selected HAL </b>
- <select id='hal_registry'></select>
-
- <b>API Verbosity </b>
- <select id='api_verbosity' onclick='mode=this.value'>
- <option value='0'>Quiet</option>
- <option value='1'>Compact</option>
- <option value='2'>Verbose</option>
- <option value='3'>Full</option>
- </select>
- <br>
- <br>
-
- <ol>
-
- <li><button onclick="callbinder('control','dispatch' ,{'target':'navigation','args':{'apihal':sndcard}});">Dispatch Navigation</button></li>
- <li><button onclick="callbinder('control','dispatch' ,{'target':'multimedia','args':{'apihal':sndcard}});">Dispatch Mutimedia</button></li>
- <li><button onclick="callbinder('control','dispatch' ,{'target':'emergency' });">Dispatch Emergency</button></li>
- <br>
- <li><button onclick="callbinder('control','lua_dostring','print(\'Bonjours\'); return true, 1234');">LUA string</button></li>
- <li><button onclick="callbinder('control','lua_docall' , {'func':'_Simple_Echo_Args', 'args':{speed:20}});">LUA function</button></li>
- <li><button onclick="callbinder('control','lua_doscript', {'script':'helloworld.lua', args:{'arg1':'abcd', 'next':7890, 'last':[1,2,3,4]}});">LUA script</button></li>
- <li><button onclick="callbinder('control','lua_docall' , {'func':'_Simple_Timer_Test', args:{label:'MyTimer', 'delay':3000, 'count':10}});">LUA Timer</button></li>
-
- </ol>
-
- <div id="main" style="visibility:hidden">
- <ol>
- <li>Question <pre id="question"></pre>
- <li>Response <pre id="output"></pre>
- <li>Events: <pre id="outevt"></pre>
- </ol>
- </div>
diff --git a/htdocs/audio-logic.html b/htdocs/audio-logic.html
deleted file mode 100644
index c31282a..0000000
--- a/htdocs/audio-logic.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<html>
-<head>
- <title>High Level API Simple Test Page</title>
-
- <script type="text/javascript" src="AFB-websock.js"></script>
- <script type="text/javascript" src="AudioBinding.js"></script>
-</head>
-
-ToBeDone \ No newline at end of file
diff --git a/htdocs/audiohl-demo.html b/htdocs/audiohl-demo.html
deleted file mode 100644
index e222707..0000000
--- a/htdocs/audiohl-demo.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<html>
-<head>
- <link rel="stylesheet" href="AudioBinding.css">
- <title>Audio High Level Test</title>
-
- <script type="text/javascript" src="AFB-websock.js"></script>
- <script type="text/javascript" src="AudioBinding.js"></script>
-</head>
-
-<body onload="init('audiohl')">
-
- <button id="connected" onclick="init('audiohl');">Binder WS Fail</button>
-
- <li>Use Case 1: Automatic EndPoint Selection</li>
- <br>
- <ol>
- <li><button onclick="callbinder('audiohl','stream_open', {audio_role:4,endpoint_type:1})">stream_open('Entertainment','Sink')</button></li>
- <li><button onclick="callbinder('audiohl','set_endpoint_volume', {endpoint_type:1,endpoint_id:2,volume:'10+'})">set_endpoint_volume('Sink',2,'10+')</button></li>
- <li><button onclick="callbinder('audiohl','stream_close', {stream_id:1})">stream_close(streamID)</button></li>
- <br>
- </ol>
-
- <li>Use Case 2: User Select EndPoint</li>
- <br>
- <ol>
- <li><button onclick="callbinder('audiohl','get_sinks', {audio_role:4})">get_sinks('Entertainment')</button></li>
- <li><button onclick="callbinder('audiohl','stream_open', {audio_role:4,endpoint_type:1,endpoint_id:10})">stream_open('Entertainment','Sink',sinkID)</button></li>
- <li><button onclick="callbinder('audiohl','post_sound_event', {event_name:'PlaySound',media_name:'HomeButton.wav'})">post_sound_event('PlaySound','HomeButton.wav')</button></li>
- <li><button onclick="callbinder('audiohl','stream_close', {stream_id:1})">stream_close(streamID)</button></li>
- <br>
- </ol>
-
- <li>Use Case 3: Select Routing</li>
- <br>
- <ol>
- <li><button onclick="callbinder('audiohl','get_available_routings', {audio_role:4})">get_available_routings('Entertainment')</button></li>
- <li><button onclick="callbinder('audiohl','add_routing', {audio_role:4,routing_id:2})">add_routing('Entertainment',routingID)</button></li>
- <br>
- </ol>
-
- <div id="main" style="visibility:hidden">
- <ol>
- <li>Question <pre id="question"></pre>
- <li>Response <pre id="output"></pre>
- <li>Events: <pre id="outevt"></pre>
- </ol>
- </div>
diff --git a/htdocs/audiohl.html b/htdocs/audiohl.html
index ec4dd43..418cf7a 100644
--- a/htdocs/audiohl.html
+++ b/htdocs/audiohl.html
@@ -1,15 +1,16 @@
<html>
+
<head>
<link rel="stylesheet" href="AudioBinding.css">
<title>Audio High Level Test</title>
-
+
<script type="text/javascript" src="AFB-websock.js"></script>
<script type="text/javascript" src="AudioBinding.js"></script>
</head>
-
-<body onload="init('audiohl'); ep_type=1 ; ar='Entertainment' ; ep_id=0 ; s_id=0 ; val='0' ; p_name='balance' ; st_state=0">
-
- <button id="connected" onclick="init('audiohl');">Binder WS Fail</button> <br><br>
+
+<body onload="init('audiohl'); ep_type='sink' ; ar='Entertainment' ; ep_id=0 ; s_id=0 ; prop_val=0 ; vol_val='0' ; p_name='balance' ; st_state='idle' ; st_mute='off'">
+
+ <button id="connected" onclick="init('audiohl');">Binder WS Fail</button> <br><br>
<button id="monitoring" onclick="window.open('/monitoring/monitor.html','_monitor_audio')">Debug/Monitoring</a></button>
<b>Audio Role</b>
@@ -24,59 +25,66 @@
<option value='Shutdown'>Shutdown</option>
</select><br>
<b>Endpoint Type</b>
- <select select_id='endpoint_type_list' onclick='ep_type=this.selectedIndex'>
- <option value=0>Source</option>
- <option selected value=1>Sink</option>
+ <select select_id='endpoint_type_list' onclick='ep_type=this.value'>
+ <option value='source'>Source</option>
+ <option selected value='sink'>Sink</option>
</select><br>
<b><label for="epidsel">Endpoint ID</label></b>
<input id="epidsel" type="number" value="0" min=0 step=1 maxlength=4 onchange='ep_id=eval(parseInt(this.value))'><br>
<b><label for="stidsel">Stream ID</label></b>
<input id="stidsel" type="number" value="0" min=0 step=1 maxlength=4 onchange='s_id=eval(parseInt(this.value))'><br>
- <b>Property</b>
- <select select_id='property_name_list' onclick='p_name=this.value'>
+ <b>Stream State</b>
+ <select select_id='stream_state' onclick='st_state=this.value'>
+ <option selected value='idle'>Idle</option>
+ <option value='running'>Running</option>
+ <option value='paused'>Paused</option>
+ </select><br>
+ <b>Stream Mute State</b>
+ <select select_id='stream_mute' onclick='st_mute=this.value'>
+ <option selected value='off'>Unmuted</option>
+ <option value='on'>Muted</option>
+ </select><br>
+ <b>Property</b>
+ <select select_id='property_name_list' onclick='p_name=this.value'>
<option selected value='balance'>Balance</option>
<option value='fade'>Fade</option>
<option value='eq_bass'>EQ Bass</option>
<option value='eq_mid'>EQ Mid</option>
- <option value='eq_treble'>EQ Treble</option>
- <option value='preset_name'>Preset Name</option>
- </select><br>
- <b><label for="valpropsel">Volume/Property Value</label></b>
- <input id="valpropsel" type="number" value="0" min=0 max=100 step=1 maxlength=4 onchange='val=this.value'><br>
- <b>Stream Active/Mute State</b>
- <select select_id='state_value_list' onclick='st_state=this.selectedIndex'>
- <option selected value='off'>Off</option>
- <option value='on'>On</option>
+ <option value='eq_treble'>EQ Treble</option>
</select><br>
+ <b><label for="valpropsel">Volume Value</label></b>
+ <input id="volvalsel" type="number" value="0" min=0 max=100 step=1 maxlength=4 onchange='vol_val=this.value'><br>
+ <b><label for="valpropsel">Property Value</label></b>
+ <input id="propvalsel" type="number" value="0" min=0 max=100 step=1 maxlength=4 onchange='prop_val=eval(parseInt(this.value))'><br>
<br>
<ol>
- <li><button onclick="callbinder('audiohl','get_sources', {audio_role:ar})">get_sources(audio_role)</button></li>
- <li><button onclick="callbinder('audiohl','get_sinks', {audio_role:ar})">get_sinks(audio_role)</button></li>
- <li><button onclick="callbinder('audiohl','stream_open', {audio_role:ar,endpoint_type:ep_type})">stream_open(audio_role,endpoint_type)</button></li>
- <li><button onclick="callbinder('audiohl','stream_open', {audio_role:ar,endpoint_type:ep_type,endpoint_id:ep_id})">stream_open(audio_role,enpoint_type,endpoint_id)</button></li>
- <li><button onclick="callbinder('audiohl','stream_close', {stream_id:s_id})">stream_close(stream_id)</button></li>
- <li><button onclick="callbinder('audiohl','get_stream_info', {stream_id:s_id})">get_stream_info(stream_id)</button></li>
- <li><button onclick="callbinder('audiohl','set_stream_state', {stream_id:s_id,state:st_state})">set_stream_state(stream_id,stream_state)</button></li>
- <li><button onclick="callbinder('audiohl','set_stream_mute', {stream_id:s_id,mute:st_state})">set_stream_mute(stream_id,mute)</button></li>
- <li><button onclick="callbinder('audiohl','set_volume', {endpoint_type:ep_type,endpoint_id:ep_id,volume:val})">set_volume(endpoint_type,endpoint_id,value)</button></li>
- <li><button onclick="callbinder('audiohl','get_volume', {endpoint_type:ep_type,endpoint_id:ep_id})">get_volume(endpoint_type,endpoint_id)</button></li>
- <li><button onclick="callbinder('audiohl','get_list_properties', {endpoint_type:ep_type,endpoint_id:ep_id})">get_list_properties(endpoint_type,endpoint_id)</button></li>
- <li><button onclick="callbinder('audiohl','set_property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name,value:val})">set_property(endpoint_type,endpoint_id,property,value)</button></li>
- <li><button onclick="callbinder('audiohl','get_property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name})">get_property(endpoint_type,endpoint_id,property)</button></li>
- <li><button onclick="callbinder('audiohl','get_list_events', {audio_role:ar})">get_list_events(audio_role)</button></li>
- <li><button onclick="callbinder('audiohl','post_event', {event_name:'play_sound',audio_role:ar,media_name:'HomeButton.wav'})">post_event(play_sound,audio_role,'HomeButton.wav')</button></li>
- <li><button onclick="callbinder('audiohl','subscribe', {events:['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_event']})">subscribe(['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_event'])</button>
- <li><button onclick="callbinder('audiohl','unsubscribe', {events:['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_event']})">unsubscribe(['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_event'])</button>
- </li>
-
- <br>
+ <li><button onclick="callbinder('audiohl','get_sources', {audio_role:ar})">get_sources(audio_role)</button></li>
+ <li><button onclick="callbinder('audiohl','get_sinks', {audio_role:ar})">get_sinks(audio_role)</button></li>
+ <li><button onclick="callbinder('audiohl','stream_open', {audio_role:ar,endpoint_type:ep_type})">stream_open(audio_role,endpoint_type)</button></li>
+ <li><button onclick="callbinder('audiohl','stream_open', {audio_role:ar,endpoint_type:ep_type,endpoint_id:ep_id})">stream_open(audio_role,enpoint_type,endpoint_id)</button></li>
+ <li><button onclick="callbinder('audiohl','stream_close', {stream_id:s_id})">stream_close(stream_id)</button></li>
+ <li><button onclick="callbinder('audiohl','get_stream_info', {stream_id:s_id})">get_stream_info(stream_id)</button></li>
+ <li><button onclick="callbinder('audiohl','set_stream_state', {stream_id:s_id,state:st_state})">set_stream_state(stream_id,stream_state)</button></li>
+ <li><button onclick="callbinder('audiohl','set_stream_mute', {stream_id:s_id,mute:st_mute})">set_stream_mute(stream_id,mute)</button></li>
+ <li><button onclick="callbinder('audiohl','set_volume', {endpoint_type:ep_type,endpoint_id:ep_id,volume:vol_val})">set_volume(endpoint_type,endpoint_id,value)</button></li>
+ <li><button onclick="callbinder('audiohl','get_volume', {endpoint_type:ep_type,endpoint_id:ep_id})">get_volume(endpoint_type,endpoint_id)</button></li>
+ <li><button onclick="callbinder('audiohl','set_property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name,value:prop_val})">set_property(endpoint_type,endpoint_id,property,value)</button></li>
+ <li><button onclick="callbinder('audiohl','get_property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name})">get_property(endpoint_type,endpoint_id,property)</button></li>
+ <li><button onclick="callbinder('audiohl','get_endpoint_info', {endpoint_type:ep_type,endpoint_id:ep_id})">get_endpoint_info(endpoint_type,endpoint_id)</button></li>
+ <li><button onclick="callbinder('audiohl','get_list_actions', {audio_role:ar})">get_list_actions(audio_role)</button></li>
+ <li><button onclick="callbinder('audiohl','post_action', {action_name:'emergency_brake',audio_role:ar,media_name:'Warning.wav'})">post_action(emergency_brake,audio_role,'Warning.wav')</button></li>
+ <li><button onclick="callbinder('audiohl','subscribe', {events:['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action']})">subscribe(['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'])</button>
+ <li><button onclick="callbinder('audiohl','unsubscribe', {events:['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action']})">unsubscribe(['ahl_endpoint_property_event','ahl_endpoint_volume_event','ahl_post_action'])</button></li>
</ol>
-
+
<div id="main" style="visibility:hidden">
- <ol>
- <li>Question <pre id="question"></pre>
- <li>Response <pre id="output"></pre>
- <li>Events: <pre id="outevt"></pre>
- </ol>
- </div>
+ <ol>
+ <li>Question
+ <pre id="question"></pre>
+ <li>Response
+ <pre id="output"></pre>
+ <li>Events:
+ <pre id="outevt"></pre>
+ </ol>
+ </div> \ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c0f6ca9..4429b0b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -39,7 +39,7 @@ PKG_CHECK_MODULES(GLIB_PKG REQUIRED glib-2.0)
PROJECT_TARGET_ADD(audiohighlevel)
# Define project Targets
- ADD_LIBRARY(${TARGET_NAME} MODULE ahl-binding.c ahl-deviceenum.c ahl-config.c ahl-policy.c)
+ ADD_LIBRARY(${TARGET_NAME} MODULE ahl-binding.c ahl-deviceenum.c ahl-config.c ahl-policy-utils.c ahl-policy.c)
# Generate API-v2 hat from OpenAPI json definition
SET_TARGET_GENSKEL(${TARGET_NAME} ahl-apidef)
diff --git a/src/ahl-apidef.h b/src/ahl-apidef.h
index e23d1e0..fd957ce 100644
--- a/src/ahl-apidef.h
+++ b/src/ahl-apidef.h
@@ -29,128 +29,127 @@ static const char _afb_description_v2_audiohl[] =
"\":{\"type\":\"int\"},\"mute\":{\"type\":\"int\"},\"device_uri\":{\"type"
"\":\"string\"},\"$ref\":\"#/components/schemas/endpoint_info\"}}},\"x-pe"
"rmissions\":{\"streamcontrol\":{\"permission\":\"urn:AGL:permission:audi"
- "o:public:streamcontrol\"},\"routingcontrol\":{\"permission\":\"urn:AGL:p"
- "ermission:audio:public:routingcontrol\"},\"audiostream\":{\"permission\""
- ":\"urn:AGL:permission:audio:public:audiostream\"},\"prioritysignal\":{\""
- "permission\":\"urn:AGL:permission:audio:public:prioritysignal\"},\"sound"
- "event\":{\"permission\":\"urn:AGL:permission:audio:public:soundevent\"},"
- "\"streammonitor\":{\"permission\":\"urn:AGL:permission:audio:public:stre"
- "ammonitor\"}},\"responses\":{\"200\":{\"description\":\"A complex object"
- " array response\",\"content\":{\"application/json\":{\"schema\":{\"$ref\""
- ":\"#/components/schemas/afb-reply\"}}}},\"400\":{\"description\":\"Inval"
- "id arguments\"}}},\"paths\":{\"/get_sources\":{\"description\":\"Retriev"
- "e array of available audio sources\",\"get\":{\"parameters\":[{\"in\":\""
- "query\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\""
- "string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/20"
- "0\",\"response\":{\"description\":\"Array of endpoint info structures\","
- "\"type\":\"array\",\"items\":{\"$ref\":\"#/components/schemas/endpoint_i"
- "nfo\"}}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_sin"
- "ks\":{\"description\":\"Retrieve array of available audio sinks\",\"get\""
- ":{\"parameters\":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\""
- ":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\""
- ":\"#/components/responses/200\",\"response\":{\"description\":\"Array of"
- " endpoint info structures\",\"type\":\"array\",\"items\":{\"$ref\":\"#/c"
- "omponents/schemas/endpoint_info\"}}},\"400\":{\"$ref\":\"#/components/re"
- "sponses/400\"}}}},\"/stream_open\":{\"description\":\"Request opening a "
- "stream\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissio"
- "ns/audiostream\"},\"parameters\":[{\"in\":\"query\",\"name\":\"audio_rol"
- "e\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\""
+ "o:public:streamcontrol\"},\"endpointcontrol\":{\"permission\":\"urn:AGL:"
+ "permission:audio:public:endpointcontrol\"},\"audiostream\":{\"permission"
+ "\":\"urn:AGL:permission:audio:public:audiostream\"},\"soundevent\":{\"pe"
+ "rmission\":\"urn:AGL:permission:audio:public:soundevent\"}},\"responses\""
+ ":{\"200\":{\"description\":\"A complex object array response\",\"content"
+ "\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/af"
+ "b-reply\"}}}},\"400\":{\"description\":\"Invalid arguments\"}}},\"paths\""
+ ":{\"/get_sources\":{\"description\":\"Retrieve array of available audio "
+ "sources\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"audio_ro"
+ "le\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":"
+ "{\"200\":{\"$ref\":\"#/components/responses/200\",\"response\":{\"descri"
+ "ption\":\"Array of endpoint info structures\",\"type\":\"array\",\"items"
+ "\":{\"$ref\":\"#/components/schemas/endpoint_info\"}}},\"400\":{\"$ref\""
+ ":\"#/components/responses/400\"}}}},\"/get_sinks\":{\"description\":\"Re"
+ "trieve array of available audio sinks\",\"get\":{\"parameters\":[{\"in\""
+ ":\"query\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\""
+ ":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses"
+ "/200\",\"response\":{\"description\":\"Array of endpoint info structures"
+ "\",\"type\":\"array\",\"items\":{\"$ref\":\"#/components/schemas/endpoin"
+ "t_info\"}}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/stre"
+ "am_open\":{\"description\":\"Request opening a stream\",\"get\":{\"x-per"
+ "missions\":{\"$ref\":\"#/components/x-permissions/audiostream\"},\"param"
+ "eters\":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\":true,\"s"
+ "chema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"endpoint_typ"
+ "e\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\","
+ "\"name\":\"endpoint_id\",\"required\":false,\"schema\":{\"type\":\"int\""
+ "}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"re"
+ "sponse\":{\"description\":\"Stream information structure\",\"$ref\":\"#/"
+ "components/schemas/stream_info\"}},\"400\":{\"$ref\":\"#/components/resp"
+ "onses/400\"}}}},\"/stream_close\":{\"description\":\"Request closing a s"
+ "tream\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permission"
+ "s/audiostream\"},\"parameters\":[{\"in\":\"query\",\"name\":\"stream_id\""
+ ",\"required\":true,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\""
+ ":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#/compone"
+ "nts/responses/400\"}}}},\"/set_stream_state\":{\"description\":\"Change "
+ "stream active state\",\"get\":{\"x-permissions\":{\"$ref\":\"#/component"
+ "s/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query\",\"nam"
+ "e\":\"stream_id\",\"required\":true,\"schema\":{\"type\":\"int\"}},{\"in"
+ "\":\"query\",\"name\":\"state\",\"required\":true,\"schema\":{\"type\":\""
+ "int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\""
+ "},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_stream_mut"
+ "e\":{\"description\":\"Change stream mute state\",\"get\":{\"x-permissio"
+ "ns\":{\"$ref\":\"#/components/x-permissions/streamcontrol\"},\"parameter"
+ "s\":[{\"in\":\"query\",\"name\":\"stream_id\",\"required\":true,\"schema"
+ "\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"mute\",\"required\":"
+ "true,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\""
+ "#/components/responses/200\"},\"400\":{\"$ref\":\"#/components/responses"
+ "/400\"}}}},\"/get_stream_info\":{\"description\":\"Retrieve stream infor"
+ "mation\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"stream_id"
+ "\",\"required\":true,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"20"
+ "0\":{\"$ref\":\"#/components/responses/200\",\"response\":{\"description"
+ "\":\"Stream information structure\",\"$ref\":\"#/components/schemas/stre"
+ "am_info\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_"
+ "volume\":{\"description\":\"Set volume on endpoint\",\"get\":{\"x-permis"
+ "sions\":{\"$ref\":\"#/components/x-permissions/endpointcontrol\"},\"para"
+ "meters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":true"
+ ",\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_i"
+ "d\",\"required\":true,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\""
+ "name\":\"volume\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\""
+ "responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{"
+ "\"$ref\":\"#/components/responses/400\"}}}},\"/get_volume\":{\"descripti"
+ "on\":\"Get endpoint volume\",\"get\":{\"parameters\":[{\"in\":\"query\","
+ "\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum"
+ "\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"sche"
+ "ma\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/component"
+ "s/responses/200\",\"response\":{\"description\":\"Endpoint volume value\""
+ ",\"type\":\"double\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}"
+ "}}},\"/get_endpoint_info\":{\"description\":\"Retrieve endpoint informat"
+ "ion including its properties\",\"get\":{\"parameters\":[{\"in\":\"query\""
",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enu"
"m\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":false,\"sc"
"hema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/compone"
- "nts/responses/200\",\"response\":{\"description\":\"Stream information s"
- "tructure\",\"$ref\":\"#/components/schemas/stream_info\"}},\"400\":{\"$r"
- "ef\":\"#/components/responses/400\"}}}},\"/stream_close\":{\"description"
- "\":\"Request closing a stream\",\"get\":{\"x-permissions\":{\"$ref\":\"#"
- "/components/x-permissions/audiostream\"},\"parameters\":[{\"in\":\"query"
- "\",\"name\":\"stream_id\",\"required\":true,\"schema\":{\"type\":\"int\""
- "}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"4"
- "00\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_stream_state\":"
- "{\"description\":\"Change stream active state\",\"get\":{\"x-permissions"
- "\":{\"$ref\":\"#/components/x-permissions/streamcontrol\"},\"parameters\""
- ":[{\"in\":\"query\",\"name\":\"stream_id\",\"required\":true,\"schema\":"
- "{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"state\",\"required\":tr"
- "ue,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/"
- "components/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/4"
- "00\"}}}},\"/set_stream_mute\":{\"description\":\"Change stream mute stat"
- "e\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/st"
- "reamcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"stream_id\","
- "\"required\":true,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"nam"
- "e\":\"mute\",\"required\":true,\"schema\":{\"type\":\"int\"}}],\"respons"
- "es\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\""
- ":\"#/components/responses/400\"}}}},\"/get_stream_info\":{\"description\""
- ":\"Retrieve stream information\",\"get\":{\"x-permissions\":{\"$ref\":\""
- "#/components/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"qu"
- "ery\",\"name\":\"stream_id\",\"required\":true,\"schema\":{\"type\":\"in"
- "t\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\""
- "response\":{\"description\":\"Stream information structure\",\"$ref\":\""
- "#/components/schemas/stream_info\"}},\"400\":{\"$ref\":\"#/components/re"
- "sponses/400\"}}}},\"/set_volume\":{\"description\":\"Set volume\",\"get\""
- ":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/streamcontrol"
- "\"},\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"requi"
- "red\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\""
- "endpoint_id\",\"required\":true,\"schema\":{\"type\":\"int\"}},{\"in\":\""
- "query\",\"name\":\"volume\",\"required\":true,\"schema\":{\"type\":\"str"
- "ing\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\""
- "},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_volume\":{"
- "\"description\":\"Get volume\",\"get\":{\"parameters\":[{\"in\":\"query\""
- ",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enu"
- "m\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"sch"
- "ema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/componen"
- "ts/responses/200\",\"response\":{\"description\":\"Endpoint volume value"
- "\",\"type\":\"double\"}},\"400\":{\"$ref\":\"#/components/responses/400\""
- "}}}},\"/get_list_properties\":{\"description\":\"Retrieve a list of supp"
- "orted properties for a particular endpoint\",\"get\":{\"parameters\":[{\""
- "in\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{"
- "\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"require"
- "d\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref"
- "\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#/components/resp"
- "onses/400\"}}}},\"/set_property\":{\"description\":\"Set property value\""
- ",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/strea"
- "mcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_type\""
- ",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"n"
- "ame\":\"endpoint_id\",\"required\":false,\"schema\":{\"type\":\"int\"}},"
- "{\"in\":\"query\",\"name\":\"property_name\",\"required\":true,\"schema\""
- ":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"value\",\"required\""
- ":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\""
- ":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#/components/respon"
- "ses/400\"}}}},\"/get_property\":{\"description\":\"Get property value\","
- "\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\""
+ "nts/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}"
+ "},\"/set_property\":{\"description\":\"Set endpoint property value\",\"g"
+ "et\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/endpointc"
+ "ontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\""
"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name"
"\":\"endpoint_id\",\"required\":false,\"schema\":{\"type\":\"int\"}},{\""
"in\":\"query\",\"name\":\"property_name\",\"required\":true,\"schema\":{"
- "\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/r"
- "esponses/200\",\"response\":{\"description\":\"Property value\",\"type\""
- ":\"double\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/ge"
- "t_list_events\":{\"description\":\"Retrieve a list of supported events f"
- "or a particular audio role\",\"get\":{\"parameters\":[{\"in\":\"query\","
- "\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"string\""
- "}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"4"
- "00\":{\"$ref\":\"#/components/responses/400\"}}}},\"/post_event\":{\"des"
- "cription\":\"Post sound or audio device related event (extendable mechan"
- "ism)\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions"
- "/soundevent\"},\"parameters\":[{\"in\":\"query\",\"name\":\"event_name\""
- ",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\""
- "name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"string\"}"
- "},{\"in\":\"query\",\"name\":\"media_name\",\"required\":false,\"schema\""
- ":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"event_context\",\"r"
- "equired\":false,\"schema\":{\"type\":\"object\"}}],\"responses\":{\"200\""
- ":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#/compone"
- "nts/responses/400\"}}}},\"/subscribe\":{\"description\":\"Subscribe to a"
- "udio high level events\",\"get\":{\"parameters\":[{\"in\":\"query\",\"na"
- "me\":\"events\",\"required\":true,\"schema\":{\"type\":\"array\",\"items"
- "\":{\"type\":\"string\"}}}],\"responses\":{\"200\":{\"$ref\":\"#/compone"
- "nts/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}"
- "},\"/unsubscribe\":{\"description\":\"Unubscribe to audio high level eve"
- "nts\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"events\",\"r"
- "equired\":true,\"schema\":{\"type\":\"array\",\"items\":{\"type\":\"stri"
- "ng\"}}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\""
- "},\"400\":{\"$ref\":\"#/components/responses/400\"}}}}}}"
+ "\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"value\",\"required\":"
+ "true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\""
+ ":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#/components/respon"
+ "ses/400\"}}}},\"/get_property\":{\"description\":\"Get endpoint property"
+ " value\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_"
+ "type\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query"
+ "\",\"name\":\"endpoint_id\",\"required\":false,\"schema\":{\"type\":\"in"
+ "t\"}},{\"in\":\"query\",\"name\":\"property_name\",\"required\":true,\"s"
+ "chema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/com"
+ "ponents/responses/200\",\"response\":{\"description\":\"Property value\""
+ ",\"type\":\"double\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}"
+ "}}},\"/get_list_actions\":{\"description\":\"Retrieve a list of supporte"
+ "d actions for a particular audio role\",\"get\":{\"parameters\":[{\"in\""
+ ":\"query\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\""
+ ":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses"
+ "/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/post_act"
+ "ion\":{\"description\":\"Post sound or audio device related action event"
+ " (extendable mechanism)\",\"get\":{\"x-permissions\":{\"$ref\":\"#/compo"
+ "nents/x-permissions/soundevent\"},\"parameters\":[{\"in\":\"query\",\"na"
+ "me\":\"action_name\",\"required\":true,\"schema\":{\"type\":\"string\"}}"
+ ",{\"in\":\"query\",\"name\":\"audio_role\",\"required\":true,\"schema\":"
+ "{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"media_name\",\"requi"
+ "red\":false,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\""
+ ":\"action_context\",\"required\":false,\"schema\":{\"type\":\"object\"}}"
+ "],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400"
+ "\":{\"$ref\":\"#/components/responses/400\"}}}},\"/subscribe\":{\"descri"
+ "ption\":\"Subscribe to audio high level events\",\"get\":{\"parameters\""
+ ":[{\"in\":\"query\",\"name\":\"events\",\"required\":true,\"schema\":{\""
+ "type\":\"array\",\"items\":{\"type\":\"string\"}}}],\"responses\":{\"200"
+ "\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#/compo"
+ "nents/responses/400\"}}}},\"/unsubscribe\":{\"description\":\"Unubscribe"
+ " to audio high level events\",\"get\":{\"parameters\":[{\"in\":\"query\""
+ ",\"name\":\"events\",\"required\":true,\"schema\":{\"type\":\"array\",\""
+ "items\":{\"type\":\"string\"}}}],\"responses\":{\"200\":{\"$ref\":\"#/co"
+ "mponents/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400"
+ "\"}}}}}}"
;
static const struct afb_auth _afb_auths_v2_audiohl[] = {
{ .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:audiostream" },
{ .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:streamcontrol" },
+ { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:endpointcontrol" },
{ .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:soundevent" }
};
@@ -163,11 +162,11 @@ static const struct afb_auth _afb_auths_v2_audiohl[] = {
void audiohlapi_get_stream_info(struct afb_req req);
void audiohlapi_set_volume(struct afb_req req);
void audiohlapi_get_volume(struct afb_req req);
- void audiohlapi_get_list_properties(struct afb_req req);
+ void audiohlapi_get_endpoint_info(struct afb_req req);
void audiohlapi_set_property(struct afb_req req);
void audiohlapi_get_property(struct afb_req req);
- void audiohlapi_get_list_events(struct afb_req req);
- void audiohlapi_post_event(struct afb_req req);
+ void audiohlapi_get_list_actions(struct afb_req req);
+ void audiohlapi_post_action(struct afb_req req);
void audiohlapi_subscribe(struct afb_req req);
void audiohlapi_unsubscribe(struct afb_req req);
@@ -217,57 +216,57 @@ static const struct afb_verb_v2 _afb_verbs_v2_audiohl[] = {
{
.verb = "get_stream_info",
.callback = audiohlapi_get_stream_info,
- .auth = &_afb_auths_v2_audiohl[1],
+ .auth = NULL,
.info = "Retrieve stream information",
.session = AFB_SESSION_NONE_V2
},
{
.verb = "set_volume",
.callback = audiohlapi_set_volume,
- .auth = &_afb_auths_v2_audiohl[1],
- .info = "Set volume",
+ .auth = &_afb_auths_v2_audiohl[2],
+ .info = "Set volume on endpoint",
.session = AFB_SESSION_NONE_V2
},
{
.verb = "get_volume",
.callback = audiohlapi_get_volume,
.auth = NULL,
- .info = "Get volume",
+ .info = "Get endpoint volume",
.session = AFB_SESSION_NONE_V2
},
{
- .verb = "get_list_properties",
- .callback = audiohlapi_get_list_properties,
+ .verb = "get_endpoint_info",
+ .callback = audiohlapi_get_endpoint_info,
.auth = NULL,
- .info = "Retrieve a list of supported properties for a particular endpoint",
+ .info = "Retrieve endpoint information including its properties",
.session = AFB_SESSION_NONE_V2
},
{
.verb = "set_property",
.callback = audiohlapi_set_property,
- .auth = &_afb_auths_v2_audiohl[1],
- .info = "Set property value",
+ .auth = &_afb_auths_v2_audiohl[2],
+ .info = "Set endpoint property value",
.session = AFB_SESSION_NONE_V2
},
{
.verb = "get_property",
.callback = audiohlapi_get_property,
.auth = NULL,
- .info = "Get property value",
+ .info = "Get endpoint property value",
.session = AFB_SESSION_NONE_V2
},
{
- .verb = "get_list_events",
- .callback = audiohlapi_get_list_events,
+ .verb = "get_list_actions",
+ .callback = audiohlapi_get_list_actions,
.auth = NULL,
- .info = "Retrieve a list of supported events for a particular audio role",
+ .info = "Retrieve a list of supported actions for a particular audio role",
.session = AFB_SESSION_NONE_V2
},
{
- .verb = "post_event",
- .callback = audiohlapi_post_event,
- .auth = &_afb_auths_v2_audiohl[2],
- .info = "Post sound or audio device related event (extendable mechanism)",
+ .verb = "post_action",
+ .callback = audiohlapi_post_action,
+ .auth = &_afb_auths_v2_audiohl[3],
+ .info = "Post sound or audio device related action event (extendable mechanism)",
.session = AFB_SESSION_NONE_V2
},
{
diff --git a/src/ahl-apidef.json b/src/ahl-apidef.json
index d486337..3ab6f4a 100644
--- a/src/ahl-apidef.json
+++ b/src/ahl-apidef.json
@@ -118,11 +118,9 @@
},
"x-permissions": {
"streamcontrol": { "permission": "urn:AGL:permission:audio:public:streamcontrol"},
- "routingcontrol": { "permission": "urn:AGL:permission:audio:public:routingcontrol"},
+ "endpointcontrol": { "permission": "urn:AGL:permission:audio:public:endpointcontrol"},
"audiostream": { "permission": "urn:AGL:permission:audio:public:audiostream"},
- "prioritysignal": { "permission": "urn:AGL:permission:audio:public:prioritysignal"},
- "soundevent": {"permission": "urn:AGL:permission:audio:public:soundevent"},
- "streammonitor": {"permission": "urn:AGL:permission:audio:public:streammonitor"}
+ "soundevent": {"permission": "urn:AGL:permission:audio:public:soundevent"}
},
"responses": {
"200": {
@@ -135,9 +133,7 @@
}
}
},
- "400": {
- "description": "Invalid arguments"
- }
+ "400": { "description": "Invalid arguments" }
}
},
"paths": {
@@ -185,8 +181,7 @@
"items": { "$ref": "#/components/schemas/endpoint_info"}
}
},
- "400": {
- "$ref": "#/components/responses/400" }
+ "400": { "$ref": "#/components/responses/400" }
}
}
},
@@ -265,12 +260,8 @@
}
],
"responses": {
- "200": {
- "$ref": "#/components/responses/200"
- },
- "400": {
- "$ref": "#/components/responses/400"
- }
+ "200": { "$ref": "#/components/responses/200" },
+ "400": { "$ref": "#/components/responses/400" }
}
}
},
@@ -303,9 +294,6 @@
"/get_stream_info": {
"description": "Retrieve stream information",
"get": {
- "x-permissions": {
- "$ref": "#/components/x-permissions/streamcontrol"
- },
"parameters": [
{
"in": "query",
@@ -327,9 +315,9 @@
}
},
"/set_volume": {
- "description": "Set volume",
+ "description": "Set volume on endpoint",
"get": {
- "x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" },
+ "x-permissions": { "$ref": "#/components/x-permissions/endpointcontrol" },
"parameters": [
{
"in": "query",
@@ -357,7 +345,7 @@
}
},
"/get_volume": {
- "description": "Get volume",
+ "description": "Get endpoint volume",
"get": {
"parameters": [
{
@@ -385,8 +373,8 @@
}
}
},
- "/get_list_properties": {
- "description": "Retrieve a list of supported properties for a particular endpoint",
+ "/get_endpoint_info": {
+ "description": "Retrieve endpoint information including its properties",
"get": {
"parameters": [
{
@@ -409,9 +397,9 @@
}
},
"/set_property": {
- "description": "Set property value",
+ "description": "Set endpoint property value",
"get": {
- "x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" },
+ "x-permissions": { "$ref": "#/components/x-permissions/endpointcontrol" },
"parameters": [
{
"in": "query",
@@ -445,7 +433,7 @@
}
},
"/get_property": {
- "description": "Get property value",
+ "description": "Get endpoint property value",
"get": {
"parameters": [
{
@@ -479,8 +467,8 @@
}
}
},
- "/get_list_events": {
- "description": "Retrieve a list of supported events for a particular audio role",
+ "/get_list_actions": {
+ "description": "Retrieve a list of supported actions for a particular audio role",
"get": {
"parameters": [
{
@@ -491,23 +479,19 @@
}
],
"responses": {
- "200": {
- "$ref": "#/components/responses/200"
- },
- "400": {
- "$ref": "#/components/responses/400"
- }
+ "200": { "$ref": "#/components/responses/200" },
+ "400": { "$ref": "#/components/responses/400" }
}
}
},
- "/post_event": {
- "description": "Post sound or audio device related event (extendable mechanism)",
+ "/post_action": {
+ "description": "Post sound or audio device related action event (extendable mechanism)",
"get": {
"x-permissions": { "$ref": "#/components/x-permissions/soundevent" },
"parameters": [
{
"in": "query",
- "name": "event_name",
+ "name": "action_name",
"required": true,
"schema": { "type": "string" }
},
@@ -525,7 +509,7 @@
},
{
"in": "query",
- "name": "event_context",
+ "name": "action_context",
"required": false,
"schema": { "type": "object" }
}
diff --git a/src/ahl-binding.c b/src/ahl-binding.c
index fe3dcee..cd89b64 100644
--- a/src/ahl-binding.c
+++ b/src/ahl-binding.c
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2017 "Audiokinetic Inc"
- * Author Francois Thibault <fthibault@audiokinetic.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,10 +21,106 @@
#include "ahl-binding.h"
#include "ahl-apidef.h" // Generated from JSON OpenAPI
#include "wrap-json.h"
+#include "ahl-policy.h"
+#include "ahl-policy-utils.h"
// Global high-level binding context
AHLCtxT g_AHLCtx;
+// TODO: Helpers that could be common
+static EndpointTypeT EndpointTypeToEnum(char * in_pEndpointTypeStr)
+{
+ if (in_pEndpointTypeStr == NULL) {
+ return ENDPOINTTYPE_MAXVALUE;
+ }
+ else if (strcasecmp(in_pEndpointTypeStr,AHL_ENDPOINTTYPE_SOURCE)==0) {
+ return ENDPOINTTYPE_SOURCE;
+ }
+ else if (strcasecmp(in_pEndpointTypeStr,AHL_ENDPOINTTYPE_SINK)==0) {
+ return ENDPOINTTYPE_SINK;
+ }
+ else
+ return ENDPOINTTYPE_MAXVALUE;
+}
+
+static StreamStateT StreamStateToEnum(char * in_pStreamStateStr)
+{
+ if (in_pStreamStateStr == NULL) {
+ return STREAM_STATE_MAXVALUE;
+ }
+ else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_IDLE)==0) {
+ return STREAM_STATE_IDLE;
+ }
+ else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_RUNNING)==0) {
+ return STREAM_STATE_RUNNING;
+ }
+ else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_PAUSED)==0) {
+ return STREAM_STATE_PAUSED;
+ }
+ else
+ return STREAM_STATE_MAXVALUE;
+}
+
+static StreamMuteT StreamMuteToEnum(char * in_pStreamMuteStr)
+{
+ if (in_pStreamMuteStr == NULL) {
+ return STREAM_MUTE_MAXVALUE;
+ }
+ else if (strcasecmp(in_pStreamMuteStr,AHL_STREAM_UNMUTED)==0) {
+ return STREAM_UNMUTED;
+ }
+ else if (strcasecmp(in_pStreamMuteStr,AHL_STREAM_MUTED)==0) {
+ return STREAM_MUTED;
+ }
+ else
+ return STREAM_MUTE_MAXVALUE;
+}
+
+static char * DeviceURITypeEnumToStr(DeviceURITypeT in_eDeviceURIType) {
+ switch(in_eDeviceURIType) {
+ case DEVICEURITYPE_ALSA_HW: // Alsa hardware device URI
+ return AHL_DEVICEURITYPE_ALSA_HW;
+ case DEVICEURITYPE_ALSA_DMIX: // Alsa Dmix device URI (only for playback devices)
+ return AHL_DEVICEURITYPE_ALSA_DMIX;
+ case DEVICEURITYPE_ALSA_DSNOOP: // Alsa DSnoop device URI (only for capture devices)
+ return AHL_DEVICEURITYPE_ALSA_DSNOOP;
+ case DEVICEURITYPE_ALSA_SOFTVOL: // Alsa softvol device URI
+ return AHL_DEVICEURITYPE_ALSA_SOFTVOL;
+ case DEVICEURITYPE_ALSA_PLUG: // Alsa plug device URI
+ return AHL_DEVICEURITYPE_ALSA_PLUG;
+ case DEVICEURITYPE_ALSA_OTHER: // Alsa domain URI device of unspecified type
+ return AHL_DEVICEURITYPE_ALSA_OTHER;
+ case DEVICEURITYPE_NOT_ALSA: // Unknown (not ALSA domain)
+ return AHL_DEVICEURITYPE_NOT_ALSA;
+ default:
+ return "Unknown";
+ }
+}
+
+static char * StreamStateEnumToStr(StreamStateT in_eStreamState) {
+ switch(in_eStreamState) {
+ case STREAM_STATE_IDLE:
+ return AHL_STREAM_STATE_IDLE;
+ case STREAM_STATE_RUNNING:
+ return AHL_STREAM_STATE_RUNNING;
+ case STREAM_STATE_PAUSED:
+ return AHL_STREAM_STATE_PAUSED;
+ default:
+ return "Unknown";
+ }
+}
+
+static char * StreamMuteEnumToStr(StreamMuteT in_eStreamMute) {
+ switch(in_eStreamMute) {
+ case STREAM_UNMUTED:
+ return AHL_STREAM_UNMUTED;
+ case STREAM_MUTED:
+ return AHL_STREAM_MUTED;
+ default:
+ return "Unknown";
+ }
+}
+
static void AudioFormatStructToJSON(json_object **audioFormatJ, AudioFormatT * pAudioFormat)
{
wrap_json_pack(audioFormatJ, "{s:i,s:i,s:i}",
@@ -34,29 +129,52 @@ static void AudioFormatStructToJSON(json_object **audioFormatJ, AudioFormatT * p
"sample_type", pAudioFormat->sampleType);
}
-// Helper macros/func for packaging JSON objects from C structures
+// Package only information that can useful to application clients when selecting endpoint
static void EndpointInfoStructToJSON(json_object **endpointInfoJ, EndpointInfoT * pEndpointInfo)
{
json_object *formatInfoJ = NULL;
- wrap_json_pack(endpointInfoJ, "{s:i,s:i,s:s,s:s,s:i}",
+ wrap_json_pack(endpointInfoJ, "{s:i,s:s,s:s,s:s,s:s,s:s,s:s}",
"endpoint_id", pEndpointInfo->endpointID,
- "endpoint_type", pEndpointInfo->type,
- "device_name", pEndpointInfo->gsDeviceName->str,
- "display_name", pEndpointInfo->gsDisplayName->str,
- "device_uri_type", pEndpointInfo->deviceURIType);
+ "endpoint_type", (pEndpointInfo->type == ENDPOINTTYPE_SOURCE) ? AHL_ENDPOINTTYPE_SOURCE : AHL_ENDPOINTTYPE_SINK,
+ "device_name", pEndpointInfo->gsDeviceName,
+ "display_name", pEndpointInfo->gsDisplayName,
+ "audio_role", pEndpointInfo->pRoleName,
+ "device_domain",pEndpointInfo->gsDeviceDomain,
+ "device_uri_type", DeviceURITypeEnumToStr(pEndpointInfo->deviceURIType));
AudioFormatStructToJSON(&formatInfoJ,&pEndpointInfo->format);
json_object_object_add(*endpointInfoJ,"format",formatInfoJ);
+
+ // Properties
+ if (pEndpointInfo->pPropTable) {
+ json_object *pPropTableJ = json_object_new_array();
+
+ GHashTableIter iter;
+ gpointer key, value;
+ g_hash_table_iter_init (&iter, pEndpointInfo->pPropTable);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ if((key!=NULL) && (value!=NULL))
+ {
+ json_object *pPropertyJ = NULL;
+ json_object_get((json_object*)value); // Don't let the framework free our object when the request is done
+ wrap_json_pack(&pPropertyJ, "{s:s,s:o}","property_name", (char*)key, "property_value", (json_object*)value);
+ json_object_array_add(pPropTableJ, pPropertyJ);
+ }
+ }
+ json_object_object_add(*endpointInfoJ,"properties",pPropTableJ);
+ }
}
+// Package only information that can useful to application clients when opening a stream
static void StreamInfoStructToJSON(json_object **streamInfoJ, StreamInfoT * pStreamInfo)
{
json_object *endpointInfoJ = NULL;
EndpointInfoStructToJSON(&endpointInfoJ,pStreamInfo->pEndpointInfo);
- wrap_json_pack(streamInfoJ, "{s:i,s:i,s:i,s:s}",
+ wrap_json_pack(streamInfoJ, "{s:i,s:s,s:s,s:s}",
"stream_id", pStreamInfo->streamID,
- "state", pStreamInfo->streamState,
- "mute", pStreamInfo->streamMute,
- "device_uri",pStreamInfo->pEndpointInfo->gsDeviceURI->str);
+ "state", StreamStateEnumToStr(pStreamInfo->streamState),
+ "mute", StreamMuteEnumToStr(pStreamInfo->streamMute),
+ "device_uri",pStreamInfo->pEndpointInfo->gsDeviceURI); // Need to open a stream to have access to the device URI
json_object_object_add(*streamInfoJ,"endpoint_info",endpointInfoJ);
}
@@ -67,62 +185,91 @@ static streamID_t CreateNewStreamID()
return newID;
}
-static int FindRoleIndex( const char * in_pAudioRole)
+static EndpointInfoT * GetEndpointInfoWithRole(endpointID_t in_endpointID, EndpointTypeT in_endpointType, RoleInfoT * in_pRole)
{
- int index = -1; // Not found
- for (int i = 0; i < g_AHLCtx.policyCtx.iNumberRoles; i++)
- {
- GString gs = g_array_index( g_AHLCtx.policyCtx.pAudioRoles, GString, i );
- if ( strcasecmp(gs.str,in_pAudioRole) == 0 )
- {
- index = i;
+ EndpointInfoT * pEndpointInfo = NULL;
+ GPtrArray * pDeviceArray = NULL;
+ if (in_endpointType == ENDPOINTTYPE_SOURCE){
+ pDeviceArray = in_pRole->pSourceEndpoints;
+ }
+ else {
+ pDeviceArray = in_pRole->pSinkEndpoints;
+ }
+ g_assert_nonnull(pDeviceArray);
+
+ for (int j = 0; j < pDeviceArray->len; j++) {
+ EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pDeviceArray,j);
+ g_assert_nonnull(pCurEndpointInfo);
+ if (pCurEndpointInfo->endpointID == in_endpointID) {
+ pEndpointInfo = pCurEndpointInfo;
break;
}
}
- return index;
+
+ return pEndpointInfo;
+}
+
+static int ReplaceEndpointInfoWithRole(endpointID_t in_endpointID, EndpointTypeT in_endpointType, RoleInfoT * in_pRole, EndpointInfoT * in_pNewEndpoint)
+{
+ GPtrArray * pDeviceArray = NULL;
+ if (in_endpointType == ENDPOINTTYPE_SOURCE){
+ pDeviceArray = in_pRole->pSourceEndpoints;
+ }
+ else {
+ pDeviceArray = in_pRole->pSinkEndpoints;
+ }
+ g_assert_nonnull(pDeviceArray);
+
+ for (int j = 0; j < pDeviceArray->len; j++) {
+ EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pDeviceArray,j);
+ g_assert_nonnull(pCurEndpointInfo);
+ if (pCurEndpointInfo->endpointID == in_endpointID) {
+ g_ptr_array_insert(pDeviceArray,j,in_pNewEndpoint);
+ g_ptr_array_remove_index(pDeviceArray,j+1);
+ TermEndpointInfo(pCurEndpointInfo);
+ // GLib automatically frees item upon array removal
+ return AHL_SUCCESS;
+ }
+ }
+
+ return AHL_FAIL;
}
static EndpointInfoT * GetEndpointInfo(endpointID_t in_endpointID, EndpointTypeT in_endpointType)
{
EndpointInfoT * pEndpointInfo = NULL;
- for (int i = 0; i < g_AHLCtx.policyCtx.iNumberRoles; i++)
+
+ GHashTableIter iter;
+ gpointer key, value;
+ g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pRoleInfo);
+ while (pEndpointInfo == NULL && g_hash_table_iter_next (&iter, &key, &value))
{
- GArray * pRoleDeviceArray = NULL;
- if (in_endpointType == ENDPOINTTYPE_SOURCE){
- pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, i );
- }
- else{
- pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, i );
- }
- for (int j = 0; j < pRoleDeviceArray->len; j++) {
- EndpointInfoT * pCurEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,j);
- if (pCurEndpointInfo->endpointID == in_endpointID) {
- pEndpointInfo = pCurEndpointInfo;
- break;
- }
- }
+ RoleInfoT * pRoleInfo = (RoleInfoT*)value;
+ pEndpointInfo = GetEndpointInfoWithRole(in_endpointID,in_endpointType,pRoleInfo);
}
+
return pEndpointInfo;
}
-static StreamInfoT * GetActiveStream(streamID_t in_streamID)
+static StreamInfoT * GetStream(streamID_t in_streamID)
{
- int iNumActiveStreams = g_AHLCtx.policyCtx.pActiveStreams->len;
- StreamInfoT * pStreamInfo = NULL;
- for ( int i = 0; i < iNumActiveStreams ; i++ ) {
- StreamInfoT * pCurStreamInfo = &g_array_index(g_AHLCtx.policyCtx.pActiveStreams,StreamInfoT,i);
- if (pCurStreamInfo->streamID == in_streamID){
- pStreamInfo = pCurStreamInfo;
- break;
- }
- }
- return pStreamInfo;
+ if (g_AHLCtx.policyCtx.pStreams == NULL)
+ return NULL;
+
+ return g_hash_table_lookup(g_AHLCtx.policyCtx.pStreams,GINT_TO_POINTER(&in_streamID));
+}
+
+static RoleInfoT * GetRole(char * in_pAudioRoleName)
+{
+ if (g_AHLCtx.policyCtx.pRoleInfo == NULL)
+ return NULL;
+
+ return g_hash_table_lookup(g_AHLCtx.policyCtx.pRoleInfo,in_pAudioRoleName);
}
static AHLClientCtxT * AllocateClientContext()
{
AHLClientCtxT * pClientCtx = malloc(sizeof(AHLClientCtxT));
- pClientCtx->pEndpointAccessList = g_array_new(FALSE, TRUE, sizeof(endpointID_t));
pClientCtx->pStreamAccessList = g_array_new(FALSE, TRUE, sizeof(streamID_t));
return pClientCtx;
}
@@ -130,31 +277,50 @@ static AHLClientCtxT * AllocateClientContext()
static void TerminateClientContext(void * ptr)
{
AHLClientCtxT * pClientCtx = (AHLClientCtxT *) ptr;
- g_array_free( pClientCtx->pEndpointAccessList, TRUE);
- g_array_free( pClientCtx->pStreamAccessList, TRUE);
- free(ptr);
-}
+ if (pClientCtx != NULL) {
-static int CheckStreamAccessControl(AHLClientCtxT * pClientCtx, streamID_t streamID)
-{
- int iAccessControl = AHL_ACCESS_CONTROL_DENIED;
- for (int i = 0; i < pClientCtx->pStreamAccessList->len ; i++) {
- streamID_t iID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i);
- if (iID == streamID) {
- iAccessControl = AHL_ACCESS_CONTROL_GRANTED;
- }
+ // Avoid having policy in bad state if client loses WS connection (e.g. app termination without close stream)
+ // Force close streams in those cases.
+
+ if (pClientCtx->pStreamAccessList != NULL) {
+#ifndef AHL_DISCONNECT_POLICY
+ for (int i = 0; i < pClientCtx->pStreamAccessList->len; i++)
+ {
+ streamID_t streamID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i);
+ // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions
+ StreamInfoT * pStreamInfo = GetStream(streamID);
+ if (pStreamInfo == NULL) {
+ AFB_ERROR("Specified stream not currently active stream_id -> %d",streamID);
+ return;
+ }
+
+ json_object *pPolicyStreamJ = NULL;
+ int err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ);
+ if (err == AHL_FAIL)
+ {
+ AFB_ERROR("Audio policy violation, Unable to get JSON object for Policy_CloseStream");
+ return;
+ }
+ Policy_CloseStream(pPolicyStreamJ);
+ }
+#endif
+ g_array_free( pClientCtx->pStreamAccessList, TRUE);
+ pClientCtx->pStreamAccessList = NULL;
+ }
+ free(pClientCtx);
}
- return iAccessControl;
}
-static int CheckEndpointAccessControl(AHLClientCtxT * pClientCtx, endpointID_t endpointID)
+static int CheckStreamAccessControl(AHLClientCtxT * pClientCtx, streamID_t streamID)
{
int iAccessControl = AHL_ACCESS_CONTROL_DENIED;
- for (int i = 0; i < pClientCtx->pEndpointAccessList->len ; i++) {
- endpointID_t iID = g_array_index(pClientCtx->pEndpointAccessList,endpointID_t,i);
- if (iID == endpointID) {
- iAccessControl = AHL_ACCESS_CONTROL_GRANTED;
- }
+ if (pClientCtx && pClientCtx->pStreamAccessList) {
+ for (int i = 0; i < pClientCtx->pStreamAccessList->len ; i++) {
+ streamID_t iID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i);
+ if (iID == streamID) {
+ iAccessControl = AHL_ACCESS_CONTROL_GRANTED;
+ }
+ }
}
return iAccessControl;
}
@@ -167,21 +333,21 @@ static int CreateEvents()
err = !afb_event_is_valid(g_AHLCtx.policyCtx.propertyEvent);
if (err) {
AFB_ERROR("Could not create endpoint property change event");
- err++;
+ return err;
}
g_AHLCtx.policyCtx.volumeEvent = afb_daemon_make_event(AHL_ENDPOINT_VOLUME_EVENT);
err = !afb_event_is_valid(g_AHLCtx.policyCtx.volumeEvent);
if (err) {
AFB_ERROR("Could not create endpoint volume change event");
- err++;
+ return err;
}
- g_AHLCtx.policyCtx.postEvent = afb_daemon_make_event(AHL_POST_EVENT);
- err = !afb_event_is_valid(g_AHLCtx.policyCtx.postEvent);
+ g_AHLCtx.policyCtx.postActionEvent = afb_daemon_make_event(AHL_POST_ACTION_EVENT);
+ err = !afb_event_is_valid(g_AHLCtx.policyCtx.postActionEvent);
if (err) {
- AFB_ERROR("Could not create post event call event");
- err++;
+ AFB_ERROR("Could not create post action event call event");
+ return err;
}
return err;
@@ -189,90 +355,205 @@ static int CreateEvents()
static void AhlBindingTerm()
{
+#ifndef AHL_DISCONNECT_POLICY
// Policy termination
Policy_Term();
-
- // Events
- for (int i = 0; i < g_AHLCtx.policyCtx.pEventList->len; i++)
- {
- // For each event within the role
- GArray * pRoleEventArray = g_ptr_array_index( g_AHLCtx.policyCtx.pEventList, i );
- for (int j = 0 ; j < pRoleEventArray->len; j++)
+#endif
+
+ // Roles
+ if (g_AHLCtx.policyCtx.pRoleInfo != NULL) {
+ GHashTableIter iter;
+ gpointer key, value;
+ g_hash_table_iter_init(&iter, g_AHLCtx.policyCtx.pRoleInfo);
+ while (g_hash_table_iter_next (&iter, &key, &value))
{
- GString * gsEventName = &g_array_index(pRoleEventArray,GString,j);
- g_string_free(gsEventName,TRUE);
+ RoleInfoT * pRole = (RoleInfoT *)value;
+ if (pRole)
+ {
+ if(pRole->pRoleName) {
+ g_free(pRole->pRoleName);
+ pRole->pRoleName = NULL;
+ }
+ // Actions
+ if (pRole->pActionList) {
+ for (int i = 0; i < pRole->pActionList->len; i++)
+ {
+ g_ptr_array_remove_index( pRole->pActionList, i ); // Free char * is called by GLib
+ }
+ }
+ // Source endpoints
+ if (pRole->pSourceEndpoints) {
+ for (int i = 0; i < pRole->pSourceEndpoints->len; i++)
+ {
+ EndpointInfoT * pEndpoint = g_ptr_array_remove_index( pRole->pSourceEndpoints, i ); // Free endpoint * is called by GLib
+ if (pEndpoint) {
+ TermEndpointInfo(pEndpoint);
+ }
+ }
+ }
+ // Sink endpoints
+ if (pRole->pSinkEndpoints) {
+ for (int i = 0; i < pRole->pSinkEndpoints->len; i++)
+ {
+ EndpointInfoT * pEndpoint = g_ptr_array_remove_index( pRole->pSinkEndpoints, i ); // Free endpoint * is called by GLib
+ if (pEndpoint) {
+ TermEndpointInfo(pEndpoint);
+ }
+ }
+ }
+ free(pRole);
+ }
}
- g_array_free(pRoleEventArray,TRUE);
- pRoleEventArray = NULL;
- }
- g_ptr_array_free(g_AHLCtx.policyCtx.pEventList,TRUE);
- g_AHLCtx.policyCtx.pEventList = NULL;
-
- // Endpoints
- TermEndpoints();
-
- // TODO: Need to free g_strings in HAL list
- g_array_free(g_AHLCtx.pHALList,TRUE);
- g_AHLCtx.pHALList = NULL;
- g_hash_table_remove_all(g_AHLCtx.policyCtx.pRolePriority);
- g_hash_table_destroy(g_AHLCtx.policyCtx.pRolePriority);
- g_AHLCtx.policyCtx.pRolePriority = NULL;
- // TODO: Need to free g_strings in audio roles list
- g_array_free(g_AHLCtx.policyCtx.pAudioRoles,TRUE);
- g_AHLCtx.policyCtx.pAudioRoles = NULL;
- g_array_free(g_AHLCtx.policyCtx.pInterruptBehavior,TRUE);
- g_AHLCtx.policyCtx.pInterruptBehavior = NULL;
- g_array_free(g_AHLCtx.policyCtx.pActiveStreams,TRUE);
- g_AHLCtx.policyCtx.pActiveStreams = NULL;
-
- AFB_INFO("Audio high-level Binding succesTermination");
+ g_hash_table_remove_all(g_AHLCtx.policyCtx.pRoleInfo);
+ g_hash_table_destroy(g_AHLCtx.policyCtx.pRoleInfo);
+ g_AHLCtx.policyCtx.pRoleInfo = NULL;
+ }
+
+ if (g_AHLCtx.policyCtx.pStreams) {
+ GHashTableIter iter;
+ gpointer key, value;
+ g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pStreams);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ if (value)
+ free(value);
+ }
+ g_hash_table_remove_all(g_AHLCtx.policyCtx.pStreams);
+ g_hash_table_destroy(g_AHLCtx.policyCtx.pStreams);
+ g_AHLCtx.policyCtx.pStreams = NULL;
+ }
+
+ if (g_AHLCtx.policyCtx.pHALList) {
+ g_ptr_array_free(g_AHLCtx.policyCtx.pHALList,TRUE);
+ g_AHLCtx.policyCtx.pHALList = NULL;
+ }
+
+ AFB_INFO("Audio high-level binding termination success");
}
// Binding initialization
PUBLIC int AhlBindingInit()
{
- int errcount = 0;
+ int err = 0;
+ memset(&g_AHLCtx,0,sizeof(g_AHLCtx));
+
+ // Register exit function
atexit(AhlBindingTerm);
- // This API uses services from low level audio. TODO: This dependency can be removed.
- errcount = afb_daemon_require_api_v2("alsacore",1) ;
- if( errcount != 0 )
+ //Create AGL Events
+ err=CreateEvents();
+ if(err)
{
- AFB_ERROR("Audio high level API requires alsacore API to be available");
- return 1;
+ //Error messages already reported inside CreateEvents
+ return err;
}
- errcount += CreateEvents();
-
// Parse high-level binding JSON configuration file (will build device lists)
- errcount += ParseHLBConfig();
-
+ err = ParseHLBConfig();
+ if(err)
+ {
+ //Error messages already reported inside ParseHLBConfig
+ return err;
+ }
+
+#ifndef AHL_DISCONNECT_POLICY
// Policy initialization
- errcount += Policy_Init();
+ err = Policy_Init();
+ if(err == AHL_POLICY_REJECT)
+ {
+ //Error messages already reported inside PolicyInit
+ return err;
+ }
+
+ // for all audio Roles
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pRoleInfo);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ RoleInfoT * pRoleInfo = (RoleInfoT*)value;
+ if (pRoleInfo->pSourceEndpoints){
+ // for all source endpoints
+ for (int j = 0; j < pRoleInfo->pSourceEndpoints->len; j++) {
+ EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pRoleInfo->pSourceEndpoints,j);
+ g_assert_nonnull(pCurEndpointInfo);
+ json_object *pPolicyEndpointJ = NULL;
+ err = PolicyEndpointStructToJSON(pCurEndpointInfo, &pPolicyEndpointJ);
+ if (err == AHL_FAIL)
+ {
+ AFB_ERROR("Unable to Create Endpoint Json object error:%s ",wrap_json_get_error_string(err));
+ return err;
+ }
+ else
+ {
+ err = Policy_Endpoint_Init(pPolicyEndpointJ);
+ if (err == AHL_POLICY_REJECT) {
+ AFB_ERROR("Policy endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type);
+ }
+ //free pPolicyEndpointJ
+ json_object_put(pPolicyEndpointJ);
+ }
+ }
+ }
+ if (pRoleInfo->pSinkEndpoints){
+ // for all sink endpoints
+ for (int j = 0; j < pRoleInfo->pSinkEndpoints->len; j++) {
+ EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pRoleInfo->pSinkEndpoints,j);
+ g_assert_nonnull(pCurEndpointInfo);
+ json_object *pPolicyEndpointJ = NULL;
+ err = PolicyEndpointStructToJSON(pCurEndpointInfo, &pPolicyEndpointJ);
+ if (err == AHL_FAIL)
+ {
+ AFB_ERROR("Unable to Create Endpoint Json object error:%s ",wrap_json_get_error_string(err));
+ return err;
+ }
+ else
+ {
+ err = Policy_Endpoint_Init(pPolicyEndpointJ);
+ if (err== AHL_POLICY_REJECT) {
+ AFB_ERROR("Policy endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type);
+ }
+ //free pPolicyEndpointJ
+ json_object_put(pPolicyEndpointJ);
+ }
+
+ }
+ }
+ }
+#endif
// Initialize list of active streams
- g_AHLCtx.iNumActiveClients = 0;
- g_AHLCtx.policyCtx.pActiveStreams = g_array_new(FALSE,TRUE,sizeof(StreamInfoT));
+ g_AHLCtx.policyCtx.pStreams = g_hash_table_new(g_int_hash, g_int_equal);
+ if(g_AHLCtx.policyCtx.pStreams == NULL)
+ {
+ AFB_ERROR("Unable to create Active Stream List");
+ return err;
+ }
// TODO: Use AGL persistence framework to retrieve and set initial volumes/properties
- AFB_DEBUG("Audio high-level Binding success errcount=%d", errcount);
- return errcount;
+ AFB_DEBUG("Audio high-level Binding success");
+ return err;
}
PUBLIC void AhlOnEvent(const char *evtname, json_object *eventJ)
{
AFB_DEBUG("AHL received event %s", evtname);
+
+ // TODO: Handle event from the policy to update internal information (currently not possible since within the same binding)
- //forward to policy to handle events
+#ifndef AHL_DISCONNECT_POLICY
+ // Temp: currently forward to policy to handle events (they will be received directly when disconnected into separate binding)
Policy_OnEvent(evtname, eventJ);
+#endif
}
PUBLIC void audiohlapi_get_sources(struct afb_req req)
{
- json_object *sourcesJ = NULL;
- json_object *sourceJ = NULL;
+ json_object *devicesJ = NULL;
+ json_object *deviceJ = NULL;
json_object *queryJ = NULL;
char * audioRole = NULL;
@@ -284,36 +565,40 @@ PUBLIC void audiohlapi_get_sources(struct afb_req req)
}
AFB_DEBUG("Filtering devices according to specified audio role=%s", audioRole);
- // List device only for current audio role and device type (pick the role device list)
- int iRoleIndex = FindRoleIndex(audioRole);
- if ( iRoleIndex < 0)
+
+ RoleInfoT * pRole = GetRole(audioRole);
+ if ( pRole == NULL )
{
afb_req_fail_f(req, "Invalid arguments", "Requested audio role does not exist in current configuration -> %s", json_object_get_string(queryJ));
return;
}
else
{
- sourcesJ = json_object_new_array();
- GArray * pRoleSourceDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, iRoleIndex );
- int iNumberDevices = pRoleSourceDeviceArray->len;
- for ( int j = 0 ; j < iNumberDevices; j++)
- {
- EndpointInfoT sourceInfo = g_array_index(pRoleSourceDeviceArray,EndpointInfoT,j);
- EndpointInfoStructToJSON(&sourceJ, &sourceInfo);
- json_object_array_add(sourcesJ, sourceJ);
+ devicesJ = json_object_new_array();
+ GPtrArray * pDeviceArray = pRole->pSourceEndpoints;
+ if (pDeviceArray) {
+ int iNumberDevices = pDeviceArray->len;
+ for ( int j = 0 ; j < iNumberDevices; j++)
+ {
+ EndpointInfoT * pEndpointInfo = g_ptr_array_index(pDeviceArray,j);
+ if (pEndpointInfo) {
+ EndpointInfoStructToJSON(&deviceJ, pEndpointInfo);
+ json_object_array_add(devicesJ, deviceJ);
+ }
+ }
}
}
- afb_req_success(req, sourcesJ, "List of sources");
+ afb_req_success(req, devicesJ, "List of sources");
}
PUBLIC void audiohlapi_get_sinks(struct afb_req req)
{
- json_object *sinksJ = NULL;
- json_object *sinkJ = NULL;
+ json_object *devicesJ = NULL;
+ json_object *deviceJ = NULL;
json_object *queryJ = NULL;
char * audioRole = NULL;
-
+
queryJ = afb_req_json(req);
int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role", &audioRole);
if (err) {
@@ -322,136 +607,162 @@ PUBLIC void audiohlapi_get_sinks(struct afb_req req)
}
AFB_DEBUG("Filtering devices according to specified audio role=%s", audioRole);
- // List device only for current audio role and device type (pick the role device list)
- int iRoleIndex = FindRoleIndex(audioRole);
- if ( iRoleIndex < 0)
+
+ RoleInfoT * pRole = GetRole(audioRole);
+ if ( pRole == NULL )
{
afb_req_fail_f(req, "Invalid arguments", "Requested audio role does not exist in current configuration -> %s", json_object_get_string(queryJ));
return;
}
else
{
- sinksJ = json_object_new_array();
- GArray * pRoleSinkDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, iRoleIndex );
- int iNumberDevices = pRoleSinkDeviceArray->len;
- for ( int j = 0 ; j < iNumberDevices; j++)
- {
- EndpointInfoT sinkInfo = g_array_index(pRoleSinkDeviceArray,EndpointInfoT,j);
- EndpointInfoStructToJSON(&sinkJ, &sinkInfo);
- json_object_array_add(sinksJ, sinkJ);
+ devicesJ = json_object_new_array();
+ GPtrArray * pDeviceArray = pRole->pSinkEndpoints;
+ if (pDeviceArray) {
+ int iNumberDevices = pDeviceArray->len;
+ for ( int j = 0 ; j < iNumberDevices; j++)
+ {
+ EndpointInfoT * pEndpointInfo = g_ptr_array_index(pDeviceArray,j);
+ EndpointInfoStructToJSON(&deviceJ, pEndpointInfo);
+ json_object_array_add(devicesJ, deviceJ);
+ }
}
}
- afb_req_success(req, sinksJ, "List of sinks");
+ afb_req_success(req, devicesJ, "List of sinks");
}
PUBLIC void audiohlapi_stream_open(struct afb_req req)
{
json_object *streamInfoJ = NULL;
- StreamInfoT streamInfo;
+ StreamInfoT * pStreamInfo = NULL;
json_object *queryJ = NULL;
char * audioRole = NULL;
+ char * endpointTypeStr = NULL;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
endpointID_t endpointID = AHL_UNDEFINED;
- int policyAllowed = AHL_POLICY_REJECT;
EndpointInfoT * pEndpointInfo = NULL;
+ EndpointSelectionModeT endpointSelMode = ENDPOINTSELMODEMAXVALUE;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:s,s:i,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointType, "endpoint_id", &endpointID);
+ int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointTypeStr, "endpoint_id", &endpointID);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = audio_role:%s endpoint_type:%d endpoint_id:%d", audioRole,endpointType,endpointID);
+ AFB_DEBUG("Parsed input arguments = audio_role:%s endpoint_type:%s endpoint_id:%d", audioRole,endpointTypeStr,endpointID);
+ endpointType = EndpointTypeToEnum(endpointTypeStr);
// Check if there is already an existing context for this client
AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure
if (pClientCtx == NULL)
{
- g_AHLCtx.iNumActiveClients++;
pClientCtx = AllocateClientContext();
afb_req_context_set(req, pClientCtx, TerminateClientContext);
}
- int iRoleIndex = FindRoleIndex(audioRole);
- if (iRoleIndex < 0) {
+ RoleInfoT * pRole = GetRole(audioRole);
+ if ( pRole == NULL )
+ {
afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole);
return;
}
- GArray * pRoleDeviceArray = NULL;
+ GPtrArray * pDeviceArray = NULL;
if (endpointType == ENDPOINTTYPE_SOURCE){
- pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, iRoleIndex );
+ pDeviceArray = pRole->pSourceEndpoints;
}
else{
- pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, iRoleIndex );
+ pDeviceArray = pRole->pSinkEndpoints;
}
- if (pRoleDeviceArray->len == 0) {
- afb_req_fail_f(req, "No available devices", "No available devices for role:%s and device type:%d",audioRole,endpointType);
+ if (pDeviceArray == NULL || pDeviceArray->len == 0) {
+ afb_req_fail_f(req, "No available devices", "No available devices for role:%s and device type:%s",audioRole,endpointTypeStr);
return;
}
if (endpointID == AHL_UNDEFINED)
{
// Assign a device based on configuration priority (first in the list for requested role and endpoint type)
- pEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,0);
- streamInfo.endpointSelMode = AHL_ENDPOINTSELMODE_AUTO;
+ pEndpointInfo = g_ptr_array_index(pDeviceArray,0);
+ endpointSelMode = ENDPOINTSELMODE_AUTO;
+
}
else{
- streamInfo.endpointSelMode = AHL_ENDPOINTSELMODE_MANUAL;
+ endpointSelMode = ENDPOINTSELMODE_MANUAL;
// Find specified endpoint ID in list of devices
- int iNumberDevices = pRoleDeviceArray->len;
+ int iNumberDevices = pDeviceArray->len;
for ( int j = 0 ; j < iNumberDevices; j++)
{
- pEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,j);
- if (pEndpointInfo->endpointID == endpointID) {
+ pEndpointInfo = g_ptr_array_index(pDeviceArray,j);
+ if (pEndpointInfo && pEndpointInfo->endpointID == endpointID) {
break;
}
+ pEndpointInfo = NULL;
}
- if (pEndpointInfo == NULL) {
- afb_req_fail_f(req, "Endpoint not available", "Specified endpoint not available for role:%s and device type:%d endpoint id %d",audioRole,endpointType,endpointID);
- return;
- }
+
}
- // Create stream
- streamInfo.streamID = CreateNewStreamID(); // create new ID
- streamInfo.streamState = STREAM_STATE_IDLE;
- streamInfo.streamMute = STREAM_UNMUTED;
- streamInfo.pEndpointInfo = pEndpointInfo;
+ if (pEndpointInfo == NULL) {
+ afb_req_fail_f(req, "Endpoint not available", "Specified endpoint not available for role:%s and device type:%d endpoint id %d",audioRole,endpointType,endpointID);
+ return;
+ }
+ pStreamInfo = (StreamInfoT*) malloc(sizeof(StreamInfoT));
+ memset(pStreamInfo,0,sizeof(StreamInfoT));
+
+ // Create stream
+ pStreamInfo->streamID = CreateNewStreamID(); // create new ID
+ pStreamInfo->streamState = STREAM_STATE_IDLE;
+ pStreamInfo->streamMute = STREAM_UNMUTED;
+ pStreamInfo->pEndpointInfo = pEndpointInfo;
+ pStreamInfo->endpointSelMode = endpointSelMode;
+ // Directly from role config for now, but could be programmatically overriden in the future
+ pStreamInfo->pRoleName = pRole->pRoleName;
+ pStreamInfo->iPriority = pRole->iPriority;
+ pStreamInfo->eInterruptBehavior = pRole->eInterruptBehavior;
+
+#ifndef AHL_DISCONNECT_POLICY
// Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions
- policyAllowed = Policy_OpenStream(&streamInfo);
+ json_object *pPolicyStreamJ = NULL;
+ err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ);
+ if (err == AHL_FAIL)
+ {
+ afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_OpenStream");
+ return;
+ }
+
+ int policyAllowed = Policy_OpenStream(pPolicyStreamJ);
if (policyAllowed == AHL_POLICY_REJECT)
{
afb_req_fail(req, "Audio policy violation", "Open stream not allowed in current context");
return;
- }
+ }
+#endif
char streamEventName[128];
- snprintf(streamEventName,128,"ahl_streamstate_%d",streamInfo.streamID);
+ snprintf(streamEventName,128,"ahl_streamstate_%d",pStreamInfo->streamID);
- streamInfo.streamStateEvent = afb_daemon_make_event(streamEventName);
- err = !afb_event_is_valid(streamInfo.streamStateEvent);
+ pStreamInfo->streamStateEvent = afb_daemon_make_event(streamEventName);
+ err = !afb_event_is_valid(pStreamInfo->streamStateEvent);
if (err) {
afb_req_fail(req, "Stream event creation failure", "Could not create stream specific state change event");
return;
}
- err = afb_req_subscribe(req,streamInfo.streamStateEvent);
+ err = afb_req_subscribe(req,pStreamInfo->streamStateEvent);
if (err) {
afb_req_fail(req, "Stream event subscription failure", "Could not subscribe to stream specific state change event");
return;
}
- // Add to client context stream ID and endpoint ID access rights
- g_array_append_val(pClientCtx->pStreamAccessList, streamInfo.streamID);
- g_array_append_val(pClientCtx->pEndpointAccessList, streamInfo.pEndpointInfo->endpointID);
+ // Add to client context stream ID access rights
+ g_array_append_val(pClientCtx->pStreamAccessList, pStreamInfo->streamID);
// Push stream on active stream list
- g_array_append_val( g_AHLCtx.policyCtx.pActiveStreams, streamInfo );
+ if (g_AHLCtx.policyCtx.pStreams)
+ g_hash_table_insert( g_AHLCtx.policyCtx.pStreams, GINT_TO_POINTER(&pStreamInfo->streamID), pStreamInfo );
- StreamInfoStructToJSON(&streamInfoJ,&streamInfo);
+ StreamInfoStructToJSON(&streamInfoJ,pStreamInfo);
afb_req_success(req, streamInfoJ, "Stream info structure");
}
@@ -460,7 +771,6 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
{
json_object *queryJ = NULL;
streamID_t streamID = AHL_UNDEFINED;
- int policyAllowed = AHL_POLICY_REJECT;
queryJ = afb_req_json(req);
int err = wrap_json_unpack(queryJ, "{s:i}", "stream_id", &streamID);
@@ -470,11 +780,18 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
}
AFB_DEBUG("Parsed input arguments = stream_id:%d", streamID);
+
+ StreamInfoT * pStreamInfo = GetStream(streamID);
+ if (pStreamInfo == NULL) {
+ afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID);
+ return;
+ }
+
// Check if there is already an existing context for this client
AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure
if (pClientCtx == NULL)
{
- afb_req_fail(req, "No context associated with the request", "No context associated with the request");
+ afb_req_fail(req, "Bad state", "No client context associated with the request (is there an opened stream by this client?)");
return;
}
@@ -486,67 +803,55 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
return;
}
- // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions
- StreamInfoT * pStreamInfo = GetActiveStream(streamID);
- if (pStreamInfo == NULL) {
- afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID);
+#ifndef AHL_DISCONNECT_POLICY
+ json_object *pPolicyStreamJ = NULL;
+ err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ);
+ if (err == AHL_FAIL)
+ {
+ afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_CloseStream");
return;
- }
-
- policyAllowed = Policy_CloseStream(pStreamInfo);
+ }
+ // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions
+ int policyAllowed = Policy_CloseStream(pPolicyStreamJ);
if (policyAllowed == AHL_POLICY_REJECT)
{
afb_req_fail(req, "Audio policy violation", "Close stream not allowed in current context");
return;
}
+#endif
- // Remove from active stream list (if present)
- int iNumActiveStreams = g_AHLCtx.policyCtx.pActiveStreams->len;
- int iStreamFound = 0;
- for ( int i = 0; i < iNumActiveStreams ; i++ ) {
- StreamInfoT streamInfo = g_array_index(g_AHLCtx.policyCtx.pActiveStreams,StreamInfoT,i);
- if (streamInfo.streamID == streamID){
- // Unsubscribe client from stream events
- char streamEventName[128];
- snprintf(streamEventName,128,"ahl_streamstate_%d",streamInfo.streamID);
- int iValid = afb_event_is_valid(streamInfo.streamStateEvent);
- if (iValid) {
- err = afb_req_unsubscribe(req,streamInfo.streamStateEvent);
- if (err) {
- afb_req_fail(req, "Stream event subscription failure", "Could not unsubscribe to stream specific state change event");
- return;
- }
- }
- else{
- AFB_WARNING("Stream event no longer valid and therefore not unsubscribed");
- break;
- }
-
- g_array_remove_index(g_AHLCtx.policyCtx.pActiveStreams,i);
- iStreamFound = 1;
- break;
+ // Unsubscribe client from stream events
+ char streamEventName[128];
+ snprintf(streamEventName,128,"ahl_streamstate_%d",streamID);
+ int iValid = afb_event_is_valid(pStreamInfo->streamStateEvent);
+ if (iValid) {
+ err = afb_req_unsubscribe(req,pStreamInfo->streamStateEvent);
+ if (err) {
+ afb_req_fail(req, "Stream event subscription failure", "Could not unsubscribe to stream specific state change event");
+ return;
}
}
- if (iStreamFound == 0) {
- afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID);
- return;
- }
+ // Remove from stream list (if present)
+ if (g_AHLCtx.policyCtx.pStreams)
+ g_hash_table_remove(g_AHLCtx.policyCtx.pStreams,GINT_TO_POINTER(&pStreamInfo->streamID));
+ free(pStreamInfo);
+ pStreamInfo = NULL;
// Find index for cases where there are multiple streams per client
// Remove from client context stream ID and endpoint ID access rights
- for (int i = 0; i < pClientCtx->pStreamAccessList->len ; i++) {
- streamID_t iID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i);
- if (iID == streamID) {
- g_array_remove_index(pClientCtx->pStreamAccessList, i);
- g_array_remove_index(pClientCtx->pEndpointAccessList, i);
- }
- }
+ if (pClientCtx->pStreamAccessList) {
+ for (int i = 0; i < pClientCtx->pStreamAccessList->len ; i++) {
+ streamID_t iID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i);
+ if (iID == streamID) {
+ g_array_remove_index(pClientCtx->pStreamAccessList, i);
+ }
+ }
- if (pClientCtx->pStreamAccessList->len == 0 && pClientCtx->pEndpointAccessList == 0) {
- // If no more streams/endpoints owner, clear session
- afb_req_context_clear(req);
- g_AHLCtx.iNumActiveClients--;
+ if (pClientCtx->pStreamAccessList->len == 0) {
+ // If no more streams/endpoints owner, clear session
+ afb_req_context_clear(req);
+ }
}
afb_req_success(req, NULL, "Stream close completed");
@@ -556,20 +861,19 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
{
json_object *queryJ = NULL;
streamID_t streamID = AHL_UNDEFINED;
- StreamStateT streamState = STREAM_STATUS_MAXVALUE;
- int policyAllowed = AHL_POLICY_REJECT;
+ char * streamStateStr = NULL;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "stream_id", &streamID,"state",&streamState);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:s}", "stream_id", &streamID,"state",&streamStateStr);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = stream_id:%d, state:%d", streamID,streamState);
-
- StreamInfoT * pStreamInfo = GetActiveStream(streamID);
+ AFB_DEBUG("Parsed input arguments = stream_id:%d, state:%s", streamID,streamStateStr);
+
+ StreamInfoT * pStreamInfo = GetStream(streamID);
if (pStreamInfo == NULL) {
- afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID);
+ afb_req_fail_f(req, "Stream not found", "Specified stream not found stream_id -> %d",streamID);
return;
}
@@ -577,7 +881,7 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure
if (pClientCtx == NULL)
{
- afb_req_fail(req, "No context associated with the request", "No context associated with the request");
+ afb_req_fail(req, "Bad state", "No client context associated with the request (is there an opened stream by this client?)");
return;
}
@@ -588,14 +892,30 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
afb_req_fail(req, "Access control denied", "Set stream state not allowed in current client context");
return;
}
- int AudioRoleIndex = FindRoleIndex(pStreamInfo->pEndpointInfo->gsAudioRole->str);
- policyAllowed = Policy_SetStreamState(pStreamInfo, AudioRoleIndex, streamState);
+ StreamStateT streamState = StreamStateToEnum(streamStateStr);
+#ifndef AHL_DISCONNECT_POLICY
+ json_object *pPolicyStreamJ = NULL;
+ err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ);
+ if (err == AHL_FAIL)
+ {
+ afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetStreamState");
+ return;
+ }
+
+ json_object *paramJ= json_object_new_int(streamState);
+ json_object_object_add(pPolicyStreamJ, "arg_stream_state", paramJ);
+
+ int policyAllowed = Policy_SetStreamState(pPolicyStreamJ);
if (policyAllowed == AHL_POLICY_REJECT)
{
afb_req_fail(req, "Audio policy violation", "Change stream state not allowed in current context");
return;
}
+#else
+ // Simulate that policy returns target state (accepted)
+ pStreamInfo->streamState = streamState;
+#endif
afb_req_success(req, NULL, "Set stream state");
}
@@ -604,18 +924,17 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
{
json_object *queryJ = NULL;
streamID_t streamID = AHL_UNDEFINED;
- StreamMuteT muteState = STREAM_MUTE_MAXVALUE;
- int policyAllowed = AHL_POLICY_REJECT;
+ char * pMuteStr = NULL;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "stream_id", &streamID,"mute",&muteState);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:s}", "stream_id", &streamID,"mute",&pMuteStr);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = stream_id:%d, mute:%d", streamID,muteState);
+ AFB_DEBUG("Parsed input arguments = stream_id:%d, mute:%s", streamID,pMuteStr);
- StreamInfoT * pStreamInfo = GetActiveStream(streamID);
+ StreamInfoT * pStreamInfo = GetStream(streamID);
if (pStreamInfo == NULL) {
afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID);
return;
@@ -625,7 +944,7 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure
if (pClientCtx == NULL)
{
- afb_req_fail(req, "No context associated with the request", "No context associated with the request");
+ afb_req_fail(req, "Bad state", "No client context associated with the request (is there an opened stream by this client?)");
return;
}
@@ -637,12 +956,30 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
return;
}
- policyAllowed = Policy_SetStreamMute(pStreamInfo,muteState);
+
+ StreamMuteT muteState = StreamMuteToEnum(pMuteStr);
+#ifndef AHL_DISCONNECT_POLICY
+ json_object *pPolicyStreamJ = NULL;
+ err = PolicyStreamStructToJSON(pStreamInfo, &pPolicyStreamJ);
+ if (err == AHL_FAIL)
+ {
+ afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetStreamMute");
+ return;
+ }
+
+ json_object *paramJ= json_object_new_int(muteState);
+ json_object_object_add(pPolicyStreamJ, "mute_state", paramJ);
+
+ int policyAllowed = Policy_SetStreamMute(pPolicyStreamJ);
if (policyAllowed == AHL_POLICY_REJECT)
{
afb_req_fail(req, "Audio policy violation", "Mute stream not allowed in current context");
return;
}
+#else
+ // Simulate that policy returns target state (accepted)
+ pStreamInfo->streamMute = muteState;
+#endif
afb_req_success(req, NULL, "Set stream mute completed");
}
@@ -661,7 +998,7 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
}
AFB_DEBUG("Parsed input arguments = stream_id:%d", streamID);
- StreamInfoT * pStreamInfo = GetActiveStream(streamID);
+ StreamInfoT * pStreamInfo = GetStream(streamID);
if (pStreamInfo == NULL) {
afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID);
return;
@@ -676,17 +1013,18 @@ PUBLIC void audiohlapi_set_volume(struct afb_req req)
{
json_object *queryJ = NULL;
endpointID_t endpointID = AHL_UNDEFINED;
+ char * pEndpointTypeStr = NULL;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
char * volumeStr = NULL;
- int policyAllowed = AHL_POLICY_REJECT;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"volume",&volumeStr);
+ int err = wrap_json_unpack(queryJ, "{s:s,s:i,s:s}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID,"volume",&volumeStr);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d volume:%s", endpointType,endpointID,volumeStr);
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%s endpoint_id:%d volume:%s", pEndpointTypeStr,endpointID,volumeStr);
+ endpointType = EndpointTypeToEnum(pEndpointTypeStr);
EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType);
if (pEndpointInfo == NULL)
@@ -695,28 +1033,28 @@ PUBLIC void audiohlapi_set_volume(struct afb_req req)
return;
}
- // Check if there is already an existing context for this client
- AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure
- if (pClientCtx == NULL)
+#ifndef AHL_DISCONNECT_POLICY
+ json_object *pPolicyEndpointJ = NULL;
+ err = PolicyEndpointStructToJSON(pEndpointInfo, &pPolicyEndpointJ);
+ if (err == AHL_FAIL)
{
- afb_req_fail(req, "No context associated with the request", "No context associated with the request");
+ afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetVolume");
return;
- }
+ }
- // Verify that this client can control the stream
- int iEndpointAccessControl = CheckEndpointAccessControl( pClientCtx, endpointID );
- if (iEndpointAccessControl == AHL_ACCESS_CONTROL_DENIED)
- {
- afb_req_fail(req, "Access control denied", "Set volume not allowed in current client context");
- return;
- }
+ json_object *paramJ= json_object_new_string(volumeStr);
+ json_object_object_add(pPolicyEndpointJ, "arg_volume", paramJ);
- policyAllowed = Policy_SetVolume(pEndpointInfo, volumeStr);
+ int policyAllowed = Policy_SetVolume(pPolicyEndpointJ);
if (!policyAllowed)
{
afb_req_fail(req, "Audio policy violation", "Set volume not allowed in current context");
return;
}
+#else
+ // Simulate that policy returns target state (accepted)
+ pEndpointInfo->iVolume = atoi(volumeStr);
+#endif
afb_req_success(req, NULL, "Set volume completed");
}
@@ -725,16 +1063,18 @@ PUBLIC void audiohlapi_get_volume(struct afb_req req)
{
json_object *queryJ = NULL;
endpointID_t endpointID = AHL_UNDEFINED;
+ char * pEndpointTypeStr = NULL;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
json_object * volumeJ = NULL;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID);
+ int err = wrap_json_unpack(queryJ, "{s:s,s:i}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d", endpointType,endpointID);
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%s endpoint_id:%d", pEndpointTypeStr,endpointID);
+ endpointType = EndpointTypeToEnum(pEndpointTypeStr);
EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType);
if (pEndpointInfo == NULL)
@@ -743,26 +1083,27 @@ PUBLIC void audiohlapi_get_volume(struct afb_req req)
return;
}
- volumeJ = json_object_new_double((double)pEndpointInfo->iVolume);
+ volumeJ = json_object_new_int(pEndpointInfo->iVolume);
afb_req_success(req, volumeJ, "Retrieved volume value");
}
// Properties
-PUBLIC void audiohlapi_get_list_properties(struct afb_req req)
+PUBLIC void audiohlapi_get_endpoint_info(struct afb_req req)
{
json_object *queryJ = NULL;
endpointID_t endpointID = AHL_UNDEFINED;
+ char * pEndpointTypeStr = NULL;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
- json_object * endpointPropsJ = NULL;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID);
+ int err = wrap_json_unpack(queryJ, "{s:s,s:i}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d", endpointType,endpointID);
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%s endpoint_id:%d", pEndpointTypeStr,endpointID);
+ endpointType = EndpointTypeToEnum(pEndpointTypeStr);
EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType);
if (pEndpointInfo == NULL)
@@ -771,38 +1112,29 @@ PUBLIC void audiohlapi_get_list_properties(struct afb_req req)
return;
}
- // Build and return list of properties for specific endpoint
- GHashTableIter iter;
- gpointer key, value;
-
- g_hash_table_iter_init (&iter, pEndpointInfo->pPropTable);
- endpointPropsJ = json_object_new_array();
- while (g_hash_table_iter_next (&iter, &key, &value))
- {
- json_object * propJ = json_object_new_string(key);
- json_object_array_add(endpointPropsJ, propJ);
- }
+ json_object *endpointInfoJ = NULL;
+ EndpointInfoStructToJSON(&endpointInfoJ,pEndpointInfo);
- afb_req_success(req, endpointPropsJ, "Retrieved property list for endpoint");
+ afb_req_success(req, endpointInfoJ, "Retrieved endpoint information and properties");
}
PUBLIC void audiohlapi_set_property(struct afb_req req)
{
json_object *queryJ = NULL;
endpointID_t endpointID = AHL_UNDEFINED;
+ char * pEndpointTypeStr = NULL;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
char * propertyName = NULL;
json_object * propValueJ = NULL;
- int policyAllowed = AHL_POLICY_REJECT;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s:o}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueJ);
+ int err = wrap_json_unpack(queryJ, "{s:s,s:i,s:s,s:o}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueJ);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d property_name:%s", endpointType,endpointID,propertyName);
-
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%s endpoint_id:%d property_name:%s", pEndpointTypeStr,endpointID,propertyName);
+ endpointType = EndpointTypeToEnum(pEndpointTypeStr);
EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType);
if (pEndpointInfo == NULL)
@@ -811,31 +1143,33 @@ PUBLIC void audiohlapi_set_property(struct afb_req req)
return;
}
- // Check if there is already an existing context for this client
- AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure
- if (pClientCtx == NULL)
+#ifndef AHL_DISCONNECT_POLICY
+ json_object *pPolicyEndpointJ = NULL;
+ err = PolicyEndpointStructToJSON(pEndpointInfo, &pPolicyEndpointJ);
+ if (err == AHL_FAIL)
{
- afb_req_fail(req, "No context associated with the request", "No context associated with the request");
+ afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetVolume");
return;
- }
+ }
- // Verify that this client can control the stream
- int iEndpointAccessControl = CheckEndpointAccessControl( pClientCtx, endpointID );
- if (iEndpointAccessControl == AHL_ACCESS_CONTROL_DENIED)
- {
- afb_req_fail(req, "Access control denied", "Set property not allowed in current client context");
- return;
- }
+ json_object *paramJ= json_object_new_string(propertyName);
+ json_object_object_add(pPolicyEndpointJ, "arg_property_name", paramJ);
+ json_object_object_add(pPolicyEndpointJ, "arg_property_value", propValueJ);
// Call policy to allow custom policy actions in current context
- policyAllowed = Policy_SetProperty(pEndpointInfo, propertyName, propValueJ);
+ int policyAllowed = Policy_SetProperty(pPolicyEndpointJ);
if (!policyAllowed)
{
afb_req_fail(req, "Audio policy violation", "Set endpoint property not allowed in current context");
return;
}
+#else
+ // Simulate that policy returns target state (accepted)
+ if (pEndpointInfo->pPropTable)
+ g_hash_table_insert(pEndpointInfo->pPropTable, propertyName, propValueJ);
+#endif
- afb_event_push(g_AHLCtx.policyCtx.propertyEvent,queryJ);
+ //afb_event_push(g_AHLCtx.policyCtx.propertyEvent,queryJ);
afb_req_success(req, NULL, "Set property completed");
}
@@ -844,16 +1178,18 @@ PUBLIC void audiohlapi_get_property(struct afb_req req)
{
json_object *queryJ = NULL;
endpointID_t endpointID = AHL_UNDEFINED;
+ char * pEndpointTypeStr = NULL;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
char * propertyName = NULL;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"property_name",&propertyName);
+ int err = wrap_json_unpack(queryJ, "{s:s,s:i,s:s}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID,"property_name",&propertyName);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d property_name:%s", endpointType,endpointID,propertyName);
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%s endpoint_id:%d property_name:%s", pEndpointTypeStr,endpointID,propertyName);
+ endpointType = EndpointTypeToEnum(pEndpointTypeStr);
EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType);
if (pEndpointInfo == NULL)
@@ -870,17 +1206,19 @@ PUBLIC void audiohlapi_get_property(struct afb_req req)
}
json_object_get(propertyValJ); // Increase ref count so that framework does not free our JSON object
+ //AFB_WARNING("properties %s", json_object_get_string(propertyValJ));
+ //json_object_get(propertyValJ); // Increase ref count so that framework does not free our JSON object
afb_req_success(req, propertyValJ, "Retrieved property value");
}
// Audio related events
-PUBLIC void audiohlapi_get_list_events(struct afb_req req)
+PUBLIC void audiohlapi_get_list_actions(struct afb_req req)
{
json_object *queryJ = NULL;
char * audioRole = NULL;
- json_object * roleEventsJ = NULL;
+ json_object * roleActionsJ = NULL;
queryJ = afb_req_json(req);
int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role",&audioRole);
@@ -890,83 +1228,85 @@ PUBLIC void audiohlapi_get_list_events(struct afb_req req)
}
AFB_DEBUG("Parsed input arguments = audio_role:%s",audioRole);
- // Build and return list of events for specific audio role
- int iRoleIndex = FindRoleIndex(audioRole);
- if (iRoleIndex < 0) {
+ // Build and return list of actions for specific audio role
+ RoleInfoT * pRole = GetRole(audioRole);
+ if ( pRole == NULL )
+ {
afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole);
return;
}
- GArray * pRoleEventArray = g_ptr_array_index( g_AHLCtx.policyCtx.pEventList, iRoleIndex );
- roleEventsJ = json_object_new_array();
- int iNumberEvents = pRoleEventArray->len;
- for ( int i = 0 ; i < iNumberEvents; i++)
- {
- GString gsEventName = g_array_index(pRoleEventArray,GString,i);
- json_object * eventJ = json_object_new_string(gsEventName.str);
- json_object_array_add(roleEventsJ, eventJ);
+ roleActionsJ = json_object_new_array();
+ if (pRole->pActionList) {
+ int iNumberActions = pRole->pActionList->len;
+ for ( int i = 0 ; i < iNumberActions; i++)
+ {
+ char * pActionName = g_ptr_array_index(pRole->pActionList,i);
+ json_object * actionJ = json_object_new_string(pActionName);
+ json_object_array_add(roleActionsJ, actionJ);
+ }
}
- afb_req_success(req, roleEventsJ, "Retrieved event list for audio role");
+ afb_req_success(req, roleActionsJ, "Retrieved action list for audio role");
}
-PUBLIC void audiohlapi_post_event(struct afb_req req)
+PUBLIC void audiohlapi_post_action(struct afb_req req)
{
json_object *queryJ = NULL;
- char * eventName = NULL;
+ char * actionName = NULL;
char * audioRole = NULL;
char * mediaName = NULL;
- json_object *eventContext = NULL;
- int policyAllowed = AHL_POLICY_REJECT;
+ json_object *actionContext = NULL;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?s,s?o}", "event_name", &eventName,"audio_role",&audioRole,"media_name",&mediaName,"event_context",&eventContext);
+ int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?s,s?o}", "action_name", &actionName,"audio_role",&audioRole,"media_name",&mediaName,"action_context",&actionContext);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = event_name:%s audio_role:%s", eventName,audioRole);
+ AFB_DEBUG("Parsed input arguments = action_name:%s audio_role:%s", actionName,audioRole);
- // Verify if known event for audio role
- int iRoleIndex = FindRoleIndex(audioRole);
- if (iRoleIndex < 0) {
+ // Verify if known action for audio role
+ RoleInfoT * pRole = GetRole(audioRole);
+ if ( pRole == NULL )
+ {
afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole);
return;
}
- GArray * pRoleEventArray = NULL;
- pRoleEventArray = g_ptr_array_index( g_AHLCtx.policyCtx.pEventList, iRoleIndex );
- if (pRoleEventArray->len == 0) {
- afb_req_fail_f(req, "No available events", "No available events for role:%s",audioRole);
- return;
- }
- // Check to find specific event
- int iEventFound = 0;
- for (int i = 0; i < pRoleEventArray->len; i++)
- {
- GString gs = g_array_index( pRoleEventArray, GString, i );
- if ( strcasecmp(gs.str,eventName) == 0 )
+ // Check to find specific action
+ int iActionFound = 0;
+ if (pRole->pActionList) {
+ int iNumberActions = pRole->pActionList->len;
+ char * pTargetActionName = NULL;
+ for ( int i = 0 ; i < iNumberActions; i++)
{
- iEventFound = 1;
- break;
+ pTargetActionName = g_ptr_array_index(pRole->pActionList,i);
+ if ( strcasecmp(pTargetActionName,actionName)==0) {
+ iActionFound = 1;
+ break;
+ }
}
}
- if (!iEventFound) {
- afb_req_fail_f(req, "Event not found for audio role", "Event not found for roke:%s",audioRole);
+
+ if (!iActionFound) {
+ afb_req_fail_f(req, "Event not found for audio role", "Event -> %s not found for role:%s",actionName,audioRole);
return;
}
+#ifndef AHL_DISCONNECT_POLICY
// Call policy to allow custom policy actions in current context (e.g. cancel playback)
- policyAllowed = Policy_PostEvent(eventName, audioRole, mediaName, (void*)eventContext);
+ int policyAllowed = Policy_PostAction(queryJ);
if (!policyAllowed)
{
- afb_req_fail(req, "Audio policy violation", "Post sound event not allowed in current context");
+ afb_req_fail(req, "Audio policy violation", "Post sound action not allowed in current context");
return;
}
+#endif
- afb_event_push(g_AHLCtx.policyCtx.postEvent,queryJ);
+ //afb_event_push(g_AHLCtx.policyCtx.postEvent,queryJ);
- afb_req_success(req, NULL, "Posted sound event");
+ afb_req_success(req, NULL, "Posted sound action");
}
@@ -989,7 +1329,11 @@ PUBLIC void audiohlapi_subscribe(struct afb_req req)
char * pEventName = NULL;
json_object * jEvent = json_object_array_get_idx(eventArrayJ,i);
pEventName = (char *)json_object_get_string(jEvent);
- if(!strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)) {
+ if(pEventName == NULL) {
+ afb_req_fail(req, "failed", "Invalid event");
+ return;
+ }
+ else if(!strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)) {
afb_req_subscribe(req, g_AHLCtx.policyCtx.propertyEvent);
AFB_DEBUG("Client subscribed to endpoint property events");
}
@@ -997,8 +1341,8 @@ PUBLIC void audiohlapi_subscribe(struct afb_req req)
afb_req_subscribe(req, g_AHLCtx.policyCtx.volumeEvent);
AFB_DEBUG("Client subscribed to endpoint volume events");
}
- else if(!strcasecmp(pEventName, AHL_POST_EVENT)) {
- afb_req_subscribe(req, g_AHLCtx.policyCtx.postEvent);
+ else if(!strcasecmp(pEventName, AHL_POST_ACTION_EVENT)) {
+ afb_req_subscribe(req, g_AHLCtx.policyCtx.postActionEvent);
AFB_DEBUG("Client subscribed to post event calls events");
}
else {
@@ -1028,7 +1372,11 @@ PUBLIC void audiohlapi_unsubscribe(struct afb_req req)
char * pEventName = NULL;
json_object * jEvent = json_object_array_get_idx(eventArrayJ,i);
pEventName = (char *)json_object_get_string(jEvent);
- if(!strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)) {
+ if(pEventName == NULL) {
+ afb_req_fail(req, "failed", "Invalid event");
+ return;
+ }
+ else if(!strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)) {
afb_req_unsubscribe(req, g_AHLCtx.policyCtx.propertyEvent);
AFB_DEBUG("Client unsubscribed to endpoint property events");
}
@@ -1036,8 +1384,8 @@ PUBLIC void audiohlapi_unsubscribe(struct afb_req req)
afb_req_unsubscribe(req, g_AHLCtx.policyCtx.volumeEvent);
AFB_DEBUG("Client unsubscribed to endpoint volume events");
}
- else if(!strcasecmp(pEventName, AHL_POST_EVENT)) {
- afb_req_unsubscribe(req, g_AHLCtx.policyCtx.postEvent);
+ else if(!strcasecmp(pEventName, AHL_POST_ACTION_EVENT)) {
+ afb_req_unsubscribe(req, g_AHLCtx.policyCtx.postActionEvent);
AFB_DEBUG("Client unsubscribed to post event calls events");
}
else {
@@ -1048,3 +1396,182 @@ PUBLIC void audiohlapi_unsubscribe(struct afb_req req)
afb_req_success(req, NULL, "Unsubscribe to events finished");
}
+
+// Since the policy is currently in the same binding, it cannot raise events on its own
+// This is a first step toward isolation, when policy is migrated in its own binding it can simply raise AGL events
+// This binding will register for these policy events and will execute the code below upon event reception
+PUBLIC void audiohlapi_raise_event(json_object * pEventDataJ)
+{
+ char * pEventName = NULL;
+
+ int err = wrap_json_unpack(pEventDataJ,"{s:s}","event_name", &pEventName);
+ if(err)
+ {
+ AFB_ERROR("Unable to retrieve event name");
+ return;
+ }
+
+ if(strcasecmp(pEventName, AHL_ENDPOINT_INIT_EVENT)==0) {
+ char * pAudioRole = NULL;
+ endpointID_t endpointID = AHL_UNDEFINED;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:s}",
+ "endpoint_id", &endpointID,
+ "endpoint_type", &endpointType,
+ "audio_role", &pAudioRole);
+ if(err)
+ {
+ AFB_ERROR("Unable to unpack property init event");
+ return;
+ }
+
+ RoleInfoT * pRole = GetRole(pAudioRole);
+ if ( pRole == NULL ){
+ AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole);
+ return;
+ }
+
+ EndpointInfoT * pEndpointInfo = InitEndpointInfo();
+ g_assert_nonnull(pEndpointInfo);
+ PolicyCtxJSONToEndpoint(pEventDataJ,pEndpointInfo);
+
+ err = ReplaceEndpointInfoWithRole(endpointID,endpointType,pRole,pEndpointInfo);
+ if(err == AHL_FAIL)
+ {
+ AFB_ERROR("Can't update EndpointInfo aborting");
+ return;
+ }
+
+ // Remove event name from object
+ // json_object_object_del(pEventDataJ,"event_name");
+ //afb_event_push(g_AHLCtx.policyCtx.propertyEvent,pEventDataJ); // Not broadcasted to application at this time
+ }
+ else if(strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)==0) {
+ char * pAudioRole = NULL;
+ char * pPropertyName = NULL;
+ endpointID_t endpointID = AHL_UNDEFINED;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ json_object * propValueJ = NULL;
+ int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:s,s:o,s:s}",
+ "endpoint_id", &endpointID,
+ "endpoint_type", &endpointType,
+ "property_name", &pPropertyName,
+ "value",&propValueJ,
+ "audio_role", &pAudioRole);
+ if(err)
+ {
+ AFB_ERROR("Unable to unpack property event");
+ return;
+ }
+ RoleInfoT * pRole = GetRole(pAudioRole);
+ if ( pRole == NULL ){
+ AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole);
+ return;
+ }
+ EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole);
+ // update property value
+ if (pEndpointInfo->pPropTable)
+ {
+ json_type jType = json_object_get_type(propValueJ);
+ switch (jType) {
+ case json_type_double:
+ Add_Endpoint_Property_Double(pEndpointInfo,pPropertyName,json_object_get_double(propValueJ));
+ break;
+ case json_type_int:
+ Add_Endpoint_Property_Int(pEndpointInfo,pPropertyName,json_object_get_int(propValueJ));
+ break;
+ case json_type_string:
+ Add_Endpoint_Property_String(pEndpointInfo,pPropertyName,json_object_get_string(propValueJ));
+ break;
+ default:
+ AFB_ERROR("Invalid property argument Property value not a valid json object query=%s", json_object_get_string(propValueJ));
+ return ;
+ }
+ }
+ // Remove event name from object
+ json_object_object_del(pEventDataJ,"event_name");
+ afb_event_push(g_AHLCtx.policyCtx.propertyEvent,pEventDataJ);
+ }
+ else if(strcasecmp(pEventName, AHL_ENDPOINT_VOLUME_EVENT)==0) {
+ char * pAudioRole = NULL;
+ endpointID_t endpointID = AHL_UNDEFINED;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ int iVolume = 0;
+ int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:i,s:s}",
+ "endpoint_id", &endpointID,
+ "endpoint_type", &endpointType,
+ "value",&iVolume,
+ "audio_role", &pAudioRole);
+ if(err)
+ {
+ AFB_ERROR("Unable to unpack volume event data");
+ return;
+ }
+ RoleInfoT * pRole = GetRole(pAudioRole);
+ if ( pRole == NULL ){
+ AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole);
+ return;
+ }
+ EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole);
+ // update volume value
+ pEndpointInfo->iVolume = iVolume;
+ // Remove event name from object
+ json_object_object_del(pEventDataJ,"event_name");
+ afb_event_push(g_AHLCtx.policyCtx.volumeEvent,pEventDataJ);
+ }
+ else if(strcasecmp(pEventName, AHL_POST_ACTION_EVENT)==0) {
+ // Remove event name from object
+ json_object_object_del(pEventDataJ,"event_name");
+ afb_event_push(g_AHLCtx.policyCtx.postActionEvent,pEventDataJ);
+ }
+ else if(strcasecmp(pEventName, AHL_STREAM_STATE_EVENT)==0) {
+ streamID_t streamID = AHL_UNDEFINED;
+ StreamEventT streamEvent = STREAM_EVENT_MAXVALUE;
+ int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i}",
+ "stream_id", &streamID,
+ "state_event", &streamEvent);
+ if(err)
+ {
+ AFB_ERROR("Unable to unpack stream event data");
+ return;
+ }
+
+ StreamInfoT * pStreamInfo = GetStream(streamID);
+ if (pStreamInfo == NULL) {
+ AFB_ERROR("Specified stream not currently active stream_id -> %d",streamID);
+ return;
+ }
+
+ // update streamstate value
+ switch (streamEvent) {
+ case STREAM_EVENT_START:
+ pStreamInfo->streamState = STREAM_STATE_RUNNING;
+ break;
+ case STREAM_EVENT_STOP:
+ pStreamInfo->streamState = STREAM_STATE_IDLE;
+ break;
+ case STREAM_EVENT_PAUSE:
+ pStreamInfo->streamState = STREAM_STATE_PAUSED;
+ break;
+ case STREAM_EVENT_RESUME:
+ pStreamInfo->streamState = STREAM_STATE_RUNNING;
+ break;
+ case STREAM_EVENT_MUTED:
+ pStreamInfo->streamMute = STREAM_MUTED;
+ break;
+ case STREAM_EVENT_UNMUTED:
+ pStreamInfo->streamMute = STREAM_UNMUTED;
+ break;
+ default:
+ AFB_ERROR("Unknown stream event");
+ }
+
+ // Remove event name from object
+ json_object_object_del(pEventDataJ,"event_name");
+ AFB_ERROR("pEventDataJ=%s", json_object_get_string(pEventDataJ));
+ afb_event_push(pStreamInfo->streamStateEvent,pEventDataJ);
+ }
+ else {
+ AFB_ERROR("Unknown event name");
+ }
+} \ No newline at end of file
diff --git a/src/ahl-binding.h b/src/ahl-binding.h
index 8f308d5..af122c3 100644
--- a/src/ahl-binding.h
+++ b/src/ahl-binding.h
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2017 "Audiokinetic Inc"
- * Author Francois Thibault <fthibault@audiokinetic.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,18 +18,25 @@
#define AHL_BINDING_INCLUDE
#define AFB_BINDING_VERSION 2
-#include <afb/afb-binding.h>
+
+//#define AHL_DISCONNECT_POLICY // define for debugging HLB in standalone only
+
#include <json-c/json.h>
#include <glib.h>
#include "ahl-interface.h"
+#include <afb/afb-binding.h>
#ifndef PUBLIC
#define PUBLIC
#endif
+#define AHL_SUCCESS 0
+#define AHL_FAIL 1
+
#define AHL_POLICY_ACCEPT 1
#define AHL_POLICY_REJECT 0
+
#define AHL_ACCESS_CONTROL_GRANTED 1
#define AHL_ACCESS_CONTROL_DENIED 0
@@ -39,19 +45,75 @@
typedef int endpointID_t;
typedef int streamID_t;
-// Define default behavior of audio role when interrupted by higher priority sources
-typedef enum InterruptedBehavior {
- AHL_INTERRUPTEDBEHAVIOR_CONTINUE = 0, // Continue to play when interrupted (e.g. media may be ducked)
- AHL_INTERRUPTEDBEHAVIOR_CANCEL, // Abort playback when interrupted (e.g. non-important HMI feedback that does not make sense later)
- AHL_INTERRUPTEDBEHAVIOR_PAUSE, // Pause source when interrupted, to be resumed afterwards (e.g. non-temporal guidance)
- AHL_INTERRUPTEDBEHAVIOR_MAXVALUE, // Enum count, keep at the end
-} InterruptedBehaviorT;
-
-typedef enum EndpointSelectionMode {
- AHL_ENDPOINTSELMODE_AUTO = 0, // Automatic endpoint selection based on config priority
- AHL_ENDPOINTSELMODE_MANUAL, // Explicit endpoint selection
- AHL_ENDPOINTSELMODEMAXVALUE, // Enum count, keep at the end
-} EndpointSelectionModeT;
+#define AHL_STR_MAX_LENGTH 256
+
+typedef enum EndpointType {
+ ENDPOINTTYPE_SOURCE = 0, // source devices
+ ENDPOINTTYPE_SINK, // sink devices
+ ENDPOINTTYPE_MAXVALUE // Enum count, keep at the end
+} EndpointTypeT;
+
+typedef enum StreamState {
+ STREAM_STATE_IDLE = 0, // Stream is inactive
+ STREAM_STATE_RUNNING, // Stream is active and running
+ STREAM_STATE_PAUSED, // Stream is active but paused
+ STREAM_STATE_MAXVALUE // Enum count, keep at the end
+} StreamStateT;
+
+typedef enum StreamMute {
+ STREAM_UNMUTED = 0, // Stream is not muted
+ STREAM_MUTED, // Stream is muted
+ STREAM_MUTE_MAXVALUE, // Enum count, keep at the end
+} StreamMuteT;
+
+typedef enum StreamEvent {
+ STREAM_EVENT_START = 0, // Stream is inactive
+ STREAM_EVENT_STOP, // Stream is running
+ STREAM_EVENT_PAUSE, // Audio stream paused
+ STREAM_EVENT_RESUME, // Audio stream resumed
+ STREAM_EVENT_MUTED, // Audio stream muted
+ STREAM_EVENT_UNMUTED, // Audio stream unmuted
+ STREAM_EVENT_MAXVALUE // Enum count, keep at the end
+} StreamEventT;
+
+// Define default behavior of audio role when interrupting lower priority sources
+typedef enum InterruptBehavior {
+ INTERRUPTBEHAVIOR_CONTINUE = 0, // Continue to play lower priority source when interrupted (e.g. media may be ducked)
+ INTERRUPTBEHAVIOR_CANCEL, // Abort playback of lower priority source when interrupted (e.g. non-important HMI feedback that does not make sense later)
+ INTERRUPTBEHAVIOR_PAUSE, // Pause lower priority source when interrupted, to be resumed afterwards (e.g. non-temporal guidance)
+ INTERRUPTBEHAVIOR_MAXVALUE, // Enum count, keep at the end
+} InterruptBehaviorT;
+
+typedef enum DeviceURIType {
+ DEVICEURITYPE_ALSA_HW = 0, // Alsa hardware device URI
+ DEVICEURITYPE_ALSA_DMIX, // Alsa Dmix device URI (only for playback devices)
+ DEVICEURITYPE_ALSA_DSNOOP, // Alsa DSnoop device URI (only for capture devices)
+ DEVICEURITYPE_ALSA_SOFTVOL, // Alsa softvol device URI
+ DEVICEURITYPE_ALSA_PLUG, // Alsa plug device URI
+ DEVICEURITYPE_ALSA_OTHER, // Alsa domain URI device of unspecified type
+ DEVICEURITYPE_NOT_ALSA, // Unknown (not ALSA domain)
+ DEVICEURITYPE_MAXVALUE // Enum count, keep at the end
+} DeviceURITypeT;
+
+// CPU endianness assumed in all formats
+typedef enum SampleType {
+ AHL_FORMAT_UNKNOWN = -1, // Unknown
+ AHL_FORMAT_U8 = 0, // Unsigned 8 bit
+ AHL_FORMAT_S16, // Signed 16 bit Little Endian
+ AHL_FORMAT_S24, // Signed 24 bit Little Endian using low three bytes in 32-bit word
+ AHL_FORMAT_S32, // Signed 32 bit Little Endian
+ AHL_FORMAT_FLOAT, // Float 32 bit Little Endian, Range -1.0 to 1.0
+ AHL_FORMAT_FLOAT64, // Float 64 bit Little Endian, Range -1.0 to 1.0
+ AHL_FORMAT_IEC958, // IEC-958 Little Endian (SPDIF)
+ AHL_FORMAT_MU_LAW, // Mu-Law
+ AHL_FORMAT_A_LAW, // A-Law
+ AHL_FORMAT_IMA_ADPCM, // Ima-ADPCM
+ AHL_FORMAT_MPEG, // MPEG
+ AHL_FORMAT_GSM, // GSM
+ AHL_FORMAT_G723, // G723
+ AHL_FORMAT_DSD, // Direct stream digital
+ AHL_FORMAT_MAXVALUE, // Enum count, keep at the end
+} SampleTypeT;
typedef struct AudioFormat {
int sampleRate; // Sample rate
@@ -67,44 +129,59 @@ typedef struct AlsaDeviceInfo {
int subDeviceNum; // HW sub device number
} AlsaDeviceInfoT;
+typedef enum EndpointSelectionMode {
+ ENDPOINTSELMODE_AUTO = 0, // Automatic endpoint selection based on config priority
+ ENDPOINTSELMODE_MANUAL, // Explicit endpoint selection
+ ENDPOINTSELMODEMAXVALUE, // Enum count, keep at the end
+} EndpointSelectionModeT;
+
typedef struct EndpointInfo
{
endpointID_t endpointID; // Unique endpoint ID (per type)
EndpointTypeT type; // Source or sink device
- GString * gsDeviceName; // Unique device card name
- GString * gsDisplayName; // Application display name
- GString * gsDeviceURI; // Associated URI
+ char * gsDeviceName; // Unique device card name
+ char * gsDisplayName; // Application display name
+ char * gsDeviceURI; // Associated URI
+ char * gsDeviceDomain; // Device URI domain (e.g. alsa or pulse)
+ char * pRoleName; // Role assigned to this endpoint
DeviceURITypeT deviceURIType; // Device URI type (includes audio domain information)
- GString * gsAudioRole; // Audio role that registered this endpoint
- GString * gsHALAPIName; // HAL associated with the device (for volume control)
+ char * gsHALAPIName; // HAL associated with the device (for volume control)
AlsaDeviceInfoT alsaInfo; // ALSA specific device information
AudioFormatT format; // Preferred audio format supported (later could be array of supported formats)
- int iVolume; // Storage for current endpoint volume (policy effected). Target volume during ramping?
+ int iVolume; // Storage for current endpoint volume (policy effected).
GHashTable * pPropTable; // Storage for array of properties (policy effected)
} EndpointInfoT;
typedef struct StreamInfo {
- streamID_t streamID; // Stream unique ID
- EndpointInfoT * pEndpointInfo; // Associated endpoint information
- StreamStateT streamState; // Stream activity state
- StreamMuteT streamMute; // Stream mute state
- struct afb_event streamStateEvent; // Stream specific event for stream state changes
- EndpointSelectionModeT endpointSelMode; // Automatic (priority based) or manual endpoint selection
+ streamID_t streamID; // Stream unique ID
+ EndpointInfoT * pEndpointInfo; // Associated endpoint information (reference)
+ StreamStateT streamState; // Stream activity state
+ StreamMuteT streamMute; // Stream mute state
+ struct afb_event streamStateEvent; // Stream specific event for stream state changes
+ EndpointSelectionModeT endpointSelMode; // Automatic (priority based) or manual endpoint selection
+ char * pRoleName; // Role string identifier (from role config but could be programatically overriden later)
+ int iPriority; // Role normalized priority (0-100) (from role config but could be programatically overriden later)
+ InterruptBehaviorT eInterruptBehavior; // Role behavior when interrupting lower priority streams (from role config but could be programatically overriden later)
} StreamInfoT;
+typedef struct RoleInfo {
+ char * pRoleName; // Role string identifier
+ int iPriority; // Role normalized priority (0-100)
+ InterruptBehaviorT eInterruptBehavior; // Role behavior when interrupting lower priority streams
+ GPtrArray * pActionList; // List of supported actions for the role (gchar*)
+ GPtrArray * pSourceEndpoints; // Source endpoints info (EndpointInfoT*)
+ GPtrArray * pSinkEndpoints; // Sink endpoints info (EndpointInfoT*)
+} RoleInfoT;
+
// Parts of the context that are visible to the policy (for state based decisions)
typedef struct AHLPolicyCtx {
- GPtrArray * pSourceEndpoints; // Array of source end points for each audio role (GArray*)
- GPtrArray * pSinkEndpoints; // Array of sink end points for each audio role (GArray*)
- GPtrArray * pEventList; // Event list per audio roles (GArray*)
- GHashTable * pRolePriority; // List of role priorities (int).
- GArray * pInterruptBehavior; // List of interrupt behavior per audio role (int/enum).
- GArray * pAudioRoles; // List of audio roles (GString)
- GArray * pActiveStreams; // List of active streams (StreamInfoT)
- int iNumberRoles; // Number of audio roles from configuration
- struct afb_event propertyEvent; // AGL event used when property changes
- struct afb_event volumeEvent; // AGL event used when volume changes
- struct afb_event postEvent; // AGL event used on post event call
+ GHashTable * pRoleInfo; // Hash table of role information structure (RoleInfoT*) accessed by role name
+ GHashTable * pStreams; // List of active streams (StreamInfoT*) accessed by streamID
+ GPtrArray * pHALList; // List of HAL dependencies
+ // TODO: Events need to be sent directly by HLB when separation with policy complete
+ struct afb_event propertyEvent; // AGL event used when property changes
+ struct afb_event volumeEvent; // AGL event used when volume changes
+ struct afb_event postActionEvent; // AGL event used on post action call
} AHLPolicyCtxT;
// Global binding context
@@ -113,14 +190,11 @@ typedef struct AHLCtx {
endpointID_t nextSourceEndpointID; // Counter to assign new ID
endpointID_t nextSinkEndpointID; // Counter to assign new ID
endpointID_t nextStreamID; // Counter to assign new ID
- GArray * pHALList; // List of HAL dependencies
- int iNumActiveClients; // Number of clients with active stream(s)
} AHLCtxT;
// Client specific binding context
typedef struct AHLClientCtx {
- GArray * pEndpointAccessList; // List of endpoints that client has control over
- GArray * pStreamAccessList; // List of streams that client has control over
+ GArray * pStreamAccessList; // List of streams that client has control over
} AHLClientCtxT;
// ahl-binding.c
@@ -128,24 +202,14 @@ PUBLIC int AhlBindingInit();
PUBLIC void AhlOnEvent(const char *evtname, json_object *eventJ);
// ahl-deviceenum.c
-int EnumerateSources(json_object * in_jSourceArray, int in_iRoleIndex, char * in_pRoleName);
-int EnumerateSinks(json_object * in_jSinkArray, int in_iRoleIndex, char * in_pRoleName);
-void TermEndpoints();
+int EnumerateDevices(json_object * in_jDeviceArray, char * in_pAudioRole, EndpointTypeT in_deviceType, GPtrArray * out_pEndpointArray);
+EndpointInfoT * InitEndpointInfo();
+void TermEndpointInfo( EndpointInfoT * out_pEndpointInfo );
// ahl-config.c
int ParseHLBConfig();
// ahl-policy.c
-int Policy_Endpoint_Property_Init(EndpointInfoT * io_pEndpointInfo);
-int Policy_OpenStream(StreamInfoT * pStreamInfo);
-int Policy_CloseStream(StreamInfoT * pStreamInfo);
-int Policy_SetStreamState(StreamInfoT * pStreamInfo, int AudioRoleIndex, StreamStateT streamState);
-int Policy_SetStreamMute(StreamInfoT * pStreamInfo, StreamMuteT streamMute);
-int Policy_PostEvent(char *eventName, char *audioRole, char *mediaName, void *audioContext);
-int Policy_AudioDeviceChange();
-int Policy_SetVolume(EndpointInfoT * f_pEndpointInfo, char *volumeStr);
-//Todo
-int Policy_SetProperty(EndpointInfoT * f_pEndpointInfo, char *propertyName, json_object *propValue);
-int Policy_Init();
-void Policy_Term();
-void Policy_OnEvent(const char *evtname, json_object *eventJ);
+#ifndef AHL_DISCONNECT_POLICY
+PUBLIC void audiohlapi_raise_event(json_object *EventDataJ);
+#endif
#endif // AHL_BINDING_INCLUDE
diff --git a/src/ahl-config.c b/src/ahl-config.c
index aa55b28..168311e 100644
--- a/src/ahl-config.c
+++ b/src/ahl-config.c
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2017 "Audiokinetic Inc"
- * Author Francois Thibault <fthibault@audiokinetic.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +24,22 @@
extern AHLCtxT g_AHLCtx;
+static InterruptBehaviorT InterruptBehaviorToEnum(char * in_pInterruptBehaviorStr)
+{
+ g_assert_nonnull(in_pInterruptBehaviorStr);
+ if (strcasecmp(in_pInterruptBehaviorStr,AHL_INTERRUPTBEHAVIOR_CONTINUE)==0) {
+ return INTERRUPTBEHAVIOR_CONTINUE;
+ }
+ else if (strcasecmp(in_pInterruptBehaviorStr,AHL_INTERRUPTBEHAVIOR_CANCEL)==0) {
+ return INTERRUPTBEHAVIOR_CANCEL;
+ }
+ else if (strcasecmp(in_pInterruptBehaviorStr,AHL_INTERRUPTBEHAVIOR_PAUSE)==0) {
+ return INTERRUPTBEHAVIOR_PAUSE;
+ }
+ else
+ return INTERRUPTBEHAVIOR_MAXVALUE;
+}
+
int ParseHLBConfig() {
char * versionStr = NULL;
json_object * jAudioRoles = NULL;
@@ -33,7 +48,7 @@ int ParseHLBConfig() {
// TODO: This should be retrieve from binding startup arguments
char configfile_path[256];
- sprintf(configfile_path, "%s/opt/config/ahl-config.json", getenv("HOME"));
+ sprintf(configfile_path, "%s", getenv("AHL_CONFIG_FILE"));
AFB_INFO("High-level config file -> %s\n", configfile_path);
// Open configuration file
@@ -49,36 +64,31 @@ int ParseHLBConfig() {
AFB_ERROR("Invalid configuration file -> %s", configfile_path);
return 1;
}
- AFB_INFO("Version: %s", versionStr);
+ AFB_INFO("High-level audio API version: %s", "1.0.0");
+ AFB_INFO("Config version: %s", versionStr);
AFB_INFO("Policy module: %s", policyModule);
int iHALListLength = json_object_array_length(jHALList);
+ g_AHLCtx.policyCtx.pHALList = g_ptr_array_new_with_free_func(g_free);
int iNumberOfRoles = json_object_array_length(jAudioRoles);
- g_AHLCtx.policyCtx.iNumberRoles = iNumberOfRoles;
-
- // Dynamically allocated based on number or roles found
- g_AHLCtx.pHALList = g_array_sized_new(FALSE, TRUE, sizeof(GString), iHALListLength);
- g_AHLCtx.policyCtx.pRolePriority = g_hash_table_new(g_str_hash, g_str_equal);
- g_AHLCtx.policyCtx.pAudioRoles = g_array_sized_new(FALSE, TRUE, sizeof(GString), iNumberOfRoles);
- g_AHLCtx.policyCtx.pInterruptBehavior = g_array_sized_new(FALSE, TRUE, sizeof(int), iNumberOfRoles);
- g_AHLCtx.policyCtx.pSourceEndpoints = g_ptr_array_sized_new(iNumberOfRoles);
- g_AHLCtx.policyCtx.pSinkEndpoints = g_ptr_array_sized_new(iNumberOfRoles);
- g_AHLCtx.policyCtx.pEventList = g_ptr_array_sized_new(iNumberOfRoles);
+ g_AHLCtx.policyCtx.pRoleInfo = g_hash_table_new(g_str_hash, g_str_equal);
for (int i = 0; i < iHALListLength; i++)
{
char * pHAL = NULL;
json_object * jHAL = json_object_array_get_idx(jHALList,i);
- pHAL = (char *)json_object_get_string(jHAL);
- GString * gHALName = g_string_new( pHAL );
- g_array_append_val( g_AHLCtx.pHALList, *gHALName );
-
- // Set dependency on HAL
- err = afb_daemon_require_api_v2(pHAL,1) ;
- if( err != 0 )
- {
- AFB_ERROR("Audio high level API could not set dependenvy on API: %s",pHAL);
- return 1;
+ if (jHAL) {
+ pHAL = (char *)json_object_get_string(jHAL);
+ char * pHALName = g_strdup( pHAL );
+ g_ptr_array_add( g_AHLCtx.policyCtx.pHALList, pHALName );
+
+ // Set dependency on HAL specified
+ err = afb_daemon_require_api_v2(pHAL,1) ;
+ if( err != 0 )
+ {
+ AFB_ERROR("Audio high level API could not set dependency on API: %s",pHAL);
+ return 1;
+ }
}
}
@@ -88,13 +98,13 @@ int ParseHLBConfig() {
json_object * jAudioRole = json_object_array_get_idx(jAudioRoles,i);
json_object * jOutputDevices = NULL;
json_object * jInputDevices = NULL;
- json_object * jEvents = NULL;
+ json_object * jActions = NULL;
char * pRoleName = NULL;
char * pInteruptBehavior = NULL;
int iNumOutDevices = 0;
int iNumInDevices = 0;
- int iNumEvents = 0;
+ int iNumActions = 0;
err = wrap_json_unpack(jAudioRole, "{s:s,s:i,s:s,s?o,s?o,s?o}",
"name", &pRoleName,
@@ -102,7 +112,7 @@ int ParseHLBConfig() {
"interupt_behavior",&pInteruptBehavior,
"output",&jOutputDevices,
"input",&jInputDevices,
- "events",&jEvents
+ "actions",&jActions
);
if (err) {
AFB_ERROR("Invalid audio role configuration : %s", json_object_to_json_string(jAudioRole));
@@ -113,61 +123,47 @@ int ParseHLBConfig() {
iNumOutDevices = json_object_array_length(jOutputDevices);
if (jInputDevices)
iNumInDevices = json_object_array_length(jInputDevices);
- if (jEvents)
- iNumEvents = json_object_array_length(jEvents);
-
- GString * gRoleName = g_string_new( pRoleName );
- g_array_append_val( g_AHLCtx.policyCtx.pAudioRoles, *gRoleName );
- g_hash_table_insert(g_AHLCtx.policyCtx.pRolePriority, pRoleName, GINT_TO_POINTER(priority));
-
- // Map interupt behavior string to enum value
- InterruptedBehaviorT interuptBehavior = AHL_INTERRUPTEDBEHAVIOR_CONTINUE;
- if ( strcasecmp(pInteruptBehavior,AHL_INTERRUPTEDBEHAVIOR_CONTINUE_STR) == 0 ) {
- interuptBehavior = AHL_INTERRUPTEDBEHAVIOR_CONTINUE;
- }
- else if ( strcasecmp(pInteruptBehavior,AHL_INTERRUPTEDBEHAVIOR_CANCEL_STR) == 0 ) {
- interuptBehavior = AHL_INTERRUPTEDBEHAVIOR_CANCEL;
- }
- else if ( strcasecmp(pInteruptBehavior,AHL_INTERRUPTEDBEHAVIOR_PAUSE_STR) == 0 ) {
- interuptBehavior = AHL_INTERRUPTEDBEHAVIOR_PAUSE;
- }
- else {
- AFB_ERROR("Unknown interrupt behavior : %s", pInteruptBehavior);
- return 1;
+ if (jActions)
+ iNumActions = json_object_array_length(jActions);
+
+ RoleInfoT * pRoleInfo = (RoleInfoT*) malloc(sizeof(RoleInfoT));
+ memset(pRoleInfo,0,sizeof(RoleInfoT));
+ pRoleInfo->pRoleName = g_strdup( pRoleName );
+ pRoleInfo->iPriority = priority;
+ pRoleInfo->eInterruptBehavior = InterruptBehaviorToEnum(pInteruptBehavior);
+
+ // Actions
+ pRoleInfo->pActionList = g_ptr_array_new_with_free_func(g_free);
+ // Parse and validate list of available actions
+ for (int i = 0; i < iNumActions; i++)
+ {
+ json_object * jAction = json_object_array_get_idx(jActions,i);
+ char * pActionName = (char *)json_object_get_string(jAction);
+ if (pActionName)
+ g_ptr_array_add(pRoleInfo->pActionList, g_strdup(pActionName));
}
- g_array_append_val(g_AHLCtx.policyCtx.pInterruptBehavior, interuptBehavior);
// Sources
- GArray * pRoleSourceDeviceArray = g_array_new(FALSE, TRUE, sizeof(EndpointInfoT));
- g_ptr_array_add(g_AHLCtx.policyCtx.pSourceEndpoints,pRoleSourceDeviceArray);
+ pRoleInfo->pSourceEndpoints = g_ptr_array_new_with_free_func(g_free);
if (iNumInDevices) {
- err = EnumerateSources(jInputDevices,i,pRoleName);
+ err = EnumerateDevices(jInputDevices,pRoleName,ENDPOINTTYPE_SOURCE,pRoleInfo->pSourceEndpoints);
if (err) {
AFB_ERROR("Invalid input devices : %s", json_object_to_json_string(jInputDevices));
return 1;
}
}
// Sinks
- GArray * pRoleSinkDeviceArray = g_array_new(FALSE, TRUE, sizeof(EndpointInfoT));
- g_ptr_array_add(g_AHLCtx.policyCtx.pSinkEndpoints,pRoleSinkDeviceArray);
+ pRoleInfo->pSinkEndpoints = g_ptr_array_new_with_free_func(g_free);
if (iNumOutDevices) {
- err = EnumerateSinks(jOutputDevices,i,pRoleName);
+ err = EnumerateDevices(jOutputDevices,pRoleName,ENDPOINTTYPE_SINK,pRoleInfo->pSinkEndpoints);
if (err) {
AFB_ERROR("Invalid output devices : %s", json_object_to_json_string(jOutputDevices));
return 1;
}
}
- // Events
- GArray * pEventsArray = g_array_new(FALSE, TRUE, sizeof(GString));
- g_ptr_array_add(g_AHLCtx.policyCtx.pEventList,pEventsArray);
- // Parse and validate list of available events
- for (int i = 0; i < iNumEvents; i++)
- {
- json_object * jEvent = json_object_array_get_idx(jEvents,i);
- char * pEventName = (char *)json_object_get_string(jEvent);
- GString * gsEventName = g_string_new(pEventName);
- g_array_append_val(pEventsArray, *gsEventName);
- }
+
+ g_hash_table_insert(g_AHLCtx.policyCtx.pRoleInfo, pRoleInfo->pRoleName, pRoleInfo);
+
}
// Build lists of all device URI referenced in config file (input/output)
diff --git a/src/ahl-deviceenum.c b/src/ahl-deviceenum.c
index 4722333..7e67d7a 100644
--- a/src/ahl-deviceenum.c
+++ b/src/ahl-deviceenum.c
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2017 "Audiokinetic Inc"
- * Author Francois Thibault <fthibault@audiokinetic.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +23,7 @@
#include "wrap-json.h"
#include "ahl-binding.h"
+#include "ahl-policy.h"
extern AHLCtxT g_AHLCtx;
@@ -44,7 +44,6 @@ static endpointID_t CreateNewSinkID()
}
// Watchout: This function uses strtok and is destructive on the input string (use a copy)
-// TODO: Perhaps it would be clearer to separate domain and device URI in both API inputs and outputs
static int SeparateDomainFromDeviceURI( char * in_pDeviceURI, char ** out_pDomain, char ** out_pDevice)
{
*out_pDomain = strtok(in_pDeviceURI, ".");
@@ -85,6 +84,8 @@ static int IsExternalDomain(const char * in_pDomainStr)
static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndpointInfo )
{
+ g_assert_nonnull(in_pPcmHandle);
+ g_assert_nonnull(out_pEndpointInfo);
snd_pcm_type_t pcmType = 0;
snd_pcm_info_t * pPcmInfo = NULL;
int iAlsaRet = 0;
@@ -172,336 +173,159 @@ static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndp
snd_ctl_close(ctlHandle);
return 1;
}
- g_string_assign(out_pEndpointInfo->gsDeviceName,pCardName);
+ out_pEndpointInfo->gsDeviceName = g_strdup(pCardName);
snd_ctl_close(ctlHandle);
return retVal;
}
-static int RetrieveAssociatedHALAPIName(EndpointInfoT * io_pEndpointInfo)
+EndpointInfoT * InitEndpointInfo()
{
- json_object *j_response, *j_query = NULL;
- int err;
- err = afb_service_call_sync("alsacore", "hallist", j_query, &j_response);
- if (err) {
- AFB_ERROR("Could not retrieve list of HAL from ALSA core");
- return 1;
- }
- AFB_DEBUG("ALSAcore hallist response=%s", json_object_to_json_string(j_response));
-
- // Look through returned list for matching card
- int found = 0;
- json_object * jRespObj = NULL;
- json_object_object_get_ex(j_response, "response", &jRespObj);
- int iNumHAL = json_object_array_length(jRespObj);
- for ( int i = 0 ; i < iNumHAL; i++)
- {
- json_object * jHAL = json_object_array_get_idx(jRespObj,i);
- char * pDevIDStr = NULL;
- char * pAPIName = NULL;
- char * pShortName = NULL;
-
- int err = wrap_json_unpack(jHAL, "{s:s,s:s,s:s}", "devid", &pDevIDStr,"api", &pAPIName,"shortname",&pShortName);
- if (err) {
- AFB_ERROR("Could not retrieve devid string=%s", json_object_get_string(jHAL));
- return 1;
- }
-
- // Retrieve card number (e.g. hw:0)
- int iCardNum = atoi(pDevIDStr+3);
- if (iCardNum == io_pEndpointInfo->alsaInfo.cardNum) {
- g_string_assign(io_pEndpointInfo->gsHALAPIName,pAPIName);
- g_string_assign(io_pEndpointInfo->gsDisplayName,pShortName);
- found = 1;
- break;
- }
- }
- return !found;
-}
-
-static void InitEndpointInfo( EndpointInfoT * out_pEndpointInfo )
-{
- out_pEndpointInfo->endpointID = AHL_UNDEFINED;
- out_pEndpointInfo->type = ENDPOINTTYPE_MAXVALUE;
- out_pEndpointInfo->gsDeviceName = g_string_new("Unassigned");
- out_pEndpointInfo->gsDisplayName = g_string_new("Unassigned");
- out_pEndpointInfo->gsDeviceURI = g_string_new("Unassigned");
- out_pEndpointInfo->deviceURIType = DEVICEURITYPE_MAXVALUE;
- out_pEndpointInfo->gsAudioRole = g_string_new("Unassigned");
- out_pEndpointInfo->gsHALAPIName = g_string_new("Unassigned");
- out_pEndpointInfo->alsaInfo.cardNum = AHL_UNDEFINED;
- out_pEndpointInfo->alsaInfo.deviceNum = AHL_UNDEFINED;
- out_pEndpointInfo->alsaInfo.subDeviceNum = AHL_UNDEFINED;
- out_pEndpointInfo->format.sampleRate = AHL_UNDEFINED;
- out_pEndpointInfo->format.numChannels = AHL_UNDEFINED;
- out_pEndpointInfo->format.sampleType = AHL_FORMAT_UNKNOWN;
- out_pEndpointInfo->pPropTable = g_hash_table_new(g_str_hash, g_str_equal);
+ EndpointInfoT * pEndpointInfo = (EndpointInfoT*) malloc(sizeof(EndpointInfoT));
+ memset(pEndpointInfo,0,sizeof(EndpointInfoT));
+ pEndpointInfo->endpointID = AHL_UNDEFINED;
+ pEndpointInfo->type = ENDPOINTTYPE_MAXVALUE;
+ pEndpointInfo->deviceURIType = DEVICEURITYPE_MAXVALUE;
+ pEndpointInfo->alsaInfo.cardNum = AHL_UNDEFINED;
+ pEndpointInfo->alsaInfo.deviceNum = AHL_UNDEFINED;
+ pEndpointInfo->alsaInfo.subDeviceNum = AHL_UNDEFINED;
+ pEndpointInfo->format.sampleRate = AHL_UNDEFINED;
+ pEndpointInfo->format.numChannels = AHL_UNDEFINED;
+ pEndpointInfo->format.sampleType = AHL_FORMAT_UNKNOWN;
+ pEndpointInfo->pPropTable = g_hash_table_new(g_str_hash, g_str_equal);
+ return pEndpointInfo;
}
-static void TermEndpointInfo( EndpointInfoT * out_pEndpointInfo )
+void TermEndpointInfo( EndpointInfoT * out_pEndpointInfo )
{
- g_string_free(out_pEndpointInfo->gsDeviceName,TRUE);
- g_string_free(out_pEndpointInfo->gsDisplayName,TRUE);
- g_string_free(out_pEndpointInfo->gsDeviceURI,TRUE);
- g_string_free(out_pEndpointInfo->gsAudioRole,TRUE);
- g_string_free(out_pEndpointInfo->gsHALAPIName,TRUE);
- // TODO: Free json_object for all property values
- g_hash_table_remove_all(out_pEndpointInfo->pPropTable);
- g_hash_table_destroy(out_pEndpointInfo->pPropTable);
-}
-
-void TermEndpoints()
-{
- // Sources for each role
- for (int i = 0; i < g_AHLCtx.policyCtx.pSourceEndpoints->len; i++)
- {
- // For each endpoint within the role
- GArray * pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, i );
- for (int j = 0 ; j < pRoleDeviceArray->len; j++)
- {
- EndpointInfoT * pEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,j);
- TermEndpointInfo(pEndpointInfo);
- }
- g_array_free(pRoleDeviceArray,TRUE);
- pRoleDeviceArray = NULL;
- }
- g_ptr_array_free(g_AHLCtx.policyCtx.pSourceEndpoints,TRUE);
- g_AHLCtx.policyCtx.pSourceEndpoints = NULL;
-
- // Sinks for each role
- for (int i = 0; i < g_AHLCtx.policyCtx.pSinkEndpoints->len; i++)
- {
- // For each endpoint within the role
- GArray * pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, i );
- for (int j = 0 ; j < pRoleDeviceArray->len; j++)
+ #define SAFE_FREE(__ptr__) if(__ptr__) g_free(__ptr__); __ptr__ = NULL;
+ SAFE_FREE(out_pEndpointInfo->gsDeviceName);
+ SAFE_FREE(out_pEndpointInfo->gsDisplayName);
+ SAFE_FREE(out_pEndpointInfo->gsDeviceDomain);
+ SAFE_FREE(out_pEndpointInfo->pRoleName);
+ SAFE_FREE(out_pEndpointInfo->gsDeviceURI);
+ SAFE_FREE(out_pEndpointInfo->gsHALAPIName);
+
+ if (out_pEndpointInfo->pPropTable) {
+ // Free json_object for all property values
+ GHashTableIter iter;
+ gpointer key, value;
+ g_hash_table_iter_init (&iter, out_pEndpointInfo->pPropTable);
+ while (g_hash_table_iter_next (&iter, &key, &value))
{
- EndpointInfoT * pEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,j);
- TermEndpointInfo(pEndpointInfo);
+ if (value)
+ json_object_put(value);
}
- g_array_free(pRoleDeviceArray,TRUE);
- pRoleDeviceArray = NULL;
+ g_hash_table_remove_all(out_pEndpointInfo->pPropTable);
+ g_hash_table_destroy(out_pEndpointInfo->pPropTable);
+ out_pEndpointInfo->pPropTable = NULL;
}
- g_ptr_array_free(g_AHLCtx.policyCtx.pSinkEndpoints,TRUE);
- g_AHLCtx.policyCtx.pSinkEndpoints = NULL;
+ // GLib automatically frees item when removed from the array
}
-#define AUDIOHL_MAX_DEVICE_URI_LENGTH 128
-
// For a given audio role
-int EnumerateSources(json_object * in_jSourceArray, int in_iRoleIndex, char * in_pRoleName) {
+int EnumerateDevices(json_object * in_jDeviceArray, char * in_pAudioRole, EndpointTypeT in_deviceType, GPtrArray * out_pEndpointArray) {
- int iNumberDevices = json_object_array_length(in_jSourceArray);
+ g_assert_nonnull(in_jDeviceArray);
+ int iNumberDevices = json_object_array_length(in_jDeviceArray);
// Parse and validate list of available devices
for (int i = 0; i < iNumberDevices; i++)
{
- char fullDeviceURI[AUDIOHL_MAX_DEVICE_URI_LENGTH]; // strtok is destructive
char * pDeviceURIDomain = NULL;
char * pFullDeviceURI = NULL;
char * pDeviceURIPCM = NULL;
int err = 0;
- EndpointInfoT endpointInfo;
-
- json_object * jSource = json_object_array_get_idx(in_jSourceArray,i);
+ json_object * jDevice = json_object_array_get_idx(in_jDeviceArray,i);
+ if (jDevice == NULL) {
+ AFB_WARNING("Invalid device array -> %s",json_object_to_json_string(in_jDeviceArray));
+ continue;
+ }
// strip domain name from URI
- pFullDeviceURI = (char *)json_object_get_string(jSource);
- strncpy(fullDeviceURI,pFullDeviceURI,AUDIOHL_MAX_DEVICE_URI_LENGTH);
- err = SeparateDomainFromDeviceURI(fullDeviceURI,&pDeviceURIDomain,&pDeviceURIPCM);
+ pFullDeviceURI = (char *)json_object_get_string(jDevice);
+ char * pFullDeviceURICopy = g_strdup(pFullDeviceURI); // strtok is destructive
+ err = SeparateDomainFromDeviceURI(pFullDeviceURICopy,&pDeviceURIDomain,&pDeviceURIPCM);
if (err)
{
- AFB_WARNING("Invalid device URI string -> %s",fullDeviceURI);
+ AFB_WARNING("Invalid device URI string -> %s",pFullDeviceURICopy);
continue;
}
- InitEndpointInfo(&endpointInfo);
+ EndpointInfoT * pEndpointInfo = InitEndpointInfo();
+ g_assert_nonnull(pEndpointInfo);
// non ALSA URI are simply passed to application (no validation) at this time
// In Non ALSA case devices in config are assumed to be available, if not application can fallback on explicit device selection
- g_string_assign(endpointInfo.gsDeviceName,pDeviceURIPCM);
+ pEndpointInfo->gsDeviceName = g_strdup(pDeviceURIPCM);
+ pEndpointInfo->gsDeviceDomain = g_strdup(pDeviceURIDomain);
+ pEndpointInfo->gsDeviceURI = g_strdup(pDeviceURIPCM);
+ pEndpointInfo->pRoleName = g_strdup(in_pAudioRole);
+
+ g_free(pFullDeviceURICopy);
+ pFullDeviceURICopy = NULL;
+ pDeviceURIDomain = NULL; //Derived from above mem
+ pDeviceURIPCM = NULL; //Derived from above mem
- if (IsAlsaDomain(pDeviceURIDomain))
+ if (IsAlsaDomain(pEndpointInfo->gsDeviceDomain))
{
// TODO: Missing support for loose name matching
// This will require using ALSA hints to get PCM names
// And would iterate over all available devices matching string (possibly all if no filtering is desired for a certain role)
- snd_pcm_t * pPcmHandle = NULL;
-
// Get PCM handle
- err = snd_pcm_open(&pPcmHandle, pDeviceURIPCM, SND_PCM_STREAM_CAPTURE, 0);
+ snd_pcm_t * pPcmHandle = NULL;
+ snd_pcm_stream_t streamType = in_deviceType == ENDPOINTTYPE_SOURCE ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK;
+ err = snd_pcm_open(&pPcmHandle, pEndpointInfo->gsDeviceURI, streamType, 0);
if (err < 0)
{
- AFB_NOTICE("Alsa PCM device was not found -> %s", pDeviceURIPCM);
+ AFB_NOTICE("Alsa PCM device was not found -> %s", pEndpointInfo->gsDeviceURI);
continue;
}
- err = FillALSAPCMInfo(pPcmHandle,&endpointInfo);
+ err = FillALSAPCMInfo(pPcmHandle,pEndpointInfo);
if (err) {
- AFB_WARNING("Unable to retrieve PCM information for PCM -> %s",pDeviceURIPCM);
+ AFB_WARNING("Unable to retrieve PCM information for PCM -> %s",pEndpointInfo->gsDeviceURI);
snd_pcm_close(pPcmHandle);
continue;
}
snd_pcm_close(pPcmHandle);
-
- // TODO: Consider policy call to determine URI for set volume execution for a particular role (hw?)
- // Retrieve HAL API name
- err = RetrieveAssociatedHALAPIName(&endpointInfo);
- if (err) {
- AFB_WARNING("SetVolume will fail without HAL association ->%s",endpointInfo.gsDeviceURI->str);
- // Choose not to skip anyhow...
- }
}
- else if (IsPulseDomain(pDeviceURIDomain)) {
+ else if (IsPulseDomain(pEndpointInfo->gsDeviceDomain)) {
// Pulse domain
// For now display name is device URI directly, could extrapolated using more heuristics or even usins Pulse API later on
- endpointInfo.deviceURIType = DEVICEURITYPE_PULSE;
+ pEndpointInfo->deviceURIType = DEVICEURITYPE_NOT_ALSA;
}
- else if (IsGStreamerDomain(pDeviceURIDomain)){
+ else if (IsGStreamerDomain(pEndpointInfo->gsDeviceDomain)){
// GStreamer domain
// For now display name is device URI directly, could extrapolated using more heuristics or even usins GStreamer API later on
- endpointInfo.deviceURIType = DEVICEURITYPE_GSTREAMER;
+ pEndpointInfo->deviceURIType = DEVICEURITYPE_NOT_ALSA;
}
- else if (IsExternalDomain(pDeviceURIDomain)){
+ else if (IsExternalDomain(pEndpointInfo->gsDeviceDomain)){
// External domain
- endpointInfo.deviceURIType = DEVICEURITYPE_EXTERNAL;
- }
- else {
- // Unknown domain
- AFB_WARNING("Unknown domain in device URI string -> %s",fullDeviceURI);
- continue;
- }
-
- g_string_assign(endpointInfo.gsDeviceURI,pDeviceURIPCM);
- g_string_assign(endpointInfo.gsAudioRole,in_pRoleName);
- endpointInfo.endpointID = CreateNewSourceID();
- endpointInfo.type = ENDPOINTTYPE_SOURCE;
- err = Policy_Endpoint_Property_Init(&endpointInfo);
- if (err) {
- AFB_WARNING("Policy endpoint properties initalization failed for endpointid :%d type:%d",endpointInfo.endpointID, endpointInfo.type);
- // Choose not to skip anyhow...
- }
-
- // add to structure to list of available source devices
- GArray * pRoleSourceDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, in_iRoleIndex );
- g_array_append_val(pRoleSourceDeviceArray, endpointInfo);
-
- } // for all devices
-
- AFB_DEBUG ("Audio high-level - Enumerate sources done");
- return 0;
-}
-
-// For a given audio role
-int EnumerateSinks(json_object * in_jSinkArray, int in_iRoleIndex, char * in_pRoleName) {
-
- int iNumberDevices = json_object_array_length(in_jSinkArray);
-
- // Parse and validate list of available devices
- for (int i = 0; i < iNumberDevices; i++)
- {
- char fullDeviceURI[AUDIOHL_MAX_DEVICE_URI_LENGTH]; // strtok is destructive
- char * pDeviceURIDomain = NULL;
- char * pFullDeviceURI = NULL;
- char * pDeviceURIPCM = NULL;
- int err = 0;
- EndpointInfoT endpointInfo;
-
- json_object * jSink = json_object_array_get_idx(in_jSinkArray,i);
-
- // strip domain name from URI
- pFullDeviceURI = (char*)json_object_get_string(jSink);
- strncpy(fullDeviceURI,pFullDeviceURI,AUDIOHL_MAX_DEVICE_URI_LENGTH);
- err = SeparateDomainFromDeviceURI(fullDeviceURI,&pDeviceURIDomain,&pDeviceURIPCM);
- if (err)
- {
- AFB_WARNING("Invalid device URI string -> %s",fullDeviceURI);
- continue;
- }
-
- // non ALSA URI are simply passed to application (no validation) at this time
- // In Non ALSA case devices in config are assumed to be available, if not application can fallback on explicit device selection
-
- InitEndpointInfo(&endpointInfo);
-
- endpointInfo.alsaInfo.cardNum = -1;
- endpointInfo.alsaInfo.deviceNum = -1;
- endpointInfo.alsaInfo.cardNum = -1;
- g_string_assign(endpointInfo.gsDeviceName,pDeviceURIPCM);
-
- if (IsAlsaDomain(pDeviceURIDomain))
- {
- // TODO: Missing support for loose name matching
- // This will require using ALSA hints to get PCM names
- // And would iterate over all available devices matching string (possibly all if no filtering is desired for a certain role)
-
- snd_pcm_t * pPcmHandle = NULL;
-
- // get PCM handle
- err = snd_pcm_open(&pPcmHandle, pDeviceURIPCM, SND_PCM_STREAM_PLAYBACK, 0);
- if (err < 0)
- {
- AFB_NOTICE("Alsa PCM device was not found -> %s", pDeviceURIPCM);
- continue;
- }
-
- err = FillALSAPCMInfo(pPcmHandle,&endpointInfo);
- if (err) {
- AFB_WARNING("Unable to retrieve PCM information for PCM -> %s",pDeviceURIPCM);
- snd_pcm_close(pPcmHandle);
- continue;
- }
-
- snd_pcm_close(pPcmHandle);
-
- // TODO: Consider policy call to determine URI for set volume execution for a particular role (hw?)
- // Retrieve HAL API name
- err = RetrieveAssociatedHALAPIName(&endpointInfo);
- if (err) {
- //AFB_WARNING("SetVolume w fail without HAL association ->%s",endpointInfo.deviceURI);
- continue;
- }
- }
- else if (IsPulseDomain(pDeviceURIDomain)) {
- // Pulse domain
- // For now display name is device URI directly, could extrapolated using more heuristics or even usins Pulse API later on
- endpointInfo.deviceURIType = DEVICEURITYPE_PULSE;
-
- }
- else if (IsGStreamerDomain(pDeviceURIDomain)){
- // GStreamer domain
- // For now display name is device URI directly, could extrapolated using more heuristics or even usins GStreamer API later on
- endpointInfo.deviceURIType = DEVICEURITYPE_GSTREAMER;
- }
- else if (IsExternalDomain(pDeviceURIDomain)){
- // External domain
-
- endpointInfo.deviceURIType = DEVICEURITYPE_EXTERNAL;
+ pEndpointInfo->deviceURIType = DEVICEURITYPE_NOT_ALSA;
}
else {
// Unknown domain
- AFB_WARNING("Unknown domain in device URI string -> %s",fullDeviceURI);
+ AFB_WARNING("Unknown domain in device URI string -> %s",pFullDeviceURI);
continue;
}
- g_string_assign(endpointInfo.gsDeviceURI,pDeviceURIPCM);
- g_string_assign(endpointInfo.gsAudioRole,in_pRoleName);
- endpointInfo.endpointID = CreateNewSinkID();
- endpointInfo.type = ENDPOINTTYPE_SINK;
- err = Policy_Endpoint_Property_Init(&endpointInfo);
- if (err) {
- AFB_WARNING("Policy endpoint properties initalization failed for endpointid :%d type:%d",endpointInfo.endpointID, endpointInfo.type);
- // Choose not to skip anyhow...
- }
+ pEndpointInfo->endpointID = in_deviceType == ENDPOINTTYPE_SOURCE ? CreateNewSourceID() : CreateNewSinkID();
+ pEndpointInfo->type = in_deviceType;
+ // Assigned by policy initialization
+ pEndpointInfo->gsDisplayName = malloc(AHL_STR_MAX_LENGTH*sizeof(char));
+ memset(pEndpointInfo->gsDisplayName,0,AHL_STR_MAX_LENGTH*sizeof(char));
+ pEndpointInfo->gsHALAPIName = malloc(AHL_STR_MAX_LENGTH*sizeof(char));
+ memset(pEndpointInfo->gsDisplayName,0,AHL_STR_MAX_LENGTH*sizeof(char));
- // add to structure to list of available source devices
- GArray * pRoleSinkDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, in_iRoleIndex );
- g_array_append_val(pRoleSinkDeviceArray, endpointInfo);
+ // add to structure to list of available devices
+ g_ptr_array_add(out_pEndpointArray, pEndpointInfo);
} // for all devices
- AFB_DEBUG ("Audio high-level - Enumerate sinks done");
+ AFB_DEBUG ("Audio high-level - Enumerate devices done");
return 0;
-}
+} \ No newline at end of file
diff --git a/src/ahl-interface.h b/src/ahl-interface.h
index 04b2865..0488e96 100644
--- a/src/ahl-interface.h
+++ b/src/ahl-interface.h
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2017 "Audiokinetic Inc"
- * Author Francois Thibault <fthibault@audiokinetic.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,76 +17,39 @@
#ifndef AHL_INTERFACE_INCLUDE
#define AHL_INTERFACE_INCLUDE
-typedef enum EndpointType {
- ENDPOINTTYPE_SOURCE = 0, // source devices
- ENDPOINTTYPE_SINK, // sink devices
- ENDPOINTTYPE_MAXVALUE // Enum count, keep at the end
-} EndpointTypeT;
-
-typedef enum DeviceURIType {
- DEVICEURITYPE_ALSA_HW = 0, // Alsa hardware device URI
- DEVICEURITYPE_ALSA_DMIX, // Alsa Dmix device URI (only for playback devices)
- DEVICEURITYPE_ALSA_DSNOOP, // Alsa DSnoop device URI (only for capture devices)
- DEVICEURITYPE_ALSA_SOFTVOL, // Alsa softvol device URI
- DEVICEURITYPE_ALSA_PLUG, // Alsa plug device URI
- DEVICEURITYPE_ALSA_OTHER, // Alsa domain URI device of unspecified type
- DEVICEURITYPE_PULSE, // Pulse device URI
- DEVICEURITYPE_GSTREAMER, // GStreamer device URI
- DEVICEURITYPE_EXTERNAL, // Device URI for external ECU device
- DEVICEURITYPE_MAXVALUE // Enum count, keep at the end
-} DeviceURITypeT;
-
-typedef enum StreamState {
- STREAM_STATE_IDLE = 0, // Stream is inactive
- STREAM_STATE_RUNNING, // Stream is active and running
- STREAM_STATE_PAUSED, // Stream is active but paused
- STREAM_STATE_MAXVALUE // Enum count, keep at the end
-} StreamStateT;
-
-typedef enum StreamMute {
- STREAM_UNMUTED = 0, // Stream is not muted
- STREAM_MUTED, // Stream is muted
- STREAM_MUTE_MAXVALUE, // Enum count, keep at the end
-} StreamMuteT;
-
-typedef enum StreamEvent {
- STREAM_EVENT_START = 0, // Stream is inactive
- STREAM_EVENT_STOP, // Stream is running
- STREAM_EVENT_PAUSE, // Audio stream paused
- STREAM_EVENT_RESUME, // Audio stream resumed
- STREAM_EVENT_MUTED, // Audio stream muted
- STREAM_EVENT_UNMUTED, // Audio stream unmuted
- STREAM_STATUS_MAXVALUE // Enum count, keep at the end
-} StreamEventT;
-
-// Define default behavior of audio role when interrupted by higher priority sources (in configuration)
-#define AHL_INTERRUPTEDBEHAVIOR_CONTINUE_STR "continue" // Continue to play when interrupted (e.g. media may be ducked)
-#define AHL_INTERRUPTEDBEHAVIOR_CANCEL_STR "cancel" // Abort playback when interrupted (e.g. non-important HMI feedback that does not make sense later)
-#define AHL_INTERRUPTEDBEHAVIOR_PAUSE_STR "pause" // Pause source when interrupted, to be resumed afterwards (e.g. non-temporal guidance)
+///// API /////
+// Endpoint types
+#define AHL_ENDPOINTTYPE_SOURCE "source" // source devices
+#define AHL_ENDPOINTTYPE_SINK "sink" // sink devices
+
+// Stream state
+#define AHL_STREAM_STATE_IDLE "idle" // Stream is inactive
+#define AHL_STREAM_STATE_RUNNING "running" // Stream is active and running
+#define AHL_STREAM_STATE_PAUSED "paused" // Stream is active but paused
+
+// Stream mute state
+#define AHL_STREAM_UNMUTED "off" // Stream is not muted
+#define AHL_STREAM_MUTED "on" // Stream is muted
+
+// Property/Volume/Action events
#define AHL_ENDPOINT_PROPERTY_EVENT "ahl_endpoint_property_event"
#define AHL_ENDPOINT_VOLUME_EVENT "ahl_endpoint_volume_event"
-#define AHL_POST_EVENT "ahl_post_event"
-
-// CPU endianness assumed in all formats
-typedef enum SampleType {
- AHL_FORMAT_UNKNOWN = -1, // Unknown
- AHL_FORMAT_U8 = 0, // Unsigned 8 bit
- AHL_FORMAT_S16, // Signed 16 bit Little Endian
- AHL_FORMAT_S24, // Signed 24 bit Little Endian using low three bytes in 32-bit word
- AHL_FORMAT_S32, // Signed 32 bit Little Endian
- AHL_FORMAT_FLOAT, // Float 32 bit Little Endian, Range -1.0 to 1.0
- AHL_FORMAT_FLOAT64, // Float 64 bit Little Endian, Range -1.0 to 1.0
- AHL_FORMAT_IEC958, // IEC-958 Little Endian (SPDIF)
- AHL_FORMAT_MU_LAW, // Mu-Law
- AHL_FORMAT_A_LAW, // A-Law
- AHL_FORMAT_IMA_ADPCM, // Ima-ADPCM
- AHL_FORMAT_MPEG, // MPEG
- AHL_FORMAT_GSM, // GSM
- AHL_FORMAT_G723, // G723
- AHL_FORMAT_DSD, // Direct stream digital
- AHL_FORMAT_MAXVALUE, // Enum count, keep at the end
-} SampleTypeT;
+#define AHL_ENDPOINT_INIT_EVENT "ahl_endpoint_init_event"
+#define AHL_POST_ACTION_EVENT "ahl_post_action"
+#define AHL_STREAM_STATE_EVENT "ahl_stream_state_event"
+#define AHL_ENDPOINT_INIT_EVENT "ahl_endpoint_init_event"
+
+
+// Stream state event types
+#define AHL_STREAM_EVENT_START "start" // Stream is inactive
+#define AHL_STREAM_EVENT_STOP "stop" // Stream is running
+#define AHL_STREAM_EVENT_PAUSE "pause" // Audio stream paused
+#define AHL_STREAM_EVENT_RESUME "resume" // Audio stream resumed
+#define AHL_STREAM_EVENT_MUTE "mute" // Audio stream muted
+#define AHL_STREAM_EVENT_UNMUTE "unmute" // Audio stream unmuted
+
+///// Interpret returned or configuration information /////
// Known audio domain string definitions (for configuration file format and device URI interpretation)
#define AHL_DOMAIN_ALSA "alsa"
@@ -95,6 +57,22 @@ typedef enum SampleType {
#define AHL_DOMAIN_GSTREAMER "gstreamer"
#define AHL_DOMAIN_EXTERNAL "external"
+// ALSA Device URI type
+#define AHL_DEVICEURITYPE_ALSA_HW "hw" // Alsa hardware device URI
+#define AHL_DEVICEURITYPE_ALSA_DMIX "dmix" // Alsa Dmix device URI (only for playback devices)
+#define AHL_DEVICEURITYPE_ALSA_DSNOOP "dsnoop" // Alsa DSnoop device URI (only for capture devices)
+#define AHL_DEVICEURITYPE_ALSA_SOFTVOL "softvol" // Alsa softvol device URI
+#define AHL_DEVICEURITYPE_ALSA_PLUG "plug" // Alsa plug device URI
+#define AHL_DEVICEURITYPE_ALSA_OTHER "other" // Alsa domain URI device of unspecified type
+#define AHL_DEVICEURITYPE_NOT_ALSA "nonalsa"
+
+// Define default behavior of audio role when interrupting lower priority sources (in configuration)
+#define AHL_INTERRUPTBEHAVIOR_CONTINUE "continue" // Continue to play when interrupted (e.g. media may be ducked)
+#define AHL_INTERRUPTBEHAVIOR_CANCEL "cancel" // Abort playback when interrupted (e.g. non-important HMI feedback that does not make sense later)
+#define AHL_INTERRUPTBEHAVIOR_PAUSE "pause" // Pause source when interrupted, to be resumed afterwards (e.g. non-temporal guidance)
+
+///// Naming convention /////
+
// Standardized name for common audio roles (not enforced in any way, just helps compatibility)
#define AHL_ROLE_WARNING "warning" // Safety-relevant or critical alerts/alarms
#define AHL_ROLE_GUIDANCE "guidance" // Important user information where user action is expected (e.g. navigation instruction)
@@ -118,5 +96,4 @@ typedef enum SampleType {
#define AHL_EVENTS_ECHOCANCEL_ENABLE "echocancel_enable"
#define AHL_EVENTS_ECHOCANCEL_DISABLE "echocancel_disable"
-
#endif // AHL_INTERFACE_INCLUDE
diff --git a/src/ahl-policy-utils.c b/src/ahl-policy-utils.c
new file mode 100644
index 0000000..52c5862
--- /dev/null
+++ b/src/ahl-policy-utils.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2017 "Audiokinetic Inc"
+ *
+ * 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 "ahl-policy-utils.h"
+#include "wrap-json.h"
+#include <json-c/json.h>
+#include <glib.h>
+
+void Add_Endpoint_Property_Double( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, double in_dPropertyValue)
+{
+ json_object * propValueJ = json_object_new_double(in_dPropertyValue);
+ g_hash_table_insert(io_pEndpointInfo->pPropTable, in_pPropertyName, propValueJ);
+}
+
+
+void Add_Endpoint_Property_Int( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, int in_iPropertyValue)
+{
+ json_object * propValueJ = json_object_new_int(in_iPropertyValue);
+ g_hash_table_insert(io_pEndpointInfo->pPropTable, in_pPropertyName, propValueJ);
+}
+
+void Add_Endpoint_Property_String( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, const char * in_pPropertyValue)
+{
+ json_object * propValueJ = json_object_new_string(in_pPropertyValue);
+ g_hash_table_insert(io_pEndpointInfo->pPropTable, in_pPropertyName, propValueJ);
+}
+
+int PolicyCtxStructToJSON(AHLPolicyCtxT * pPolicyCtx, json_object **ppPolicyCxtJ)
+{
+ if(pPolicyCtx == NULL)
+ {
+ AFB_ERROR("Policy context is NULL");
+ return AHL_FAIL;
+ }
+
+ //Create json object for HALList
+ json_object *HallListJ = json_object_new_array();
+ for(int i=0; i<pPolicyCtx->pHALList->len; i++)
+ {
+ char *hallname = g_ptr_array_index(pPolicyCtx->pHALList, i);
+ json_object *hallNameJ = json_object_new_string(hallname);
+ json_object_array_add(HallListJ, hallNameJ);
+ }
+
+ int err = wrap_json_pack(ppPolicyCxtJ, "{s:i,s:o}",
+ "number_roles", (int)g_hash_table_size(pPolicyCtx->pRoleInfo),
+ "halllist", HallListJ);
+ if (err) {
+ AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err));
+ }
+
+ return AHL_SUCCESS;
+}
+
+
+ int PolicyCtxJSONToStruct(json_object *CtxJ, int *pNbAudioRole, GPtrArray *pHALList)
+{
+ if(CtxJ == NULL || pHALList == NULL)
+ {
+ AFB_ERROR("Invalid arguments to PolicyCtxJSONToStruct");
+ return AHL_FAIL;
+ }
+
+ json_object *HallListJ = NULL;
+ int err = wrap_json_unpack(CtxJ, "{s:i, s:o}",
+ "number_roles", &pNbAudioRole,
+ "halllist", &HallListJ);
+ if (err) {
+ AFB_ERROR("Unable to unpack JSON endpoint, =%s", wrap_json_get_error_string(err));
+ return AHL_FAIL;
+ }
+
+ if(HallListJ)
+ {
+ int nbHall = json_object_array_length(HallListJ);
+ //Create HallList
+ for(int i=0; i<nbHall; i++)
+ {
+ char * pHAL = NULL;
+ json_object * jHAL = json_object_array_get_idx(HallListJ,i);
+ if (jHAL) {
+ pHAL = (char *)json_object_get_string(jHAL);
+ char * pHALName = g_strdup( pHAL );
+ g_ptr_array_add( pHALList, pHALName );
+ }
+ }
+ }
+
+ return AHL_SUCCESS;
+}
+
+int PolicyEndpointStructToJSON(EndpointInfoT * pEndpointInfo, json_object **ppPolicyEndpointJ)
+{
+ if(pEndpointInfo == NULL || pEndpointInfo->pPropTable == NULL)
+ {
+ AFB_ERROR("Invalid PolicyEndpointStructToJSON arguments");
+ return AHL_FAIL;
+ }
+
+ //Create json object for PropTable
+ json_object *pPropTableJ = json_object_new_array();
+ if(pEndpointInfo->pPropTable) {
+ GHashTableIter iter;
+ gpointer key, value;
+ g_hash_table_iter_init (&iter, pEndpointInfo->pPropTable);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ json_object *pPropertyJ = NULL;
+ int error=wrap_json_pack(&pPropertyJ, "{s:s,s:o}",
+ "property_name", (char*)key,
+ "property_value", value
+ );
+ if(error)
+ {
+ AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(error));
+ return AHL_FAIL;
+ }
+ json_object_array_add(pPropTableJ, pPropertyJ);
+ }
+ AFB_DEBUG("json object query=%s", json_object_get_string(pPropTableJ));
+ }
+
+ //Create json object for Endpoint
+ int err= wrap_json_pack(ppPolicyEndpointJ, "{s:i,s:i,s:s,s:s,s:s,s:s,s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:o}",
+ "endpoint_id", pEndpointInfo->endpointID,
+ "endpoint_type", pEndpointInfo->type,
+ "device_name", pEndpointInfo->gsDeviceName,
+ "display_name", pEndpointInfo->gsDisplayName,
+ "device_uri", pEndpointInfo->gsDeviceURI,
+ "device_domain", pEndpointInfo->gsDeviceDomain,
+ "audio_role",pEndpointInfo->pRoleName,
+ "device_uri_type", pEndpointInfo->deviceURIType,
+ "hal_api_name", pEndpointInfo->gsHALAPIName,
+ "alsa_cardNum", pEndpointInfo->alsaInfo.cardNum,
+ "alsa_deviceNum", pEndpointInfo->alsaInfo.deviceNum,
+ "alsa_subDeviceNum", pEndpointInfo->alsaInfo.subDeviceNum,
+ "format_samplerate", pEndpointInfo->format.sampleRate,
+ "format_numchannels", pEndpointInfo->format.numChannels,
+ "format_sampletype",pEndpointInfo->format.sampleType,
+ "volume", pEndpointInfo->iVolume,
+ "property_table", pPropTableJ
+ );
+ if (err) {
+ AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err));
+ return AHL_FAIL;
+ }
+ AFB_DEBUG("JSON endpoint information=%s", json_object_get_string(*ppPolicyEndpointJ));
+ return AHL_SUCCESS;
+}
+
+int PolicyStreamStructToJSON(StreamInfoT * pPolicyStream, json_object **ppPolicyStreamJ)
+{
+ if(pPolicyStream == NULL)
+ {
+ AFB_ERROR("Invalid arguments to PolicyStreamStructToJSON");
+ return AHL_FAIL;
+ }
+
+ json_object * pEndpointJ = NULL;
+ int iRet = PolicyEndpointStructToJSON(pPolicyStream->pEndpointInfo, &pEndpointJ);
+ if (iRet) {
+ return iRet;
+ }
+
+ //Create json object for stream
+ int err = wrap_json_pack(ppPolicyStreamJ, "{s:i,s:i,s:i,s:I,s:i,s:s,s:i,s:i,s:o}",
+ "stream_id", pPolicyStream->streamID,
+ "stream_state", pPolicyStream->streamState,
+ "stream_mute", pPolicyStream->streamMute,
+ "stream_state_event", &pPolicyStream->streamStateEvent,
+ "endpoint_sel_mod", pPolicyStream->endpointSelMode,
+ "role_name", pPolicyStream->pRoleName,
+ "priority", pPolicyStream->iPriority,
+ "interrupt_behavior", pPolicyStream->eInterruptBehavior,
+ "endpoint_info", pEndpointJ
+ );
+ if (err) {
+ AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err));
+ return AHL_FAIL;
+ }
+
+ AFB_DEBUG("JSON stream information=%s", json_object_get_string(*ppPolicyStreamJ));
+
+ return AHL_SUCCESS;
+}
+
+int PolicyCtxJSONToEndpoint(json_object *pEndpointJ, EndpointInfoT * pEndpointInfo)
+{
+ if(pEndpointJ == NULL || pEndpointInfo == NULL /*|| pEndpointInfo->pPropTable == NULL */ )
+ {
+ AFB_ERROR("Invalid arguments for PolicyCtxJSONToEndpoint");
+ return AHL_FAIL;
+ }
+
+ //Unpack Endpoint
+ json_object *pPropTableJ = NULL;
+ int err = wrap_json_unpack(pEndpointJ, "{s:i,s:i,s:s,s:s,s:s,s:s,s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:o}",
+ "endpoint_id", &pEndpointInfo->endpointID,
+ "endpoint_type", &pEndpointInfo->type,
+ "device_name", &pEndpointInfo->gsDeviceName,
+ "display_name", &pEndpointInfo->gsDisplayName,
+ "device_uri", &pEndpointInfo->gsDeviceURI,
+ "device_domain", &pEndpointInfo->gsDeviceDomain,
+ "audio_role", &pEndpointInfo->pRoleName,
+ "device_uri_type", &pEndpointInfo->deviceURIType,
+ "hal_api_name", &pEndpointInfo->gsHALAPIName,
+ "alsa_cardNum", &pEndpointInfo->alsaInfo.cardNum,
+ "alsa_deviceNum", &pEndpointInfo->alsaInfo.deviceNum,
+ "alsa_subDeviceNum", &pEndpointInfo->alsaInfo.subDeviceNum,
+ "format_samplerate", &pEndpointInfo->format.sampleRate,
+ "format_numchannels", &pEndpointInfo->format.numChannels,
+ "format_sampletype",&pEndpointInfo->format.sampleType,
+ "volume", &pEndpointInfo->iVolume,
+ "property_table", &pPropTableJ
+ );
+ if (err) {
+ AFB_ERROR("Unable to unpack JSON endpoint, =%s", wrap_json_get_error_string(err));
+ return AHL_FAIL;
+ }
+
+ // Unpack prop table
+ if(pPropTableJ)
+ {
+ pEndpointInfo->pPropTable = g_hash_table_new(g_str_hash, g_str_equal);
+
+ int nbProperties = json_object_array_length(pPropTableJ);
+ for(int i=0; i<nbProperties; i++)
+ {
+ json_object * propJ = json_object_array_get_idx(pPropTableJ,i);
+ if (propJ) {
+ char * pPropertyName = NULL;
+ json_object * pPropertyValueJ = NULL;
+
+ int err=wrap_json_unpack(propJ, "{s:s,s:o}",
+ "property_name", &pPropertyName,
+ "property_value", &pPropertyValueJ);
+ if (err) {
+ AFB_ERROR("Unable to unpack JSON endpoint, = %s", wrap_json_get_error_string(err));
+ return AHL_FAIL;
+ }
+
+ // Object type detection for property value (string = state, numeric = property)
+ json_type jType = json_object_get_type(pPropertyValueJ);
+ switch (jType) {
+ case json_type_double:
+ Add_Endpoint_Property_Double(pEndpointInfo,pPropertyName,json_object_get_double(pPropertyValueJ));
+ break;
+ case json_type_int:
+ Add_Endpoint_Property_Int(pEndpointInfo,pPropertyName,json_object_get_int(pPropertyValueJ));
+ break;
+ case json_type_string:
+ Add_Endpoint_Property_String(pEndpointInfo,pPropertyName,json_object_get_string(pPropertyValueJ));
+ break;
+ default:
+ AFB_ERROR("Invalid property argument Property value not a valid json object query=%s", json_object_get_string(pPropertyValueJ));
+ return AHL_FAIL;
+ }
+ }
+ }
+ }
+
+ return AHL_SUCCESS;
+}
+
+int PolicyCtxJSONToStream(json_object *pStreamJ, StreamInfoT * pPolicyStream)
+{
+ if(pStreamJ == NULL || pPolicyStream == NULL)
+ {
+ AFB_ERROR("Invalid arguments for PolicyCtxJSONToStream");
+ return AHL_FAIL;
+ }
+
+ //Unpack StreamInfo
+ json_object *pEndpointJ = NULL;
+ AFB_WARNING("json object query=%s", json_object_get_string(pStreamJ));
+ int err=wrap_json_unpack(pStreamJ, "{s:i,s:i,s:i,s:I,s:i,s:s,s:i,s:i,s:o}",
+ "stream_id", &pPolicyStream->streamID,
+ "stream_state", &pPolicyStream->streamState,
+ "stream_mute", &pPolicyStream->streamMute,
+ "stream_state_event", &pPolicyStream->streamStateEvent,
+ "endpoint_sel_mod", &pPolicyStream->endpointSelMode,
+ "role_name", &pPolicyStream->pRoleName,
+ "priority", &pPolicyStream->iPriority,
+ "interrupt_behavior", &pPolicyStream->eInterruptBehavior,
+ "endpoint_info", &pEndpointJ
+ );
+
+ if (err) {
+ AFB_ERROR("Unable to parse JSON stream information=%s", json_object_get_string(pStreamJ));
+ return AHL_FAIL;
+ }
+
+ int iRet = PolicyCtxJSONToEndpoint(pEndpointJ,pPolicyStream->pEndpointInfo);
+ if (iRet) {
+ return iRet;
+ }
+ return AHL_SUCCESS;
+}
+ \ No newline at end of file
diff --git a/src/ahl-policy-utils.h b/src/ahl-policy-utils.h
new file mode 100644
index 0000000..6774f13
--- /dev/null
+++ b/src/ahl-policy-utils.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 "Audiokinetic Inc"
+ *
+ * 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.
+ */
+
+#ifndef AHL_POLICY_UTILS_INCLUDE
+#define AHL_POLICY_UTILS_INCLUDE
+
+#include "ahl-binding.h"
+
+void Add_Endpoint_Property_Double( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, double in_dPropertyValue);
+void Add_Endpoint_Property_Int( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, int in_iPropertyValue);
+void Add_Endpoint_Property_String( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, const char * in_pPropertyValue);
+
+int PolicyCtxStructToJSON(AHLPolicyCtxT * pPolicyCtx, json_object **ppPolicyCxtJ);
+int PolicyCtxJSONToStruct(json_object *CtxJ, int *pNbAudioRole,GPtrArray *pHALList);
+int PolicyEndpointStructToJSON(EndpointInfoT * pPolicyEndpoint, json_object **ppPolicyEndpointJ);
+int PolicyCtxJSONToEndpoint(json_object *pEndpointJ, EndpointInfoT * pPolicyStream);
+int PolicyStreamStructToJSON(StreamInfoT * pPolicyStream, json_object **ppPolicyStreamJ);
+int PolicyCtxJSONToStream(json_object *pStreamJ, StreamInfoT * pPolicyStream);
+
+#endif // AHL_POLICY_UTILS_INCLUDE
diff --git a/src/ahl-policy.c b/src/ahl-policy.c
index 110f7e9..ba15fd2 100644
--- a/src/ahl-policy.c
+++ b/src/ahl-policy.c
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2017 "Audiokinetic Inc"
- * Author Francois Thibault <fthibault@audiokinetic.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,218 +18,117 @@
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
-#include "ahl-binding.h"
#include "wrap-json.h"
+#include "ahl-policy-utils.h"
+#include "ahl-policy.h"
-#define MAX_ACTIVE_STREAM_POLICY 30
-
-// This file provides example of custom, business logic driven policy actions that can affect behavior of the high level
-// TODO: Currently only isolated in separate source file. Objective is to make this to at least a shared lib plug-in (shared C context)
-// Going a step further would be to implement this within a distinct policy binding (requires to switch to JSON interface)
-
-extern AHLCtxT g_AHLCtx; // TODO: Cannot stay if moved to external module
-
-typedef struct StreamPolicyInfo {
- int RolePriority;
- int iVolume;
- int iVolumeSavedMute;
- streamID_t streamID;
- InterruptedBehaviorT interruptBehavior;
-} StreamPolicyInfoT;
-
-
-typedef struct EndPointPolicyInfo {
- int endpointKey;
- endpointID_t endpointID;
- EndpointTypeT type;
- GArray * streamInfo; //List of playing or duck stream at a given endpoint
-} EndPointPolicyInfoT;
-
-typedef enum SystemState {
- SYSTEM_STARTUP = 0, // Startup
- SYSTEM_SHUTDOWN, // ShutDown
- SYSTEM_NORMAL, // Normal
- SYSTEM_LOW_POWER, // Low Power, save mode
- SYSTEM_MAXVALUE // Enum count, keep at the end
-} SystemStateT;
-
-
-// Global Policy Local context
-typedef struct PolicyLocalCtx {
- GArray * pSourceEndpoints; // List of Source Endpoint with playing stream or interrupted stream
- GArray * pSinkEndpoints; // List of Sink Endpoint with playing stream or interrupted stream
- GArray * pStreamOpenPerPolicy; //List of number of openstream per policy
- GArray * pMaxStreamOpenPerPolicy; //List of number of openstream per policy
- GArray * pVolDuckPerPolicy; //List of number of openstream per policy
- SystemStateT systemState;
-} PolicyLocalCtxT;
+#ifndef AHL_DISCONNECT_POLICY
+// Global Context
PolicyLocalCtxT g_PolicyCtx;
-//Helper Functions
-static void Add_Endpoint_Property_Double( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, double in_dPropertyValue)
-{
- json_object * propValueJ = json_object_new_double(in_dPropertyValue);
- g_hash_table_insert(io_pEndpointInfo->pPropTable, in_pPropertyName, propValueJ);
-}
-
-
-static void Add_Endpoint_Property_Int( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, int in_iPropertyValue)
-{
- json_object * propValueJ = json_object_new_int(in_iPropertyValue);
- g_hash_table_insert(io_pEndpointInfo->pPropTable, in_pPropertyName, propValueJ);
-}
-
-static void Add_Endpoint_Property_String( EndpointInfoT * io_pEndpointInfo, char * in_pPropertyName, const char * in_pPropertyValue)
-{
- json_object * propValueJ = json_object_new_string(in_pPropertyValue);
- g_hash_table_insert(io_pEndpointInfo->pPropTable, in_pPropertyName, propValueJ);
-}
-
-static int PolicySetVolume(EndpointInfoT * pEndpointInfo, int iVolume)
+// Helper Functions
+static int getStreamConfig(char *pAudioRole, StreamConfigT *pStreamConfig)
{
-
- // Using audio role available from endpoint to target the right HAL control (build string based on convention)
- GString * gsHALControlName;
- switch(pEndpointInfo->deviceURIType)
+ if(pAudioRole == NULL || pStreamConfig==NULL)
{
- case DEVICEURITYPE_ALSA_HW:
- gsHALControlName = g_string_new("Master_Playback_Volume");
- break;
- case DEVICEURITYPE_ALSA_DMIX:
- case DEVICEURITYPE_ALSA_DSNOOP:
- case DEVICEURITYPE_ALSA_PLUG:
- case DEVICEURITYPE_ALSA_SOFTVOL:
- gsHALControlName = g_string_new(pEndpointInfo->gsAudioRole->str);
- g_string_append(gsHALControlName,"_Vol"); // Or _Vol for direct control (no ramping)
- break;
- default:
- //Set volume to zero for display purpose only.
- //Not support yet
- AFB_WARNING("Endpoint %s is not support Device Type and can't set volume",pEndpointInfo->gsDeviceName->str);
- break;
+ return POLICY_FAIL;
}
- // Set endpoint volume using HAL services (leveraging ramps etc.)
- json_object *j_response, *j_query = NULL;
-
- // Package query
- int err = wrap_json_pack(&j_query,"{s:s,s:i}","label",gsHALControlName->str, "val",iVolume);
- if (err)
+ if ( strcasecmp(pAudioRole,AHL_ROLE_WARNING) == 0 )
{
- AFB_ERROR("Invalid query for HAL ctlset: %s",json_object_to_json_string(j_query));
- return err;
+ pStreamConfig->iNbMaxStream = 4;
+ pStreamConfig->iVolumeInit = 80;
+ pStreamConfig->iVolumeDuckValue = 0;
}
-
- //TODO implement Volume limitation based on policy
-
- // Set the volume using the HAL
- err = afb_service_call_sync(pEndpointInfo->gsHALAPIName->str, "ctlset", j_query, &j_response);
- if (err)
+ else if ( strcasecmp(pAudioRole,AHL_ROLE_GUIDANCE) == 0 )
{
- AFB_ERROR("Could not ctlset on HAL: %s",pEndpointInfo->gsHALAPIName->str);
- return err;
+ pStreamConfig->iNbMaxStream = 10;
+ pStreamConfig->iVolumeInit = 70;
+ pStreamConfig->iVolumeDuckValue = 30;
}
- AFB_DEBUG("HAL ctlset response=%s", json_object_to_json_string(j_response));
-
- // Package event data
- json_object * eventDataJ = NULL;
- err = wrap_json_pack(&eventDataJ,"{s:i,s:i,s:i}","endpoint_id", pEndpointInfo->endpointID,"endpoint_type",pEndpointInfo->type,"value",iVolume);
- if (err)
+ else if ( strcasecmp(pAudioRole,AHL_ROLE_NOTIFICATION) == 0 )
{
- AFB_ERROR("Invalid event data for volume event, Invalid event data for volume event: %s",json_object_to_json_string(eventDataJ));
- return err;
+ pStreamConfig->iNbMaxStream = 4;
+ pStreamConfig->iVolumeInit = 80;
+ pStreamConfig->iVolumeDuckValue = 10;
}
- afb_event_push(g_AHLCtx.policyCtx.volumeEvent,eventDataJ);
-
- pEndpointInfo->iVolume = iVolume;
-
- return 0;
-}
-
-
-static int PolicySetVolumeMute(EndpointInfoT * pEndpointInfo, int iVolume)
-{
-
- // Using audio role available from endpoint to target the right HAL control (build string based on convention)
- GString * gsHALControlName;
- switch(pEndpointInfo->deviceURIType)
+ else if ( strcasecmp(pAudioRole,AHL_ROLE_COMMUNICATION) == 0 )
{
- case DEVICEURITYPE_ALSA_HW:
- gsHALControlName = g_string_new("Master_Playback_Volume");
- break;
- case DEVICEURITYPE_ALSA_DMIX:
- case DEVICEURITYPE_ALSA_DSNOOP:
- case DEVICEURITYPE_ALSA_PLUG:
- case DEVICEURITYPE_ALSA_SOFTVOL:
- gsHALControlName = g_string_new(pEndpointInfo->gsAudioRole->str);
- g_string_append(gsHALControlName,"_Vol"); // Or _Vol for direct control (no ramping)
- break;
- default:
- //Set volume to zero for display purpose only.
- //Not support yet
- AFB_WARNING("Endpoint %s is not support Device Type and can't set volume",pEndpointInfo->gsDeviceName->str);
- break;
+ pStreamConfig->iNbMaxStream = 10;
+ pStreamConfig->iVolumeInit = 70;
+ pStreamConfig->iVolumeDuckValue = 10;
}
-
- // Set endpoint volume using HAL services (leveraging ramps etc.)
- json_object *j_response, *j_query = NULL;
-
- // Package query
- int err = wrap_json_pack(&j_query,"{s:s,s:i}","label",gsHALControlName->str, "val",iVolume);
- if (err)
+ else if ( strcasecmp(pAudioRole,AHL_ROLE_ENTERTAINMENT) == 0 )
{
- AFB_ERROR("Invalid query for HAL ctlset: %s",json_object_to_json_string(j_query));
- return err;
+ pStreamConfig->iNbMaxStream = MAX_ACTIVE_STREAM_POLICY;
+ pStreamConfig->iVolumeInit = 60;
+ pStreamConfig->iVolumeDuckValue = 40;
}
-
- //TODO implement Volume limitation based on policy
-
- // Set the volume using the HAL
- err = afb_service_call_sync(pEndpointInfo->gsHALAPIName->str, "ctlset", j_query, &j_response);
- if (err)
+ else if ( strcasecmp(pAudioRole,AHL_ROLE_SYSTEM) == 0 )
{
- AFB_ERROR("Could not ctlset on HAL: %s",pEndpointInfo->gsHALAPIName->str);
- return err;
+ pStreamConfig->iNbMaxStream = 2;
+ pStreamConfig->iVolumeInit = 100;
+ pStreamConfig->iVolumeDuckValue = 0;
}
- AFB_DEBUG("HAL ctlset response=%s", json_object_to_json_string(j_response));
-
- // Package event data
- json_object * eventDataJ = NULL;
- err = wrap_json_pack(&eventDataJ,"{s:i,s:i,s:i}","endpoint_id", pEndpointInfo->endpointID,"endpoint_type",pEndpointInfo->type,"value",iVolume);
- if (err)
+ else if ( strcasecmp(pAudioRole,AHL_ROLE_STARTUP) == 0 )
{
- AFB_ERROR("Invalid event data for volume event, Invalid event data for volume event: %s",json_object_to_json_string(eventDataJ));
- return err;
+ pStreamConfig->iNbMaxStream = 1;
+ pStreamConfig->iVolumeInit = 90;
+ pStreamConfig->iVolumeDuckValue = 0;
}
- afb_event_push(g_AHLCtx.policyCtx.volumeEvent,eventDataJ);
-
- return 0;
+ else if ( strcasecmp(pAudioRole,AHL_ROLE_SHUTDOWN) == 0 )
+ {
+ pStreamConfig->iNbMaxStream = 1;
+ pStreamConfig->iVolumeInit = 90;
+ pStreamConfig->iVolumeDuckValue = 0;
+ }
+ return POLICY_SUCCESS;
}
-
-static int PolicySetVolumeRamp(EndpointInfoT * pEndpointInfo, int iVolume)
+static int PolicySetVolume(int iEndpointID, int iEndpointType, char *pHalApiName, char *AudioRole, DeviceURITypeT deviceType, int iVolume, bool bMute)
{
-
+ if(pHalApiName == NULL)
+ {
+ AFB_WARNING("SetVolume cannot be accomplished without proper HAL association");
+ return POLICY_FAIL;
+ }
+
+ if(AudioRole == NULL)
+ {
+ AFB_ERROR("Invalid AudioRole : %s",AudioRole);
+ return POLICY_FAIL;
+ }
+
// Using audio role available from endpoint to target the right HAL control (build string based on convention)
- GString * gsHALControlName;
- switch(pEndpointInfo->deviceURIType)
+ GString * gsHALControlName = NULL;
+ switch(deviceType)
{
case DEVICEURITYPE_ALSA_HW:
- gsHALControlName = g_string_new("Master_Ramp");
+ gsHALControlName = g_string_new("Master_Playback_Volume");
break;
case DEVICEURITYPE_ALSA_DMIX:
case DEVICEURITYPE_ALSA_DSNOOP:
case DEVICEURITYPE_ALSA_PLUG:
case DEVICEURITYPE_ALSA_SOFTVOL:
- gsHALControlName = g_string_new(pEndpointInfo->gsAudioRole->str);
- g_string_append(gsHALControlName,"_Ramp"); // Or _Vol for direct control (no ramping)
+ gsHALControlName = g_string_new(AudioRole);
+ if(bMute == false)
+ {
+ AFB_DEBUG("Using ramp");
+ g_string_append(gsHALControlName,"_Ramp");
+ }
+ else
+ {
+ AFB_DEBUG("Not using ramp");
+ g_string_append(gsHALControlName,"_Vol"); // no ramping
+ }
break;
default:
//Set volume to zero for display purpose only.
//Not support yet
- AFB_WARNING("Endpoint %s is not a support Device Type and can't set volume",pEndpointInfo->gsDeviceName->str);
+ AFB_WARNING("Device Type %i is not support and can't set volume on HalName %s",deviceType, pHalApiName);
+ return POLICY_FAIL;
break;
}
@@ -241,44 +139,43 @@ static int PolicySetVolumeRamp(EndpointInfoT * pEndpointInfo, int iVolume)
int err = wrap_json_pack(&j_query,"{s:s,s:i}","label",gsHALControlName->str, "val",iVolume);
if (err)
{
- AFB_WARNING("Invalid query for HAL ctlset: %s",json_object_to_json_string(j_query));
- return err;
+ AFB_ERROR("Invalid query for HAL ctlset: %s with errorcode: %i",json_object_to_json_string(j_query), err);
+ return POLICY_FAIL;
}
//TODO implement Volume limitation based on policy
// Set the volume using the HAL
- err = afb_service_call_sync(pEndpointInfo->gsHALAPIName->str, "ctlset", j_query, &j_response);
- if (err)
- {
- AFB_WARNING("Could not ctlset on HAL: %s",pEndpointInfo->gsHALAPIName->str);
- return err;
- }
- AFB_DEBUG("HAL ctlset response=%s", json_object_to_json_string(j_response));
-
- // Package event data
- json_object * eventDataJ = NULL;
- err = wrap_json_pack(&eventDataJ,"{s:i,s:i,s:i, s:s}","endpoint_id", pEndpointInfo->endpointID,"endpoint_type",pEndpointInfo->type,"value",iVolume, "audio_role",gsHALControlName->str);
- if (err)
- {
- AFB_WARNING("Invalid event data for volume event, Invalid event data for volume event: %s",json_object_to_json_string(eventDataJ));
- return err;
- }
- afb_event_push(g_AHLCtx.policyCtx.volumeEvent,eventDataJ);
+ err = afb_service_call_sync(pHalApiName, "ctlset", j_query, &j_response);
+ if (err)
+ {
+ AFB_ERROR("Could not ctlset on HAL: %s with errorcode: %i",pHalApiName, err);
+ return POLICY_FAIL;
+ }
+ AFB_DEBUG("HAL ctlset response=%s", json_object_to_json_string(j_response));
+
+ if (bMute == false) {
+ // Package event data
+ json_object * eventDataJ = NULL;
+ err = wrap_json_pack(&eventDataJ,"{s:s,s:i,s:i,s:i,s:s}","event_name", AHL_ENDPOINT_VOLUME_EVENT,"endpoint_id", iEndpointID, "endpoint_type", iEndpointType,"value",iVolume, "audio_role", AudioRole);
+ if (err)
+ {
+ AFB_ERROR("Invalid event data for volume event %s with errorcode: %i",json_object_to_json_string(eventDataJ), err);
+ return POLICY_FAIL;
+ }
- pEndpointInfo->iVolume = iVolume;
+ audiohlapi_raise_event(eventDataJ);
+ }
- return 0;
+ return POLICY_SUCCESS;
}
-static int PolicyGetVolume(EndpointInfoT * pEndpointInfo)
+static int PolicyGetVolume(int iEndpointID, int iEndpointType, char *pHalApiName, char *AudioRole, DeviceURITypeT deviceType, int *pVolume)
{
-
-
- GString * gsHALControlName;
+ GString * gsHALControlName = NULL;
// Using audio role available from endpoint to target the right HAL control (build string based on convention)
- switch(pEndpointInfo->deviceURIType)
+ switch(deviceType)
{
case DEVICEURITYPE_ALSA_HW:
gsHALControlName = g_string_new("Master_Playback_Volume");
@@ -287,37 +184,37 @@ static int PolicyGetVolume(EndpointInfoT * pEndpointInfo)
case DEVICEURITYPE_ALSA_DSNOOP:
case DEVICEURITYPE_ALSA_PLUG:
case DEVICEURITYPE_ALSA_SOFTVOL:
- gsHALControlName = g_string_new(pEndpointInfo->gsAudioRole->str);
+ gsHALControlName = g_string_new(AudioRole);
g_string_append(gsHALControlName,"_Vol"); // Or _Vol for direct control (no ramping)
break;
default:
- //Set volume to zero for display purpose only.
- //Not support yet
- pEndpointInfo->iVolume = 0;
- AFB_WARNING("Endpoint %s is a support Device Type and can't get volume",pEndpointInfo->gsDeviceName->str);
- break;
+ // Set volume to zero for display purpose only.
+ // Not supported yet
+ *pVolume = 0;
+ AFB_WARNING("Can't get volume on HAL: %s for device type: %d",pHalApiName,deviceType);
+ return POLICY_FAIL;
}
// Set endpoint volume using HAL services (leveraging ramps etc.)
- json_object *j_response, *j_query = NULL;
+ json_object *j_response = NULL, *j_query = NULL;
// Package query
int err = wrap_json_pack(&j_query,"{s:s}","label",gsHALControlName->str);
if (err)
{
- AFB_WARNING("Invalid query for HAL ctlset: %s",json_object_to_json_string(j_query));
- return err;
+ AFB_WARNING("Invalid query for HAL ctlset: %s, errorcode: %i",json_object_to_json_string(j_query),err);
+ return POLICY_FAIL;
}
//TODO implement Volume limitation based on policy
- // Set the volume using the HAL
- err = afb_service_call_sync(pEndpointInfo->gsHALAPIName->str, "ctlget", j_query, &j_response);
+ // Get the volume using the HAL
+ err = afb_service_call_sync(pHalApiName, "ctlget", j_query, &j_response);
if (err)
{
- AFB_WARNING("Could not ctlset on HAL: %s",pEndpointInfo->gsHALAPIName->str);
- return err;
- }
+ AFB_WARNING("Could not ctlset on HAL: %s, errorcode: %i",pHalApiName, err);
+ return POLICY_FAIL;
+ }
AFB_DEBUG("HAL ctlget response=%s", json_object_to_json_string(j_response));
// Parse response
@@ -333,7 +230,7 @@ static int PolicyGetVolume(EndpointInfoT * pEndpointInfo)
err = wrap_json_unpack(jVal, "[ii]", &val1, &val2);
if (err) {
AFB_ERROR("Volume retrieve failed Could not retrieve volume value -> %s", json_object_get_string(jVal));
- return -1;
+ return POLICY_FAIL;
}
}
@@ -342,38 +239,43 @@ static int PolicyGetVolume(EndpointInfoT * pEndpointInfo)
err = wrap_json_unpack(jVal, "[i]", &val1);
if (err) {
AFB_ERROR("Volume retrieve failed Could not retrieve volume value -> %s", json_object_get_string(jVal));
- return -1;
+ return POLICY_FAIL;
}
}
- pEndpointInfo->iVolume = val1;
+ *pVolume = val1;
+
+ // Package event data
+ json_object * eventDataJ = NULL;
+ err = wrap_json_pack(&eventDataJ,"{s:s,s:i,s:i,s:i,s:s}","event_name", AHL_ENDPOINT_VOLUME_EVENT,"endpoint_id", iEndpointID, "endpoint_type", iEndpointType,"value",*pVolume, "audio_role", AudioRole);
+ if (err)
+ {
+ AFB_ERROR("Invalid event data for volume event %s with errorcode: %i",json_object_to_json_string(eventDataJ), err);
+ return POLICY_FAIL;
+ }
+
+ audiohlapi_raise_event(eventDataJ);
- return 0;
+ return POLICY_SUCCESS;
}
-static void PolicyPostStateEvent(struct afb_event streamStateEvent, StreamEventT eventState)
+static void PolicyPostStateEvent(int iStreamID, StreamEventT eventState)
{
json_object * eventDataJ = NULL;
- int err = wrap_json_pack(&eventDataJ,"{s:i}","stateEvent",eventState);
+ int err = wrap_json_pack(&eventDataJ,"{s:s,s:i,s:i}","event_name", AHL_STREAM_STATE_EVENT,"stream_id",iStreamID, "state_event",eventState);
if (err)
{
AFB_ERROR("Invalid event data for stream state event: %s",json_object_to_json_string(eventDataJ));
}
else
{
- afb_event_push(streamStateEvent,eventDataJ);
+ audiohlapi_raise_event(eventDataJ);
}
}
-//This function is based on ALSA right now but it could be adapt to support other framework like pulseaudio or gstreamer
-static int PolicyGenEndPointKey(EndpointInfoT *pEndPointInfo)
-{
- return (pEndPointInfo->type << 24)|(pEndPointInfo->alsaInfo.cardNum << 16)|(pEndPointInfo->alsaInfo.deviceNum << 8)|(pEndPointInfo->alsaInfo.subDeviceNum);
-}
-
-static EndPointPolicyInfoT *PolicySearchEndPoint(EndpointTypeT type, int key)
+static EndPointPolicyInfoT *PolicySearchEndPoint(EndpointTypeT type, char *pDeviceName)
{
GArray *pcurEndpointArray = NULL;
@@ -390,7 +292,7 @@ static EndPointPolicyInfoT *PolicySearchEndPoint(EndpointTypeT type, int key)
{
EndPointPolicyInfoT * pCurEndpoint = &g_array_index(pcurEndpointArray,EndPointPolicyInfoT,i);
- if(pCurEndpoint->endpointKey == key)
+ if(strcasecmp(pCurEndpoint->pDeviceName,pDeviceName)==0)
{
return pCurEndpoint;
}
@@ -399,395 +301,403 @@ static EndPointPolicyInfoT *PolicySearchEndPoint(EndpointTypeT type, int key)
return NULL;
}
-static StreamPolicyInfoT *PolicySearchStream(EndPointPolicyInfoT * pCurEndpoint, int streamID)
-{
- for(int i=0; i<pCurEndpoint->streamInfo->len; i++)
+static int PolicyAddEndPoint(StreamInfoT *pStreamInfo)
+{
+ EndPointPolicyInfoT *pPolicyEndPoint = PolicySearchEndPoint(pStreamInfo->pEndpointInfo->type, pStreamInfo->pEndpointInfo->gsDeviceName);
+ if(pPolicyEndPoint == NULL)
{
- StreamPolicyInfoT * pCurStream = &g_array_index(pCurEndpoint->streamInfo,StreamPolicyInfoT,i);
-
- if(pCurStream->streamID == streamID)
+ //create EndPoint and add playing stream
+ EndPointPolicyInfoT newEndPointPolicyInfo;
+ newEndPointPolicyInfo.endpointID = pStreamInfo->pEndpointInfo->endpointID;
+ newEndPointPolicyInfo.type = pStreamInfo->pEndpointInfo->type;
+ newEndPointPolicyInfo.deviceType = pStreamInfo->pEndpointInfo->deviceURIType;
+ newEndPointPolicyInfo.pDeviceName = strdup(pStreamInfo->pEndpointInfo->gsDeviceName);
+ newEndPointPolicyInfo.pHalApiName = strdup(pStreamInfo->pEndpointInfo->gsHALAPIName);
+ newEndPointPolicyInfo.iVolume = pStreamInfo->pEndpointInfo->iVolume;
+ newEndPointPolicyInfo.streamInfo = g_array_new(FALSE, TRUE, sizeof(StreamPolicyInfoT));;
+
+ if(pStreamInfo->pEndpointInfo->type == ENDPOINTTYPE_SINK)
{
- return pCurStream;
+ g_array_append_val(g_PolicyCtx.pSinkEndpoints, newEndPointPolicyInfo);
}
+ else
+ {
+ g_array_append_val(g_PolicyCtx.pSourceEndpoints, newEndPointPolicyInfo);
+ }
+
}
-
- return NULL;
+ return POLICY_SUCCESS;
}
-static StreamInfoT * PolicyGetActiveStream(streamID_t in_streamID)
+static int PolicyAddStream(EndPointPolicyInfoT *pCurrEndPointPolicy, StreamInfoT *pStreamInfo)
{
- int iNumActiveStreams = g_AHLCtx.policyCtx.pActiveStreams->len;
- StreamInfoT * pStreamInfo = NULL;
- for ( int i = 0; i < iNumActiveStreams ; i++ ) {
- StreamInfoT * pCurStreamInfo = &g_array_index(g_AHLCtx.policyCtx.pActiveStreams,StreamInfoT,i);
- if (pCurStreamInfo->streamID == in_streamID){
- pStreamInfo = pCurStreamInfo;
- break;
- }
- }
- return pStreamInfo;
+ StreamPolicyInfoT newStreamPolicyInfo;
+
+ newStreamPolicyInfo.streamID = pStreamInfo->streamID;
+ newStreamPolicyInfo.RolePriority = pStreamInfo->iPriority;
+ newStreamPolicyInfo.pAudioRole = pStreamInfo->pRoleName;
+ newStreamPolicyInfo.interruptBehavior = pStreamInfo->eInterruptBehavior;
+ newStreamPolicyInfo.iDuckVolume = 0;
+ g_array_append_val(pCurrEndPointPolicy->streamInfo, newStreamPolicyInfo);
+ return POLICY_SUCCESS;
}
-
-static int PolicyFindRoleIndex( const char * in_pAudioRole)
+static int PolicyRunningIdleTransition(EndPointPolicyInfoT *pCurrEndPointPolicy,StreamInfoT * pStreamInfo)
{
- int index = -1; // Not found
- for (int i = 0; i < g_AHLCtx.policyCtx.iNumberRoles; i++)
+ int err=0;
+ if(pCurrEndPointPolicy == NULL || pCurrEndPointPolicy->streamInfo->len == 0)
{
- GString gs = g_array_index( g_AHLCtx.policyCtx.pAudioRoles, GString, i );
- if ( strcasecmp(gs.str,in_pAudioRole) == 0 )
+ //Remove endpoint
+ AFB_ERROR("StreamID not found in active Endpoint when Running to Idle transition is request");
+ return POLICY_FAIL;
+ }
+ //Search for the matching stream
+ for(int i=0; i<pCurrEndPointPolicy->streamInfo->len; i++)
+ {
+ StreamPolicyInfoT currentPolicyStreamInfo = g_array_index(pCurrEndPointPolicy->streamInfo,StreamPolicyInfoT,i);
+ if(currentPolicyStreamInfo.streamID == pStreamInfo->streamID)
{
- index = i;
- break;
+ //remove the current stream
+ g_array_remove_index(pCurrEndPointPolicy->streamInfo, i);
+ if(pCurrEndPointPolicy->streamInfo->len > 0) //need to unduck
+ {
+ //check the last element(Akways highest priority)
+ StreamPolicyInfoT HighPriorityStreamInfo = g_array_index(pCurrEndPointPolicy->streamInfo,StreamPolicyInfoT,pCurrEndPointPolicy->streamInfo->len-1);
+ switch(currentPolicyStreamInfo.interruptBehavior)
+ {
+ case INTERRUPTBEHAVIOR_CONTINUE:
+ //unduck and set Volume back to original value
+ err= PolicySetVolume(pCurrEndPointPolicy->endpointID,
+ pCurrEndPointPolicy->type,
+ pCurrEndPointPolicy->pHalApiName,
+ HighPriorityStreamInfo.pAudioRole,
+ pCurrEndPointPolicy->deviceType,
+ HighPriorityStreamInfo.iDuckVolume,
+ false);
+ if(err)
+ {
+ return POLICY_FAIL;
+ }
+
+ return POLICY_SUCCESS;
+ break;
+ case INTERRUPTBEHAVIOR_PAUSE:
+ //pInterruptStreamInfo->streamState = STREAM_STATE_RUNNING;
+ PolicyPostStateEvent(HighPriorityStreamInfo.streamID,STREAM_EVENT_RESUME);
+ return POLICY_SUCCESS;
+ break;
+
+ case INTERRUPTBEHAVIOR_CANCEL:
+ PolicyPostStateEvent(HighPriorityStreamInfo.streamID,STREAM_EVENT_START);
+ return POLICY_SUCCESS;
+ break;
+ default:
+ AFB_ERROR("Unsupported Intterupt Behavior");
+ return POLICY_FAIL;
+ break;
+ }
+ }
}
- }
- return index;
+ }
+ return POLICY_SUCCESS;
}
-static int PolicyRemoveStream(EndPointPolicyInfoT *pCurrEndPointPolicy, int RemoveIndex)
+static int PolicyIdleRunningTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, StreamInfoT * pStreamInfo)
{
- //Validate
- if(RemoveIndex >= pCurrEndPointPolicy->streamInfo->len)
+ int err=0;
+ if(pCurrEndPointPolicy->streamInfo == NULL)
{
- return -1;
-
+ AFB_ERROR("pCurrEndPointPolicy->streamInfo is null on an active endpoint");
+ return POLICY_FAIL;
}
-
- g_array_remove_index(pCurrEndPointPolicy->streamInfo,RemoveIndex);
-
- if(pCurrEndPointPolicy->streamInfo->len == 0)
+ if(pCurrEndPointPolicy->streamInfo->len == 0) //No stream is playing on this endpoint
+ {
+ PolicyAddStream(pCurrEndPointPolicy, pStreamInfo);
+ }
+ else //Interrupt case
{
-
- //Free streem
- g_array_free(pCurrEndPointPolicy->streamInfo,TRUE);
- pCurrEndPointPolicy->streamInfo = NULL;
-
- GArray *pcurEndpointArray = NULL;
-
- if(pCurrEndPointPolicy->type==ENDPOINTTYPE_SINK)
+ //check the last element
+ StreamPolicyInfoT *pCurrentActiveStreamInfo = &g_array_index(pCurrEndPointPolicy->streamInfo,StreamPolicyInfoT,pCurrEndPointPolicy->streamInfo->len-1);
+ g_assert_nonnull(pCurrentActiveStreamInfo);
+ if(pStreamInfo->iPriority >= pCurrentActiveStreamInfo->RolePriority)
{
- pcurEndpointArray = g_PolicyCtx.pSinkEndpoints;
+ switch(pStreamInfo->eInterruptBehavior)
+ {
+ case INTERRUPTBEHAVIOR_CONTINUE:
+ //Save the current Volume and set the docking volume
+ pCurrentActiveStreamInfo->iDuckVolume = pStreamInfo->pEndpointInfo->iVolume;
+ StreamConfigT StreamConfig;
+ err = getStreamConfig(pStreamInfo->pRoleName, &StreamConfig);
+ if(err == POLICY_FAIL)
+ {
+ AFB_ERROR("Error getting stream configuration for audiorole: %s", pStreamInfo->pRoleName);
+ return POLICY_FAIL;
+ }
+ err= PolicySetVolume(pCurrEndPointPolicy->endpointID,
+ pCurrEndPointPolicy->type,
+ pCurrEndPointPolicy->pHalApiName,
+ pCurrentActiveStreamInfo->pAudioRole,
+ pCurrEndPointPolicy->deviceType,
+ StreamConfig.iVolumeDuckValue,
+ false);
+ if(err)
+ {
+ AFB_ERROR("Set Volume return with errorcode%i for streamID: %i and Hal:%s", err, pCurrentActiveStreamInfo->streamID, pCurrEndPointPolicy->pHalApiName);
+ return POLICY_FAIL;
+ }
+ break;
+ case INTERRUPTBEHAVIOR_PAUSE:
+ PolicyPostStateEvent(pCurrentActiveStreamInfo->streamID,STREAM_EVENT_PAUSE);
+ break;
+
+ case INTERRUPTBEHAVIOR_CANCEL:
+ PolicyPostStateEvent(pCurrentActiveStreamInfo->streamID,STREAM_EVENT_STOP);
+ g_array_remove_index(pCurrEndPointPolicy->streamInfo, pCurrEndPointPolicy->streamInfo->len-1);
+ break;
+ default:
+ AFB_ERROR("Unsupported Intterupt Behavior");
+ return AHL_POLICY_REJECT;
+ break;
+
+ }
+
+ //Add the playing stream at last
+ PolicyAddStream(pCurrEndPointPolicy, pStreamInfo);
}
else
{
- pcurEndpointArray = g_PolicyCtx.pSourceEndpoints;
+ //Higher Priority Stream is playing
+ AFB_ERROR("Higher Priority Stream is playing");
+ return POLICY_FAIL;
}
- for(int i=0; i<pcurEndpointArray->len; i++)
- {
- EndPointPolicyInfoT * pCurEndpoint = &g_array_index(pcurEndpointArray,EndPointPolicyInfoT,i);
- if(pCurEndpoint->endpointKey == pCurrEndPointPolicy->endpointKey)
- {
- g_array_remove_index(pcurEndpointArray, i);
- return 0;
- }
- }
+
}
-
- return 0;
+
+ return POLICY_SUCCESS;
}
-static int PolicyRunningIdleTransition(EndPointPolicyInfoT *pCurrEndPointPolicy,StreamInfoT * pStreamInfo)
+static void PolicySpeedModify(int speed)
{
- if(pCurrEndPointPolicy == NULL)
+
+ for(int i=0; i<g_PolicyCtx.pSinkEndpoints->len; i++)
{
- //Remove endpoint
- AFB_ERROR("No Active Endpoint has been found");
- return -1;
- }
+ EndPointPolicyInfoT * pCurEndpoint = &g_array_index(g_PolicyCtx.pSinkEndpoints,EndPointPolicyInfoT,i);
+ if(pCurEndpoint == NULL)
+ {
+ AFB_WARNING("Sink Endpoint not found");
+ return;
+ }
- if(pCurrEndPointPolicy->streamInfo->len>0)
- {
- //Search for the matching stream
- int iNumStream = pCurrEndPointPolicy->streamInfo->len;
- for(int i=0; i<iNumStream; i++)
+ //check if active
+ if(pCurEndpoint->streamInfo->len > 0 )
{
- StreamPolicyInfoT currentPolicyStreamInfo = g_array_index(pCurrEndPointPolicy->streamInfo,StreamPolicyInfoT,i);
- if(currentPolicyStreamInfo.streamID == pStreamInfo->streamID)
+ StreamPolicyInfoT * pCurStream = &g_array_index(pCurEndpoint->streamInfo,StreamPolicyInfoT,pCurEndpoint->streamInfo->len-1);
+ if(strcasecmp(pCurStream->pAudioRole,AHL_ROLE_ENTERTAINMENT)==0)
{
-
- //Unduck case
- if((i==(pCurrEndPointPolicy->streamInfo->len-1)) && (pCurrEndPointPolicy->streamInfo->len > 1))
- {
- //remove the current stream
- g_array_remove_index(pCurrEndPointPolicy->streamInfo, i);
-
- //check the last element(Akways highest priority)
- StreamPolicyInfoT HighPriorityStreamInfo = g_array_index(pCurrEndPointPolicy->streamInfo,StreamPolicyInfoT,pCurrEndPointPolicy->streamInfo->len-1);
-
- //Get Stream Info
- StreamInfoT * pInterruptStreamInfo = PolicyGetActiveStream(HighPriorityStreamInfo.streamID);
- if (pInterruptStreamInfo == NULL) {
- AFB_ERROR("Stream not found, Specified stream not currently active stream_id -> %d",HighPriorityStreamInfo.streamID);
- return -1;
- }
-
- int err;
- switch(currentPolicyStreamInfo.interruptBehavior)
- {
- case AHL_INTERRUPTEDBEHAVIOR_CONTINUE:
- //unduck and set Volume back to original value
- err= PolicySetVolumeRamp(pInterruptStreamInfo->pEndpointInfo, HighPriorityStreamInfo.iVolume);
- if(err)
- {
- AFB_ERROR("Endpoint:%s Set Volume return with errorcode%i", pInterruptStreamInfo->pEndpointInfo->gsDeviceName->str, err);
- return -1;
- }
- break;
- case AHL_INTERRUPTEDBEHAVIOR_PAUSE:
- pInterruptStreamInfo->streamState = STREAM_STATE_RUNNING;
- PolicyPostStateEvent(pInterruptStreamInfo->streamStateEvent,STREAM_EVENT_RESUME);
- break;
-
- case AHL_INTERRUPTEDBEHAVIOR_CANCEL:
- AFB_ERROR("StreamID with Cancel InterruptedBehavior can't be unInterrupted");
- return -1;
- break;
- default:
- AFB_ERROR("Unsupported Intterupt Behavior");
- return -1;
- break;
- }
-
- }
- else
+ if(speed > 30 && speed < 100)
{
- //remove the current stream
- PolicyRemoveStream(pCurrEndPointPolicy, i);
- }
- return 0;
- }
-
- }
+ int volume =speed;
+ PolicySetVolume(pCurEndpoint->endpointID,
+ pCurEndpoint->type,
+ pCurEndpoint->pHalApiName,
+ pCurStream->pAudioRole,
+ pCurEndpoint->deviceType,
+ volume,
+ false);
+ }
+ }
+ }
}
-
- AFB_ERROR("StreamID does not match any playing stream");
- return -1;
}
-static int PolicyIdleRunningTransition(EndPointPolicyInfoT *pCurrEndPointPolicy, StreamInfoT * pStreamInfo, int AudioRoleIndex, int EndPointKey)
+static int RetrieveAssociatedHALAPIName(EndpointInfoT * io_pEndpointInfo)
{
- int stream_priority;
- int ori_key_pos;
-
- bool bKeyFound=g_hash_table_lookup_extended(g_AHLCtx.policyCtx.pRolePriority,pStreamInfo->pEndpointInfo->gsAudioRole->str,&ori_key_pos,&stream_priority);
- if(bKeyFound==false)
+ if(g_PolicyCtx.pHALList)
{
- AFB_ERROR("Can't find stream priority, request will be rejected");
- return -1;
+ for(int i=0; i<g_PolicyCtx.pHALList->len; i++)
+ {
+ HalInfoT *pHalInfo = g_ptr_array_index(g_PolicyCtx.pHALList, i);
+ // Retrieve card number (e.g. hw:0)
+ int iCardNum = atoi(pHalInfo->pDevID+3);
+ if (iCardNum == io_pEndpointInfo->alsaInfo.cardNum) {
+ io_pEndpointInfo->gsHALAPIName=strdup(pHalInfo->pAPIName);
+ io_pEndpointInfo->gsDisplayName=strdup(pHalInfo->pDisplayName);
+ return POLICY_SUCCESS;
+ }
+ }
}
- //stream_priority = GPOINTER_TO_INT(value);
+ io_pEndpointInfo->gsHALAPIName=strdup(AHL_POLICY_UNDEFINED_HALNAME);
+ io_pEndpointInfo->gsDisplayName=strdup(AHL_POLICY_UNDEFINED_DISPLAYNAME);
+
+ return POLICY_FAIL;
+}
+static int GetHALList(void)
+{
+ json_object *j_response, *j_query = NULL;
+ int err;
+ err = afb_service_call_sync("alsacore", "hallist", j_query, &j_response);
+ if (err) {
+ AFB_ERROR("Could not retrieve list of HAL from ALSA core");
+ return POLICY_FAIL;
+ }
+ AFB_DEBUG("ALSAcore hallist response=%s", json_object_to_json_string(j_response));
- InterruptedBehaviorT InterruptBehavior = g_array_index(g_AHLCtx.policyCtx.pInterruptBehavior,InterruptedBehaviorT,AudioRoleIndex);
- int err;
- if(pCurrEndPointPolicy == NULL) //No stream is playing on this endpoint
+ // Look through returned list for matching card
+ json_object * jRespObj = NULL;
+ json_object_object_get_ex(j_response, "response", &jRespObj);
+ int iNumHAL = json_object_array_length(jRespObj);
+ for ( int i = 0 ; i < iNumHAL; i++)
{
- EndPointPolicyInfoT newEndPointPolicyInfo ;
- StreamPolicyInfoT newStreamPolicyInfo;
-
- //create EndPoint and add playing stream
- newEndPointPolicyInfo.endpointKey = EndPointKey;
- newEndPointPolicyInfo.endpointID = pStreamInfo->pEndpointInfo->endpointID;
- newEndPointPolicyInfo.type = pStreamInfo->pEndpointInfo->type;
- newEndPointPolicyInfo.streamInfo = g_array_new(FALSE,TRUE,sizeof(StreamPolicyInfoT));
-
- newStreamPolicyInfo.RolePriority = stream_priority;
- newStreamPolicyInfo.iVolume = pStreamInfo->pEndpointInfo->iVolume;
- newStreamPolicyInfo.streamID = pStreamInfo->streamID;
- newStreamPolicyInfo.interruptBehavior = InterruptBehavior;
-
- g_array_append_val(newEndPointPolicyInfo.streamInfo, newStreamPolicyInfo);
- g_array_append_val(g_PolicyCtx.pSinkEndpoints, newEndPointPolicyInfo);
+ json_object * jHAL = json_object_array_get_idx(jRespObj,i);
+ char * pDevIDStr = NULL;
+ char * pAPIName = NULL;
+ char * pShortName = NULL;
+ int err = wrap_json_unpack(jHAL, "{s:s,s:s,s:s}", "devid", &pDevIDStr,"api", &pAPIName,"shortname",&pShortName);
+ if (err) {
+ AFB_ERROR("Could not retrieve devid string=%s", json_object_get_string(jHAL));
+ return POLICY_FAIL;
+ }
- /*
- int *pVolume = &g_array_index(g_PolicyCtx.pVolInitPerPolicy,int,AudioRoleIndex);
- pStreamInfo->pEndpointInfo->iVolume = *pVolume;
-
- err= PolicySetVolumeRamp(pStreamInfo->pEndpointInfo, pStreamInfo->pEndpointInfo->iVolume);
- if(err)
- {
- AFB_ERROR("Endpoint:%s Set Volume return with errorcode%i",pStreamInfo->pEndpointInfo->gsDeviceName->str, err);
- return err;
- } */
- }
- else
- {
- //Currently contains playing or duck stream
- if(pCurrEndPointPolicy->streamInfo->len > 0)
+ HalInfoT *pHalInfo = (HalInfoT*)malloc(sizeof(HalInfoT));
+ if(pHalInfo == NULL)
{
- //check the last element
- StreamPolicyInfoT HighPriorityStreamInfo = g_array_index(pCurrEndPointPolicy->streamInfo,StreamPolicyInfoT,pCurrEndPointPolicy->streamInfo->len-1);
- if((stream_priority) >= HighPriorityStreamInfo.RolePriority)
- {
- //Get Stream Info
- StreamInfoT * pInterruptStreamInfo = PolicyGetActiveStream(HighPriorityStreamInfo.streamID);
- if (pInterruptStreamInfo == NULL) {
- AFB_ERROR("Stream not found Specified stream not currently active stream_id -> %d",HighPriorityStreamInfo.streamID);
- return -1;
- }
-
- switch(InterruptBehavior)
- {
- case AHL_INTERRUPTEDBEHAVIOR_CONTINUE:
- //Save the current Volume and set the docking volume
- HighPriorityStreamInfo.iVolume = pInterruptStreamInfo->pEndpointInfo->iVolume;
-
- int *pVolume = &g_array_index(g_PolicyCtx.pVolDuckPerPolicy,int,AudioRoleIndex);
- err= PolicySetVolumeRamp(pInterruptStreamInfo->pEndpointInfo, *pVolume);
- if(err)
- {
- AFB_ERROR("Endpoint:%s Set Volume return with errorcode%i", pInterruptStreamInfo->pEndpointInfo->gsDeviceName->str, err);
- return -1;
- }
- break;
- case AHL_INTERRUPTEDBEHAVIOR_PAUSE:
- pInterruptStreamInfo->streamState = STREAM_STATE_PAUSED;
- PolicyPostStateEvent(pInterruptStreamInfo->streamStateEvent,STREAM_EVENT_PAUSE);
- break;
-
- case AHL_INTERRUPTEDBEHAVIOR_CANCEL:
- pInterruptStreamInfo->streamState = STREAM_STATE_IDLE;
- PolicyPostStateEvent(pInterruptStreamInfo->streamStateEvent,STREAM_EVENT_STOP);
- g_array_remove_index(pCurrEndPointPolicy->streamInfo, pCurrEndPointPolicy->streamInfo->len-1);
-
- break;
- default:
- AFB_ERROR("Unsupported Intterupt Behavior");
- return AHL_POLICY_REJECT;
- break;
-
- }
-
- //Add the playing stream at index 0
- StreamPolicyInfoT newStreamPolicyInfo;
- newStreamPolicyInfo.RolePriority = stream_priority;
- newStreamPolicyInfo.iVolume = pStreamInfo->pEndpointInfo->iVolume;
- newStreamPolicyInfo.streamID = pStreamInfo->streamID;
- newStreamPolicyInfo.interruptBehavior = InterruptBehavior;
-
- //Insert at the end, become highest priority streamID
- g_array_append_val(pCurrEndPointPolicy->streamInfo, newStreamPolicyInfo);
-
- err= PolicySetVolumeRamp(pStreamInfo->pEndpointInfo, pStreamInfo->pEndpointInfo->iVolume);
- if(err)
- {
- AFB_ERROR("Endpoint:%s Set Volume return with errorcode%i", pInterruptStreamInfo->pEndpointInfo->gsDeviceName->str, err);
- return err;
- }
-
- }
- else
- {
- //Higher Priority Stream is playing
- AFB_NOTICE("Higher Priority Stream is playing");
- return -1;
- }
-
+ AFB_ERROR("Unable to allocate memory for HalInfo");
+ return POLICY_FAIL;
}
- else
- {
- //Remove endpoint
- AFB_ERROR("Active EndPoint is not attached to any active stream");
- return -1;
- }
+ pHalInfo->pDevID = strdup(pDevIDStr);
+ pHalInfo->pAPIName = strdup(pAPIName);
+ pHalInfo->pDisplayName = strdup(pShortName);
+
+ g_ptr_array_add( g_PolicyCtx.pHALList, pHalInfo);
}
- return 0;
+ return POLICY_SUCCESS;
}
-//Policy API
-int Policy_OpenStream(StreamInfoT * pStreamInfo)
+//
+// Policy API Functions
+//
+int Policy_OpenStream(json_object *pPolicyStreamJ)
{
+ StreamInfoT PolicyStream;
+ EndpointInfoT EndpointInfo;
+ PolicyStream.pEndpointInfo =&EndpointInfo;
+
+ int err = PolicyCtxJSONToStream(pPolicyStreamJ, &PolicyStream);
+ if(err == AHL_FAIL)
+ {
+ return AHL_POLICY_ACCEPT;
+ }
+
// Example rule -> when system is in shutdown or low power mode, no audio stream can be opened (return AHL_POLICY_REJECT)
// Should receive event from lower level layer
if(g_PolicyCtx.systemState != SYSTEM_NORMAL)
{
return AHL_POLICY_REJECT;
}
-
- //Implement Policy open stream rules, limit to a certain number of stream open based on policy
- int index = PolicyFindRoleIndex(pStreamInfo->pEndpointInfo->gsAudioRole->str);
- int *pNumberOpenStream = &g_array_index(g_PolicyCtx.pStreamOpenPerPolicy,int,index);
- int MaxNumberOpenStream = g_array_index(g_PolicyCtx.pMaxStreamOpenPerPolicy,int,index);
-
- *pNumberOpenStream +=1;
- if((*pNumberOpenStream) > MaxNumberOpenStream )
+
+ StreamConfigT StreamConfig;
+ err = getStreamConfig(PolicyStream.pRoleName, &StreamConfig);
+ if(err == POLICY_FAIL)
{
- return AHL_POLICY_REJECT;
+ return AHL_POLICY_ACCEPT;
}
- //Get actual Volume
- int err=PolicyGetVolume(pStreamInfo->pEndpointInfo);
- if(err != 0)
- {
- AFB_WARNING("Can't get volume of Endpoint %s",pStreamInfo->pEndpointInfo->gsDeviceName->str);
+ if(PolicyStream.pEndpointInfo->deviceURIType != DEVICEURITYPE_NOT_ALSA) {
+ err=PolicyGetVolume(PolicyStream.pEndpointInfo->endpointID,
+ PolicyStream.pEndpointInfo->type,
+ PolicyStream.pEndpointInfo->gsHALAPIName,
+ PolicyStream.pEndpointInfo->pRoleName,
+ PolicyStream.pEndpointInfo->deviceURIType,
+ &PolicyStream.pEndpointInfo->iVolume);
+ if(err == POLICY_FAIL)
+ {
+ return AHL_POLICY_REJECT;
+ }
}
+ err = PolicyAddEndPoint(&PolicyStream);
+ if(err == POLICY_FAIL)
+ {
+ return AHL_POLICY_REJECT;
+ }
return AHL_POLICY_ACCEPT;
}
-int Policy_CloseStream(StreamInfoT * pStreamInfo)
+int Policy_CloseStream(json_object *pPolicyStreamJ)
{
- //Decrement the number of openstream
- int index = PolicyFindRoleIndex(pStreamInfo->pEndpointInfo->gsAudioRole->str);
- int *pNumberOpenStream = &g_array_index(g_PolicyCtx.pStreamOpenPerPolicy,int,index);
-
- *pNumberOpenStream -= 1;
+ //TODO remove Endpoint when there is no stream
+ StreamInfoT PolicyStream;
+ EndpointInfoT EndpointInfo;
+ PolicyStream.pEndpointInfo =&EndpointInfo;
+ int err = PolicyCtxJSONToStream(pPolicyStreamJ, &PolicyStream);
+ if(err == AHL_FAIL)
+ {
+ return AHL_POLICY_ACCEPT;
+ }
return AHL_POLICY_ACCEPT;
}
-int Policy_SetStreamState(StreamInfoT * pStreamInfo, int AudioRoleIndex, StreamStateT streamState)
+int Policy_SetStreamState(json_object *pPolicyStreamJ)
{
- //DONE
- // If higher priority audio role stream requires audio ducking (and un-ducking) of other streams (e.g. navigation ducks entertainment)
- // Could potentially provide a fairly generic system using interupt behavior information and audio role priority (implemented and can be customized here)
- // Specific exception can also be
- // Source exclusion. E.g. When a source (e.g tuner) with same audio role as already active stream (e.g. media player) with same endpoint target,
- // the former source is stopped (i.e. raise streamstate stop event)
- // If source on communication role is active (e.g. handsfree call), activating entertainment sources is prohibited
- // Startup or Shutdown audio stream mute entertainment (unmut when stream no longer active)
-
//TODO
// Optional: Mute endpoint before activation, unmute afterwards (after a delay?) to avoid noises
- int err;
+ StreamInfoT PolicyStream;
+ EndpointInfoT EndpointInfo;
+ PolicyStream.pEndpointInfo =&EndpointInfo;
+
+
+ StreamStateT streamState = 0;
+ StreamInfoT * pPolicyStream = &PolicyStream;
+ int err = PolicyCtxJSONToStream(pPolicyStreamJ, pPolicyStream);
+ if(err == AHL_FAIL)
+ {
+ return AHL_POLICY_ACCEPT;
+ }
+
+ json_object *streamStateJ=NULL;
+
+ if(!json_object_object_get_ex(pPolicyStreamJ, "arg_stream_state", &streamStateJ))
+ {
+ return AHL_POLICY_ACCEPT;
+ }
+ streamState = (StreamStateT)json_object_get_int(streamStateJ);
//Change of state
- if(pStreamInfo->streamState != streamState)
+ if(pPolicyStream->streamState != streamState)
{
- //seach corresponding endpoint and gather information on it
- int key = PolicyGenEndPointKey(pStreamInfo->pEndpointInfo);
- EndPointPolicyInfoT *pCurrEndPointPolicy = PolicySearchEndPoint(pStreamInfo->pEndpointInfo->type , key);
+ //seach corresponding endpoint and gather information on it
+ EndPointPolicyInfoT *pCurrEndPointPolicy = PolicySearchEndPoint(pPolicyStream->pEndpointInfo->type , pPolicyStream->pEndpointInfo->gsDeviceName);
- switch(pStreamInfo->streamState)
+ switch(pPolicyStream->streamState)
{
case STREAM_STATE_IDLE:
switch(streamState)
{
case STREAM_STATE_RUNNING:
- err = PolicyIdleRunningTransition(pCurrEndPointPolicy, pStreamInfo, AudioRoleIndex, key);
+ err = PolicyIdleRunningTransition(pCurrEndPointPolicy, pPolicyStream);
if(err)
{
return AHL_POLICY_REJECT;
}
- pStreamInfo->streamState = STREAM_STATE_RUNNING;
- PolicyPostStateEvent(pStreamInfo->streamStateEvent,STREAM_EVENT_START);
+ PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_START);
break;
case STREAM_STATE_PAUSED:
- err = PolicyIdleRunningTransition(pCurrEndPointPolicy, pStreamInfo, AudioRoleIndex, key);
+ err = PolicyIdleRunningTransition(pCurrEndPointPolicy, pPolicyStream);
if(err)
{
return AHL_POLICY_REJECT;
}
- pStreamInfo->streamState = STREAM_STATE_PAUSED;
- PolicyPostStateEvent(pStreamInfo->streamStateEvent,STREAM_EVENT_PAUSE);
+ PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_PAUSE);
break;
default:
return AHL_POLICY_REJECT;
@@ -798,17 +708,15 @@ int Policy_SetStreamState(StreamInfoT * pStreamInfo, int AudioRoleIndex, Stream
switch(streamState)
{
case STREAM_STATE_IDLE:
- err = PolicyRunningIdleTransition(pCurrEndPointPolicy, pStreamInfo);
+ err = PolicyRunningIdleTransition(pCurrEndPointPolicy, pPolicyStream);
if(err)
{
return AHL_POLICY_REJECT;
- }
- pStreamInfo->streamState = STREAM_STATE_IDLE;
- PolicyPostStateEvent(pStreamInfo->streamStateEvent,STREAM_EVENT_STOP);
+ }
+ PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_STOP);
break;
case STREAM_STATE_PAUSED:
- pStreamInfo->streamState = STREAM_STATE_PAUSED;
- PolicyPostStateEvent(pStreamInfo->streamStateEvent,STREAM_EVENT_PAUSE);
+ PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_PAUSE);
break;
default:
return AHL_POLICY_REJECT;
@@ -819,17 +727,15 @@ int Policy_SetStreamState(StreamInfoT * pStreamInfo, int AudioRoleIndex, Stream
switch(streamState)
{
case STREAM_STATE_IDLE:
- err = PolicyRunningIdleTransition(pCurrEndPointPolicy, pStreamInfo);
+ err = PolicyRunningIdleTransition(pCurrEndPointPolicy, pPolicyStream);
if(err)
{
return AHL_POLICY_REJECT;
}
- pStreamInfo->streamState = STREAM_STATE_IDLE;
- PolicyPostStateEvent(pStreamInfo->streamStateEvent,STREAM_EVENT_STOP);
+ PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_STOP);
break;
case STREAM_STATE_RUNNING:
- pStreamInfo->streamState = STREAM_STATE_RUNNING;
- PolicyPostStateEvent(pStreamInfo->streamStateEvent,STREAM_EVENT_RESUME);
+ PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_RESUME);
break;
default:
return AHL_POLICY_REJECT;
@@ -844,46 +750,101 @@ int Policy_SetStreamState(StreamInfoT * pStreamInfo, int AudioRoleIndex, Stream
return AHL_POLICY_ACCEPT;
}
-int Policy_SetStreamMute(StreamInfoT * pStreamInfo, StreamMuteT streamMute)
+int Policy_SetStreamMute(json_object *pPolicyStreamJ)
{
- int err;
-
- if(streamMute == STREAM_MUTED)
+ StreamMuteT streamMute = 0;
+ StreamInfoT PolicyStream;
+ EndpointInfoT EndpointInfo;
+ PolicyStream.pEndpointInfo =&EndpointInfo;
+ StreamInfoT * pPolicyStream = &PolicyStream;
+
+ int err = PolicyCtxJSONToStream(pPolicyStreamJ, pPolicyStream);
+ if(err == AHL_FAIL)
{
- err= PolicySetVolumeMute(pStreamInfo->pEndpointInfo, 0);
- if(err)
- {
- AFB_ERROR("Endpoint:%s Set Volume return with errorcode%i",pStreamInfo->pEndpointInfo->gsDeviceName->str, err);
- return AHL_POLICY_REJECT;
- }
- PolicyPostStateEvent(pStreamInfo->streamStateEvent,STREAM_EVENT_MUTED);
+ return AHL_POLICY_ACCEPT;
}
- else
- {
- err= PolicySetVolumeMute(pStreamInfo->pEndpointInfo, pStreamInfo->pEndpointInfo->iVolume);
- if(err)
- {
- AFB_ERROR("Endpoint:%s Set Volume return with errorcode%i",pStreamInfo->pEndpointInfo->gsDeviceName->str, err);
- return AHL_POLICY_REJECT;
- }
- PolicyPostStateEvent(pStreamInfo->streamStateEvent,STREAM_EVENT_UNMUTED);
+ json_object *streamMuteJ=NULL;
+ if(!json_object_object_get_ex(pPolicyStreamJ, "mute_state", &streamMuteJ))
+ {
+ return AHL_POLICY_ACCEPT;
}
+ streamMute = (StreamMuteT)json_object_get_int(streamMuteJ);
+
+ if(streamMute != pPolicyStream->streamMute)
+ {
+ if(streamMute == STREAM_MUTED)
+ {
+
+ err= PolicySetVolume(pPolicyStream->pEndpointInfo->endpointID,
+ pPolicyStream->pEndpointInfo->type,
+ pPolicyStream->pEndpointInfo->gsHALAPIName,
+ pPolicyStream->pRoleName,
+ pPolicyStream->pEndpointInfo->deviceURIType,
+ 0,
+ true);
+ if(err)
+ {
+ AFB_ERROR("StreamID:%i Set Volume return with errorcode%i",pPolicyStream->streamID, err);
+ return AHL_POLICY_REJECT;
+ }
+ PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_MUTED);
+ }
+ else
+ {
+ err= PolicySetVolume(pPolicyStream->pEndpointInfo->endpointID,
+ pPolicyStream->pEndpointInfo->type,
+ pPolicyStream->pEndpointInfo->gsHALAPIName,
+ pPolicyStream->pRoleName,
+ pPolicyStream->pEndpointInfo->deviceURIType,
+ pPolicyStream->pEndpointInfo->iVolume,
+ true);
+ if(err)
+ {
+ AFB_ERROR("Endpoint:%i Set Volume return with errorcode%i",pPolicyStream->streamID, err);
+ return AHL_POLICY_REJECT;
+ }
+ PolicyPostStateEvent(pPolicyStream->streamID,STREAM_EVENT_UNMUTED);
- pStreamInfo->streamMute = streamMute;
+ }
+ pPolicyStream->streamMute = streamMute;
+ }
+
return AHL_POLICY_ACCEPT;
}
-int Policy_SetVolume(EndpointInfoT * f_pEndpointInfo, char *volumeStr)
+int Policy_SetVolume(json_object *pPolicyEndpointJ)
{
+ char *volumeStr = NULL;
+ EndpointInfoT EndpointInfo;
+
+ int err = PolicyCtxJSONToEndpoint(pPolicyEndpointJ, &EndpointInfo);
+ if(err == AHL_FAIL)
+ {
+ return AHL_POLICY_ACCEPT;
+ }
+
+ json_object *volumeJ=NULL;
+
+ if(!json_object_object_get_ex(pPolicyEndpointJ, "arg_volume", &volumeJ))
+ {
+ return AHL_POLICY_ACCEPT;
+ }
+ volumeStr = (char*)json_object_get_string(volumeJ);
// TODO: Parse volume string to support increment/absolute/percent notation (or delegate to action / policy layer to interpret)
int vol = atoi(volumeStr);
//Set the volume
- int err = PolicySetVolumeRamp(f_pEndpointInfo, vol);
+ err= PolicySetVolume(EndpointInfo.endpointID,
+ EndpointInfo.type,
+ EndpointInfo.gsHALAPIName,
+ EndpointInfo.pRoleName,
+ EndpointInfo.deviceURIType,
+ vol,
+ false);
if (err)
{
AFB_ERROR("Set Volume return with errorcode%i", err);
@@ -893,186 +854,186 @@ int Policy_SetVolume(EndpointInfoT * f_pEndpointInfo, char *volumeStr)
return AHL_POLICY_ACCEPT;
}
-int Policy_SetProperty(EndpointInfoT * f_pEndpointInfo, char *propertyName, json_object *propValue)
+int Policy_SetProperty(json_object *pPolicyEndpointJ)
{
+ char *propertyName = NULL;
+ EndpointInfoT EndpointInfo;
+
+ int err = PolicyCtxJSONToEndpoint(pPolicyEndpointJ, &EndpointInfo);
+ if(err == AHL_FAIL)
+ {
+ return AHL_POLICY_ACCEPT;
+ }
+
+ json_object *propertyNameJ=NULL;
+
+ if(!json_object_object_get_ex(pPolicyEndpointJ, "arg_property_name", &propertyNameJ))
+ {
+ return AHL_POLICY_ACCEPT;
+ }
+ propertyName = (char*)json_object_get_string(propertyNameJ);
+
+ json_object *propValueJ;
+ if(!json_object_object_get_ex(pPolicyEndpointJ, "arg_property_value", &propValueJ))
+ {
+ return AHL_POLICY_ACCEPT;
+ }
+
gpointer *key_value=NULL;
- key_value=g_hash_table_lookup(f_pEndpointInfo->pPropTable,propertyName);
- if(key_value==NULL)
+
+ key_value = g_hash_table_lookup(EndpointInfo.pPropTable,propertyName);
+ if(key_value == NULL)
{
AFB_ERROR("Can't find property %s, request will be rejected", propertyName);
return AHL_POLICY_REJECT;
}
- // Object type detection for property value (string = state, numeric = property)
- json_type jType = json_object_get_type(propValue);
- switch (jType) {
- case json_type_double:
- Add_Endpoint_Property_Double(f_pEndpointInfo,propertyName,json_object_get_double(propValue));
- case json_type_int:
- Add_Endpoint_Property_Int(f_pEndpointInfo,propertyName,json_object_get_int(propValue));
- case json_type_string:
- Add_Endpoint_Property_String(f_pEndpointInfo,propertyName,json_object_get_string(propValue));
- break;
- default:
- AFB_ERROR("Invalid property argument Property value not a valid json object query=%s", json_object_get_string(propValue ));
- return AHL_POLICY_REJECT;
+ //Get JsonObjectype
+ json_type currentjType = json_object_get_type((json_object*)key_value);
+ json_type newjType = json_object_get_type(propValueJ);
+
+ //Apply policy on set property if needed here
+ //Here we only validate that the type is the same
+ if(currentjType != newjType)
+ {
+ AFB_ERROR("Property Value Type is wrong");
+ return AHL_POLICY_REJECT;
+ }
+
+
+ //Create a new Json Object
+ json_object *pEventDataJ = NULL;
+ err = wrap_json_pack(&pEventDataJ,"{s:s,s:i,s:i,s:s,s:o,s:s}",
+ "event_name", AHL_ENDPOINT_PROPERTY_EVENT,
+ "endpoint_id", EndpointInfo.endpointID,
+ "endpoint_type", EndpointInfo.type,
+ "property_name", propertyName,
+ "value",propValueJ,
+ "audio_role", EndpointInfo.pRoleName);
+ if(err)
+ {
+ AFB_ERROR("Unable to pack property event");
+ return AHL_POLICY_REJECT;
}
+ //Raise Event to update HLB
+ audiohlapi_raise_event(pEventDataJ);
return AHL_POLICY_ACCEPT;
}
-int Policy_PostEvent(char *eventName, char *audioRole, char *mediaName, void *audioContext)
+int Policy_PostAction(json_object *pPolicyActionJ)
{
+ char * actionName = NULL;
+ char * audioRole = NULL;
+ char * mediaName = NULL;
+ json_object *actionContext = NULL;
+
+ int err = wrap_json_unpack(pPolicyActionJ, "{s:s,s:s,s?s,s?o}", "action_name", &actionName,"audio_role",&audioRole,"media_name",&mediaName,"action_context",&actionContext);
+ if (err) {
+ AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err));
+ return AHL_POLICY_REJECT;
+ }
+
// TODO: Any event with media specified should trigger action on provided rendering services (e.g. Wwise binding, gstreamer file player wrapper, MPDC? simple ALSA player (aplay)?)
-
// Example (when the policy is hooked to CAN events). Post audio playback events other than safety during reverse gear engaged declined
-
// Example post HMI audio role playback events declined when higher priority streams are active
+
+ //In this use case just return the action back to highlevel binding.
+
+ json_object *pEventDataJ = NULL;
+ err = wrap_json_pack(&pEventDataJ, "{s:s,s:s,s:s,s?s,s?o}", "event_name", AHL_POST_ACTION_EVENT, "action_name", &actionName,"audio_role",&audioRole,"media_name",&mediaName,"action_context",&actionContext);
+ if (err) {
+ AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err));
+ return AHL_POLICY_REJECT;
+ }
+ audiohlapi_raise_event(pEventDataJ);
return AHL_POLICY_ACCEPT;
}
-int Policy_AudioDeviceChange()
+int Policy_Endpoint_Init(json_object *pPolicyEndpointJ)
{
- // Allow or disallow a new audio endpoint to be used by the system
- // TODO: Policy assigns audio role(s) for device (or default)
- // TODO: Raise events to engage device switching if active stream in audio role assigned to the new endpoint
-
- return AHL_POLICY_ACCEPT;
-}
+ EndpointInfoT EndpointInfo;
+
+ int err = PolicyCtxJSONToEndpoint(pPolicyEndpointJ, &EndpointInfo);
+ if(err == AHL_FAIL)
+ {
+ return AHL_POLICY_REJECT;
+ }
-int Policy_Endpoint_Property_Init(EndpointInfoT * io_EndpointInfo)
-{
- // Populate list of supported properties for specified endpoint (use helper functions)
- // Setup initial values for all properties GHashTabl
+ if (EndpointInfo.deviceURIType != DEVICEURITYPE_NOT_ALSA) {
+ // Update Hal Name
+ err = RetrieveAssociatedHALAPIName(&EndpointInfo);
+ if (err) {
+ AFB_ERROR("HAL not found for Device %s", EndpointInfo.gsDeviceName);
+ return AHL_POLICY_REJECT;
+ }
- // TODO: Switch on different known endpoints to populate different properties
+ //Set Init Volume
+ StreamConfigT StreamConfig;
+ getStreamConfig(EndpointInfo.pRoleName, &StreamConfig);
+ err = PolicySetVolume(EndpointInfo.endpointID,
+ EndpointInfo.type,
+ EndpointInfo.gsHALAPIName,
+ EndpointInfo.pRoleName,
+ EndpointInfo.deviceURIType,
+ StreamConfig.iVolumeInit,
+ false);
+ if(err) {
+ return AHL_POLICY_REJECT;
+ }
+ }
// Test example
- Add_Endpoint_Property_Int(io_EndpointInfo,AHL_PROPERTY_EQ_LOW,3);
- Add_Endpoint_Property_Int(io_EndpointInfo,AHL_PROPERTY_EQ_MID,0);
- Add_Endpoint_Property_Int(io_EndpointInfo,AHL_PROPERTY_EQ_HIGH,6);
- Add_Endpoint_Property_Int(io_EndpointInfo,AHL_PROPERTY_BALANCE,0);
- Add_Endpoint_Property_Int(io_EndpointInfo,AHL_PROPERTY_FADE,30);
- Add_Endpoint_Property_String(io_EndpointInfo,"preset_name","flat");
-
- return 0; // No errors
+ Add_Endpoint_Property_Int(&EndpointInfo,AHL_PROPERTY_EQ_LOW,3);
+ Add_Endpoint_Property_Int(&EndpointInfo,AHL_PROPERTY_EQ_MID,0);
+ Add_Endpoint_Property_Int(&EndpointInfo,AHL_PROPERTY_EQ_HIGH,6);
+ Add_Endpoint_Property_Int(&EndpointInfo,AHL_PROPERTY_BALANCE,0);
+ Add_Endpoint_Property_Int(&EndpointInfo,AHL_PROPERTY_FADE,30);
+ Add_Endpoint_Property_String(&EndpointInfo,"preset_name","flat");
+
+
+ gpointer *key_value = g_hash_table_lookup(EndpointInfo.pPropTable,AHL_PROPERTY_BALANCE);
+ if(key_value == NULL)
+ {
+ AFB_ERROR("Can't find property %s, request will be rejected", AHL_PROPERTY_BALANCE);
+ return AHL_POLICY_REJECT;
+ }
+
+ //Create a new Json Object
+ json_object *pNewPolicyEndpointJ = NULL;
+ err = PolicyEndpointStructToJSON(&EndpointInfo, &pNewPolicyEndpointJ);
+ if (err == AHL_FAIL)
+ {
+ return AHL_POLICY_REJECT;
+ }
+ json_object *paramJ= json_object_new_string(AHL_ENDPOINT_INIT_EVENT);
+ json_object_object_add(pNewPolicyEndpointJ, "event_name", paramJ);
+
+ //Raise Event to update HLB
+ audiohlapi_raise_event(pNewPolicyEndpointJ);
+
+ return AHL_POLICY_ACCEPT; // No errors
}
int Policy_Init()
{
// Initialize Ressources
g_PolicyCtx.pSourceEndpoints =g_array_new(FALSE,TRUE,sizeof(EndPointPolicyInfoT));
- g_PolicyCtx.pSinkEndpoints = g_array_new(FALSE,TRUE,sizeof(EndPointPolicyInfoT));
- g_PolicyCtx.pStreamOpenPerPolicy = g_array_sized_new(FALSE, TRUE, sizeof(int), g_AHLCtx.policyCtx.iNumberRoles);
- g_PolicyCtx.pMaxStreamOpenPerPolicy = g_array_sized_new(FALSE, TRUE, sizeof(int), g_AHLCtx.policyCtx.iNumberRoles);
- g_PolicyCtx.pVolDuckPerPolicy = g_array_sized_new(FALSE, TRUE, sizeof(int), g_AHLCtx.policyCtx.iNumberRoles);
-
- int initial_value=0;
- int max_value=0;
- int vol_init_value=0;
- int vol_duck_value=0;
- GArray * pRoleDeviceArray = NULL;
- //Init the number of open stream
- for(int i=0; i<g_AHLCtx.policyCtx.iNumberRoles; i++)
- {
- g_array_append_val(g_PolicyCtx.pStreamOpenPerPolicy, initial_value);
-
- GString gs = g_array_index( g_AHLCtx.policyCtx.pAudioRoles, GString, i );
- max_value = MAX_ACTIVE_STREAM_POLICY;
- if ( strcasecmp(gs.str,AHL_ROLE_WARNING) == 0 )
- {
- max_value = 4;
- vol_init_value=80;
- vol_duck_value = 0;
- }
- else if ( strcasecmp(gs.str,AHL_ROLE_GUIDANCE) == 0 )
- {
- max_value = 10;
- vol_init_value=70;
- vol_duck_value = 40;
- }
- else if ( strcasecmp(gs.str,AHL_ROLE_NOTIFICATION) == 0 )
- {
- max_value = 4;
- vol_init_value=80;
- vol_duck_value = 0;
- }
- else if ( strcasecmp(gs.str,AHL_ROLE_COMMUNICATION) == 0 )
- {
- max_value = 10;
- vol_init_value=70;
- vol_duck_value = 30;
- }
- else if ( strcasecmp(gs.str,AHL_ROLE_ENTERTAINMENT) == 0 )
- {
- max_value = MAX_ACTIVE_STREAM_POLICY;
- vol_init_value=60;
- vol_duck_value = 40;
- }
- else if ( strcasecmp(gs.str,AHL_ROLE_SYSTEM) == 0 )
- {
- max_value = 2;
- vol_init_value=100;
- vol_duck_value = 0;
- }
- else if ( strcasecmp(gs.str,AHL_ROLE_STARTUP) == 0 )
- {
- max_value = 1;
- vol_init_value=90;
- vol_duck_value = 0;
- }
- else if ( strcasecmp(gs.str,AHL_ROLE_SHUTDOWN) == 0 )
- {
- max_value = 1;
- vol_init_value=90;
- vol_duck_value = 0;
- }
-
- g_array_append_val(g_PolicyCtx.pMaxStreamOpenPerPolicy, max_value);
- g_array_append_val(g_PolicyCtx.pVolDuckPerPolicy, vol_duck_value);
+ g_PolicyCtx.pSinkEndpoints = g_array_new(FALSE,TRUE,sizeof(EndPointPolicyInfoT));
+ g_PolicyCtx.pHALList = g_ptr_array_new_with_free_func(g_free);
- //Get actual volume value
- pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, i );
- if (pRoleDeviceArray != NULL)
- {
- for ( int j = 0 ; j < pRoleDeviceArray->len; j++)
- {
-
- //Init all Volume
- EndpointInfoT * pEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,j);
- int err= PolicySetVolumeRamp(pEndpointInfo, vol_init_value);
- if(err)
- {
- AFB_WARNING("Endpoint:%s Set Volume return with errorcode%i",pEndpointInfo->gsDeviceName->str, err);
- //try to read volume instead
- err=PolicyGetVolume(pEndpointInfo);
- if(err != 0)
- {
- AFB_WARNING("Can't get volume of Endpoint %s",pEndpointInfo->gsDeviceName->str);
- }
- }
-
- /*
- EndpointInfoT * pEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,j);
- int err=PolicyGetVolume(pEndpointInfo);
- if(err != 0)
- {
- AFB_WARNING("Can't get volume of Endpoint %s",pEndpointInfo->gsDeviceName->str);
- }
-*/
- }
- }
- }
-
+ //Get HalList
+ GetHALList();
//Set System Normal for now, this should be set by an event
//TODO: Receive event from low level
g_PolicyCtx.systemState = SYSTEM_NORMAL;
//register audio backend events
- json_object *queryurl, *responseJ, *devidJ, *eventsJ;
+ //This is to simulate can bus, only used for demo
+ json_object *queryurl, *responseJ, *eventsJ;
eventsJ = json_object_new_array();
json_object_array_add(eventsJ, json_object_new_string("audiod_system_event"));
@@ -1081,17 +1042,19 @@ int Policy_Init()
int returnResult = afb_service_call_sync("audiod", "subscribe", queryurl, &responseJ);
if (returnResult) {
AFB_ERROR("Fail subscribing to Audio Backend System events");
- return -1;
+ return AHL_POLICY_REJECT;
}
-
-
- return 0; // No errors
+ return AHL_POLICY_ACCEPT;
}
void Policy_Term()
{
-
//Free Ressources
+ if (g_PolicyCtx.pHALList) {
+ g_ptr_array_free(g_PolicyCtx.pHALList,TRUE);
+ g_PolicyCtx.pHALList = NULL;
+ }
+
for(int i=0; i<g_PolicyCtx.pSourceEndpoints->len; i++)
{
EndPointPolicyInfoT * pCurEndpoint = &g_array_index(g_PolicyCtx.pSourceEndpoints,EndPointPolicyInfoT,i);
@@ -1110,70 +1073,17 @@ void Policy_Term()
g_PolicyCtx.pSourceEndpoints = NULL;
g_array_free(g_PolicyCtx.pSinkEndpoints,TRUE);
g_PolicyCtx.pSinkEndpoints = NULL;
-
- g_array_free(g_PolicyCtx.pStreamOpenPerPolicy,TRUE);
- g_PolicyCtx.pStreamOpenPerPolicy = NULL;
- g_array_free(g_PolicyCtx.pMaxStreamOpenPerPolicy,TRUE);
- g_PolicyCtx.pMaxStreamOpenPerPolicy = NULL;
- g_array_free(g_PolicyCtx.pVolDuckPerPolicy, TRUE);
- g_PolicyCtx.pVolDuckPerPolicy = NULL;
-}
-
-static void PolicySpeedModify(int speed)
-{
-
- for(int i=0; i<g_PolicyCtx.pSinkEndpoints->len; i++)
- {
- EndPointPolicyInfoT * pCurEndpoint = &g_array_index(g_PolicyCtx.pSinkEndpoints,EndPointPolicyInfoT,i);
- if(pCurEndpoint == NULL)
- {
- AFB_WARNING("Sink Endpoint not found");
- return;
-
- }
- //check if active
- if(pCurEndpoint->streamInfo->len > 0 )
- {
- StreamPolicyInfoT * pCurStream = &g_array_index(pCurEndpoint->streamInfo,StreamPolicyInfoT,pCurEndpoint->streamInfo->len-1);
-
-
- //Get Stream Info
- StreamInfoT * pActiveStreamInfo = PolicyGetActiveStream(pCurStream->streamID);
- if (pActiveStreamInfo == NULL) {
- AFB_WARNING("Stream not found, Specified stream not currently active stream_id -> %d",pCurStream->streamID);
- return;
- }
-
- if(strcasecmp(pActiveStreamInfo->pEndpointInfo->gsAudioRole->str,AHL_ROLE_ENTERTAINMENT)==0)
- {
-
- if(speed > 30 && speed < 100)
- {
- int volume =speed;
- PolicySetVolumeRamp(pActiveStreamInfo->pEndpointInfo,volume);
- }
-
-
- }
-
- }
-
- }
}
-
void Policy_OnEvent(const char *evtname, json_object *eventJ)
{
AFB_DEBUG("Policy received event %s", evtname);
-
char *eventName = NULL;
json_object *event_parameter = NULL;
int speed = 0;
-
if(strcasecmp(evtname, "audiod/system_events")==0)
{
-
int err = wrap_json_unpack(eventJ, "{s:s,s:o}", "event_name", &eventName, "event_parameter", &event_parameter);
if (err) {
AFB_WARNING("Invalid arguments, Args not a valid json object query=%s", json_object_get_string(eventJ));
@@ -1188,14 +1098,10 @@ void Policy_OnEvent(const char *evtname, json_object *eventJ)
AFB_WARNING("Invalid arguments, Args not a valid json object query=%s", json_object_get_string(event_parameter));
return;
}
-
//When speed change Modify volume on Endpoint where entertainment change
PolicySpeedModify(speed);
}
-
}
+}
-
-
-
-} \ No newline at end of file
+#endif // AHL_DISCONNECT_POLICY \ No newline at end of file
diff --git a/src/ahl-policy.h b/src/ahl-policy.h
new file mode 100644
index 0000000..dfb3d7c
--- /dev/null
+++ b/src/ahl-policy.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 "Audiokinetic Inc"
+ *
+ * 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.
+ */
+
+#ifndef AHL_POLICY_INCLUDE
+#define AHL_POLICY_INCLUDE
+#include "ahl-binding.h"
+
+#ifndef AHL_DISCONNECT_POLICY
+
+#define MAX_ACTIVE_STREAM_POLICY 30
+#define POLICY_FAIL 1
+#define POLICY_SUCCESS 0
+
+#define AHL_POLICY_UNDEFINED_HALNAME "UNDEFINED"
+#define AHL_POLICY_UNDEFINED_DISPLAYNAME "DeviceNotFound"
+
+typedef enum SystemState {
+ SYSTEM_STARTUP = 0, // Startup
+ SYSTEM_SHUTDOWN, // ShutDown
+ SYSTEM_NORMAL, // Normal
+ SYSTEM_LOW_POWER, // Low Power, save mode
+ SYSTEM_MAXVALUE // Enum count, keep at the end
+} SystemStateT;
+
+typedef struct StreamPolicyInfo {
+ streamID_t streamID;
+ int RolePriority;
+ char * pAudioRole;
+ InterruptBehaviorT interruptBehavior;
+ int iDuckVolume; //duck Volume
+} StreamPolicyInfoT;
+
+typedef struct EndPointPolicyInfo {
+ endpointID_t endpointID;
+ EndpointTypeT type;
+ DeviceURITypeT deviceType;
+ char * pDeviceName;
+ char * pHalApiName;
+ int iVolume; //Current Volume
+ GArray * streamInfo; //List of playing or duck stream at a given endpoint
+} EndPointPolicyInfoT;
+
+
+typedef struct HalInfo {
+ char *pDevID;
+ char *pAPIName;
+ char *pDisplayName;
+} HalInfoT;
+
+typedef struct StreamConfig {
+ int iNbMaxStream;
+ int iVolumeInit;
+ int iVolumeDuckValue;
+} StreamConfigT;
+
+// Global Policy Local context
+typedef struct PolicyLocalCtx {
+ GArray * pSourceEndpoints; // List of Source Endpoint with playing stream or interrupted stream
+ GArray * pSinkEndpoints; // List of Sink Endpoint with playing stream or interrupted stream
+ GPtrArray * pHALList;
+ SystemStateT systemState;
+} PolicyLocalCtxT;
+
+int Policy_Endpoint_Init(json_object *pPolicyEndpointJ);
+int Policy_OpenStream(json_object *pPolicyStreamJ);
+int Policy_CloseStream(json_object *pPolicyStreamJ);
+int Policy_SetStreamState(json_object *pPolicyStreamJ);
+int Policy_SetStreamMute(json_object *pPolicyStreamJ);
+int Policy_PostAction(json_object *pPolicyActionJ);
+int Policy_SetVolume(json_object *pPolicyEndpointJ);
+int Policy_SetProperty(json_object *pPolicyEndpointJ);
+int Policy_Init();
+void Policy_Term();
+void Policy_OnEvent(const char *evtname, json_object *eventJ);
+
+#endif // AHL_DISCONNECT_POLICY
+#endif // AHL_POLICY_INCLUDE \ No newline at end of file