Loading media/java/android/media/AudioManager.java +59 −200 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.media.AudioAttributes.AttributeSystemUsage; import android.media.CallbackUtil.ListenerInfo; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener; import android.media.audiopolicy.AudioProductStrategy; Loading Loading @@ -2900,77 +2901,31 @@ public class AudioManager { * List is lazy-initialized on first registration */ @GuardedBy("mModeListenerLock") private @Nullable ArrayList<ModeListenerInfo> mModeListeners; private @Nullable ArrayList<ListenerInfo<OnModeChangedListener>> mModeListeners; @GuardedBy("mModeListenerLock") private ModeDispatcherStub mModeDispatcherStub; private final class ModeDispatcherStub extends IAudioModeDispatcher.Stub { private final class ModeDispatcherStub extends IAudioModeDispatcher.Stub { @Override public void dispatchAudioModeChanged(int mode) { // make a shallow copy of listeners so callback is not executed under lock final ArrayList<ModeListenerInfo> modeListeners; synchronized (mModeListenerLock) { if (mModeListeners == null || mModeListeners.size() == 0) { return; } modeListeners = (ArrayList<ModeListenerInfo>) mModeListeners.clone(); } final long ident = Binder.clearCallingIdentity(); public void register(boolean register) { try { for (ModeListenerInfo info : modeListeners) { info.mExecutor.execute(() -> info.mListener.onModeChanged(mode)); } } finally { Binder.restoreCallingIdentity(ident); } } } private static class ModeListenerInfo { final @NonNull OnModeChangedListener mListener; final @NonNull Executor mExecutor; ModeListenerInfo(OnModeChangedListener listener, Executor exe) { mListener = listener; mExecutor = exe; } } @GuardedBy("mModeListenerLock") private boolean hasModeListener(OnModeChangedListener listener) { return getModeListenerInfo(listener) != null; } @GuardedBy("mModeListenerLock") private @Nullable ModeListenerInfo getModeListenerInfo( OnModeChangedListener listener) { if (mModeListeners == null) { return null; } for (ModeListenerInfo info : mModeListeners) { if (info.mListener == listener) { return info; if (register) { getService().registerModeDispatcher(this); } else { getService().unregisterModeDispatcher(this); } } catch (RemoteException e) { e.rethrowFromSystemServer(); } return null; } @GuardedBy("mModeListenerLock") /** * @return true if the listener was removed from the list */ private boolean removeModeListener(OnModeChangedListener listener) { final ModeListenerInfo infoToRemove = getModeListenerInfo(listener); if (infoToRemove != null) { mModeListeners.remove(infoToRemove); return true; @Override @SuppressLint("GuardedBy") // lock applied inside callListeners method public void dispatchAudioModeChanged(int mode) { CallbackUtil.callListeners(mModeListeners, mModeListenerLock, (listener) -> listener.onModeChanged(mode)); } return false; } /** Loading @@ -2982,30 +2937,14 @@ public class AudioManager { public void addOnModeChangedListener( @NonNull @CallbackExecutor Executor executor, @NonNull OnModeChangedListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); synchronized (mModeListenerLock) { if (hasModeListener(listener)) { throw new IllegalArgumentException("attempt to call addOnModeChangedListener() " + "on a previously registered listener"); } // lazy initialization of the list of strategy-preferred device listener if (mModeListeners == null) { mModeListeners = new ArrayList<>(); } final int oldCbCount = mModeListeners.size(); mModeListeners.add(new ModeListenerInfo(listener, executor)); if (oldCbCount == 0) { // register binder for callbacks if (mModeDispatcherStub == null) { mModeDispatcherStub = new ModeDispatcherStub(); } try { getService().registerModeDispatcher(mModeDispatcherStub); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } final Pair<ArrayList<ListenerInfo<OnModeChangedListener>>, ModeDispatcherStub> res = CallbackUtil.addListener("addOnModeChangedListener", executor, listener, mModeListeners, mModeDispatcherStub, () -> new ModeDispatcherStub(), stub -> stub.register(true)); mModeListeners = res.first; mModeDispatcherStub = res.second; } } Loading @@ -3015,23 +2954,13 @@ public class AudioManager { * @param listener */ public void removeOnModeChangedListener(@NonNull OnModeChangedListener listener) { Objects.requireNonNull(listener); synchronized (mModeListenerLock) { if (!removeModeListener(listener)) { throw new IllegalArgumentException("attempt to call removeOnModeChangedListener() " + "on an unregistered listener"); } if (mModeListeners.size() == 0) { // unregister binder for callbacks try { getService().unregisterModeDispatcher(mModeDispatcherStub); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { mModeDispatcherStub = null; mModeListeners = null; } } final Pair<ArrayList<ListenerInfo<OnModeChangedListener>>, ModeDispatcherStub> res = CallbackUtil.removeListener("removeOnModeChangedListener", listener, mModeListeners, mModeDispatcherStub, stub -> stub.register(false)); mModeListeners = res.first; mModeDispatcherStub = res.second; } } Loading Loading @@ -7646,31 +7575,15 @@ public class AudioManager { public void addOnCommunicationDeviceChangedListener( @NonNull @CallbackExecutor Executor executor, @NonNull OnCommunicationDeviceChangedListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); synchronized (mCommDevListenerLock) { if (hasCommDevListener(listener)) { throw new IllegalArgumentException( "attempt to call addOnCommunicationDeviceChangedListener() " + "on a previously registered listener"); } // lazy initialization of the list of strategy-preferred device listener if (mCommDevListeners == null) { mCommDevListeners = new ArrayList<>(); } final int oldCbCount = mCommDevListeners.size(); mCommDevListeners.add(new CommDevListenerInfo(listener, executor)); if (oldCbCount == 0 && mCommDevListeners.size() > 0) { // register binder for callbacks if (mCommDevDispatcherStub == null) { mCommDevDispatcherStub = new CommunicationDeviceDispatcherStub(); } try { getService().registerCommunicationDeviceDispatcher(mCommDevDispatcherStub); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } final Pair<ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>>, CommunicationDeviceDispatcherStub> res = CallbackUtil.addListener("addOnCommunicationDeviceChangedListener", executor, listener, mCommDevListeners, mCommDevDispatcherStub, () -> new CommunicationDeviceDispatcherStub(), stub -> stub.register(true)); mCommDevListeners = res.first; mCommDevDispatcherStub = res.second; } } Loading @@ -7681,25 +7594,14 @@ public class AudioManager { */ public void removeOnCommunicationDeviceChangedListener( @NonNull OnCommunicationDeviceChangedListener listener) { Objects.requireNonNull(listener); synchronized (mCommDevListenerLock) { if (!removeCommDevListener(listener)) { throw new IllegalArgumentException( "attempt to call removeOnCommunicationDeviceChangedListener() " + "on an unregistered listener"); } if (mCommDevListeners.size() == 0) { // unregister binder for callbacks try { getService().unregisterCommunicationDeviceDispatcher( mCommDevDispatcherStub); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { mCommDevDispatcherStub = null; mCommDevListeners = null; } } final Pair<ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>>, CommunicationDeviceDispatcherStub> res = CallbackUtil.removeListener("removeOnCommunicationDeviceChangedListener", listener, mCommDevListeners, mCommDevDispatcherStub, stub -> stub.register(false)); mCommDevListeners = res.first; mCommDevDispatcherStub = res.second; } } Loading @@ -7709,17 +7611,8 @@ public class AudioManager { * List is lazy-initialized on first registration */ @GuardedBy("mCommDevListenerLock") private @Nullable ArrayList<CommDevListenerInfo> mCommDevListeners; private static class CommDevListenerInfo { final @NonNull OnCommunicationDeviceChangedListener mListener; final @NonNull Executor mExecutor; CommDevListenerInfo(OnCommunicationDeviceChangedListener listener, Executor exe) { mListener = listener; mExecutor = exe; } } private @Nullable ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>> mCommDevListeners; @GuardedBy("mCommDevListenerLock") private CommunicationDeviceDispatcherStub mCommDevDispatcherStub; Loading @@ -7727,59 +7620,25 @@ public class AudioManager { private final class CommunicationDeviceDispatcherStub extends ICommunicationDeviceDispatcher.Stub { @Override public void dispatchCommunicationDeviceChanged(int portId) { // make a shallow copy of listeners so callback is not executed under lock final ArrayList<CommDevListenerInfo> commDevListeners; synchronized (mCommDevListenerLock) { if (mCommDevListeners == null || mCommDevListeners.size() == 0) { return; } commDevListeners = (ArrayList<CommDevListenerInfo>) mCommDevListeners.clone(); } AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS); final long ident = Binder.clearCallingIdentity(); public void register(boolean register) { try { for (CommDevListenerInfo info : commDevListeners) { info.mExecutor.execute(() -> info.mListener.onCommunicationDeviceChanged(device)); } } finally { Binder.restoreCallingIdentity(ident); } } } @GuardedBy("mCommDevListenerLock") private @Nullable CommDevListenerInfo getCommDevListenerInfo( OnCommunicationDeviceChangedListener listener) { if (mCommDevListeners == null) { return null; } for (CommDevListenerInfo info : mCommDevListeners) { if (info.mListener == listener) { return info; if (register) { getService().registerCommunicationDeviceDispatcher(this); } else { getService().unregisterCommunicationDeviceDispatcher(this); } } catch (RemoteException e) { e.rethrowFromSystemServer(); } return null; } @GuardedBy("mCommDevListenerLock") private boolean hasCommDevListener(OnCommunicationDeviceChangedListener listener) { return getCommDevListenerInfo(listener) != null; } @GuardedBy("mCommDevListenerLock") /** * @return true if the listener was removed from the list */ private boolean removeCommDevListener(OnCommunicationDeviceChangedListener listener) { final CommDevListenerInfo infoToRemove = getCommDevListenerInfo(listener); if (infoToRemove != null) { mCommDevListeners.remove(infoToRemove); return true; @Override @SuppressLint("GuardedBy") // lock applied inside callListeners method public void dispatchCommunicationDeviceChanged(int portId) { AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS); CallbackUtil.callListeners(mCommDevListeners, mCommDevListenerLock, (listener) -> listener.onCommunicationDeviceChanged(device)); } return false; } //--------------------------------------------------------- Loading media/java/android/media/CallbackUtil.java 0 → 100644 +224 −0 Original line number Diff line number Diff line /* * 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; import android.annotation.NonNull; import android.annotation.Nullable; import android.media.permission.ClearCallingIdentityContext; import android.media.permission.SafeCloseable; import android.util.Log; import android.util.Pair; import java.util.ArrayList; import java.util.Objects; import java.util.concurrent.Executor; /** * @hide * A utility class to implement callback listeners and their management. * This is meant to be used for lazily-initialized listener lists and stubs for event reception, * typically received from server (e.g. AudioService). */ /*package*/ class CallbackUtil { private static final String TAG = "CallbackUtil"; /** * Container class to store a listener and associated Executor * @param <T> the type of the listener */ static class ListenerInfo<T> { final @NonNull T mListener; final @NonNull Executor mExecutor; ListenerInfo(@NonNull T listener, @NonNull Executor exe) { mListener = listener; mExecutor = exe; } } /** * Finds the listener information (listener + Executor) in a given list of listeners * @param listener the listener to find * @param listeners the list of listener informations, can be null if not instantiated yet * @param <T> the type of the listeners * @return null if the listener is not in the given list of listener informations */ static <T> @Nullable ListenerInfo<T> getListenerInfo( @NonNull T listener, @Nullable ArrayList<ListenerInfo<T>> listeners) { if (listeners == null) { return null; } for (ListenerInfo<T> info : listeners) { if (info.mListener == listener) { return info; } } return null; } /** * Returns true if the given listener is present in the list of listener informations * @param listener the listener to find * @param listeners the list of listener informations, can be null if not instantiated yet * @param <T> the type of the listeners * @return true if the listener is in the list */ static <T> boolean hasListener(@NonNull T listener, @Nullable ArrayList<ListenerInfo<T>> listeners) { return getListenerInfo(listener, listeners) != null; } /** * Removes the given listener from the list of listener informations * @param listener the listener to remove * @param listeners the list of listener informations, can be null if not instantiated yet * @param <T> the type of the listeners * @return true if the listener was found and removed from the list, false otherwise */ static <T> boolean removeListener(@NonNull T listener, @Nullable ArrayList<ListenerInfo<T>> listeners) { final ListenerInfo<T> infoToRemove = getListenerInfo(listener, listeners); if (infoToRemove != null) { listeners.remove(infoToRemove); return true; } return false; } /** * Adds a listener and associated Executor in the list of listeners. * This method handles the lazy initialization of both the list of listeners and the stub * used to receive the events that will be forwarded to the listener, see the returned pair * for the updated references. * @param methodName the name of the method calling this, for inclusion in the * string in case of IllegalArgumentException * @param executor the Executor for the listener * @param listener the listener to add * @param listeners the list of listener informations, can be null if not instantiated yet * @param dispatchStub the stub that receives the events to be forwarded to the listeners, * can be null if not instantiated yet * @param newStub the function to create a new stub if needed * @param registerStub the function for the stub registration if needed * @param <T> the type of the listener interface * @param <S> the type of the event receiver stub * @return a pair of the listener list and the event receiver stub which may have been * initialized if needed (e.g. on the first ever addition of a listener) */ static <T, S> Pair<ArrayList<ListenerInfo<T>>, S> addListener(String methodName, @NonNull Executor executor, @NonNull T listener, @Nullable ArrayList<ListenerInfo<T>> listeners, @Nullable S dispatchStub, @NonNull java.util.function.Supplier<S> newStub, @NonNull java.util.function.Consumer<S> registerStub) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); if (hasListener(listener, listeners)) { throw new IllegalArgumentException("attempt to call " + methodName + "on a previously registered listener"); } // lazy initialization of the list of strategy-preferred device listener if (listeners == null) { listeners = new ArrayList<>(); } if (listeners.size() == 0) { // register binder for callbacks if (dispatchStub == null) { try { dispatchStub = newStub.get(); } catch (Exception e) { Log.e(TAG, "Exception while creating stub in " + methodName, e); return new Pair<>(null, null); } } registerStub.accept(dispatchStub); } listeners.add(new ListenerInfo<T>(listener, executor)); return new Pair(listeners, dispatchStub); } /** * Removes a listener from the list of listeners. * This method handles the freeing of both the list of listeners and the stub * used to receive the events that will be forwarded to the listener,see the returned pair * for the updated references. * @param methodName the name of the method calling this, for inclusion in the * string in case of IllegalArgumentException * @param listener the listener to remove * @param listeners the list of listener informations, can be null if not instantiated yet * @param dispatchStub the stub that receives the events to be forwarded to the listeners, * can be null if not instantiated yet * @param unregisterStub the function to unregister the stub if needed * @param <T> the type of the listener interface * @param <S> the type of the event receiver stub * @return a pair of the listener list and the event receiver stub which may have been * changed if needed (e.g. on the removal of the last listener) */ static <T, S> Pair<ArrayList<ListenerInfo<T>>, S> removeListener(String methodName, @NonNull T listener, @Nullable ArrayList<ListenerInfo<T>> listeners, @Nullable S dispatchStub, @NonNull java.util.function.Consumer<S> unregisterStub) { Objects.requireNonNull(listener); if (!removeListener(listener, listeners)) { throw new IllegalArgumentException("attempt to call " + methodName + "on an unregistered listener"); } if (listeners.size() == 0) { unregisterStub.accept(dispatchStub); return new Pair<>(null, null); } else { return new Pair<>(listeners, dispatchStub); } } interface CallbackMethod<T> { void callbackMethod(T listener); } /** * Exercise the callback of the listeners * @param listeners the list of listeners * @param listenerLock the lock guarding the list of listeners * @param callback the function to call for each listener * @param <T> the type of the listener interface */ static <T> void callListeners( @Nullable ArrayList<ListenerInfo<T>> listeners, @NonNull Object listenerLock, @NonNull CallbackMethod<T> callback) { Objects.requireNonNull(listenerLock); // make a shallow copy of listeners so callback is not executed under lock final ArrayList<ListenerInfo<T>> listenersShallowCopy; synchronized (listenerLock) { if (listeners == null || listeners.size() == 0) { return; } listenersShallowCopy = (ArrayList<ListenerInfo<T>>) listeners.clone(); } try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { for (ListenerInfo<T> info : listenersShallowCopy) { info.mExecutor.execute(() -> callback.callbackMethod(info.mListener)); } } } } media/java/android/media/Spatializer.java +72 −227 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
media/java/android/media/AudioManager.java +59 −200 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.media.AudioAttributes.AttributeSystemUsage; import android.media.CallbackUtil.ListenerInfo; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener; import android.media.audiopolicy.AudioProductStrategy; Loading Loading @@ -2900,77 +2901,31 @@ public class AudioManager { * List is lazy-initialized on first registration */ @GuardedBy("mModeListenerLock") private @Nullable ArrayList<ModeListenerInfo> mModeListeners; private @Nullable ArrayList<ListenerInfo<OnModeChangedListener>> mModeListeners; @GuardedBy("mModeListenerLock") private ModeDispatcherStub mModeDispatcherStub; private final class ModeDispatcherStub extends IAudioModeDispatcher.Stub { private final class ModeDispatcherStub extends IAudioModeDispatcher.Stub { @Override public void dispatchAudioModeChanged(int mode) { // make a shallow copy of listeners so callback is not executed under lock final ArrayList<ModeListenerInfo> modeListeners; synchronized (mModeListenerLock) { if (mModeListeners == null || mModeListeners.size() == 0) { return; } modeListeners = (ArrayList<ModeListenerInfo>) mModeListeners.clone(); } final long ident = Binder.clearCallingIdentity(); public void register(boolean register) { try { for (ModeListenerInfo info : modeListeners) { info.mExecutor.execute(() -> info.mListener.onModeChanged(mode)); } } finally { Binder.restoreCallingIdentity(ident); } } } private static class ModeListenerInfo { final @NonNull OnModeChangedListener mListener; final @NonNull Executor mExecutor; ModeListenerInfo(OnModeChangedListener listener, Executor exe) { mListener = listener; mExecutor = exe; } } @GuardedBy("mModeListenerLock") private boolean hasModeListener(OnModeChangedListener listener) { return getModeListenerInfo(listener) != null; } @GuardedBy("mModeListenerLock") private @Nullable ModeListenerInfo getModeListenerInfo( OnModeChangedListener listener) { if (mModeListeners == null) { return null; } for (ModeListenerInfo info : mModeListeners) { if (info.mListener == listener) { return info; if (register) { getService().registerModeDispatcher(this); } else { getService().unregisterModeDispatcher(this); } } catch (RemoteException e) { e.rethrowFromSystemServer(); } return null; } @GuardedBy("mModeListenerLock") /** * @return true if the listener was removed from the list */ private boolean removeModeListener(OnModeChangedListener listener) { final ModeListenerInfo infoToRemove = getModeListenerInfo(listener); if (infoToRemove != null) { mModeListeners.remove(infoToRemove); return true; @Override @SuppressLint("GuardedBy") // lock applied inside callListeners method public void dispatchAudioModeChanged(int mode) { CallbackUtil.callListeners(mModeListeners, mModeListenerLock, (listener) -> listener.onModeChanged(mode)); } return false; } /** Loading @@ -2982,30 +2937,14 @@ public class AudioManager { public void addOnModeChangedListener( @NonNull @CallbackExecutor Executor executor, @NonNull OnModeChangedListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); synchronized (mModeListenerLock) { if (hasModeListener(listener)) { throw new IllegalArgumentException("attempt to call addOnModeChangedListener() " + "on a previously registered listener"); } // lazy initialization of the list of strategy-preferred device listener if (mModeListeners == null) { mModeListeners = new ArrayList<>(); } final int oldCbCount = mModeListeners.size(); mModeListeners.add(new ModeListenerInfo(listener, executor)); if (oldCbCount == 0) { // register binder for callbacks if (mModeDispatcherStub == null) { mModeDispatcherStub = new ModeDispatcherStub(); } try { getService().registerModeDispatcher(mModeDispatcherStub); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } final Pair<ArrayList<ListenerInfo<OnModeChangedListener>>, ModeDispatcherStub> res = CallbackUtil.addListener("addOnModeChangedListener", executor, listener, mModeListeners, mModeDispatcherStub, () -> new ModeDispatcherStub(), stub -> stub.register(true)); mModeListeners = res.first; mModeDispatcherStub = res.second; } } Loading @@ -3015,23 +2954,13 @@ public class AudioManager { * @param listener */ public void removeOnModeChangedListener(@NonNull OnModeChangedListener listener) { Objects.requireNonNull(listener); synchronized (mModeListenerLock) { if (!removeModeListener(listener)) { throw new IllegalArgumentException("attempt to call removeOnModeChangedListener() " + "on an unregistered listener"); } if (mModeListeners.size() == 0) { // unregister binder for callbacks try { getService().unregisterModeDispatcher(mModeDispatcherStub); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { mModeDispatcherStub = null; mModeListeners = null; } } final Pair<ArrayList<ListenerInfo<OnModeChangedListener>>, ModeDispatcherStub> res = CallbackUtil.removeListener("removeOnModeChangedListener", listener, mModeListeners, mModeDispatcherStub, stub -> stub.register(false)); mModeListeners = res.first; mModeDispatcherStub = res.second; } } Loading Loading @@ -7646,31 +7575,15 @@ public class AudioManager { public void addOnCommunicationDeviceChangedListener( @NonNull @CallbackExecutor Executor executor, @NonNull OnCommunicationDeviceChangedListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); synchronized (mCommDevListenerLock) { if (hasCommDevListener(listener)) { throw new IllegalArgumentException( "attempt to call addOnCommunicationDeviceChangedListener() " + "on a previously registered listener"); } // lazy initialization of the list of strategy-preferred device listener if (mCommDevListeners == null) { mCommDevListeners = new ArrayList<>(); } final int oldCbCount = mCommDevListeners.size(); mCommDevListeners.add(new CommDevListenerInfo(listener, executor)); if (oldCbCount == 0 && mCommDevListeners.size() > 0) { // register binder for callbacks if (mCommDevDispatcherStub == null) { mCommDevDispatcherStub = new CommunicationDeviceDispatcherStub(); } try { getService().registerCommunicationDeviceDispatcher(mCommDevDispatcherStub); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } final Pair<ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>>, CommunicationDeviceDispatcherStub> res = CallbackUtil.addListener("addOnCommunicationDeviceChangedListener", executor, listener, mCommDevListeners, mCommDevDispatcherStub, () -> new CommunicationDeviceDispatcherStub(), stub -> stub.register(true)); mCommDevListeners = res.first; mCommDevDispatcherStub = res.second; } } Loading @@ -7681,25 +7594,14 @@ public class AudioManager { */ public void removeOnCommunicationDeviceChangedListener( @NonNull OnCommunicationDeviceChangedListener listener) { Objects.requireNonNull(listener); synchronized (mCommDevListenerLock) { if (!removeCommDevListener(listener)) { throw new IllegalArgumentException( "attempt to call removeOnCommunicationDeviceChangedListener() " + "on an unregistered listener"); } if (mCommDevListeners.size() == 0) { // unregister binder for callbacks try { getService().unregisterCommunicationDeviceDispatcher( mCommDevDispatcherStub); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { mCommDevDispatcherStub = null; mCommDevListeners = null; } } final Pair<ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>>, CommunicationDeviceDispatcherStub> res = CallbackUtil.removeListener("removeOnCommunicationDeviceChangedListener", listener, mCommDevListeners, mCommDevDispatcherStub, stub -> stub.register(false)); mCommDevListeners = res.first; mCommDevDispatcherStub = res.second; } } Loading @@ -7709,17 +7611,8 @@ public class AudioManager { * List is lazy-initialized on first registration */ @GuardedBy("mCommDevListenerLock") private @Nullable ArrayList<CommDevListenerInfo> mCommDevListeners; private static class CommDevListenerInfo { final @NonNull OnCommunicationDeviceChangedListener mListener; final @NonNull Executor mExecutor; CommDevListenerInfo(OnCommunicationDeviceChangedListener listener, Executor exe) { mListener = listener; mExecutor = exe; } } private @Nullable ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>> mCommDevListeners; @GuardedBy("mCommDevListenerLock") private CommunicationDeviceDispatcherStub mCommDevDispatcherStub; Loading @@ -7727,59 +7620,25 @@ public class AudioManager { private final class CommunicationDeviceDispatcherStub extends ICommunicationDeviceDispatcher.Stub { @Override public void dispatchCommunicationDeviceChanged(int portId) { // make a shallow copy of listeners so callback is not executed under lock final ArrayList<CommDevListenerInfo> commDevListeners; synchronized (mCommDevListenerLock) { if (mCommDevListeners == null || mCommDevListeners.size() == 0) { return; } commDevListeners = (ArrayList<CommDevListenerInfo>) mCommDevListeners.clone(); } AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS); final long ident = Binder.clearCallingIdentity(); public void register(boolean register) { try { for (CommDevListenerInfo info : commDevListeners) { info.mExecutor.execute(() -> info.mListener.onCommunicationDeviceChanged(device)); } } finally { Binder.restoreCallingIdentity(ident); } } } @GuardedBy("mCommDevListenerLock") private @Nullable CommDevListenerInfo getCommDevListenerInfo( OnCommunicationDeviceChangedListener listener) { if (mCommDevListeners == null) { return null; } for (CommDevListenerInfo info : mCommDevListeners) { if (info.mListener == listener) { return info; if (register) { getService().registerCommunicationDeviceDispatcher(this); } else { getService().unregisterCommunicationDeviceDispatcher(this); } } catch (RemoteException e) { e.rethrowFromSystemServer(); } return null; } @GuardedBy("mCommDevListenerLock") private boolean hasCommDevListener(OnCommunicationDeviceChangedListener listener) { return getCommDevListenerInfo(listener) != null; } @GuardedBy("mCommDevListenerLock") /** * @return true if the listener was removed from the list */ private boolean removeCommDevListener(OnCommunicationDeviceChangedListener listener) { final CommDevListenerInfo infoToRemove = getCommDevListenerInfo(listener); if (infoToRemove != null) { mCommDevListeners.remove(infoToRemove); return true; @Override @SuppressLint("GuardedBy") // lock applied inside callListeners method public void dispatchCommunicationDeviceChanged(int portId) { AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS); CallbackUtil.callListeners(mCommDevListeners, mCommDevListenerLock, (listener) -> listener.onCommunicationDeviceChanged(device)); } return false; } //--------------------------------------------------------- Loading
media/java/android/media/CallbackUtil.java 0 → 100644 +224 −0 Original line number Diff line number Diff line /* * 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; import android.annotation.NonNull; import android.annotation.Nullable; import android.media.permission.ClearCallingIdentityContext; import android.media.permission.SafeCloseable; import android.util.Log; import android.util.Pair; import java.util.ArrayList; import java.util.Objects; import java.util.concurrent.Executor; /** * @hide * A utility class to implement callback listeners and their management. * This is meant to be used for lazily-initialized listener lists and stubs for event reception, * typically received from server (e.g. AudioService). */ /*package*/ class CallbackUtil { private static final String TAG = "CallbackUtil"; /** * Container class to store a listener and associated Executor * @param <T> the type of the listener */ static class ListenerInfo<T> { final @NonNull T mListener; final @NonNull Executor mExecutor; ListenerInfo(@NonNull T listener, @NonNull Executor exe) { mListener = listener; mExecutor = exe; } } /** * Finds the listener information (listener + Executor) in a given list of listeners * @param listener the listener to find * @param listeners the list of listener informations, can be null if not instantiated yet * @param <T> the type of the listeners * @return null if the listener is not in the given list of listener informations */ static <T> @Nullable ListenerInfo<T> getListenerInfo( @NonNull T listener, @Nullable ArrayList<ListenerInfo<T>> listeners) { if (listeners == null) { return null; } for (ListenerInfo<T> info : listeners) { if (info.mListener == listener) { return info; } } return null; } /** * Returns true if the given listener is present in the list of listener informations * @param listener the listener to find * @param listeners the list of listener informations, can be null if not instantiated yet * @param <T> the type of the listeners * @return true if the listener is in the list */ static <T> boolean hasListener(@NonNull T listener, @Nullable ArrayList<ListenerInfo<T>> listeners) { return getListenerInfo(listener, listeners) != null; } /** * Removes the given listener from the list of listener informations * @param listener the listener to remove * @param listeners the list of listener informations, can be null if not instantiated yet * @param <T> the type of the listeners * @return true if the listener was found and removed from the list, false otherwise */ static <T> boolean removeListener(@NonNull T listener, @Nullable ArrayList<ListenerInfo<T>> listeners) { final ListenerInfo<T> infoToRemove = getListenerInfo(listener, listeners); if (infoToRemove != null) { listeners.remove(infoToRemove); return true; } return false; } /** * Adds a listener and associated Executor in the list of listeners. * This method handles the lazy initialization of both the list of listeners and the stub * used to receive the events that will be forwarded to the listener, see the returned pair * for the updated references. * @param methodName the name of the method calling this, for inclusion in the * string in case of IllegalArgumentException * @param executor the Executor for the listener * @param listener the listener to add * @param listeners the list of listener informations, can be null if not instantiated yet * @param dispatchStub the stub that receives the events to be forwarded to the listeners, * can be null if not instantiated yet * @param newStub the function to create a new stub if needed * @param registerStub the function for the stub registration if needed * @param <T> the type of the listener interface * @param <S> the type of the event receiver stub * @return a pair of the listener list and the event receiver stub which may have been * initialized if needed (e.g. on the first ever addition of a listener) */ static <T, S> Pair<ArrayList<ListenerInfo<T>>, S> addListener(String methodName, @NonNull Executor executor, @NonNull T listener, @Nullable ArrayList<ListenerInfo<T>> listeners, @Nullable S dispatchStub, @NonNull java.util.function.Supplier<S> newStub, @NonNull java.util.function.Consumer<S> registerStub) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); if (hasListener(listener, listeners)) { throw new IllegalArgumentException("attempt to call " + methodName + "on a previously registered listener"); } // lazy initialization of the list of strategy-preferred device listener if (listeners == null) { listeners = new ArrayList<>(); } if (listeners.size() == 0) { // register binder for callbacks if (dispatchStub == null) { try { dispatchStub = newStub.get(); } catch (Exception e) { Log.e(TAG, "Exception while creating stub in " + methodName, e); return new Pair<>(null, null); } } registerStub.accept(dispatchStub); } listeners.add(new ListenerInfo<T>(listener, executor)); return new Pair(listeners, dispatchStub); } /** * Removes a listener from the list of listeners. * This method handles the freeing of both the list of listeners and the stub * used to receive the events that will be forwarded to the listener,see the returned pair * for the updated references. * @param methodName the name of the method calling this, for inclusion in the * string in case of IllegalArgumentException * @param listener the listener to remove * @param listeners the list of listener informations, can be null if not instantiated yet * @param dispatchStub the stub that receives the events to be forwarded to the listeners, * can be null if not instantiated yet * @param unregisterStub the function to unregister the stub if needed * @param <T> the type of the listener interface * @param <S> the type of the event receiver stub * @return a pair of the listener list and the event receiver stub which may have been * changed if needed (e.g. on the removal of the last listener) */ static <T, S> Pair<ArrayList<ListenerInfo<T>>, S> removeListener(String methodName, @NonNull T listener, @Nullable ArrayList<ListenerInfo<T>> listeners, @Nullable S dispatchStub, @NonNull java.util.function.Consumer<S> unregisterStub) { Objects.requireNonNull(listener); if (!removeListener(listener, listeners)) { throw new IllegalArgumentException("attempt to call " + methodName + "on an unregistered listener"); } if (listeners.size() == 0) { unregisterStub.accept(dispatchStub); return new Pair<>(null, null); } else { return new Pair<>(listeners, dispatchStub); } } interface CallbackMethod<T> { void callbackMethod(T listener); } /** * Exercise the callback of the listeners * @param listeners the list of listeners * @param listenerLock the lock guarding the list of listeners * @param callback the function to call for each listener * @param <T> the type of the listener interface */ static <T> void callListeners( @Nullable ArrayList<ListenerInfo<T>> listeners, @NonNull Object listenerLock, @NonNull CallbackMethod<T> callback) { Objects.requireNonNull(listenerLock); // make a shallow copy of listeners so callback is not executed under lock final ArrayList<ListenerInfo<T>> listenersShallowCopy; synchronized (listenerLock) { if (listeners == null || listeners.size() == 0) { return; } listenersShallowCopy = (ArrayList<ListenerInfo<T>>) listeners.clone(); } try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { for (ListenerInfo<T> info : listenersShallowCopy) { info.mExecutor.execute(() -> callback.callbackMethod(info.mListener)); } } } }
media/java/android/media/Spatializer.java +72 −227 File changed.Preview size limit exceeded, changes collapsed. Show changes