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

Commit 890a5d47 authored by Eric Laurent's avatar Eric Laurent Committed by Automerger Merge Worker
Browse files

AudioManager: Add communication device management APIs am: f39726f2

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1534432

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I6df2d1f67e71f5550edf7c0c490c7d50782f339d
parents ad870982 f39726f2
Loading
Loading
Loading
Loading
+9 −0
Original line number Original line Diff line number Diff line
@@ -19373,14 +19373,17 @@ package android.media {
  public class AudioManager {
  public class AudioManager {
    method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener);
    method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener);
    method public int abandonAudioFocusRequest(@NonNull android.media.AudioFocusRequest);
    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 adjustStreamVolume(int, int, int);
    method public void adjustStreamVolume(int, int, int);
    method public void adjustSuggestedStreamVolume(int, int, int);
    method public void adjustSuggestedStreamVolume(int, int, int);
    method public void adjustVolume(int, int);
    method public void adjustVolume(int, int);
    method public void clearDeviceForCommunication();
    method public void dispatchMediaKeyEvent(android.view.KeyEvent);
    method public void dispatchMediaKeyEvent(android.view.KeyEvent);
    method public int generateAudioSessionId();
    method public int generateAudioSessionId();
    method @NonNull public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
    method @NonNull public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
    method @NonNull public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
    method @NonNull public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
    method public int getAllowedCapturePolicy();
    method public int getAllowedCapturePolicy();
    method @Nullable public android.media.AudioDeviceInfo getDeviceForCommunication();
    method public android.media.AudioDeviceInfo[] getDevices(int);
    method public android.media.AudioDeviceInfo[] getDevices(int);
    method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
    method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
    method public int getMode();
    method public int getMode();
@@ -19415,11 +19418,13 @@ package android.media {
    method @Deprecated public void registerMediaButtonEventReceiver(android.app.PendingIntent);
    method @Deprecated public void registerMediaButtonEventReceiver(android.app.PendingIntent);
    method @Deprecated public void registerRemoteControlClient(android.media.RemoteControlClient);
    method @Deprecated public void registerRemoteControlClient(android.media.RemoteControlClient);
    method @Deprecated public boolean registerRemoteController(android.media.RemoteController);
    method @Deprecated public boolean registerRemoteController(android.media.RemoteController);
    method public void removeOnCommunicationDeviceChangedListener(@NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener);
    method @Deprecated public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
    method @Deprecated public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
    method public int requestAudioFocus(@NonNull android.media.AudioFocusRequest);
    method public int requestAudioFocus(@NonNull android.media.AudioFocusRequest);
    method public void setAllowedCapturePolicy(int);
    method public void setAllowedCapturePolicy(int);
    method @Deprecated public void setBluetoothA2dpOn(boolean);
    method @Deprecated public void setBluetoothA2dpOn(boolean);
    method public void setBluetoothScoOn(boolean);
    method public void setBluetoothScoOn(boolean);
    method public boolean setDeviceForCommunication(@NonNull android.media.AudioDeviceInfo);
    method public void setMicrophoneMute(boolean);
    method public void setMicrophoneMute(boolean);
    method public void setMode(int);
    method public void setMode(int);
    method public void setParameters(String);
    method public void setParameters(String);
@@ -19554,6 +19559,10 @@ package android.media {
    method public void onAudioFocusChange(int);
    method public void onAudioFocusChange(int);
  }
  }
  public static interface AudioManager.OnCommunicationDeviceChangedListener {
    method public void onCommunicationDeviceChanged(@Nullable android.media.AudioDeviceInfo);
  }
  public final class AudioMetadata {
  public final class AudioMetadata {
    method @NonNull public static android.media.AudioMetadataMap createMap();
    method @NonNull public static android.media.AudioMetadataMap createMap();
  }
  }
+1 −0
Original line number Original line Diff line number Diff line
@@ -850,6 +850,7 @@ package android.media {
  }
  }


  public class AudioManager {
  public class AudioManager {
    method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
    method public boolean hasRegisteredDynamicPolicy();
    method public boolean hasRegisteredDynamicPolicy();
  }
  }


+314 −0
Original line number Original line Diff line number Diff line
@@ -6123,6 +6123,29 @@ public class AudioManager {
        return infoListFromPortList(ports, flags);
        return infoListFromPortList(ports, flags);
    }
    }


    /**
     * Returns an {@link AudioDeviceInfo} corresponding to the specified {@link AudioPort} ID.
     * @param portId The audio port ID to look up for.
     * @param flags A set of bitflags specifying the criteria to test.
     * @see #GET_DEVICES_OUTPUTS
     * @see #GET_DEVICES_INPUTS
     * @see #GET_DEVICES_ALL
     * @return An AudioDeviceInfo or null if no device with matching port ID is found.
     * @hide
     */
    public static AudioDeviceInfo getDeviceForPortId(int portId, int flags) {
        if (portId == 0) {
            return null;
        }
        AudioDeviceInfo[] devices = getDevicesStatic(flags);
        for (AudioDeviceInfo device : devices) {
            if (device.getId() == portId) {
                return device;
            }
        }
        return null;
    }

    /**
    /**
     * Registers an {@link AudioDeviceCallback} object to receive notifications of changes
     * Registers an {@link AudioDeviceCallback} object to receive notifications of changes
     * to the set of connected audio devices.
     * to the set of connected audio devices.
@@ -6666,6 +6689,297 @@ public class AudioManager {
        }
        }
    }
    }


    /**
     * Selects the audio device that should be used for communication use cases, for instance voice
     * or video calls. This method can be used by voice or video chat applications to select a
     * different audio device than the one selected by default by the platform.
     * <p>The device selection is expressed as an {@link AudioDeviceInfo}, of role sink
     * ({@link AudioDeviceInfo#isSink()} is <code>true</code>) and of one of the following types:
     * <ul>
     *   <li> {@link AudioDeviceInfo#TYPE_BUILTIN_EARPIECE}
     *   <li> {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}
     *   <li> {@link AudioDeviceInfo#TYPE_WIRED_HEADSET}
     *   <li> {@link AudioDeviceInfo#TYPE_BLUETOOTH_SCO}
     *   <li> {@link AudioDeviceInfo#TYPE_USB_HEADSET}
     *   <li> {@link AudioDeviceInfo#TYPE_BLE_HEADSET}
     * </ul>
     * The selection is active as long as the requesting application lives, until
     * {@link #clearDeviceForCommunication} is called or until the device is disconnected.
     * It is therefore important for applications to clear the request when a call ends or the
     * application is paused.
     * <p>In case of simultaneous requests by multiple applications the priority is given to the
     * application currently controlling the audio mode (see {@link #setMode(int)}). This is the
     * latest application having selected mode {@link #MODE_IN_COMMUNICATION} or mode
     * {@link #MODE_IN_CALL}. Note that <code>MODE_IN_CALL</code> can only be selected by the main
     * telephony application with permission
     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}.
     * <p> If the requested devices is not currently available, the request will be rejected and
     * the method will return false.
     * <p>This API replaces the following deprecated APIs:
     * <ul>
     *   <li> {@link #startBluetoothSco()}
     *   <li> {@link #stopBluetoothSco()}
     *   <li> {@link #setSpeakerphoneOn(boolean)}
     * </ul>
     * <h4>Example</h4>
     * <p>The example below shows how to enable and disable speakerphone mode.
     * <pre class="prettyprint">
     * // Get an AudioManager instance
     * AudioManager audioManager = Context.getSystemService(AudioManager.class);
     * try {
     *     AudioDeviceInfo speakerDevice = null;
     *     AudioDeviceInfo[] devices = audioManager.getDevices(GET_DEVICES_OUTPUTS);
     *     for (AudioDeviceInfo device : devices) {
     *         if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
     *             speakerDevice = device;
     *             break;
     *         }
     *     }
     *     if (speakerDevice != null) {
     *         // Turn speakerphone ON.
     *         boolean result = audioManager.setDeviceForCommunication(speakerDevice);
     *         if (!result) {
     *             // Handle error.
     *         }
     *         // Turn speakerphone OFF.
     *         audioManager.clearDeviceForCommunication();
     *     }
     * } catch (IllegalArgumentException e) {
     *     // Handle exception.
     * }
     * </pre>
     * @param device the requested audio device.
     * @return <code>true</code> if the request was accepted, <code>false</code> otherwise.
     * @throws IllegalArgumentException If an invalid device is specified.
     */
    public boolean setDeviceForCommunication(@NonNull AudioDeviceInfo device) {
        Objects.requireNonNull(device);
        try {
            if (device.getId() == 0) {
                throw new IllegalArgumentException("In valid device: " + device);
            }
            return getService().setDeviceForCommunication(mICallBack, device.getId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Cancels previous communication device selection made with
     * {@link #setDeviceForCommunication(AudioDeviceInfo)}.
     */
    public void clearDeviceForCommunication() {
        try {
            getService().setDeviceForCommunication(mICallBack, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Returns currently selected audio device for communication.
     * <p>This API replaces the following deprecated APIs:
     * <ul>
     *   <li> {@link #isBluetoothScoOn()}
     *   <li> {@link #isSpeakerphoneOn()}
     * </ul>
     * @return an {@link AudioDeviceInfo} indicating which audio device is
     * currently selected or communication use cases or null if default selection
     * is used.
     */
    @Nullable
    public AudioDeviceInfo getDeviceForCommunication() {
        try {
            return getDeviceForPortId(
                    getService().getDeviceForCommunication(), GET_DEVICES_OUTPUTS);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Returns an {@link AudioDeviceInfo} corresponding to a connected device of the type provided.
     * The type must be a valid output type defined in <code>AudioDeviceInfo</code> class,
     * for instance {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}.
     * The method will return null if no device of the provided type is connected.
     * If more than one device of the provided type is connected, an object corresponding to the
     * first device encountered in the enumeration list will be returned.
     * @param deviceType The device device for which an <code>AudioDeviceInfo</code>
     * object is queried.
     * @return An AudioDeviceInfo object or null if no device with the requested type is connected.
     * @throws IllegalArgumentException If an invalid device type is specified.
     */
    @TestApi
    @Nullable
    public static AudioDeviceInfo getDeviceInfoFromType(
            @AudioDeviceInfo.AudioDeviceTypeOut int deviceType) {
        AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_OUTPUTS);
        for (AudioDeviceInfo device : devices) {
            if (device.getType() == deviceType) {
                return device;
            }
        }
        return null;
    }

    /**
     * Listener registered by client to be notified upon communication audio device change.
     * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
     */
    public interface OnCommunicationDeviceChangedListener {
        /**
         * Callback method called upon communication audio device change.
         * @param device the audio device selected for communication use cases
         */
        void onCommunicationDeviceChanged(@Nullable AudioDeviceInfo device);
    }

    /**
     * Adds a listener for being notified of changes to the communication audio device.
     * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
     * @param executor
     * @param listener
     */
    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();
                }
            }
        }
    }

    /**
     * Removes a previously added listener of changes to the communication audio device.
     * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
     * @param listener
     */
    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;
                }
            }
        }
    }

    private final Object mCommDevListenerLock = new Object();
    /**
     * List of listeners for preferred device for strategy and their associated Executor.
     * 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;
        }
    }

    @GuardedBy("mCommDevListenerLock")
    private CommunicationDeviceDispatcherStub mCommDevDispatcherStub;

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

    //---------------------------------------------------------
    //---------------------------------------------------------
    // Inner classes
    // Inner classes
    //--------------------
    //--------------------
+10 −0
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@ import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher;
import android.media.IAudioServerStateDispatcher;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
import android.media.IPlaybackConfigDispatcher;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
import android.media.IRingtonePlayer;
@@ -320,4 +321,13 @@ interface IAudioService {


    oneway void unregisterCapturePresetDevicesRoleDispatcher(
    oneway void unregisterCapturePresetDevicesRoleDispatcher(
            ICapturePresetDevicesRoleDispatcher dispatcher);
            ICapturePresetDevicesRoleDispatcher dispatcher);

    boolean setDeviceForCommunication(IBinder cb, int portId);

    int getDeviceForCommunication();

    void registerCommunicationDeviceDispatcher(ICommunicationDeviceDispatcher dispatcher);

    oneway void unregisterCommunicationDeviceDispatcher(
            ICommunicationDeviceDispatcher dispatcher);
}
}
+28 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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 AudioService to signal audio communication device updates.
 *
 * {@hide}
 */
oneway interface ICommunicationDeviceDispatcher {

    void dispatchCommunicationDeviceChanged(int portId);

}
Loading