Merge "AudioManager: add API for mode listener" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
ec16222b30
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
28
media/java/android/media/IAudioModeDispatcher.aidl
Normal file
28
media/java/android/media/IAudioModeDispatcher.aidl
Normal 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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user