Merge "AudioManager: add API for mode listener" into sc-dev

This commit is contained in:
Jean-Michel Trivi
2021-04-22 13:53:02 +00:00
committed by Android (Google) Code Review
5 changed files with 233 additions and 0 deletions

View File

@@ -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);
@@ -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);
@@ -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();
}

View File

@@ -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>

View File

@@ -0,0 +1,28 @@
/*
* 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);
}

View File

@@ -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;
@@ -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);
}

View File

@@ -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;
@@ -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;
@@ -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(),
@@ -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,
@@ -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) {
@@ -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;
}
}
}