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

Commit b5565599 authored by Robert Wu's avatar Robert Wu
Browse files

Change Java APIs to use multiple routed devices

After you open an audio stream, you can call getRoutedDevice() to
get which output device is actually used.

However, if you play an ALARM with a headset attached, audio comes
out of both the speaker and the headset. This is not properly reflected
with our current APIs.

This CL makes changes to pass multiple devices to our APIs.

Bug: 367816690
Test: Alarm with and without USB headset
Flag: android.media.audio.routed_device_ids
Change-Id: I245e47449d64fee2dccbc750f42bfb4ac8d615c8
parent 53357ba9
Loading
Loading
Loading
Loading
+17 −9
Original line number Diff line number Diff line
@@ -584,14 +584,23 @@ static jboolean android_media_AudioRecord_setInputDevice(
    return lpRecorder->setInputDevice(device_id) == NO_ERROR;
}

static jint android_media_AudioRecord_getRoutedDeviceId(
                JNIEnv *env,  jobject thiz) {

static jintArray android_media_AudioRecord_getRoutedDeviceIds(JNIEnv *env, jobject thiz) {
    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    if (lpRecorder == 0) {
        return 0;
    if (lpRecorder == NULL) {
        return NULL;
    }
    DeviceIdVector deviceIds = lpRecorder->getRoutedDeviceIds();
    jintArray result;
    result = env->NewIntArray(deviceIds.size());
    if (result == NULL) {
        return NULL;
    }
    jint *values = env->GetIntArrayElements(result, 0);
    for (unsigned int i = 0; i < deviceIds.size(); i++) {
        values[i++] = static_cast<jint>(deviceIds[i]);
    }
    return (jint)lpRecorder->getRoutedDeviceId();
    env->ReleaseIntArrayElements(result, values, 0);
    return result;
}

// Enable and Disable Callback methods are synchronized on the Java side
@@ -821,8 +830,7 @@ static const JNINativeMethod gMethods[] = {
        // name,               signature,  funcPtr
        {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
        {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
        {"native_setup",
         "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILandroid/os/Parcel;JII)I",
        {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILandroid/os/Parcel;JII)I",
         (void *)android_media_AudioRecord_setup},
        {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
        {"native_release", "()V", (void *)android_media_AudioRecord_release},
@@ -846,7 +854,7 @@ static const JNINativeMethod gMethods[] = {
        {"native_getMetrics", "()Landroid/os/PersistableBundle;",
         (void *)android_media_AudioRecord_native_getMetrics},
        {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
        {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
        {"native_getRoutedDeviceIds", "()[I", (void *)android_media_AudioRecord_getRoutedDeviceIds},
        {"native_enableDeviceCallback", "()V",
         (void *)android_media_AudioRecord_enableDeviceCallback},
        {"native_disableDeviceCallback", "()V",
+15 −7
Original line number Diff line number Diff line
@@ -1190,15 +1190,23 @@ static jboolean android_media_AudioTrack_setOutputDevice(
    }
    return lpTrack->setOutputDevice(device_id) == NO_ERROR;
}

static jint android_media_AudioTrack_getRoutedDeviceId(
                JNIEnv *env,  jobject thiz) {

static jintArray android_media_AudioTrack_getRoutedDeviceIds(JNIEnv *env, jobject thiz) {
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    if (lpTrack == NULL) {
        return 0;
        return NULL;
    }
    DeviceIdVector deviceIds = lpTrack->getRoutedDeviceIds();
    jintArray result;
    result = env->NewIntArray(deviceIds.size());
    if (result == NULL) {
        return NULL;
    }
    jint *values = env->GetIntArrayElements(result, 0);
    for (unsigned int i = 0; i < deviceIds.size(); i++) {
        values[i++] = static_cast<jint>(deviceIds[i]);
    }
    return (jint)lpTrack->getRoutedDeviceId();
    env->ReleaseIntArrayElements(result, values, 0);
    return result;
}

static void android_media_AudioTrack_enableDeviceCallback(
@@ -1503,7 +1511,7 @@ static const JNINativeMethod gMethods[] = {
         (void *)android_media_AudioTrack_setAuxEffectSendLevel},
        {"native_attachAuxEffect", "(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
        {"native_setOutputDevice", "(I)Z", (void *)android_media_AudioTrack_setOutputDevice},
        {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
        {"native_getRoutedDeviceIds", "()[I", (void *)android_media_AudioTrack_getRoutedDeviceIds},
        {"native_enableDeviceCallback", "()V",
         (void *)android_media_AudioTrack_enableDeviceCallback},
        {"native_disableDeviceCallback", "()V",
+7 −8
Original line number Diff line number Diff line
@@ -61,21 +61,20 @@ JNIDeviceCallback::~JNIDeviceCallback()
}

void JNIDeviceCallback::onAudioDeviceUpdate(audio_io_handle_t audioIo,
                                            audio_port_handle_t deviceId)
{
                                            const DeviceIdVector& deviceIds) {
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (env == NULL) {
        return;
    }

    ALOGV("%s audioIo %d deviceId %d", __FUNCTION__, audioIo, deviceId);
    env->CallStaticVoidMethod(mClass,
                              mPostEventFromNative,
                              mObject,
                              AUDIO_NATIVE_EVENT_ROUTING_CHANGE, deviceId, 0, NULL);
    ALOGV("%s audioIo %d deviceIds %s", __FUNCTION__, audioIo, toString(deviceIds).c_str());
    // Java should query the new device ids once it gets the event.
    // TODO(b/378505346): Pass the deviceIds to Java to avoid race conditions.
    env->CallStaticVoidMethod(mClass, mPostEventFromNative, mObject,
                              AUDIO_NATIVE_EVENT_ROUTING_CHANGE, 0 /*arg1*/, 0 /*arg2*/,
                              NULL /*obj*/);
    if (env->ExceptionCheck()) {
        ALOGW("An exception occurred while notifying an event.");
        env->ExceptionClear();
    }
}
+1 −2
Original line number Diff line number Diff line
@@ -31,8 +31,7 @@ public:
    JNIDeviceCallback(JNIEnv* env, jobject thiz, jobject weak_thiz, jmethodID postEventFromNative);
    ~JNIDeviceCallback();

    virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
                                     audio_port_handle_t deviceId);
    virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo, const DeviceIdVector& deviceIds);

private:
    void sendEvent(int event);
+41 −26
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

@@ -59,6 +60,8 @@ public final class AudioPlaybackConfiguration implements Parcelable {
    public static final int PLAYER_UPID_INVALID = -1;
    /** @hide */
    public static final int PLAYER_DEVICEID_INVALID = 0;
    /** @hide */
    public static final int[] PLAYER_DEVICEIDS_INVALID = new int[0];

    // information about the implementation
    /**
@@ -335,7 +338,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
    private final Object mUpdateablePropLock = new Object();

    @GuardedBy("mUpdateablePropLock")
    private int mDeviceId;
    private @NonNull int[] mDeviceIds = AudioPlaybackConfiguration.PLAYER_DEVICEIDS_INVALID;
    @GuardedBy("mUpdateablePropLock")
    private int mSessionId;
    @GuardedBy("mUpdateablePropLock")
@@ -364,7 +367,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
        mClientUid = uid;
        mClientPid = pid;
        mMutedState = 0;
        mDeviceId = PLAYER_DEVICEID_INVALID;
        mDeviceIds = AudioPlaybackConfiguration.PLAYER_DEVICEIDS_INVALID;
        mPlayerState = PLAYER_STATE_IDLE;
        mPlayerAttr = pic.mAttributes;
        if ((sPlayerDeathMonitor != null) && (pic.mIPlayer != null)) {
@@ -388,10 +391,11 @@ public final class AudioPlaybackConfiguration implements Parcelable {
    }

    // sets the fields that are updateable and require synchronization
    private void setUpdateableFields(int deviceId, int sessionId, int mutedState, FormatInfo format)
    private void setUpdateableFields(int[] deviceIds, int sessionId, int mutedState,
            FormatInfo format)
    {
        synchronized (mUpdateablePropLock) {
            mDeviceId = deviceId;
            mDeviceIds = deviceIds;
            mSessionId = sessionId;
            mMutedState = mutedState;
            mFormatInfo = format;
@@ -427,7 +431,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
        anonymCopy.mClientPid = PLAYER_UPID_INVALID;
        anonymCopy.mIPlayerShell = null;
        anonymCopy.setUpdateableFields(
                /*deviceId*/ PLAYER_DEVICEID_INVALID,
                /*deviceIds*/ new int[0],
                /*sessionId*/ AudioSystem.AUDIO_SESSION_ALLOCATE,
                /*mutedState*/ 0,
                FormatInfo.DEFAULT);
@@ -471,14 +475,14 @@ public final class AudioPlaybackConfiguration implements Parcelable {
    @Deprecated
    @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
    public @Nullable AudioDeviceInfo getAudioDeviceInfo() {
        final int deviceId;
        final int[] deviceIds;
        synchronized (mUpdateablePropLock) {
            deviceId = mDeviceId;
            deviceIds = mDeviceIds;
        }
        if (deviceId == PLAYER_DEVICEID_INVALID) {
        if (deviceIds.length == 0) {
            return null;
        }
        return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_OUTPUTS);
        return AudioManager.getDeviceForPortId(deviceIds[0], AudioManager.GET_DEVICES_OUTPUTS);
    }

    /**
@@ -491,10 +495,18 @@ public final class AudioPlaybackConfiguration implements Parcelable {
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
    public @NonNull List<AudioDeviceInfo> getAudioDeviceInfos() {
        List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
        AudioDeviceInfo audioDeviceInfo = getAudioDeviceInfo();
        final int[] deviceIds;
        synchronized (mUpdateablePropLock) {
            deviceIds = mDeviceIds;
        }

        for (int i = 0; i < deviceIds.length; i++) {
            AudioDeviceInfo audioDeviceInfo = AudioManager.getDeviceForPortId(deviceIds[i],
                    AudioManager.GET_DEVICES_OUTPUTS);
            if (audioDeviceInfo != null) {
                audioDeviceInfos.add(audioDeviceInfo);
            }
        }
        return audioDeviceInfos;
    }

@@ -701,15 +713,15 @@ public final class AudioPlaybackConfiguration implements Parcelable {
     * @hide
     * Handle a player state change
     * @param event
     * @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID}
     * <br>Note device id is valid for {@code PLAYER_UPDATE_DEVICE_ID} or
     * <br>{@code PLAYER_STATE_STARTED} events, as the device id will be reset to none when
     * <br>pausing or stopping playback. It will be set to active device when playback starts or
     * @param deviceIds an array of device ids. This can be empty.
     * <br>Note device ids are non-empty for {@code PLAYER_UPDATE_DEVICE_ID} or
     * <br>{@code PLAYER_STATE_STARTED} events, as the device ids will be emptied when pausing
     * <br>or stopping playback. It will be set to active devices when playback starts or
     * <br>it will be changed when PLAYER_UPDATE_DEVICE_ID is sent. The latter can happen if the
     * <br>device changes in the middle of playback.
     * <br>devices change in the middle of playback.
     * @return true if the state changed, false otherwise
     */
    public boolean handleStateEvent(int event, int deviceId) {
    public boolean handleStateEvent(int event, int[] deviceIds) {
        boolean changed = false;
        synchronized (mUpdateablePropLock) {

@@ -720,8 +732,8 @@ public final class AudioPlaybackConfiguration implements Parcelable {
            }

            if (event == PLAYER_STATE_STARTED || event == PLAYER_UPDATE_DEVICE_ID) {
                changed = changed || (mDeviceId != deviceId);
                mDeviceId = deviceId;
                changed = changed || !Arrays.equals(mDeviceIds, deviceIds);
                mDeviceIds = deviceIds;
            }

            if (changed && (event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) {
@@ -801,8 +813,8 @@ public final class AudioPlaybackConfiguration implements Parcelable {
    @Override
    public int hashCode() {
        synchronized (mUpdateablePropLock) {
            return Objects.hash(mPlayerIId, mDeviceId, mMutedState, mPlayerType, mClientUid,
                    mClientPid, mSessionId);
            return Objects.hash(mPlayerIId, Arrays.toString(mDeviceIds), mMutedState, mPlayerType,
                    mClientUid, mClientPid, mSessionId);
        }
    }

@@ -815,7 +827,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
    public void writeToParcel(Parcel dest, int flags) {
        synchronized (mUpdateablePropLock) {
            dest.writeInt(mPlayerIId);
            dest.writeInt(mDeviceId);
            dest.writeIntArray(mDeviceIds);
            dest.writeInt(mMutedState);
            dest.writeInt(mPlayerType);
            dest.writeInt(mClientUid);
@@ -834,7 +846,10 @@ public final class AudioPlaybackConfiguration implements Parcelable {

    private AudioPlaybackConfiguration(Parcel in) {
        mPlayerIId = in.readInt();
        mDeviceId = in.readInt();
        mDeviceIds = new int[in.readInt()];
        for (int i = 0; i < mDeviceIds.length; i++) {
            mDeviceIds[i] = in.readInt();
        }
        mMutedState = in.readInt();
        mPlayerType = in.readInt();
        mClientUid = in.readInt();
@@ -855,7 +870,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
        AudioPlaybackConfiguration that = (AudioPlaybackConfiguration) o;

        return ((mPlayerIId == that.mPlayerIId)
                && (mDeviceId == that.mDeviceId)
                && Arrays.equals(mDeviceIds, that.mDeviceIds)
                && (mMutedState == that.mMutedState)
                && (mPlayerType == that.mPlayerType)
                && (mClientUid == that.mClientUid)
@@ -868,7 +883,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
        StringBuilder apcToString = new StringBuilder();
        synchronized (mUpdateablePropLock) {
            apcToString.append("AudioPlaybackConfiguration piid:").append(mPlayerIId).append(
                    " deviceId:").append(mDeviceId).append(" type:").append(
                    " deviceIds:").append(Arrays.toString(mDeviceIds)).append(" type:").append(
                    toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append(
                    mClientUid).append(
                    "/").append(mClientPid).append(" state:").append(
Loading