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  */
16 package com.android.car.hal;
17 
18 import android.car.media.CarAudioManager;
19 import android.os.ServiceSpecificException;
20 import android.util.Log;
21 
22 import com.android.car.AudioRoutingPolicy;
23 import com.android.car.CarAudioAttributesUtil;
24 import com.android.car.CarLog;
25 import com.android.car.vehiclenetwork.VehicleNetwork;
26 import com.android.car.vehiclenetwork.VehicleNetworkConsts;
27 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioContextFlag;
28 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioExtFocusFlag;
29 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusIndex;
30 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusRequest;
31 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusState;
32 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioHwVariantConfigFlag;
33 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioRoutingPolicyIndex;
34 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioStreamState;
35 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioStreamStateIndex;
36 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioVolumeIndex;
37 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig;
38 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfigs;
39 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
40 
41 import java.io.PrintWriter;
42 import java.util.HashMap;
43 import java.util.LinkedList;
44 import java.util.List;
45 
46 public 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 
audioFocusRequestToString(int request)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 
audioFocusStateToString(int state)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 
audioStreamStateToString(int state)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          */
onFocusChange(int focusState, int streams, int externalFocus)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          */
onVolumeChange(int streamNumber, int volume, int volumeState)144         void onVolumeChange(int streamNumber, int volume, int volumeState);
145         /**
146          * Volume limit change from car.
147          * @param streamNumber
148          * @param volume
149          */
onVolumeLimitChange(int streamNumber, int volume)150         void onVolumeLimitChange(int streamNumber, int volume);
151         /**
152          * Stream state change (start / stop) from android
153          * @param streamNumber
154          * @param state
155          */
onStreamStatusChange(int streamNumber, int state)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 
AudioHalService(VehicleHal vehicleHal)167     public AudioHalService(VehicleHal vehicleHal) {
168         mVehicleHal = vehicleHal;
169     }
170 
setListener(AudioHalListener listener)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 
setAudioRoutingPolicy(AudioRoutingPolicy policy)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      */
logicalStreamToHalContextType(int logicalStream)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 
requestAudioFocusChange(int request, int streams, int audioContexts)244     public void requestAudioFocusChange(int request, int streams, int audioContexts) {
245         requestAudioFocusChange(request, streams, VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG, audioContexts);
246     }
247 
requestAudioFocusChange(int request, int streams, int extFocus, int audioContexts)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 
getHwVariant()254     public synchronized int getHwVariant() {
255         return mVariant;
256     }
257 
isRadioExternal()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 
isFocusSupported()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      */
getCurrentFocusState()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 
isPropertySupportedLocked(int property)290     private boolean isPropertySupportedLocked(int property) {
291         VehiclePropConfig config = mProperties.get(property);
292         return config != null;
293     }
294 
295     @Override
init()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
release()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
takeSupportedProperties( List<VehiclePropConfig> allProperties)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
handleHalEvents(List<VehiclePropValue> values)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 
dispatchEventToListener(AudioHalListener listener, List<VehiclePropValue> values)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
dump(PrintWriter writer)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