diff options
Diffstat (limited to 'media')
6 files changed, 112 insertions, 402 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 4aba491c291e..f0890d11c95e 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -65,7 +65,7 @@ import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener; import android.media.audiopolicy.AudioProductStrategy; import android.media.audiopolicy.AudioVolumeGroup; -import android.media.audiopolicy.AudioVolumeGroupChangeHandler; +import android.media.audiopolicy.IAudioVolumeChangeDispatcher; import android.media.projection.MediaProjection; import android.media.session.MediaController; import android.media.session.MediaSession; @@ -128,8 +128,6 @@ public class AudioManager { private static final String TAG = "AudioManager"; private static final boolean DEBUG = false; private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler(); - private static final AudioVolumeGroupChangeHandler sAudioAudioVolumeGroupChangedHandler = - new AudioVolumeGroupChangeHandler(); private static WeakReference<Context> sContext; @@ -8761,9 +8759,13 @@ public class AudioManager { } } + //==================================================================== + // Notification of volume group changes /** + * Callback to receive updates on volume group changes, register using + * {@link AudioManager#registerVolumeGroupCallback(Executor, AudioVolumeCallback)}. + * * @hide - * Callback registered by client to be notified upon volume group change. */ @SystemApi public abstract static class VolumeGroupCallback { @@ -8774,35 +8776,63 @@ public class AudioManager { public void onAudioVolumeGroupChanged(int group, int flags) {} } - /** - * @hide - * Register an audio volume group change listener. - * @param callback the {@link VolumeGroupCallback} to register - */ + /** + * @hide + * Register an audio volume group change listener. + * + * @param executor {@link Executor} to handle the callbacks + * @param callback the callback to receive the audio volume group changes + * @throws SecurityException if the caller doesn't have the required permission. + */ @SystemApi - public void registerVolumeGroupCallback( - @NonNull Executor executor, + public void registerVolumeGroupCallback(@NonNull Executor executor, @NonNull VolumeGroupCallback callback) { - Preconditions.checkNotNull(executor, "executor must not be null"); - Preconditions.checkNotNull(callback, "volume group change cb must not be null"); - sAudioAudioVolumeGroupChangedHandler.init(); - // TODO: make use of executor - sAudioAudioVolumeGroupChangedHandler.registerListener(callback); + mVolumeChangedListenerMgr.addListener(executor, callback, "registerVolumeGroupCallback", + () -> new AudioVolumeChangeDispatcherStub()); } - /** - * @hide - * Unregister an audio volume group change listener. - * @param callback the {@link VolumeGroupCallback} to unregister - */ + /** + * @hide + * Unregister an audio volume group change listener. + * @param callback the {@link VolumeGroupCallback} to unregister + */ @SystemApi - public void unregisterVolumeGroupCallback( - @NonNull VolumeGroupCallback callback) { - Preconditions.checkNotNull(callback, "volume group change cb must not be null"); - sAudioAudioVolumeGroupChangedHandler.unregisterListener(callback); + public void unregisterVolumeGroupCallback(@NonNull VolumeGroupCallback callback) { + mVolumeChangedListenerMgr.removeListener(callback, "unregisterVolumeGroupCallback"); } /** + * Manages the VolumeGroupCallback listeners and the AudioVolumeChangeDispatcherStub + */ + private final CallbackUtil.LazyListenerManager<VolumeGroupCallback> mVolumeChangedListenerMgr = + new CallbackUtil.LazyListenerManager(); + + final class AudioVolumeChangeDispatcherStub extends IAudioVolumeChangeDispatcher.Stub + implements CallbackUtil.DispatcherStub { + + @Override + public void register(boolean register) { + try { + if (register) { + getService().registerAudioVolumeCallback(this); + } else { + getService().unregisterAudioVolumeCallback(this); + } + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + @Override + public void onAudioVolumeGroupChanged(int group, int flags) { + mVolumeChangedListenerMgr.callListeners((listener) -> + listener.onAudioVolumeGroupChanged(group, flags)); + } + } + + //==================================================================== + + /** * Return if an asset contains haptic channels or not. * * @param context the {@link Context} to resolve the uri. diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index ad6f2e52fd97..4906cd3fb1e5 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -2732,4 +2732,25 @@ public class AudioSystem * @hide */ public static native void triggerSystemPropertyUpdate(long handle); + + /** + * Registers the given {@link INativeAudioVolumeGroupCallback} to native audioserver. + * @param callback to register + * @return {@link #SUCCESS} if successfully registered. + * + * @hide + */ + public static native int registerAudioVolumeGroupCallback( + INativeAudioVolumeGroupCallback callback); + + /** + * Unegisters the given {@link INativeAudioVolumeGroupCallback} from native audioserver + * previously registered via {@link #registerAudioVolumeGroupCallback}. + * @param callback to register + * @return {@link #SUCCESS} if successfully registered. + * + * @hide + */ + public static native int unregisterAudioVolumeGroupCallback( + INativeAudioVolumeGroupCallback callback); } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 8aadb418cf5a..b97b943113b6 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -65,6 +65,7 @@ import android.media.audiopolicy.AudioPolicyConfig; import android.media.audiopolicy.AudioProductStrategy; import android.media.audiopolicy.AudioVolumeGroup; import android.media.audiopolicy.IAudioPolicyCallback; +import android.media.audiopolicy.IAudioVolumeChangeDispatcher; import android.media.projection.IMediaProjection; import android.net.Uri; import android.os.PersistableBundle; @@ -446,6 +447,10 @@ interface IAudioService { boolean isAudioServerRunning(); + void registerAudioVolumeCallback(IAudioVolumeChangeDispatcher avc); + + oneway void unregisterAudioVolumeCallback(IAudioVolumeChangeDispatcher avc); + int setUidDeviceAffinity(in IAudioPolicyCallback pcb, in int uid, in int[] deviceTypes, in String[] deviceAddresses); diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java deleted file mode 100644 index 022cfeeb4e43..000000000000 --- a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2018 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.audiopolicy; - -import android.annotation.NonNull; -import android.media.AudioManager; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Message; - -import com.android.internal.util.Preconditions; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; - -/** - * The AudioVolumeGroupChangeHandler handles AudioManager.OnAudioVolumeGroupChangedListener - * callbacks posted from JNI - * - * TODO: Make use of Executor of callbacks. - * @hide - */ -public class AudioVolumeGroupChangeHandler { - private Handler mHandler; - private HandlerThread mHandlerThread; - private final ArrayList<AudioManager.VolumeGroupCallback> mListeners = - new ArrayList<AudioManager.VolumeGroupCallback>(); - - private static final String TAG = "AudioVolumeGroupChangeHandler"; - - private static final int AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED = 1000; - private static final int AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER = 4; - - /** - * Accessed by native methods: JNI Callback context. - */ - @SuppressWarnings("unused") - private long mJniCallback; - - /** - * Initialization - */ - public void init() { - synchronized (this) { - if (mHandler != null) { - return; - } - // create a new thread for our new event handler - mHandlerThread = new HandlerThread(TAG); - mHandlerThread.start(); - - if (mHandlerThread.getLooper() == null) { - mHandler = null; - return; - } - mHandler = new Handler(mHandlerThread.getLooper()) { - @Override - public void handleMessage(Message msg) { - ArrayList<AudioManager.VolumeGroupCallback> listeners; - synchronized (this) { - if (msg.what == AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER) { - listeners = - new ArrayList<AudioManager.VolumeGroupCallback>(); - if (mListeners.contains(msg.obj)) { - listeners.add( - (AudioManager.VolumeGroupCallback) msg.obj); - } - } else { - listeners = (ArrayList<AudioManager.VolumeGroupCallback>) - mListeners.clone(); - } - } - if (listeners.isEmpty()) { - return; - } - - switch (msg.what) { - case AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED: - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).onAudioVolumeGroupChanged((int) msg.arg1, - (int) msg.arg2); - } - break; - - default: - break; - } - } - }; - native_setup(new WeakReference<AudioVolumeGroupChangeHandler>(this)); - } - } - - private native void native_setup(Object moduleThis); - - @Override - protected void finalize() { - native_finalize(); - if (mHandlerThread.isAlive()) { - mHandlerThread.quit(); - } - } - private native void native_finalize(); - - /** - * @param cb the {@link AudioManager.VolumeGroupCallback} to register - */ - public void registerListener(@NonNull AudioManager.VolumeGroupCallback cb) { - Preconditions.checkNotNull(cb, "volume group callback shall not be null"); - synchronized (this) { - mListeners.add(cb); - } - if (mHandler != null) { - Message m = mHandler.obtainMessage( - AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER, 0, 0, cb); - mHandler.sendMessage(m); - } - } - - /** - * @param cb the {@link AudioManager.VolumeGroupCallback} to unregister - */ - public void unregisterListener(@NonNull AudioManager.VolumeGroupCallback cb) { - Preconditions.checkNotNull(cb, "volume group callback shall not be null"); - synchronized (this) { - mListeners.remove(cb); - } - } - - Handler handler() { - return mHandler; - } - - @SuppressWarnings("unused") - private static void postEventFromNative(Object moduleRef, - int what, int arg1, int arg2, Object obj) { - AudioVolumeGroupChangeHandler eventHandler = - (AudioVolumeGroupChangeHandler) ((WeakReference) moduleRef).get(); - if (eventHandler == null) { - return; - } - - if (eventHandler != null) { - Handler handler = eventHandler.handler(); - if (handler != null) { - Message m = handler.obtainMessage(what, arg1, arg2, obj); - // Do not remove previous messages, as we would lose notification of group changes - handler.sendMessage(m); - } - } - } -} diff --git a/media/java/android/media/audiopolicy/IAudioVolumeChangeDispatcher.aidl b/media/java/android/media/audiopolicy/IAudioVolumeChangeDispatcher.aidl new file mode 100644 index 000000000000..e6f9024cfd1e --- /dev/null +++ b/media/java/android/media/audiopolicy/IAudioVolumeChangeDispatcher.aidl @@ -0,0 +1,31 @@ +/* Copyright (C) 2025 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.audiopolicy; + +/** + * AIDL for the AudioService to signal audio volume groups changes + * + * {@hide} + */ +oneway interface IAudioVolumeChangeDispatcher { + + /** + * Called when a volume group has been changed + * @param group id of the volume group that has changed. + * @param flags one or more flags to describe the volume change. + */ + void onAudioVolumeGroupChanged(int group, int flags); +} diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java deleted file mode 100644 index 82394a2eb420..000000000000 --- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2020 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 com.android.audiopolicytest; - -import static androidx.test.core.app.ApplicationProvider.getApplicationContext; - -import static com.android.audiopolicytest.AudioVolumeTestUtil.DEFAULT_ATTRIBUTES; -import static com.android.audiopolicytest.AudioVolumeTestUtil.incrementVolumeIndex; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; - -import android.media.AudioAttributes; -import android.media.AudioManager; -import android.media.audiopolicy.AudioVolumeGroup; -import android.media.audiopolicy.AudioVolumeGroupChangeHandler; -import android.platform.test.annotations.Presubmit; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.List; - -@Presubmit -@RunWith(AndroidJUnit4.class) -public class AudioVolumeGroupChangeHandlerTest { - private static final String TAG = "AudioVolumeGroupChangeHandlerTest"; - - @Rule - public final AudioVolumesTestRule rule = new AudioVolumesTestRule(); - - private AudioManager mAudioManager; - - @Before - public void setUp() { - mAudioManager = getApplicationContext().getSystemService(AudioManager.class); - } - - @Test - public void testRegisterInvalidCallback() { - final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler = - new AudioVolumeGroupChangeHandler(); - - audioAudioVolumeGroupChangedHandler.init(); - - assertThrows(NullPointerException.class, () -> { - AudioManager.VolumeGroupCallback nullCb = null; - audioAudioVolumeGroupChangedHandler.registerListener(nullCb); - }); - } - - @Test - public void testUnregisterInvalidCallback() { - final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler = - new AudioVolumeGroupChangeHandler(); - - audioAudioVolumeGroupChangedHandler.init(); - - final AudioVolumeGroupCallbackHelper cb = new AudioVolumeGroupCallbackHelper(); - audioAudioVolumeGroupChangedHandler.registerListener(cb); - - assertThrows(NullPointerException.class, () -> { - AudioManager.VolumeGroupCallback nullCb = null; - audioAudioVolumeGroupChangedHandler.unregisterListener(nullCb); - }); - audioAudioVolumeGroupChangedHandler.unregisterListener(cb); - } - - @Test - public void testRegisterUnregisterCallback() { - final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler = - new AudioVolumeGroupChangeHandler(); - - audioAudioVolumeGroupChangedHandler.init(); - final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper(); - - // Should not assert, otherwise test will fail - audioAudioVolumeGroupChangedHandler.registerListener(validCb); - - // Should not assert, otherwise test will fail - audioAudioVolumeGroupChangedHandler.unregisterListener(validCb); - } - - @Test - public void testCallbackReceived() { - final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler = - new AudioVolumeGroupChangeHandler(); - - audioAudioVolumeGroupChangedHandler.init(); - - final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper(); - audioAudioVolumeGroupChangedHandler.registerListener(validCb); - - List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups(); - assertTrue(audioVolumeGroups.size() > 0); - - try { - for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) { - int volumeGroupId = audioVolumeGroup.getId(); - - List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes(); - // Set the volume per attributes (if valid) and wait the callback - if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(DEFAULT_ATTRIBUTES)) { - // Some volume groups may not have valid attributes, used for internal - // volume management like patch/rerouting - // so bailing out strategy retrieval from attributes - continue; - } - final AudioAttributes aa = avgAttributes.get(0); - - int index = mAudioManager.getVolumeIndexForAttributes(aa); - int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa); - int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa); - - final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax); - - // Set the receiver to filter only the current group callback - validCb.setExpectedVolumeGroup(volumeGroupId); - mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/); - assertTrue(validCb.waitForExpectedVolumeGroupChanged( - AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS)); - - final int readIndex = mAudioManager.getVolumeIndexForAttributes(aa); - assertEquals(readIndex, indexForAa); - } - } finally { - audioAudioVolumeGroupChangedHandler.unregisterListener(validCb); - } - } - - @Test - public void testMultipleCallbackReceived() { - - final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler = - new AudioVolumeGroupChangeHandler(); - - audioAudioVolumeGroupChangedHandler.init(); - - final int callbackCount = 10; - final List<AudioVolumeGroupCallbackHelper> validCbs = - new ArrayList<AudioVolumeGroupCallbackHelper>(); - for (int i = 0; i < callbackCount; i++) { - validCbs.add(new AudioVolumeGroupCallbackHelper()); - } - for (final AudioVolumeGroupCallbackHelper cb : validCbs) { - audioAudioVolumeGroupChangedHandler.registerListener(cb); - } - - List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups(); - assertTrue(audioVolumeGroups.size() > 0); - - try { - for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) { - int volumeGroupId = audioVolumeGroup.getId(); - - List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes(); - // Set the volume per attributes (if valid) and wait the callback - if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(DEFAULT_ATTRIBUTES)) { - // Some volume groups may not have valid attributes, used for internal - // volume management like patch/rerouting - // so bailing out strategy retrieval from attributes - continue; - } - AudioAttributes aa = avgAttributes.get(0); - - int index = mAudioManager.getVolumeIndexForAttributes(aa); - int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa); - int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa); - - final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax); - - // Set the receiver to filter only the current group callback - for (final AudioVolumeGroupCallbackHelper cb : validCbs) { - cb.setExpectedVolumeGroup(volumeGroupId); - } - mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/); - - for (final AudioVolumeGroupCallbackHelper cb : validCbs) { - assertTrue(cb.waitForExpectedVolumeGroupChanged( - AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS)); - } - int readIndex = mAudioManager.getVolumeIndexForAttributes(aa); - assertEquals(readIndex, indexForAa); - } - } finally { - for (final AudioVolumeGroupCallbackHelper cb : validCbs) { - audioAudioVolumeGroupChangedHandler.unregisterListener(cb); - } - } - } -} |