android_frameworks_base/media/jni/soundpool/android_media_SoundPool.cpp
Jean-Michel Trivi 8e48c6939b Refactor SoundPool for appOps handling through PlayerBase
Modified the signature of the abstract volume methods so
  it is clear at the subclass level whether the volume
  command is for a mute or a volume control.
  Changed the implementations in the subclasses
  accordingly.
Removed appOps handling inside SoundPool and made it
  inherit from PlayerBase.
Moved handling of the camera sound restriction from
  SoundPool to PlayerBase.
Added support in SoundPool native implementation for
  muting, as each player has its own volume.

Test: play a long file with SoundPool and enter DnD mode

Bug: 30955183
Bug: 28249605

Change-Id: I0fcd7480f9a455c06aa4f7092486f5c65bc9d7db
2016-11-14 09:36:11 -08:00

372 lines
12 KiB
C++

/*
* Copyright (C) 2008 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.
*/
#include <stdio.h>
//#define LOG_NDEBUG 0
#define LOG_TAG "SoundPool-JNI"
#include <utils/Log.h>
#include <nativehelper/jni.h>
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include "SoundPool.h"
using namespace android;
static struct fields_t {
jfieldID mNativeContext;
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
android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
jlong offset, jlong length, jint priority)
{
ALOGV("android_media_SoundPool_load_FD");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return 0;
return (jint) ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor),
int64_t(offset), int64_t(length), int(priority));
}
static jboolean
android_media_SoundPool_unload(JNIEnv *env, jobject thiz, jint sampleID) {
ALOGV("android_media_SoundPool_unload\n");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return JNI_FALSE;
return ap->unload(sampleID) ? JNI_TRUE : JNI_FALSE;
}
static jint
android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
jfloat rate)
{
ALOGV("android_media_SoundPool_play\n");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return 0;
return (jint) ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
}
static void
android_media_SoundPool_pause(JNIEnv *env, jobject thiz, jint channelID)
{
ALOGV("android_media_SoundPool_pause");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->pause(channelID);
}
static void
android_media_SoundPool_resume(JNIEnv *env, jobject thiz, jint channelID)
{
ALOGV("android_media_SoundPool_resume");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->resume(channelID);
}
static void
android_media_SoundPool_autoPause(JNIEnv *env, jobject thiz)
{
ALOGV("android_media_SoundPool_autoPause");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->autoPause();
}
static void
android_media_SoundPool_autoResume(JNIEnv *env, jobject thiz)
{
ALOGV("android_media_SoundPool_autoResume");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->autoResume();
}
static void
android_media_SoundPool_stop(JNIEnv *env, jobject thiz, jint channelID)
{
ALOGV("android_media_SoundPool_stop");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->stop(channelID);
}
static void
android_media_SoundPool_setVolume(JNIEnv *env, jobject thiz, jint channelID,
jfloat leftVolume, jfloat rightVolume)
{
ALOGV("android_media_SoundPool_setVolume");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setVolume(channelID, (float) leftVolume, (float) rightVolume);
}
static void
android_media_SoundPool_mute(JNIEnv *env, jobject thiz, jboolean muting)
{
ALOGV("android_media_SoundPool_mute(%d)", muting);
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->mute(muting == JNI_TRUE);
}
static void
android_media_SoundPool_setPriority(JNIEnv *env, jobject thiz, jint channelID,
jint priority)
{
ALOGV("android_media_SoundPool_setPriority");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setPriority(channelID, (int) priority);
}
static void
android_media_SoundPool_setLoop(JNIEnv *env, jobject thiz, jint channelID,
int loop)
{
ALOGV("android_media_SoundPool_setLoop");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setLoop(channelID, loop);
}
static void
android_media_SoundPool_setRate(JNIEnv *env, jobject thiz, jint channelID,
jfloat rate)
{
ALOGV("android_media_SoundPool_setRate");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setRate(channelID, (float) rate);
}
static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, void* user)
{
ALOGV("callback: (%d, %d, %d, %p, %p)", event.mMsg, event.mArg1, event.mArg2, soundPool, user);
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->CallStaticVoidMethod(fields.mSoundPoolClass, fields.mPostEvent, user, event.mMsg, event.mArg1, event.mArg2, NULL);
}
static jint
android_media_SoundPool_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_native_setup");
SoundPool *ap = new SoundPool(maxChannels, paa);
if (ap == NULL) {
return -1;
}
// save pointer to SoundPool C++ object in opaque field in Java object
env->SetLongField(thiz, fields.mNativeContext, (jlong) ap);
// 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;
}
static void
android_media_SoundPool_release(JNIEnv *env, jobject thiz)
{
ALOGV("android_media_SoundPool_release");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap != NULL) {
// release weak reference and clear callback
jobject weakRef = (jobject) ap->getUserData();
ap->setCallback(NULL, NULL);
if (weakRef != NULL) {
env->DeleteGlobalRef(weakRef);
}
// clear native context
env->SetLongField(thiz, fields.mNativeContext, 0);
delete ap;
}
}
// ----------------------------------------------------------------------------
// Dalvik VM type signatures
static JNINativeMethod gMethods[] = {
{ "_load",
"(Ljava/io/FileDescriptor;JJI)I",
(void *)android_media_SoundPool_load_FD
},
{ "unload",
"(I)Z",
(void *)android_media_SoundPool_unload
},
{ "_play",
"(IFFIIF)I",
(void *)android_media_SoundPool_play
},
{ "pause",
"(I)V",
(void *)android_media_SoundPool_pause
},
{ "resume",
"(I)V",
(void *)android_media_SoundPool_resume
},
{ "autoPause",
"()V",
(void *)android_media_SoundPool_autoPause
},
{ "autoResume",
"()V",
(void *)android_media_SoundPool_autoResume
},
{ "stop",
"(I)V",
(void *)android_media_SoundPool_stop
},
{ "_setVolume",
"(IFF)V",
(void *)android_media_SoundPool_setVolume
},
{ "_mute",
"(Z)V",
(void *)android_media_SoundPool_mute
},
{ "setPriority",
"(II)V",
(void *)android_media_SoundPool_setPriority
},
{ "setLoop",
"(II)V",
(void *)android_media_SoundPool_setLoop
},
{ "setRate",
"(IF)V",
(void *)android_media_SoundPool_setRate
},
{ "native_setup",
"(Ljava/lang/Object;ILjava/lang/Object;)I",
(void*)android_media_SoundPool_native_setup
},
{ "native_release",
"()V",
(void*)android_media_SoundPool_release
}
};
static const char* const kClassPathName = "android/media/SoundPool";
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
JNIEnv* env = NULL;
jint result = -1;
jclass clazz;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed\n");
return result;
}
assert(env != NULL);
clazz = env->FindClass(kClassPathName);
if (clazz == NULL) {
ALOGE("Can't find %s", kClassPathName);
return result;
}
fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.mNativeContext == NULL) {
ALOGE("Can't find SoundPool.mNativeContext");
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/SoundPool.postEventFromNative");
return result;
}
// create a reference to class. Technically, we're leaking this reference
// since it's a static object.
fields.mSoundPoolClass = (jclass) env->NewGlobalRef(clazz);
if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
return result;
// 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;
}