diff options
author | Jean-Michel Trivi <jmtrivi@google.com> | 2021-10-13 03:43:04 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2021-10-13 03:43:04 +0000 |
commit | 31ecebec87862fb370558bdce82aa6a6283d790f (patch) | |
tree | d962687d6b6099cd18a838277f5e3abf5c3a58c5 | |
parent | 2f645f23e7a81a9bab4316c4d1cf969f6a1cdbe5 (diff) | |
parent | 859de8a0eda93a82dad7dbc110b0a6a7cfe4bceb (diff) |
Merge "Spatializer: add API for monitoring output changes" into sc-v2-dev
-rw-r--r-- | core/api/system-current.txt | 7 | ||||
-rwxr-xr-x | media/java/android/media/IAudioService.aidl | 7 | ||||
-rw-r--r-- | media/java/android/media/ISpatializerOutputCallback.aidl | 27 | ||||
-rw-r--r-- | media/java/android/media/Spatializer.java | 117 | ||||
-rw-r--r-- | services/core/java/com/android/server/audio/AudioService.java | 21 | ||||
-rw-r--r-- | services/core/java/com/android/server/audio/SpatializerHelper.java | 64 |
6 files changed, 243 insertions, 0 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 1d406a5fdb28..5b27019f300e 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5377,10 +5377,12 @@ package android.media { method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void addCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes); method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void addOnHeadTrackingModeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadTrackingModeChangedListener); method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void clearOnHeadToSoundstagePoseUpdatedListener(); + method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void clearOnSpatializerOutputChangedListener(); method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<android.media.AudioDeviceAttributes> getCompatibleAudioDevices(); method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getDesiredHeadTrackingMode(); method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void getEffectParameter(int, @NonNull byte[]); method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getHeadTrackingMode(); + method @IntRange(from=0) @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getOutput(); method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<java.lang.Integer> getSupportedHeadTrackingModes(); method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void recenterHeadTracker(); method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes); @@ -5390,6 +5392,7 @@ package android.media { method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setEnabled(boolean); method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setGlobalTransform(@NonNull float[]); method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setOnHeadToSoundstagePoseUpdatedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadToSoundstagePoseUpdatedListener); + method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setOnSpatializerOutputChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerOutputChangedListener); field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_DISABLED = -1; // 0xffffffff field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_OTHER = 0; // 0x0 field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_RELATIVE_DEVICE = 2; // 0x2 @@ -5406,6 +5409,10 @@ package android.media { method public void onHeadTrackingModeChanged(@NonNull android.media.Spatializer, int); } + public static interface Spatializer.OnSpatializerOutputChangedListener { + method public void onSpatializerOutputChanged(@NonNull android.media.Spatializer, @IntRange(from=0) int); + } + } package android.media.audiofx { diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 6d52b664cee9..abd067cfe2f3 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -38,6 +38,7 @@ import android.media.IStrategyPreferredDevicesDispatcher; import android.media.ISpatializerCallback; import android.media.ISpatializerHeadTrackingModeCallback; import android.media.ISpatializerHeadToSoundStagePoseCallback; +import android.media.ISpatializerOutputCallback; import android.media.IVolumeController; import android.media.IVolumeController; import android.media.PlayerBase; @@ -440,4 +441,10 @@ interface IAudioService { void setSpatializerParameter(int key, in byte[] value); void getSpatializerParameter(int key, inout byte[] value); + + int getSpatializerOutput(); + + void registerSpatializerOutputCallback(in ISpatializerOutputCallback cb); + + void unregisterSpatializerOutputCallback(in ISpatializerOutputCallback cb); } diff --git a/media/java/android/media/ISpatializerOutputCallback.aidl b/media/java/android/media/ISpatializerOutputCallback.aidl new file mode 100644 index 000000000000..57572a81a366 --- /dev/null +++ b/media/java/android/media/ISpatializerOutputCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * 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. + */ + +package android.media; + +/** + * AIDL for the AudioService to signal Spatializer output changes. + * + * {@hide} + */ +oneway interface ISpatializerOutputCallback { + + void dispatchSpatializerOutputChanged(int output); +} diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java index 844f16912554..e6fff392c264 100644 --- a/media/java/android/media/Spatializer.java +++ b/media/java/android/media/Spatializer.java @@ -18,6 +18,7 @@ package android.media; import android.annotation.CallbackExecutor; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -298,6 +299,24 @@ public class Spatializer { @HeadTrackingModeSet int mode); } + + /** + * @hide + * An interface to be notified of changes to the output stream used by the spatializer + * effect. + * @see #getOutput() + */ + @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) + public interface OnSpatializerOutputChangedListener { + /** + * Called when the id of the output stream of the spatializer effect changed. + * @param spatializer the {@code Spatializer} instance whose output is updated + * @param output the id of the output stream, or 0 when there is no spatializer output + */ + void onSpatializerOutputChanged(@NonNull Spatializer spatializer, + @IntRange(from = 0) int output); + } + /** * @hide * An interface to be notified of updates to the head to soundstage pose, as represented by the @@ -844,6 +863,73 @@ public class Spatializer { } } + /** + * @hide + * Returns the id of the output stream used for the spatializer effect playback + * @return id of the output stream, or 0 if no spatializer playback is active + */ + @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) + @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS) + public @IntRange(from = 0) int getOutput() { + try { + return mAm.getService().getSpatializerOutput(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling getSpatializerOutput", e); + return 0; + } + } + + /** + * @hide + * Sets the listener to receive spatializer effect output updates + * @param executor the {@code Executor} handling the callbacks + * @param listener the listener to register + * @see #clearOnSpatializerOutputChangedListener() + * @see #getOutput() + */ + @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) + @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS) + public void setOnSpatializerOutputChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnSpatializerOutputChangedListener listener) { + Objects.requireNonNull(executor); + Objects.requireNonNull(listener); + synchronized (mOutputListenerLock) { + if (mOutputListener != null) { + throw new IllegalStateException("Trying to overwrite existing listener"); + } + mOutputListener = + new ListenerInfo<OnSpatializerOutputChangedListener>(listener, executor); + mOutputDispatcher = new SpatializerOutputDispatcherStub(); + try { + mAm.getService().registerSpatializerOutputCallback(mOutputDispatcher); + } catch (RemoteException e) { + mOutputListener = null; + mOutputDispatcher = null; + } + } + } + + /** + * @hide + * Clears the listener for spatializer effect output updates + * @see #setOnSpatializerOutputChangedListener(Executor, OnSpatializerOutputChangedListener) + */ + @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) + @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS) + public void clearOnSpatializerOutputChangedListener() { + synchronized (mOutputListenerLock) { + if (mOutputDispatcher == null) { + throw (new IllegalStateException("No listener to clear")); + } + try { + mAm.getService().unregisterSpatializerOutputCallback(mOutputDispatcher); + } catch (RemoteException e) { } + mOutputListener = null; + mOutputDispatcher = null; + } + } + //----------------------------------------------------------------------------- // callback helper definitions @@ -969,4 +1055,35 @@ public class Spatializer { } } } + + //----------------------------------------------------------------------------- + // output callback management and stub + private final Object mOutputListenerLock = new Object(); + /** + * Listener for output updates + */ + @GuardedBy("mOutputListenerLock") + private @Nullable ListenerInfo<OnSpatializerOutputChangedListener> mOutputListener; + @GuardedBy("mOutputListenerLock") + private @Nullable SpatializerOutputDispatcherStub mOutputDispatcher; + + private final class SpatializerOutputDispatcherStub + extends ISpatializerOutputCallback.Stub { + + @Override + public void dispatchSpatializerOutputChanged(int output) { + // make a copy of ref to listener so callback is not executed under lock + final ListenerInfo<OnSpatializerOutputChangedListener> listener; + synchronized (mOutputListenerLock) { + listener = mOutputListener; + } + if (listener == null) { + return; + } + try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { + listener.mExecutor.execute(() -> listener.mListener + .onSpatializerOutputChanged(Spatializer.this, output)); + } + } + } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index e09ba346f8d8..655278657b83 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -96,6 +96,7 @@ import android.media.IRingtonePlayer; import android.media.ISpatializerCallback; import android.media.ISpatializerHeadToSoundStagePoseCallback; import android.media.ISpatializerHeadTrackingModeCallback; +import android.media.ISpatializerOutputCallback; import android.media.IStrategyPreferredDevicesDispatcher; import android.media.IVolumeController; import android.media.MediaMetrics; @@ -8498,6 +8499,26 @@ public class AudioService extends IAudioService.Stub mSpatializerHelper.getEffectParameter(key, value); } + /** @see Spatializer#getOutput */ + public int getSpatializerOutput() { + enforceModifyDefaultAudioEffectsPermission(); + return mSpatializerHelper.getOutput(); + } + + /** @see Spatializer#setOnSpatializerOutputChangedListener */ + public void registerSpatializerOutputCallback(ISpatializerOutputCallback cb) { + enforceModifyDefaultAudioEffectsPermission(); + Objects.requireNonNull(cb); + mSpatializerHelper.registerSpatializerOutputCallback(cb); + } + + /** @see Spatializer#clearOnSpatializerOutputChangedListener */ + public void unregisterSpatializerOutputCallback(ISpatializerOutputCallback cb) { + enforceModifyDefaultAudioEffectsPermission(); + Objects.requireNonNull(cb); + mSpatializerHelper.unregisterSpatializerOutputCallback(cb); + } + /** * post a message to schedule init/release of head tracking sensors * @param init initialization if true, release if false diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index 16c858829913..7cd027c7550f 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -31,6 +31,7 @@ import android.media.ISpatializerCallback; import android.media.ISpatializerHeadToSoundStagePoseCallback; import android.media.ISpatializerHeadTrackingCallback; import android.media.ISpatializerHeadTrackingModeCallback; +import android.media.ISpatializerOutputCallback; import android.media.SpatializationLevel; import android.media.Spatializer; import android.media.SpatializerHeadTrackingMode; @@ -76,6 +77,7 @@ public class SpatializerHelper { private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; private int mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED; private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED; + private int mSpatOutput = 0; private @Nullable ISpatializer mSpat; private @Nullable SpatializerCallback mSpatCallback; private @Nullable SpatializerHeadTrackingCallback mSpatHeadTrackingCallback; @@ -216,6 +218,14 @@ public class SpatializerHelper { public void onOutputChanged(int output) { logd("SpatializerCallback.onOutputChanged output:" + output); + int oldOutput; + synchronized (SpatializerHelper.this) { + oldOutput = mSpatOutput; + mSpatOutput = output; + } + if (oldOutput != output) { + dispatchOutputUpdate(output); + } } }; @@ -786,6 +796,60 @@ public class SpatializerHelper { } //------------------------------------------------------ + // output + + /** @see Spatializer#getOutput */ + synchronized int getOutput() { + switch (mState) { + case STATE_UNINITIALIZED: + case STATE_NOT_SUPPORTED: + throw (new IllegalStateException( + "Can't get output without a spatializer")); + case STATE_ENABLED_UNAVAILABLE: + case STATE_DISABLED_UNAVAILABLE: + case STATE_DISABLED_AVAILABLE: + case STATE_ENABLED_AVAILABLE: + if (mSpat == null) { + throw (new IllegalStateException( + "null Spatializer for getOutput")); + } + break; + } + // mSpat != null + try { + return mSpat.getOutput(); + } catch (RemoteException e) { + Log.e(TAG, "Error in getOutput", e); + return 0; + } + } + + final RemoteCallbackList<ISpatializerOutputCallback> mOutputCallbacks = + new RemoteCallbackList<ISpatializerOutputCallback>(); + + synchronized void registerSpatializerOutputCallback( + @NonNull ISpatializerOutputCallback callback) { + mOutputCallbacks.register(callback); + } + + synchronized void unregisterSpatializerOutputCallback( + @NonNull ISpatializerOutputCallback callback) { + mOutputCallbacks.unregister(callback); + } + + private void dispatchOutputUpdate(int output) { + final int nbCallbacks = mOutputCallbacks.beginBroadcast(); + for (int i = 0; i < nbCallbacks; i++) { + try { + mOutputCallbacks.getBroadcastItem(i).dispatchSpatializerOutputChanged(output); + } catch (RemoteException e) { + Log.e(TAG, "Error in dispatchOutputUpdate", e); + } + } + mOutputCallbacks.finishBroadcast(); + } + + //------------------------------------------------------ // sensors private void initSensors(boolean init) { if (mSensorManager == null) { |