1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.car.hal;
17
18import android.car.media.CarAudioManager;
19import android.os.ServiceSpecificException;
20import android.util.Log;
21
22import com.android.car.AudioRoutingPolicy;
23import com.android.car.CarAudioAttributesUtil;
24import com.android.car.CarLog;
25import com.android.car.vehiclenetwork.VehicleNetwork;
26import com.android.car.vehiclenetwork.VehicleNetworkConsts;
27import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioContextFlag;
28import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioExtFocusFlag;
29import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusIndex;
30import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusRequest;
31import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusState;
32import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioHwVariantConfigFlag;
33import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioRoutingPolicyIndex;
34import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioStreamState;
35import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioStreamStateIndex;
36import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioVolumeIndex;
37import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig;
38import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfigs;
39import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
40
41import java.io.PrintWriter;
42import java.util.HashMap;
43import java.util.LinkedList;
44import java.util.List;
45
46public class AudioHalService extends HalServiceBase {
47
48    public static final int VEHICLE_AUDIO_FOCUS_REQUEST_INVALID = -1;
49    public static final int VEHICLE_AUDIO_FOCUS_REQUEST_GAIN =
50            VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN;
51    public static final int VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT =
52            VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT;
53    public static final int VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK =
54            VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK;
55    public static final int VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE =
56            VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE;
57
58    public static String audioFocusRequestToString(int request) {
59        return VehicleAudioFocusRequest.enumToString(request);
60    }
61
62    public static final int VEHICLE_AUDIO_FOCUS_STATE_INVALID = -1;
63    public static final int VEHICLE_AUDIO_FOCUS_STATE_GAIN =
64            VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN;
65    public static final int VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT =
66            VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT;
67    public static final int VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_CAN_DUCK =
68            VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_CAN_DUCK;
69    public static final int VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT =
70            VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT;
71    public static final int VEHICLE_AUDIO_FOCUS_STATE_LOSS =
72            VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
73    public static final int VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_EXLCUSIVE =
74            VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_EXLCUSIVE;
75
76    public static String audioFocusStateToString(int state) {
77        return VehicleAudioFocusState.enumToString(state);
78    }
79
80    public static final int VEHICLE_AUDIO_STREAM_STATE_STOPPED =
81            VehicleAudioStreamState.VEHICLE_AUDIO_STREAM_STATE_STOPPED;
82    public static final int VEHICLE_AUDIO_STREAM_STATE_STARTED =
83            VehicleAudioStreamState.VEHICLE_AUDIO_STREAM_STATE_STARTED;
84
85    public static String audioStreamStateToString(int state) {
86        return VehicleAudioStreamState.enumToString(state);
87    }
88
89    public static final int VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG =
90            VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG;
91    public static final int VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG =
92            VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG;
93    public static final int VEHICLE_AUDIO_EXT_FOCUS_CAR_TRANSIENT_FLAG =
94            VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_TRANSIENT_FLAG;
95    public static final int VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG =
96            VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG;
97
98    public static final int STREAM_NUM_DEFAULT = 0;
99
100    public static final int FOCUS_STATE_ARRAY_INDEX_STATE = 0;
101    public static final int FOCUS_STATE_ARRAY_INDEX_STREAMS = 1;
102    public static final int FOCUS_STATE_ARRAY_INDEX_EXTERNAL_FOCUS = 2;
103
104    public static final int AUDIO_CONTEXT_MUSIC_FLAG =
105            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG;
106    public static final int AUDIO_CONTEXT_NAVIGATION_FLAG =
107            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG;
108    public static final int AUDIO_CONTEXT_VOICE_COMMAND_FLAG =
109            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG;
110    public static final int AUDIO_CONTEXT_CALL_FLAG =
111            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG;
112    public static final int AUDIO_CONTEXT_ALARM_FLAG =
113            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG;
114    public static final int AUDIO_CONTEXT_NOTIFICATION_FLAG =
115            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG;
116    public static final int AUDIO_CONTEXT_UNKNOWN_FLAG =
117            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG;
118    public static final int AUDIO_CONTEXT_SAFETY_ALERT_FLAG =
119            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG;
120    public static final int AUDIO_CONTEXT_RADIO_FLAG =
121            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG;
122    public static final int AUDIO_CONTEXT_CD_ROM_FLAG =
123            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG;
124    public static final int AUDIO_CONTEXT_AUX_AUDIO_FLAG =
125            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG;
126    public static final int AUDIO_CONTEXT_SYSTEM_SOUND_FLAG =
127            VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG;
128
129    public interface AudioHalListener {
130        /**
131         * Audio focus change from car.
132         * @param focusState
133         * @param streams
134         * @param externalFocus Flags of active external audio focus.
135         *            0 means no external audio focus.
136         */
137        void onFocusChange(int focusState, int streams, int externalFocus);
138        /**
139         * Audio volume change from car.
140         * @param streamNumber
141         * @param volume
142         * @param volumeState
143         */
144        void onVolumeChange(int streamNumber, int volume, int volumeState);
145        /**
146         * Volume limit change from car.
147         * @param streamNumber
148         * @param volume
149         */
150        void onVolumeLimitChange(int streamNumber, int volume);
151        /**
152         * Stream state change (start / stop) from android
153         * @param streamNumber
154         * @param state
155         */
156        void onStreamStatusChange(int streamNumber, int state);
157    }
158
159    private final VehicleHal mVehicleHal;
160    private AudioHalListener mListener;
161    private int mVariant;
162
163    private List<VehiclePropValue> mQueuedEvents;
164
165    private final HashMap<Integer, VehiclePropConfig> mProperties = new HashMap<>();
166
167    public AudioHalService(VehicleHal vehicleHal) {
168        mVehicleHal = vehicleHal;
169    }
170
171    public void setListener(AudioHalListener listener) {
172        List<VehiclePropValue> eventsToDispatch = null;
173        synchronized (this) {
174            mListener = listener;
175            if (mQueuedEvents != null) {
176                eventsToDispatch = mQueuedEvents;
177                mQueuedEvents = null;
178            }
179        }
180        if (eventsToDispatch != null) {
181            dispatchEventToListener(listener, eventsToDispatch);
182        }
183    }
184
185    public void setAudioRoutingPolicy(AudioRoutingPolicy policy) {
186        VehicleNetwork vn = mVehicleHal.getVehicleNetwork();
187        VehiclePropConfigs configs = vn.listProperties(
188                VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY);
189        if (configs == null) {
190            Log.w(CarLog.TAG_AUDIO,
191                    "Vehicle HAL did not implement VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY");
192            return;
193        }
194        int[] policyToSet = new int[2];
195        for (int i = 0; i < policy.getPhysicalStreamsCount(); i++) {
196            policyToSet[VehicleAudioRoutingPolicyIndex.VEHICLE_AUDIO_ROUTING_POLICY_INDEX_STREAM] =
197                    i;
198            int contexts = 0;
199            for (int logicalStream : policy.getLogicalStreamsForPhysicalStream(i)) {
200                contexts |= logicalStreamToHalContextType(logicalStream);
201            }
202            policyToSet[VehicleAudioRoutingPolicyIndex.VEHICLE_AUDIO_ROUTING_POLICY_INDEX_CONTEXTS]
203                    = contexts;
204            vn.setIntVectorProperty(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY,
205                    policyToSet);
206        }
207    }
208
209    /**
210     * Convert car audio manager stream type (usage) into audio context type.
211     */
212    public static int logicalStreamToHalContextType(int logicalStream) {
213        switch (logicalStream) {
214            case CarAudioManager.CAR_AUDIO_USAGE_RADIO:
215                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG;
216            case CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL:
217                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG;
218            case CarAudioManager.CAR_AUDIO_USAGE_MUSIC:
219                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG;
220            case CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE:
221                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG;
222            case CarAudioManager.CAR_AUDIO_USAGE_VOICE_COMMAND:
223                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG;
224            case CarAudioManager.CAR_AUDIO_USAGE_ALARM:
225                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG;
226            case CarAudioManager.CAR_AUDIO_USAGE_NOTIFICATION:
227                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG;
228            case CarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT:
229                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG;
230            case CarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SOUND:
231                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG;
232            case CarAudioManager.CAR_AUDIO_USAGE_DEFAULT:
233                return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG;
234            case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_BOTTOM:
235            case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_CAR_PROXY:
236                // internal tag not associated with any stream
237                return 0;
238            default:
239                Log.w(CarLog.TAG_AUDIO, "Unknown logical stream:" + logicalStream);
240                return 0;
241        }
242    }
243
244    public void requestAudioFocusChange(int request, int streams, int audioContexts) {
245        requestAudioFocusChange(request, streams, VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG, audioContexts);
246    }
247
248    public void requestAudioFocusChange(int request, int streams, int extFocus, int audioContexts) {
249        int[] payload = { request, streams, extFocus, audioContexts };
250        mVehicleHal.getVehicleNetwork().setIntVectorProperty(
251                VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, payload);
252    }
253
254    public synchronized int getHwVariant() {
255        return mVariant;
256    }
257
258    public synchronized boolean isRadioExternal() {
259        VehiclePropConfig config = mProperties.get(
260                VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_HW_VARIANT);
261        if (config == null) {
262            return true;
263        }
264        return (config.getConfigArray(0) &
265                VehicleAudioHwVariantConfigFlag.VEHICLE_AUDIO_HW_VARIANT_FLAG_PASS_RADIO_AUDIO_FOCUS_FLAG)
266                == 0;
267    }
268
269    public synchronized boolean isFocusSupported() {
270        return isPropertySupportedLocked(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS);
271    }
272
273    /**
274     * Get the current audio focus state.
275     * @return 0: focusState, 1: streams, 2: externalFocus
276     */
277    public int[] getCurrentFocusState() {
278        if (!isFocusSupported()) {
279            return new int[] { VEHICLE_AUDIO_FOCUS_STATE_GAIN, 0xffffffff, 0};
280        }
281        try {
282            return mVehicleHal.getVehicleNetwork().getIntVectorProperty(
283                    VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS);
284        } catch (ServiceSpecificException e) {
285            Log.e(CarLog.TAG_AUDIO, "VEHICLE_PROPERTY_AUDIO_HW_VARIANT not ready", e);
286            return new int[] { VEHICLE_AUDIO_FOCUS_STATE_LOSS, 0x0, 0};
287        }
288    }
289
290    private boolean isPropertySupportedLocked(int property) {
291        VehiclePropConfig config = mProperties.get(property);
292        return config != null;
293    }
294
295    @Override
296    public synchronized void init() {
297        for (VehiclePropConfig config : mProperties.values()) {
298            if (VehicleHal.isPropertySubscribable(config)) {
299                mVehicleHal.subscribeProperty(this, config.getProp(), 0);
300            }
301        }
302        try {
303            mVariant = mVehicleHal.getVehicleNetwork().getIntProperty(
304                    VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_HW_VARIANT);
305        } catch (IllegalArgumentException e) {
306            // no variant. Set to default, 0.
307            mVariant = 0;
308        } catch (ServiceSpecificException e) {
309            Log.e(CarLog.TAG_AUDIO, "VEHICLE_PROPERTY_AUDIO_HW_VARIANT not ready", e);
310            mVariant = 0;
311        }
312    }
313
314    @Override
315    public synchronized void release() {
316        for (VehiclePropConfig config : mProperties.values()) {
317            if (VehicleHal.isPropertySubscribable(config)) {
318                mVehicleHal.unsubscribeProperty(this, config.getProp());
319            }
320        }
321        mProperties.clear();
322    }
323
324    @Override
325    public synchronized List<VehiclePropConfig> takeSupportedProperties(
326            List<VehiclePropConfig> allProperties) {
327        for (VehiclePropConfig p : allProperties) {
328            switch (p.getProp()) {
329                case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS:
330                case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME:
331                case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT:
332                case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_HW_VARIANT:
333                case VehicleNetworkConsts.VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE:
334                    mProperties.put(p.getProp(), p);
335                    break;
336            }
337        }
338        return new LinkedList<VehiclePropConfig>(mProperties.values());
339    }
340
341    @Override
342    public void handleHalEvents(List<VehiclePropValue> values) {
343        AudioHalListener listener = null;
344        synchronized (this) {
345            listener = mListener;
346            if (listener == null) {
347                if (mQueuedEvents == null) {
348                    mQueuedEvents = new LinkedList<VehiclePropValue>();
349                }
350                mQueuedEvents.addAll(values);
351            }
352        }
353        if (listener != null) {
354            dispatchEventToListener(listener, values);
355        }
356    }
357
358    private void dispatchEventToListener(AudioHalListener listener, List<VehiclePropValue> values) {
359        for (VehiclePropValue v : values) {
360            switch (v.getProp()) {
361                case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS: {
362                    int focusState = v.getInt32Values(
363                            VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_FOCUS);
364                    int streams = v.getInt32Values(
365                            VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_STREAMS);
366                    int externalFocus = v.getInt32Values(
367                            VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE);
368                    listener.onFocusChange(focusState, streams, externalFocus);
369                } break;
370                case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME: {
371                    int volume = v.getInt32Values(
372                            VehicleAudioVolumeIndex.VEHICLE_AUDIO_VOLUME_INDEX_VOLUME);
373                    int streamNum = v.getInt32Values(
374                            VehicleAudioVolumeIndex.VEHICLE_AUDIO_VOLUME_INDEX_STREAM);
375                    int volumeState = v.getInt32Values(
376                            VehicleAudioVolumeIndex.VEHICLE_AUDIO_VOLUME_INDEX_STATE);
377                    listener.onVolumeChange(streamNum, volume, volumeState);
378                } break;
379                case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: {
380                    //TODO
381                } break;
382                case VehicleNetworkConsts.VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: {
383                    int state = v.getInt32Values(
384                            VehicleAudioStreamStateIndex.VEHICLE_AUDIO_STREAM_STATE_INDEX_STATE);
385                    int streamNum = v.getInt32Values(
386                            VehicleAudioStreamStateIndex.VEHICLE_AUDIO_STREAM_STATE_INDEX_STREAM);
387                    listener.onStreamStatusChange(streamNum, state);
388                } break;
389            }
390        }
391    }
392
393    @Override
394    public void dump(PrintWriter writer) {
395        writer.println("*Audio HAL*");
396        writer.println(" audio H/W variant:" + mVariant);
397        writer.println(" Supported properties");
398        VehicleHal.dumpProperties(writer, mProperties.values());
399    }
400
401}
402