Loading core/api/current.txt +6 −0 Original line number Diff line number Diff line Loading @@ -20358,6 +20358,7 @@ package android.media { method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener); method public int abandonAudioFocusRequest(@NonNull android.media.AudioFocusRequest); method public void addOnCommunicationDeviceChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener); method public void addOnModeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnModeChangedListener); method public void adjustStreamVolume(int, int, int); method public void adjustSuggestedStreamVolume(int, int, int); method public void adjustVolume(int, int); Loading Loading @@ -20408,6 +20409,7 @@ package android.media { method @Deprecated public void registerRemoteControlClient(android.media.RemoteControlClient); method @Deprecated public boolean registerRemoteController(android.media.RemoteController); method public void removeOnCommunicationDeviceChangedListener(@NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener); method public void removeOnModeChangedListener(@NonNull android.media.AudioManager.OnModeChangedListener); method @Deprecated public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int); method public int requestAudioFocus(@NonNull android.media.AudioFocusRequest); method public void setAllowedCapturePolicy(int); Loading Loading @@ -20563,6 +20565,10 @@ package android.media { method public void onCommunicationDeviceChanged(@Nullable android.media.AudioDeviceInfo); } public static interface AudioManager.OnModeChangedListener { method public void onModeChanged(int); } public final class AudioMetadata { method @NonNull public static android.media.AudioMetadataMap createMap(); } media/java/android/media/AudioManager.java +153 −0 Original line number Diff line number Diff line Loading @@ -2845,6 +2845,159 @@ public class AudioManager { } } /** * Interface definition of a callback that is notified when the audio mode changes */ public interface OnModeChangedListener { /** * Called on the listener to indicate that the audio mode has changed * * @param mode The current audio mode */ void onModeChanged(@AudioMode int mode); } private final Object mModeListenerLock = new Object(); /** * List of listeners for audio mode and their associated Executor. * List is lazy-initialized on first registration */ @GuardedBy("mModeListenerLock") private @Nullable ArrayList<ModeListenerInfo> mModeListeners; @GuardedBy("mModeListenerLock") private ModeDispatcherStub mModeDispatcherStub; 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(); 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; } } 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; } return false; } /** * Adds a listener to be notified of changes to the audio mode. * See {@link #getMode()} * @param executor * @param listener */ 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(); } } } } /** * Removes a previously added listener for changes to audio mode. * See {@link #getMode()} * @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; } } } } /** * Indicates if the platform supports a special call screening and call monitoring mode. * <p> Loading media/java/android/media/IAudioModeDispatcher.aidl 0 → 100644 +28 −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; /** * AIDL for the AudioService to signal audio mode changes. * * {@hide} */ oneway interface IAudioModeDispatcher { void dispatchAudioModeChanged(int mode); } media/java/android/media/IAudioService.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; import android.media.IAudioServerStateDispatcher; import android.media.ICapturePresetDevicesRoleDispatcher; Loading Loading @@ -383,4 +384,8 @@ interface IAudioService { in AudioAttributes aa, in String callingPackageName); long getFadeOutDurationOnFocusLossMillis(in AudioAttributes aa); void registerModeDispatcher(IAudioModeDispatcher dispatcher); oneway void unregisterModeDispatcher(IAudioModeDispatcher dispatcher); } services/core/java/com/android/server/audio/AudioService.java +41 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; import android.media.IAudioServerStateDispatcher; import android.media.IAudioService; Loading Loading @@ -120,6 +121,7 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; Loading Loading @@ -312,6 +314,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_RECORDING_CONFIG_CHANGE = 37; private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38; private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39; private static final int MSG_DISPATCH_AUDIO_MODE = 40; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), Loading Loading @@ -4712,6 +4715,8 @@ public class AudioService extends IAudioService.Stub if (DEBUG_MODE) { Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode); } sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_MODE, SENDMSG_REPLACE, mode, 0, /*obj*/ null, /*delay*/ 0); int previousMode = mMode.getAndSet(mode); // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL mModeLogger.log(new PhoneStateEvent(requesterPackage, requesterPid, Loading Loading @@ -4756,6 +4761,38 @@ public class AudioService extends IAudioService.Stub return mIsCallScreeningModeSupported; } protected void dispatchMode(int mode) { final int nbDispatchers = mModeDispatchers.beginBroadcast(); for (int i = 0; i < nbDispatchers; i++) { try { mModeDispatchers.getBroadcastItem(i).dispatchAudioModeChanged(mode); } catch (RemoteException e) { } } mModeDispatchers.finishBroadcast(); } final RemoteCallbackList<IAudioModeDispatcher> mModeDispatchers = new RemoteCallbackList<IAudioModeDispatcher>(); /** * @see {@link AudioManager#addOnModeChangedListener(Executor, AudioManager.OnModeChangedListener)} * @param dispatcher */ public void registerModeDispatcher( @NonNull IAudioModeDispatcher dispatcher) { mModeDispatchers.register(dispatcher); } /** * @see {@link AudioManager#removeOnModeChangedListener(AudioManager.OnModeChangedListener)} * @param dispatcher */ public void unregisterModeDispatcher( @NonNull IAudioModeDispatcher dispatcher) { mModeDispatchers.unregister(dispatcher); } /** @see AudioManager#setRttEnabled() */ @Override public void setRttEnabled(boolean rttEnabled) { Loading Loading @@ -7522,6 +7559,10 @@ public class AudioService extends IAudioService.Stub case MSG_A2DP_DEV_CONFIG_CHANGE: mDeviceBroker.postBluetoothA2dpDeviceConfigChange((BluetoothDevice) msg.obj); break; case MSG_DISPATCH_AUDIO_MODE: dispatchMode(msg.arg1); break; } } } Loading Loading
core/api/current.txt +6 −0 Original line number Diff line number Diff line Loading @@ -20358,6 +20358,7 @@ package android.media { method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener); method public int abandonAudioFocusRequest(@NonNull android.media.AudioFocusRequest); method public void addOnCommunicationDeviceChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener); method public void addOnModeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnModeChangedListener); method public void adjustStreamVolume(int, int, int); method public void adjustSuggestedStreamVolume(int, int, int); method public void adjustVolume(int, int); Loading Loading @@ -20408,6 +20409,7 @@ package android.media { method @Deprecated public void registerRemoteControlClient(android.media.RemoteControlClient); method @Deprecated public boolean registerRemoteController(android.media.RemoteController); method public void removeOnCommunicationDeviceChangedListener(@NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener); method public void removeOnModeChangedListener(@NonNull android.media.AudioManager.OnModeChangedListener); method @Deprecated public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int); method public int requestAudioFocus(@NonNull android.media.AudioFocusRequest); method public void setAllowedCapturePolicy(int); Loading Loading @@ -20563,6 +20565,10 @@ package android.media { method public void onCommunicationDeviceChanged(@Nullable android.media.AudioDeviceInfo); } public static interface AudioManager.OnModeChangedListener { method public void onModeChanged(int); } public final class AudioMetadata { method @NonNull public static android.media.AudioMetadataMap createMap(); }
media/java/android/media/AudioManager.java +153 −0 Original line number Diff line number Diff line Loading @@ -2845,6 +2845,159 @@ public class AudioManager { } } /** * Interface definition of a callback that is notified when the audio mode changes */ public interface OnModeChangedListener { /** * Called on the listener to indicate that the audio mode has changed * * @param mode The current audio mode */ void onModeChanged(@AudioMode int mode); } private final Object mModeListenerLock = new Object(); /** * List of listeners for audio mode and their associated Executor. * List is lazy-initialized on first registration */ @GuardedBy("mModeListenerLock") private @Nullable ArrayList<ModeListenerInfo> mModeListeners; @GuardedBy("mModeListenerLock") private ModeDispatcherStub mModeDispatcherStub; 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(); 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; } } 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; } return false; } /** * Adds a listener to be notified of changes to the audio mode. * See {@link #getMode()} * @param executor * @param listener */ 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(); } } } } /** * Removes a previously added listener for changes to audio mode. * See {@link #getMode()} * @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; } } } } /** * Indicates if the platform supports a special call screening and call monitoring mode. * <p> Loading
media/java/android/media/IAudioModeDispatcher.aidl 0 → 100644 +28 −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; /** * AIDL for the AudioService to signal audio mode changes. * * {@hide} */ oneway interface IAudioModeDispatcher { void dispatchAudioModeChanged(int mode); }
media/java/android/media/IAudioService.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; import android.media.IAudioServerStateDispatcher; import android.media.ICapturePresetDevicesRoleDispatcher; Loading Loading @@ -383,4 +384,8 @@ interface IAudioService { in AudioAttributes aa, in String callingPackageName); long getFadeOutDurationOnFocusLossMillis(in AudioAttributes aa); void registerModeDispatcher(IAudioModeDispatcher dispatcher); oneway void unregisterModeDispatcher(IAudioModeDispatcher dispatcher); }
services/core/java/com/android/server/audio/AudioService.java +41 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; import android.media.IAudioServerStateDispatcher; import android.media.IAudioService; Loading Loading @@ -120,6 +121,7 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; Loading Loading @@ -312,6 +314,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_RECORDING_CONFIG_CHANGE = 37; private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38; private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39; private static final int MSG_DISPATCH_AUDIO_MODE = 40; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), Loading Loading @@ -4712,6 +4715,8 @@ public class AudioService extends IAudioService.Stub if (DEBUG_MODE) { Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode); } sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_MODE, SENDMSG_REPLACE, mode, 0, /*obj*/ null, /*delay*/ 0); int previousMode = mMode.getAndSet(mode); // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL mModeLogger.log(new PhoneStateEvent(requesterPackage, requesterPid, Loading Loading @@ -4756,6 +4761,38 @@ public class AudioService extends IAudioService.Stub return mIsCallScreeningModeSupported; } protected void dispatchMode(int mode) { final int nbDispatchers = mModeDispatchers.beginBroadcast(); for (int i = 0; i < nbDispatchers; i++) { try { mModeDispatchers.getBroadcastItem(i).dispatchAudioModeChanged(mode); } catch (RemoteException e) { } } mModeDispatchers.finishBroadcast(); } final RemoteCallbackList<IAudioModeDispatcher> mModeDispatchers = new RemoteCallbackList<IAudioModeDispatcher>(); /** * @see {@link AudioManager#addOnModeChangedListener(Executor, AudioManager.OnModeChangedListener)} * @param dispatcher */ public void registerModeDispatcher( @NonNull IAudioModeDispatcher dispatcher) { mModeDispatchers.register(dispatcher); } /** * @see {@link AudioManager#removeOnModeChangedListener(AudioManager.OnModeChangedListener)} * @param dispatcher */ public void unregisterModeDispatcher( @NonNull IAudioModeDispatcher dispatcher) { mModeDispatchers.unregister(dispatcher); } /** @see AudioManager#setRttEnabled() */ @Override public void setRttEnabled(boolean rttEnabled) { Loading Loading @@ -7522,6 +7559,10 @@ public class AudioService extends IAudioService.Stub case MSG_A2DP_DEV_CONFIG_CHANGE: mDeviceBroker.postBluetoothA2dpDeviceConfigChange((BluetoothDevice) msg.obj); break; case MSG_DISPATCH_AUDIO_MODE: dispatchMode(msg.arg1); break; } } } Loading