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

Commit eb9bf64d authored by Dorin Drimus's avatar Dorin Drimus
Browse files

Add getDirectProfilesForAttributes

Allows any app to query AudioManager on the available direct
AudioProfiles for the specified AudioAttributes. Only the active paths
that will be actually used to output sound are returned.
go/audio-route-t

Bug: 190810951
Test: atest android.media.audio.cts.DirectAudioProfilesForAttributesTest
Change-Id: Ia5ce587addadb52725084bd32a6244577626f44d
parent 2d0e9dee
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -21110,6 +21110,7 @@ package android.media {
    method @Nullable public android.media.AudioDeviceInfo getCommunicationDevice();
    method public android.media.AudioDeviceInfo[] getDevices(int);
    method public static int getDirectPlaybackSupport(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes);
    method @NonNull public java.util.List<android.media.AudioProfile> getDirectProfilesForAttributes(@NonNull android.media.AudioAttributes);
    method public int getEncodedSurroundMode();
    method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
    method public int getMode();
+107 −1
Original line number Diff line number Diff line
@@ -1236,6 +1236,65 @@ static bool isAudioPortArrayCountOutOfBounds(const struct audio_port_v7 *nAudioP
    return false;
}

static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile,
                                          const audio_profile *nAudioProfile, bool useInMask) {
    size_t numPositionMasks = 0;
    size_t numIndexMasks = 0;

    // count up how many masks are positional and indexed
    for (size_t index = 0; index < nAudioProfile->num_channel_masks; index++) {
        const audio_channel_mask_t mask = nAudioProfile->channel_masks[index];
        if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
            numIndexMasks++;
        } else {
            numPositionMasks++;
        }
    }

    ScopedLocalRef<jintArray> jSamplingRates(env,
                                             env->NewIntArray(nAudioProfile->num_sample_rates));
    ScopedLocalRef<jintArray> jChannelMasks(env, env->NewIntArray(numPositionMasks));
    ScopedLocalRef<jintArray> jChannelIndexMasks(env, env->NewIntArray(numIndexMasks));
    if (!jSamplingRates.get() || !jChannelMasks.get() || !jChannelIndexMasks.get()) {
        return AUDIO_JAVA_ERROR;
    }

    if (nAudioProfile->num_sample_rates) {
        env->SetIntArrayRegion(jSamplingRates.get(), 0 /*start*/, nAudioProfile->num_sample_rates,
                               (jint *)nAudioProfile->sample_rates);
    }

    // put the masks in the output arrays
    for (size_t maskIndex = 0, posMaskIndex = 0, indexedMaskIndex = 0;
         maskIndex < nAudioProfile->num_channel_masks; maskIndex++) {
        const audio_channel_mask_t mask = nAudioProfile->channel_masks[maskIndex];
        if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
            jint jMask = audio_channel_mask_get_bits(mask);
            env->SetIntArrayRegion(jChannelIndexMasks.get(), indexedMaskIndex++, 1, &jMask);
        } else {
            jint jMask = useInMask ? inChannelMaskFromNative(mask) : outChannelMaskFromNative(mask);
            env->SetIntArrayRegion(jChannelMasks.get(), posMaskIndex++, 1, &jMask);
        }
    }

    int encapsulationType;
    if (audioEncapsulationTypeFromNative(nAudioProfile->encapsulation_type, &encapsulationType) !=
        NO_ERROR) {
        ALOGW("Unknown encapsulation type for JAVA API: %u", nAudioProfile->encapsulation_type);
    }

    *jAudioProfile =
            env->NewObject(gAudioProfileClass, gAudioProfileCstor,
                           audioFormatFromNative(nAudioProfile->format), jSamplingRates.get(),
                           jChannelMasks.get(), jChannelIndexMasks.get(), encapsulationType);

    if (jAudioProfile == nullptr) {
        return AUDIO_JAVA_ERROR;
    }

    return AUDIO_JAVA_SUCCESS;
}

static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort,
                                       const struct audio_port_v7 *nAudioPort) {
    jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
@@ -2814,6 +2873,50 @@ static jint android_media_AudioSystem_getDirectPlaybackSupport(JNIEnv *env, jobj
    return convertAudioDirectModeFromNative(directMode);
}

static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env, jobject thiz,
                                                                     jobject jAudioAttributes,
                                                                     jobject jAudioProfilesList) {
    ALOGV("getDirectProfilesForAttributes");

    if (jAudioAttributes == nullptr) {
        ALOGE("jAudioAttributes is NULL");
        return (jint)AUDIO_JAVA_BAD_VALUE;
    }
    if (jAudioProfilesList == nullptr) {
        ALOGE("jAudioProfilesList is NULL");
        return (jint)AUDIO_JAVA_BAD_VALUE;
    }
    if (!env->IsInstanceOf(jAudioProfilesList, gArrayListClass)) {
        ALOGE("jAudioProfilesList not an ArrayList");
        return (jint)AUDIO_JAVA_BAD_VALUE;
    }

    JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique();
    jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAudioAttributes, paa.get());
    if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
        return jStatus;
    }

    std::vector<audio_profile> audioProfiles;
    status_t status = AudioSystem::getDirectProfilesForAttributes(paa.get(), &audioProfiles);
    if (status != NO_ERROR) {
        ALOGE("AudioSystem::getDirectProfilesForAttributes error %d", status);
        jStatus = nativeToJavaStatus(status);
        return jStatus;
    }

    for (const auto &audioProfile : audioProfiles) {
        jobject jAudioProfile;
        jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &audioProfile, false);
        if (jStatus != AUDIO_JAVA_SUCCESS) {
            return jStatus;
        }
        env->CallBooleanMethod(jAudioProfilesList, gArrayListMethods.add, jAudioProfile);
        env->DeleteLocalRef(jAudioProfile);
    }
    return jStatus;
}

// ----------------------------------------------------------------------------

static const JNINativeMethod gMethods[] =
@@ -2960,7 +3063,10 @@ static const JNINativeMethod gMethods[] =
          (void *)android_media_AudioSystem_canBeSpatialized},
         {"getDirectPlaybackSupport",
          "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I",
          (void *)android_media_AudioSystem_getDirectPlaybackSupport}};
          (void *)android_media_AudioSystem_getDirectPlaybackSupport},
         {"getDirectProfilesForAttributes",
          "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I",
          (void *)android_media_AudioSystem_getDirectProfilesForAttributes}};

static const JNINativeMethod gEventHandlerMethods[] = {
    {"native_setup",
+27 −0
Original line number Diff line number Diff line
@@ -7670,6 +7670,33 @@ public class AudioManager {
        }
    }

    /**
     * Returns a list of direct {@link AudioProfile} that are supported for the specified
     * {@link AudioAttributes}. This can be empty in case of an error or if no direct playback
     * is possible.
     *
     * <p>Direct playback means that the audio stream is not resampled or downmixed
     * by the framework. Checking for direct support can help the app select the representation
     * of audio content that most closely matches the capabilities of the device and peripherals
     * (e.g. A/V receiver) connected to it. Note that the provided stream can still be re-encoded
     * or mixed with other streams, if needed.
     * <p>When using this information to inform your application which audio format to play,
     * query again whenever audio output devices change (see {@link AudioDeviceCallback}).
     * @param attributes a non-null {@link AudioAttributes} instance.
     * @return a list of {@link AudioProfile}
     */
    @NonNull
    public List<AudioProfile> getDirectProfilesForAttributes(@NonNull AudioAttributes attributes) {
        Objects.requireNonNull(attributes);
        ArrayList<AudioProfile> audioProfilesList = new ArrayList<>();
        int status = AudioSystem.getDirectProfilesForAttributes(attributes, audioProfilesList);
        if (status != SUCCESS) {
            Log.w(TAG, "getDirectProfilesForAttributes failed.");
            return new ArrayList<>();
        }
        return audioProfilesList;
    }

    /**
     * @hide
     * Returns an {@link AudioDeviceInfo} corresponding to a connected device of the type provided.
+9 −0
Original line number Diff line number Diff line
@@ -2108,6 +2108,15 @@ public class AudioSystem
                                              AudioFormat format,
                                              AudioDeviceAttributes[] devices);

    /**
     * @hide
     * @param attributes audio attributes describing the playback use case
     * @param audioProfilesList the list of AudioProfiles that can be played as direct output
     * @return {@link #SUCCESS} if the list of AudioProfiles was successfully created (can be empty)
     */
    public static native int getDirectProfilesForAttributes(@NonNull AudioAttributes attributes,
            @NonNull ArrayList<AudioProfile> audioProfilesList);

    // Items shared with audio service

    /**