plumbing for volume support

- add volume key listening to CarInputService
- separate volume property monitoring from AudioHalService
  with additional APIs for volume capability check
- add audio context listening to CarAudioService

bug: 27744968

Change-Id: I5cebe141115ce86dc07fa8d590874ff5bb9bf66b
diff --git a/service/src/com/android/car/hal/AudioHalService.java b/service/src/com/android/car/hal/AudioHalService.java
index 893fbff..16d3d38 100644
--- a/service/src/com/android/car/hal/AudioHalService.java
+++ b/service/src/com/android/car/hal/AudioHalService.java
@@ -33,7 +33,9 @@
 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioRoutingPolicyIndex;
 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioStreamState;
 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioStreamStateIndex;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioVolumeCapabilityFlag;
 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioVolumeIndex;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioVolumeLimitIndex;
 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig;
 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfigs;
 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
@@ -126,7 +128,7 @@
     public static final int AUDIO_CONTEXT_SYSTEM_SOUND_FLAG =
             VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG;
 
-    public interface AudioHalListener {
+    public interface AudioHalFocusListener {
         /**
          * Audio focus change from car.
          * @param focusState
@@ -136,6 +138,15 @@
          */
         void onFocusChange(int focusState, int streams, int externalFocus);
         /**
+         * Stream state change (start / stop) from android
+         * @param streamNumber
+         * @param state
+         */
+        void onStreamStatusChange(int streamNumber, int state);
+    }
+
+    public interface AudioHalVolumeListener {
+        /**
          * Audio volume change from car.
          * @param streamNumber
          * @param volume
@@ -148,16 +159,11 @@
          * @param volume
          */
         void onVolumeLimitChange(int streamNumber, int volume);
-        /**
-         * Stream state change (start / stop) from android
-         * @param streamNumber
-         * @param state
-         */
-        void onStreamStatusChange(int streamNumber, int state);
     }
 
     private final VehicleHal mVehicleHal;
-    private AudioHalListener mListener;
+    private AudioHalFocusListener mFocusListener;
+    private AudioHalVolumeListener mVolumeListener;
     private int mVariant;
 
     private List<VehiclePropValue> mQueuedEvents;
@@ -168,17 +174,41 @@
         mVehicleHal = vehicleHal;
     }
 
-    public void setListener(AudioHalListener listener) {
+    public void setFocusListener(AudioHalFocusListener focusListener) {
         List<VehiclePropValue> eventsToDispatch = null;
+        AudioHalVolumeListener volumeListener;
+        boolean shouldDispatch = false;
         synchronized (this) {
-            mListener = listener;
-            if (mQueuedEvents != null) {
+            mFocusListener = focusListener;
+            volumeListener = mVolumeListener;
+            shouldDispatch = canDispatchEventLocked();
+            if (shouldDispatch) {
+                if (mQueuedEvents != null) {
+                    eventsToDispatch = mQueuedEvents;
+                    mQueuedEvents = null;
+                }
+            }
+        }
+        if (shouldDispatch && eventsToDispatch != null) {
+            dispatchEventToListener(focusListener, volumeListener, eventsToDispatch);
+        }
+    }
+
+    public void setVolumeListener(AudioHalVolumeListener volumeListener) {
+        List<VehiclePropValue> eventsToDispatch = null;
+        AudioHalFocusListener focusListener;
+        boolean shouldDispatch = false;
+        synchronized (this) {
+            mVolumeListener = volumeListener;
+            focusListener = mFocusListener;
+            shouldDispatch = canDispatchEventLocked();
+            if (shouldDispatch) {
                 eventsToDispatch = mQueuedEvents;
                 mQueuedEvents = null;
             }
         }
-        if (eventsToDispatch != null) {
-            dispatchEventToListener(listener, eventsToDispatch);
+        if (shouldDispatch && eventsToDispatch != null) {
+            dispatchEventToListener(focusListener, volumeListener, eventsToDispatch);
         }
     }
 
@@ -270,6 +300,44 @@
         return isPropertySupportedLocked(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS);
     }
 
+    public synchronized boolean isAudioVolumeSupported() {
+        return isPropertySupportedLocked(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME);
+    }
+
+    public synchronized int getSupportedAudioVolumeContexts() {
+        if (!isPropertySupportedLocked(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME)) {
+            throw new IllegalStateException("VEHICLE_PROPERTY_AUDIO_VOLUME not supported");
+        }
+        VehiclePropConfig config = mProperties.get(
+                VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME);
+        return config.getConfigArray(0);
+    }
+
+    /**
+     * Whether external audio module can memorize logical audio volumes or not.
+     * @return
+     */
+    public synchronized boolean isExternalAudioVolumePersistent() {
+        if (!isPropertySupportedLocked(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME)) {
+            throw new IllegalStateException("VEHICLE_PROPERTY_AUDIO_VOLUME not supported");
+        }
+        VehiclePropConfig config = mProperties.get(
+                VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME);
+        if (config.getConfigArray(0) == 0) { // physical streams only
+            return false;
+        }
+        if ((config.getConfigArray(1) &
+                VehicleAudioVolumeCapabilityFlag.VEHICLE_AUDIO_VOLUME_CAPABILITY_PERSISTENT_STORAGE)
+                != 0) {
+            return true;
+        }
+        return false;
+    }
+
+    public synchronized boolean isAudioVolumeLimitSupported() {
+        return isPropertySupportedLocked(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT);
+    }
+
     /**
      * Get the current audio focus state.
      * @return 0: focusState, 1: streams, 2: externalFocus
@@ -340,22 +408,38 @@
 
     @Override
     public void handleHalEvents(List<VehiclePropValue> values) {
-        AudioHalListener listener = null;
+        AudioHalFocusListener focusListener = null;
+        AudioHalVolumeListener volumeListener = null;
+        boolean shouldDispatch = false;
         synchronized (this) {
-            listener = mListener;
-            if (listener == null) {
+            focusListener = mFocusListener;
+            volumeListener = mVolumeListener;
+            shouldDispatch = canDispatchEventLocked();
+            if (!shouldDispatch) {
                 if (mQueuedEvents == null) {
                     mQueuedEvents = new LinkedList<VehiclePropValue>();
                 }
                 mQueuedEvents.addAll(values);
             }
         }
-        if (listener != null) {
-            dispatchEventToListener(listener, values);
+        if (shouldDispatch) {
+            dispatchEventToListener(focusListener, volumeListener, values);
         }
     }
 
-    private void dispatchEventToListener(AudioHalListener listener, List<VehiclePropValue> values) {
+    private boolean canDispatchEventLocked() {
+        if (mFocusListener == null && isFocusSupported()) {
+            return false;
+        }
+        if (mVolumeListener == null && isAudioVolumeSupported()) {
+            return false;
+        }
+        return true;
+    }
+
+    private void dispatchEventToListener(AudioHalFocusListener focusListener,
+            AudioHalVolumeListener volumeListener,
+            List<VehiclePropValue> values) {
         for (VehiclePropValue v : values) {
             switch (v.getProp()) {
                 case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS: {
@@ -365,7 +449,7 @@
                             VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_STREAMS);
                     int externalFocus = v.getInt32Values(
                             VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE);
-                    listener.onFocusChange(focusState, streams, externalFocus);
+                    focusListener.onFocusChange(focusState, streams, externalFocus);
                 } break;
                 case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME: {
                     int volume = v.getInt32Values(
@@ -374,20 +458,25 @@
                             VehicleAudioVolumeIndex.VEHICLE_AUDIO_VOLUME_INDEX_STREAM);
                     int volumeState = v.getInt32Values(
                             VehicleAudioVolumeIndex.VEHICLE_AUDIO_VOLUME_INDEX_STATE);
-                    listener.onVolumeChange(streamNum, volume, volumeState);
+                    volumeListener.onVolumeChange(streamNum, volume, volumeState);
                 } break;
                 case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: {
-                    //TODO
+                    int stream = v.getInt32Values(
+                            VehicleAudioVolumeLimitIndex.VEHICLE_AUDIO_VOLUME_LIMIT_INDEX_STREAM);
+                    int maxVolume = v.getInt32Values(
+                            VehicleAudioVolumeLimitIndex.VEHICLE_AUDIO_VOLUME_LIMIT_INDEX_MAX_VOLUME);
+                    volumeListener.onVolumeLimitChange(stream, maxVolume);
                 } break;
                 case VehicleNetworkConsts.VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: {
                     int state = v.getInt32Values(
                             VehicleAudioStreamStateIndex.VEHICLE_AUDIO_STREAM_STATE_INDEX_STATE);
                     int streamNum = v.getInt32Values(
                             VehicleAudioStreamStateIndex.VEHICLE_AUDIO_STREAM_STATE_INDEX_STREAM);
-                    listener.onStreamStatusChange(streamNum, state);
+                    focusListener.onStreamStatusChange(streamNum, state);
                 } break;
             }
         }
+        values.clear();
     }
 
     @Override