summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/system-current.txt7
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl7
-rw-r--r--media/java/android/media/ISpatializerOutputCallback.aidl27
-rw-r--r--media/java/android/media/Spatializer.java117
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java21
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java64
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) {