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

Commit 55a30c41 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

AudioAttributes for SoundPool

Add support for building a SoundPool instance and specify
 the AudioAttributes.
Remove SRC quality which was never implemented, while leaving
 room for supporting it later through the Builder pattern.
Remove stream types.
Update AudioService's use of SoundPool to the new scheme.

Change-Id: Ie51e4008684e5ba25f9b7368098e4f20266a15c7
parent 7226b9b6
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -3500,7 +3500,13 @@ public class AudioService extends IAudioService.Stub {

                loadTouchSoundAssets();

                mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
                mSoundPool = new SoundPool.Builder()
                        .setMaxStreams(NUM_SOUNDPOOL_CHANNELS)
                        .setAudioAttributes(new AudioAttributes.Builder()
                            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                            .build())
                        .build();
                mSoundPoolCallBack = null;
                mSoundPoolListenerThread = new SoundPoolListenerThread();
                mSoundPoolListenerThread.start();
+68 −8
Original line number Diff line number Diff line
@@ -128,10 +128,69 @@ public class SoundPool {
     * @return a SoundPool object, or null if creation failed
     */
    public SoundPool(int maxStreams, int streamType, int srcQuality) {
        this(maxStreams,
                new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build());
    }

    private SoundPool(int maxStreams, AudioAttributes attributes) {
        if (SystemProperties.getBoolean("config.disable_media", false)) {
            mImpl = new SoundPoolStub();
        } else {
            mImpl = new SoundPoolImpl(this, maxStreams, streamType, srcQuality);
            mImpl = new SoundPoolImpl(this, maxStreams, attributes);
        }
    }

    /**
     * @hide
     * CANDIDATE FOR PUBLIC API
     * Builder class for {@link SoundPool} objects.
     */
    public static class Builder {
        private int mMaxStreams = 1;
        private AudioAttributes mAudioAttributes;

        /**
         * Constructs a new Builder with the defaults format values.
         */
        public Builder() {
        }

        /**
         * Sets the maximum of number of simultaneous streams that can be played simultaneously.
         * @param maxStreams a value equal to 1 or greater.
         * @return the same Builder instance
         * @throws IllegalArgumentException
         */
        public Builder setMaxStreams(int maxStreams) throws IllegalArgumentException {
            if (maxStreams <= 0) {
                throw new IllegalArgumentException(
                        "Strictly position value required for the maximum number of streams");
            }
            mMaxStreams = maxStreams;
            return this;
        }

        /**
         * Sets the {@link AudioAttributes}. For examples, game applications will use attributes
         * built with usage information set to {@link AudioAttributes#USAGE_GAME}.
         * @param attributes a non-null
         * @return
         */
        public Builder setAudioAttributes(AudioAttributes attributes)
                throws IllegalArgumentException {
            if (attributes == null) {
                throw new IllegalArgumentException("Invalid null AudioAttributes");
            }
            mAudioAttributes = attributes;
            return this;
        }

        public SoundPool build() {
            if (mAudioAttributes == null) {
                mAudioAttributes = new AudioAttributes.Builder()
                        .setUsage(AudioAttributes.USAGE_MEDIA).build();
            }
            return new SoundPool(mMaxStreams, mAudioAttributes);
        }
    }

@@ -457,7 +516,7 @@ public class SoundPool {
        private SoundPool mProxy;

        private final Object mLock;
        private final int mStreamType;
        private final AudioAttributes mAttributes;
        private final IAppOpsService mAppOps;

        // SoundPool messages
@@ -465,15 +524,15 @@ public class SoundPool {
        // must match SoundPool.h
        private static final int SAMPLE_LOADED = 1;

        public SoundPoolImpl(SoundPool proxy, int maxStreams, int streamType, int srcQuality) {
        public SoundPoolImpl(SoundPool proxy, int maxStreams, AudioAttributes attr) {

            // do native setup
            if (native_setup(new WeakReference(this), maxStreams, streamType, srcQuality) != 0) {
            if (native_setup(new WeakReference(this), maxStreams, attr) != 0) {
                throw new RuntimeException("Native setup failed");
            }
            mLock = new Object();
            mProxy = proxy;
            mStreamType = streamType;
            mAttributes = attr;
            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
            mAppOps = IAppOpsService.Stub.asInterface(b);
        }
@@ -548,9 +607,9 @@ public class SoundPool {

        private boolean isRestricted() {
            try {
                final int usage = AudioAttributes.usageForLegacyStreamType(mStreamType);
                final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
                        usage, Process.myUid(), ActivityThread.currentPackageName());
                        mAttributes.getUsage(),
                        Process.myUid(), ActivityThread.currentPackageName());
                return mode != AppOpsManager.MODE_ALLOWED;
            } catch (RemoteException e) {
                return false;
@@ -648,7 +707,8 @@ public class SoundPool {

        public native final void release();

        private native final int native_setup(Object weakRef, int maxStreams, int streamType, int srcQuality);
        private native final int native_setup(Object weakRef, int maxStreams,
                Object/*AudioAttributes*/ attributes);

        protected void finalize() { release(); }
    }
+62 −13
Original line number Diff line number Diff line
@@ -32,10 +32,17 @@ static struct fields_t {
    jmethodID   mPostEvent;
    jclass      mSoundPoolClass;
} fields;

static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
    return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext);
}
static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
struct audio_attributes_fields_t {
    jfieldID  fieldUsage;        // AudioAttributes.mUsage
    jfieldID  fieldContentType;  // AudioAttributes.mContentType
    jfieldID  fieldFlags;        // AudioAttributes.mFlags
    jfieldID  fieldFormattedTags;// AudioAttributes.mFormattedTags
};
static audio_attributes_fields_t javaAudioAttrFields;

// ----------------------------------------------------------------------------
static jint
@@ -176,10 +183,30 @@ static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, v
}

static jint
android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,
        jint maxChannels, jobject jaa)
{
    if (jaa == 0) {
        ALOGE("Error creating SoundPool: invalid audio attributes");
        return -1;
    }

    audio_attributes_t *paa = NULL;
    // read the AudioAttributes values
    paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
    const jstring jtags =
            (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
    const char* tags = env->GetStringUTFChars(jtags, NULL);
    // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
    strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
    env->ReleaseStringUTFChars(jtags, tags);
    paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
    paa->content_type =
            (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
    paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);

    ALOGV("android_media_SoundPool_SoundPoolImpl_native_setup");
    SoundPool *ap = new SoundPool(maxChannels, (audio_stream_type_t) streamType, srcQuality);
    SoundPool *ap = new SoundPool(maxChannels, paa);
    if (ap == NULL) {
        return -1;
    }
@@ -190,6 +217,10 @@ android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jo
    // set callback with weak reference
    jobject globalWeakRef = env->NewGlobalRef(weakRef);
    ap->setCallback(android_media_callback, globalWeakRef);

    // audio attributes were copied in SoundPool creation
    free(paa);

    return 0;
}

@@ -270,7 +301,7 @@ static JNINativeMethod gMethods[] = {
        (void *)android_media_SoundPool_SoundPoolImpl_setRate
    },
    {   "native_setup",
        "(Ljava/lang/Object;III)I",
        "(Ljava/lang/Object;ILjava/lang/Object;)I",
        (void*)android_media_SoundPool_SoundPoolImpl_native_setup
    },
    {   "release",
@@ -289,27 +320,27 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("ERROR: GetEnv failed\n");
        goto bail;
        return result;
    }
    assert(env != NULL);

    clazz = env->FindClass(kClassPathName);
    if (clazz == NULL) {
        ALOGE("Can't find %s", kClassPathName);
        goto bail;
        return result;
    }

    fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
    if (fields.mNativeContext == NULL) {
        ALOGE("Can't find SoundPoolImpl.mNativeContext");
        goto bail;
        return result;
    }

    fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    if (fields.mPostEvent == NULL) {
        ALOGE("Can't find android/media/SoundPoolImpl.postEventFromNative");
        goto bail;
        return result;
    }

    // create a reference to class. Technically, we're leaking this reference
@@ -317,11 +348,29 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
    fields.mSoundPoolClass = (jclass) env->NewGlobalRef(clazz);

    if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
        goto bail;

    /* success -- return valid version number */
    result = JNI_VERSION_1_4;
        return result;

bail:
    // Get the AudioAttributes class and fields
    jclass audioAttrClass = env->FindClass(kAudioAttributesClassPathName);
    if (audioAttrClass == NULL) {
        ALOGE("Can't find %s", kAudioAttributesClassPathName);
        return result;
    }
    jclass audioAttributesClassRef = (jclass)env->NewGlobalRef(audioAttrClass);
    javaAudioAttrFields.fieldUsage = env->GetFieldID(audioAttributesClassRef, "mUsage", "I");
    javaAudioAttrFields.fieldContentType
                                   = env->GetFieldID(audioAttributesClassRef, "mContentType", "I");
    javaAudioAttrFields.fieldFlags = env->GetFieldID(audioAttributesClassRef, "mFlags", "I");
    javaAudioAttrFields.fieldFormattedTags =
            env->GetFieldID(audioAttributesClassRef, "mFormattedTags", "Ljava/lang/String;");
    env->DeleteGlobalRef(audioAttributesClassRef);
    if (javaAudioAttrFields.fieldUsage == NULL || javaAudioAttrFields.fieldContentType == NULL
            || javaAudioAttrFields.fieldFlags == NULL
            || javaAudioAttrFields.fieldFormattedTags == NULL) {
        ALOGE("Can't initialize AudioAttributes fields");
        return result;
    }

    /* success -- return valid version number */
    return JNI_VERSION_1_4;
}