Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ec16222b authored by Jean-Michel Trivi's avatar Jean-Michel Trivi Committed by Android (Google) Code Review
Browse files

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

parents 4ea22413 08635fe0
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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();
  }
+153 −0
Original line number Diff line number Diff line
@@ -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 −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);

}
+5 −0
Original line number Diff line number Diff line
@@ -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);
}
+41 −0
Original line number Diff line number Diff line
@@ -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;
            }
        }
    }