Spatializer: add API for monitoring output changes
Add privileged API for monitoring audio output stream changes for the output used by the spatializer effect. Bug: 202723925 Test: atest SpatializerTest Change-Id: Ie35ff41e2bec8448997873ca4cf1e229080d2f6a
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
27
media/java/android/media/ISpatializerOutputCallback.aidl
Normal file
27
media/java/android/media/ISpatializerOutputCallback.aidl
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -293,6 +294,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
|
||||
@@ -839,6 +858,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
|
||||
|
||||
@@ -964,4 +1050,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -785,6 +795,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) {
|
||||
|
||||
Reference in New Issue
Block a user