AudioVolumeGroup introspection & callback JAVA APIs
This CL adds introspection APIs for Audio Volume group and callback notification on group volume change. It allows to keep the same level of service available today in CarAudioManager and is a starting point to get rid of Stream Alias hard coded in AudioServer. Bug: 124767636 Test: dumpsys media.audio_policy Change-Id: I48909df93c146f071acd24cd3ecc608cb98b4d7e Signed-off-by: François Gaffie <francois.gaffie@renault.com>
This commit is contained in:
committed by
Hongwei Wang
parent
4ed935a5aa
commit
adcd00a7c8
@@ -3456,15 +3456,18 @@ package android.media {
|
||||
method public void clearAudioServerStateCallback();
|
||||
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
|
||||
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.audiopolicy.AudioProductStrategies getAudioProductStrategies();
|
||||
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.audiopolicy.AudioVolumeGroups getAudioVolumeGroups();
|
||||
method public boolean isAudioServerRunning();
|
||||
method public boolean isHdmiSystemAudioSupported();
|
||||
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
|
||||
method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
|
||||
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
|
||||
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
|
||||
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int requestAudioFocus(@NonNull android.media.AudioFocusRequest, @Nullable android.media.audiopolicy.AudioPolicy);
|
||||
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
|
||||
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
|
||||
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
|
||||
method public void unregisterVolumeGroupCallback(@NonNull android.media.AudioManager.VolumeGroupCallback);
|
||||
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
|
||||
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
|
||||
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
|
||||
@@ -3478,6 +3481,11 @@ package android.media {
|
||||
method public void onAudioServerUp();
|
||||
}
|
||||
|
||||
public abstract static class AudioManager.VolumeGroupCallback {
|
||||
ctor public AudioManager.VolumeGroupCallback();
|
||||
method public void onAudioVolumeGroupChanged(int, int);
|
||||
}
|
||||
|
||||
public final class AudioPlaybackConfiguration implements android.os.Parcelable {
|
||||
method public int getClientPid();
|
||||
method public int getClientUid();
|
||||
@@ -3649,6 +3657,8 @@ package android.media.audiopolicy {
|
||||
method @Nullable public android.media.audiopolicy.AudioProductStrategy getById(int);
|
||||
method public int getLegacyStreamTypeForAudioAttributes(@NonNull android.media.AudioAttributes);
|
||||
method @Nullable public android.media.audiopolicy.AudioProductStrategy getProductStrategyForAudioAttributes(@NonNull android.media.AudioAttributes);
|
||||
method public int getVolumeGroupIdForAttributes(@NonNull android.media.AudioAttributes);
|
||||
method public int getVolumeGroupIdForLegacyStreamType(int);
|
||||
method public java.util.Iterator<android.media.audiopolicy.AudioProductStrategy> iterator();
|
||||
method public int size();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
@@ -3664,6 +3674,27 @@ package android.media.audiopolicy {
|
||||
field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioProductStrategy> CREATOR;
|
||||
}
|
||||
|
||||
public final class AudioVolumeGroup implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public java.util.List<android.media.AudioAttributes> getAudioAttributes();
|
||||
method public int getId();
|
||||
method @NonNull public int[] getLegacyStreamTypes();
|
||||
method @NonNull public String name();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioVolumeGroup> CREATOR;
|
||||
}
|
||||
|
||||
public final class AudioVolumeGroups implements java.lang.Iterable<android.media.audiopolicy.AudioVolumeGroup> android.os.Parcelable {
|
||||
ctor public AudioVolumeGroups();
|
||||
method public int describeContents();
|
||||
method @Nullable public android.media.audiopolicy.AudioVolumeGroup getById(int);
|
||||
method public java.util.Iterator<android.media.audiopolicy.AudioVolumeGroup> iterator();
|
||||
method public int size();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioVolumeGroups> CREATOR;
|
||||
field public static final int DEFAULT_VOLUME_GROUP = -1; // 0xffffffff
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.media.session {
|
||||
|
||||
@@ -165,6 +165,8 @@ cc_library_shared {
|
||||
"android_media_AudioTrack.cpp",
|
||||
"android_media_AudioAttributes.cpp",
|
||||
"android_media_AudioProductStrategies.cpp",
|
||||
"android_media_AudioVolumeGroups.cpp",
|
||||
"android_media_AudioVolumeGroupCallback.cpp",
|
||||
"android_media_DeviceCallback.cpp",
|
||||
"android_media_JetPlayer.cpp",
|
||||
"android_media_MediaMetricsJNI.cpp",
|
||||
|
||||
@@ -110,6 +110,8 @@ extern int register_android_media_AudioSystem(JNIEnv *env);
|
||||
extern int register_android_media_AudioTrack(JNIEnv *env);
|
||||
extern int register_android_media_AudioAttributes(JNIEnv *env);
|
||||
extern int register_android_media_AudioProductStrategies(JNIEnv *env);
|
||||
extern int register_android_media_AudioVolumeGroups(JNIEnv *env);
|
||||
extern int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env);
|
||||
extern int register_android_media_MicrophoneInfo(JNIEnv *env);
|
||||
extern int register_android_media_JetPlayer(JNIEnv *env);
|
||||
extern int register_android_media_ToneGenerator(JNIEnv *env);
|
||||
@@ -1524,6 +1526,8 @@ static const RegJNIRec gRegJNI[] = {
|
||||
REG_JNI(register_android_media_AudioTrack),
|
||||
REG_JNI(register_android_media_AudioAttributes),
|
||||
REG_JNI(register_android_media_AudioProductStrategies),
|
||||
REG_JNI(register_android_media_AudioVolumeGroups),
|
||||
REG_JNI(register_android_media_AudioVolumeGroupChangeHandler),
|
||||
REG_JNI(register_android_media_JetPlayer),
|
||||
REG_JNI(register_android_media_MicrophoneInfo),
|
||||
REG_JNI(register_android_media_RemoteDisplay),
|
||||
|
||||
@@ -57,7 +57,7 @@ static struct {
|
||||
static jclass gAudioAttributesGroupClass;
|
||||
static jmethodID gAudioAttributesGroupCstor;
|
||||
static struct {
|
||||
jfieldID mGroupId;
|
||||
jfieldID mVolumeGroupId;
|
||||
jfieldID mLegacyStreamType;
|
||||
jfieldID mAudioAttributes;
|
||||
} gAudioAttributesGroupsFields;
|
||||
@@ -207,7 +207,7 @@ android_media_AudioSystem_getProductStrategyFromAudioAttributes(JNIEnv *env, job
|
||||
}
|
||||
product_strategy_t psId;
|
||||
status_t status = AudioSystem::getProductStrategyFromAudioAttributes(
|
||||
AudioAttributes(*attributes.get()), psId);
|
||||
AudioAttributes(*attributes.get()), psId);
|
||||
if (status != NO_ERROR) {
|
||||
return nativeToJavaStatus(status);
|
||||
}
|
||||
@@ -249,8 +249,8 @@ int register_android_media_AudioProductStrategies(JNIEnv *env)
|
||||
gAudioAttributesGroupClass = MakeGlobalRefOrDie(env, audioAttributesGroupClass);
|
||||
gAudioAttributesGroupCstor = GetMethodIDOrDie(env, audioAttributesGroupClass, "<init>",
|
||||
"(II[Landroid/media/AudioAttributes;)V");
|
||||
gAudioAttributesGroupsFields.mGroupId = GetFieldIDOrDie(
|
||||
env, audioAttributesGroupClass, "mGroupId", "I");
|
||||
gAudioAttributesGroupsFields.mVolumeGroupId = GetFieldIDOrDie(
|
||||
env, audioAttributesGroupClass, "mVolumeGroupId", "I");
|
||||
gAudioAttributesGroupsFields.mLegacyStreamType = GetFieldIDOrDie(
|
||||
env, audioAttributesGroupClass, "mLegacyStreamType", "I");
|
||||
gAudioAttributesGroupsFields.mAudioAttributes = GetFieldIDOrDie(
|
||||
|
||||
175
core/jni/android_media_AudioVolumeGroupCallback.cpp
Normal file
175
core/jni/android_media_AudioVolumeGroupCallback.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#define LOG_TAG "AudioVolumeGroupCallback-JNI"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
#include "core_jni_helpers.h"
|
||||
|
||||
#include "android_media_AudioVolumeGroupCallback.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
using namespace android;
|
||||
|
||||
static const char* const kAudioVolumeGroupChangeHandlerClassPathName =
|
||||
"android/media/audiopolicy/AudioVolumeGroupChangeHandler";
|
||||
|
||||
static struct {
|
||||
jfieldID mJniCallback;
|
||||
} gAudioVolumeGroupChangeHandlerFields;
|
||||
|
||||
static struct {
|
||||
jmethodID postEventFromNative;
|
||||
} gAudioVolumeGroupChangeHandlerMethods;
|
||||
|
||||
static Mutex gLock;
|
||||
|
||||
JNIAudioVolumeGroupCallback::JNIAudioVolumeGroupCallback(JNIEnv* env,
|
||||
jobject thiz,
|
||||
jobject weak_thiz)
|
||||
{
|
||||
jclass clazz = env->GetObjectClass(thiz);
|
||||
if (clazz == NULL) {
|
||||
ALOGE("Can't find class %s", kAudioVolumeGroupChangeHandlerClassPathName);
|
||||
return;
|
||||
}
|
||||
mClass = (jclass)env->NewGlobalRef(clazz);
|
||||
|
||||
// We use a weak reference so the AudioVolumeGroupChangeHandler object can be garbage collected.
|
||||
// The reference is only used as a proxy for callbacks.
|
||||
mObject = env->NewGlobalRef(weak_thiz);
|
||||
}
|
||||
|
||||
JNIAudioVolumeGroupCallback::~JNIAudioVolumeGroupCallback()
|
||||
{
|
||||
// remove global references
|
||||
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
||||
if (env == NULL) {
|
||||
return;
|
||||
}
|
||||
env->DeleteGlobalRef(mObject);
|
||||
env->DeleteGlobalRef(mClass);
|
||||
}
|
||||
|
||||
void JNIAudioVolumeGroupCallback::onAudioVolumeGroupChanged(volume_group_t group, int flags)
|
||||
{
|
||||
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
||||
if (env == NULL) {
|
||||
return;
|
||||
}
|
||||
ALOGV("%s volume group id %d", __FUNCTION__, group);
|
||||
env->CallStaticVoidMethod(mClass,
|
||||
gAudioVolumeGroupChangeHandlerMethods.postEventFromNative,
|
||||
mObject,
|
||||
AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED, group, flags, NULL);
|
||||
if (env->ExceptionCheck()) {
|
||||
ALOGW("An exception occurred while notifying an event.");
|
||||
env->ExceptionClear();
|
||||
}
|
||||
}
|
||||
|
||||
void JNIAudioVolumeGroupCallback::onServiceDied()
|
||||
{
|
||||
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
||||
if (env == NULL) {
|
||||
return;
|
||||
}
|
||||
env->CallStaticVoidMethod(mClass,
|
||||
gAudioVolumeGroupChangeHandlerMethods.postEventFromNative,
|
||||
mObject,
|
||||
AUDIOVOLUMEGROUP_EVENT_SERVICE_DIED, 0, 0, NULL);
|
||||
if (env->ExceptionCheck()) {
|
||||
ALOGW("An exception occurred while notifying an event.");
|
||||
env->ExceptionClear();
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
sp<JNIAudioVolumeGroupCallback> setJniCallback(JNIEnv* env,
|
||||
jobject thiz,
|
||||
const sp<JNIAudioVolumeGroupCallback>& callback)
|
||||
{
|
||||
Mutex::Autolock l(gLock);
|
||||
sp<JNIAudioVolumeGroupCallback> old = (JNIAudioVolumeGroupCallback*)env->GetLongField(
|
||||
thiz, gAudioVolumeGroupChangeHandlerFields.mJniCallback);
|
||||
if (callback.get()) {
|
||||
callback->incStrong((void*)setJniCallback);
|
||||
}
|
||||
if (old != 0) {
|
||||
old->decStrong((void*)setJniCallback);
|
||||
}
|
||||
env->SetLongField(thiz, gAudioVolumeGroupChangeHandlerFields.mJniCallback,
|
||||
(jlong)callback.get());
|
||||
return old;
|
||||
}
|
||||
|
||||
static void
|
||||
android_media_AudioVolumeGroupChangeHandler_eventHandlerSetup(JNIEnv *env,
|
||||
jobject thiz,
|
||||
jobject weak_this)
|
||||
{
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
sp<JNIAudioVolumeGroupCallback> callback =
|
||||
new JNIAudioVolumeGroupCallback(env, thiz, weak_this);
|
||||
|
||||
if (AudioSystem::addAudioVolumeGroupCallback(callback) == NO_ERROR) {
|
||||
setJniCallback(env, thiz, callback);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
android_media_AudioVolumeGroupChangeHandler_eventHandlerFinalize(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
sp<JNIAudioVolumeGroupCallback> callback = setJniCallback(env, thiz, 0);
|
||||
if (callback != 0) {
|
||||
AudioSystem::removeAudioVolumeGroupCallback(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* JNI registration.
|
||||
*/
|
||||
static const JNINativeMethod gMethods[] = {
|
||||
{"native_setup", "(Ljava/lang/Object;)V",
|
||||
(void *)android_media_AudioVolumeGroupChangeHandler_eventHandlerSetup},
|
||||
{"native_finalize", "()V",
|
||||
(void *)android_media_AudioVolumeGroupChangeHandler_eventHandlerFinalize},
|
||||
};
|
||||
|
||||
int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env)
|
||||
{
|
||||
jclass audioVolumeGroupChangeHandlerClass =
|
||||
FindClassOrDie(env, kAudioVolumeGroupChangeHandlerClassPathName);
|
||||
gAudioVolumeGroupChangeHandlerMethods.postEventFromNative =
|
||||
GetStaticMethodIDOrDie(env, audioVolumeGroupChangeHandlerClass, "postEventFromNative",
|
||||
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
|
||||
|
||||
gAudioVolumeGroupChangeHandlerFields.mJniCallback =
|
||||
GetFieldIDOrDie(env, audioVolumeGroupChangeHandlerClass, "mJniCallback", "J");
|
||||
|
||||
env->DeleteLocalRef(audioVolumeGroupChangeHandlerClass);
|
||||
|
||||
return RegisterMethodsOrDie(env,
|
||||
kAudioVolumeGroupChangeHandlerClassPathName,
|
||||
gMethods,
|
||||
NELEM(gMethods));
|
||||
}
|
||||
|
||||
44
core/jni/android_media_AudioVolumeGroupCallback.h
Normal file
44
core/jni/android_media_AudioVolumeGroupCallback.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <system/audio.h>
|
||||
#include <media/AudioSystem.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// keep in sync with AudioManager.AudioVolumeGroupChangeHandler.java
|
||||
#define AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED 1000
|
||||
#define AUDIOVOLUMEGROUP_EVENT_SERVICE_DIED 1001
|
||||
|
||||
class JNIAudioVolumeGroupCallback: public AudioSystem::AudioVolumeGroupCallback
|
||||
{
|
||||
public:
|
||||
JNIAudioVolumeGroupCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
|
||||
~JNIAudioVolumeGroupCallback();
|
||||
|
||||
void onAudioVolumeGroupChanged(volume_group_t group, int flags) override;
|
||||
void onServiceDied() override;
|
||||
|
||||
private:
|
||||
void sendEvent(int event);
|
||||
|
||||
jclass mClass; /**< Reference to AudioVolumeGroupChangeHandler class. */
|
||||
jobject mObject; /**< Weak ref to AudioVolumeGroupChangeHandler object to call on. */
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
183
core/jni/android_media_AudioVolumeGroups.cpp
Normal file
183
core/jni/android_media_AudioVolumeGroups.cpp
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#define LOG_TAG "AudioVolumeGroups-JNI"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <jni.h>
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
#include "core_jni_helpers.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <vector>
|
||||
|
||||
#include <media/AudioSystem.h>
|
||||
#include <media/AudioPolicy.h>
|
||||
|
||||
#include <nativehelper/ScopedUtfChars.h>
|
||||
|
||||
#include "android_media_AudioAttributes.h"
|
||||
#include "android_media_AudioErrors.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
using namespace android;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
static const char* const kClassPathName = "android/media/audiopolicy/AudioVolumeGroups";
|
||||
static const char* const kAudioVolumeGroupClassPathName =
|
||||
"android/media/audiopolicy/AudioVolumeGroup";
|
||||
|
||||
static jclass gAudioVolumeGroupClass;
|
||||
static jmethodID gAudioVolumeGroupCstor;
|
||||
static struct {
|
||||
jfieldID mName;
|
||||
jfieldID mId;
|
||||
} gAudioVolumeGroupFields;
|
||||
|
||||
static jclass gArrayListClass;
|
||||
static jmethodID gArrayListCstor;
|
||||
static struct {
|
||||
jmethodID add;
|
||||
jmethodID toArray;
|
||||
} gArrayListMethods;
|
||||
|
||||
|
||||
static jint convertAudioVolumeGroupsFromNative(
|
||||
JNIEnv *env, jobject *jGroup, const AudioVolumeGroup &group)
|
||||
{
|
||||
jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
|
||||
jstring jName = NULL;
|
||||
jint Id = NULL;
|
||||
|
||||
jintArray jLegacyStreamTypes = NULL;
|
||||
jobjectArray jAudioAttributes = NULL;
|
||||
jint numAttributes;
|
||||
jobject jAudioAttribute = NULL;
|
||||
|
||||
jName = env->NewStringUTF(group.getName().c_str());
|
||||
Id = static_cast<jint>(group.getId());
|
||||
|
||||
// Legacy stream types array
|
||||
jLegacyStreamTypes = env->NewIntArray(group.getStreamTypes().size());
|
||||
if (jLegacyStreamTypes == NULL) {
|
||||
jStatus = (jint)AUDIO_JAVA_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
for (size_t streamIndex = 0; streamIndex < group.getStreamTypes().size(); streamIndex++) {
|
||||
jint jStream = group.getStreamTypes()[streamIndex];
|
||||
env->SetIntArrayRegion(jLegacyStreamTypes, streamIndex, 1, &jStream);
|
||||
}
|
||||
|
||||
// Audio Attributes array
|
||||
numAttributes = group.getAudioAttributes().size();
|
||||
jStatus = JNIAudioAttributeHelper::getJavaArray(env, &jAudioAttributes, numAttributes);
|
||||
if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < static_cast<size_t>(numAttributes); j++) {
|
||||
auto attributes = group.getAudioAttributes()[j];
|
||||
|
||||
jStatus = JNIAudioAttributeHelper::nativeToJava(env, &jAudioAttribute, attributes);
|
||||
if (jStatus != AUDIO_JAVA_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
env->SetObjectArrayElement(jAudioAttributes, j, jAudioAttribute);
|
||||
}
|
||||
|
||||
*jGroup = env->NewObject(gAudioVolumeGroupClass, gAudioVolumeGroupCstor,
|
||||
jName, Id, jAudioAttributes, jLegacyStreamTypes);
|
||||
exit:
|
||||
if (jName != NULL) {
|
||||
env->DeleteLocalRef(jName);
|
||||
}
|
||||
return jStatus;
|
||||
}
|
||||
|
||||
static jint
|
||||
android_media_AudioSystem_listAudioVolumeGroups(JNIEnv *env, jobject clazz, jobject jVolumeGroups)
|
||||
{
|
||||
if (env == NULL) {
|
||||
return AUDIO_JAVA_DEAD_OBJECT;
|
||||
}
|
||||
if (jVolumeGroups == NULL) {
|
||||
ALOGE("listAudioVolumeGroups NULL AudioVolumeGroups");
|
||||
return (jint)AUDIO_JAVA_BAD_VALUE;
|
||||
}
|
||||
if (!env->IsInstanceOf(jVolumeGroups, gArrayListClass)) {
|
||||
ALOGE("listAudioVolumeGroups not an arraylist");
|
||||
return (jint)AUDIO_JAVA_BAD_VALUE;
|
||||
}
|
||||
|
||||
status_t status;
|
||||
AudioVolumeGroupVector groups;
|
||||
jint jStatus;
|
||||
jobject jGroup = NULL;
|
||||
|
||||
status = AudioSystem::listAudioVolumeGroups(groups);
|
||||
if (status != NO_ERROR) {
|
||||
ALOGE("AudioSystem::listAudioVolumeGroups error %d", status);
|
||||
return nativeToJavaStatus(status);
|
||||
}
|
||||
for (const auto &group : groups) {
|
||||
jStatus = convertAudioVolumeGroupsFromNative(env, &jGroup, group);
|
||||
if (jStatus != AUDIO_JAVA_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
env->CallBooleanMethod(jVolumeGroups, gArrayListMethods.add, jGroup);
|
||||
}
|
||||
exit:
|
||||
if (jGroup != NULL) {
|
||||
env->DeleteLocalRef(jGroup);
|
||||
}
|
||||
return jStatus;
|
||||
}
|
||||
|
||||
/*
|
||||
* JNI registration.
|
||||
*/
|
||||
static const JNINativeMethod gMethods[] = {
|
||||
{"native_list_audio_volume_groups", "(Ljava/util/ArrayList;)I",
|
||||
(void *)android_media_AudioSystem_listAudioVolumeGroups},
|
||||
};
|
||||
|
||||
int register_android_media_AudioVolumeGroups(JNIEnv *env)
|
||||
{
|
||||
jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
|
||||
gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
|
||||
gArrayListCstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
|
||||
gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
|
||||
gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass,
|
||||
"toArray", "()[Ljava/lang/Object;");
|
||||
|
||||
jclass audioVolumeGroupClass = FindClassOrDie(env, kAudioVolumeGroupClassPathName);
|
||||
gAudioVolumeGroupClass = MakeGlobalRefOrDie(env, audioVolumeGroupClass);
|
||||
gAudioVolumeGroupCstor = GetMethodIDOrDie(
|
||||
env, audioVolumeGroupClass, "<init>",
|
||||
"(Ljava/lang/String;I[Landroid/media/AudioAttributes;[I)V");
|
||||
|
||||
gAudioVolumeGroupFields.mName = GetFieldIDOrDie(
|
||||
env, audioVolumeGroupClass, "mName", "Ljava/lang/String;");
|
||||
gAudioVolumeGroupFields.mId = GetFieldIDOrDie(
|
||||
env, audioVolumeGroupClass, "mId", "I");
|
||||
|
||||
env->DeleteLocalRef(audioVolumeGroupClass);
|
||||
|
||||
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
|
||||
}
|
||||
@@ -37,6 +37,8 @@ import android.content.Intent;
|
||||
import android.media.audiopolicy.AudioPolicy;
|
||||
import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
|
||||
import android.media.audiopolicy.AudioProductStrategies;
|
||||
import android.media.audiopolicy.AudioVolumeGroupChangeHandler;
|
||||
import android.media.audiopolicy.AudioVolumeGroups;
|
||||
import android.media.projection.MediaProjection;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSession;
|
||||
@@ -61,6 +63,7 @@ import android.util.Pair;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -90,6 +93,8 @@ public class AudioManager {
|
||||
private static final String TAG = "AudioManager";
|
||||
private static final boolean DEBUG = false;
|
||||
private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler();
|
||||
private static final AudioVolumeGroupChangeHandler sAudioAudioVolumeGroupChangedHandler =
|
||||
new AudioVolumeGroupChangeHandler();
|
||||
|
||||
/**
|
||||
* Broadcast intent, a hint for applications that audio is about to become
|
||||
@@ -5246,6 +5251,60 @@ public class AudioManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Introspection API to retrieve audio volume groups.
|
||||
* When implementing {Car|Oem}AudioManager, use this method to retrieve the collection of
|
||||
* audio volume groups.
|
||||
* @return a (possibly zero-length) array of
|
||||
* {@see android.media.audiopolicy.AudioVolumeGroups} objects.
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
|
||||
public @NonNull AudioVolumeGroups getAudioVolumeGroups() {
|
||||
return new AudioVolumeGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Callback registered by client to be notified upon volume group change.
|
||||
*/
|
||||
@SystemApi
|
||||
public abstract static class VolumeGroupCallback {
|
||||
/**
|
||||
* Callback method called upon audio volume group change.
|
||||
* @param group the group for which the volume has changed
|
||||
*/
|
||||
public void onAudioVolumeGroupChanged(int group, int flags) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Register an audio volume group change listener.
|
||||
* @param callback the {@link VolumeGroupCallback} to register
|
||||
*/
|
||||
@SystemApi
|
||||
public void registerVolumeGroupCallback(
|
||||
@NonNull Executor executor,
|
||||
@NonNull VolumeGroupCallback callback) {
|
||||
Preconditions.checkNotNull(executor, "executor must not be null");
|
||||
Preconditions.checkNotNull(callback, "volume group change cb must not be null");
|
||||
sAudioAudioVolumeGroupChangedHandler.init();
|
||||
// TODO: make use of executor
|
||||
sAudioAudioVolumeGroupChangedHandler.registerListener(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Unregister an audio volume group change listener.
|
||||
* @param callback the {@link VolumeGroupCallback} to unregister
|
||||
*/
|
||||
@SystemApi
|
||||
public void unregisterVolumeGroupCallback(
|
||||
@NonNull VolumeGroupCallback callback) {
|
||||
Preconditions.checkNotNull(callback, "volume group change cb must not be null");
|
||||
sAudioAudioVolumeGroupChangedHandler.unregisterListener(callback);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Inner classes
|
||||
|
||||
@@ -177,7 +177,50 @@ public final class AudioProductStrategies implements Iterable<AudioProductStrate
|
||||
@Nullable
|
||||
public AudioProductStrategy getProductStrategyForAudioAttributes(@NonNull AudioAttributes aa) {
|
||||
Preconditions.checkNotNull(aa, "attributes must not be null");
|
||||
return getById(native_get_product_strategies_from_audio_attributes(aa));
|
||||
int productStrategyId = native_get_product_strategies_from_audio_attributes(aa);
|
||||
if (productStrategyId < 0) {
|
||||
Log.w(TAG, "no strategy found for Attributes " + aa.toString());
|
||||
return null;
|
||||
}
|
||||
return getById(productStrategyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @param attributes the {@link AudioAttributes} to be considered
|
||||
* @return volume group associated to the given {@link AudioAttributes}.
|
||||
* If no group supports the given {@link AudioAttributes}, it returns the volume group
|
||||
* for the default attributes.
|
||||
* If no group supports the default attributes, it returns {@link #DEFAULT_VOLUME_GROUP}
|
||||
*/
|
||||
@SystemApi
|
||||
public int getVolumeGroupIdForAttributes(@NonNull AudioAttributes attributes) {
|
||||
Preconditions.checkNotNull(attributes, "attributes must not be null");
|
||||
int volumeGroupId = getVolumeGroupIdForAttributesInt(attributes);
|
||||
if (volumeGroupId != AudioVolumeGroups.DEFAULT_VOLUME_GROUP) {
|
||||
return volumeGroupId;
|
||||
}
|
||||
// The default volume group is the one hosted by default product strategy, i.e.
|
||||
// supporting Default Attributes
|
||||
return getVolumeGroupIdForAttributesInt(AudioProductStrategy.sDefaultAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @param streamType to be considered
|
||||
* @return volume group associated to the given stream type.
|
||||
*/
|
||||
@SystemApi
|
||||
public int getVolumeGroupIdForLegacyStreamType(int streamType) {
|
||||
for (final AudioProductStrategy productStrategy : this) {
|
||||
int volumeGroupId = productStrategy.getVolumeGroupIdForLegacyStreamType(streamType);
|
||||
if (volumeGroupId != AudioVolumeGroups.DEFAULT_VOLUME_GROUP) {
|
||||
return volumeGroupId;
|
||||
}
|
||||
}
|
||||
// The default volume group is the one hosted by default product strategy, i.e.
|
||||
// supporting Default Attributes
|
||||
return getVolumeGroupIdForAttributesInt(AudioProductStrategy.sDefaultAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,6 +236,21 @@ public final class AudioProductStrategies implements Iterable<AudioProductStrate
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param attributes to be considered
|
||||
* @return volume group associated to the given {@link AudioAttributes}.
|
||||
*/
|
||||
private int getVolumeGroupIdForAttributesInt(@NonNull AudioAttributes attributes) {
|
||||
Preconditions.checkNotNull(attributes, "attributes must not be null");
|
||||
for (final AudioProductStrategy productStrategy : this) {
|
||||
int volumeGroupId = productStrategy.getVolumeGroupIdForAudioAttributes(attributes);
|
||||
if (volumeGroupId != AudioVolumeGroups.DEFAULT_VOLUME_GROUP) {
|
||||
return volumeGroupId;
|
||||
}
|
||||
}
|
||||
return AudioVolumeGroups.DEFAULT_VOLUME_GROUP;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<AudioProductStrategies> CREATOR =
|
||||
new Parcelable.Creator<AudioProductStrategies>() {
|
||||
@Override
|
||||
|
||||
@@ -157,32 +157,32 @@ public final class AudioProductStrategy implements Parcelable {
|
||||
/**
|
||||
* @hide
|
||||
* @param streamType legacy stream type used for volume operation only
|
||||
* @return the {@link AudioAttributes} relevant for the given streamType.
|
||||
* If none is found, it builds the default attributes.
|
||||
* @return the volume group id relevant for the given streamType.
|
||||
* If none is found, {@link AudioVolumeGroups#DEFAULT_VOLUME_GROUP} is returned.
|
||||
*/
|
||||
public int getGroupIdForLegacyStreamType(int streamType) {
|
||||
public int getVolumeGroupIdForLegacyStreamType(int streamType) {
|
||||
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
|
||||
if (aag.supportsStreamType(streamType)) {
|
||||
return aag.getGroupId();
|
||||
return aag.getVolumeGroupId();
|
||||
}
|
||||
}
|
||||
return DEFAULT_GROUP;
|
||||
return AudioVolumeGroups.DEFAULT_VOLUME_GROUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @param aa the {@link AudioAttributes} to be considered
|
||||
* @return the group id associated with the given audio attributes if found,
|
||||
* default value otherwise.
|
||||
* @return the volume group id associated with the given audio attributes if found,
|
||||
* {@link AudioVolumeGroups#DEFAULT_VOLUME_GROUP} otherwise.
|
||||
*/
|
||||
public int getGroupIdForAudioAttributes(@NonNull AudioAttributes aa) {
|
||||
public int getVolumeGroupIdForAudioAttributes(@NonNull AudioAttributes aa) {
|
||||
Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
|
||||
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
|
||||
if (aag.supportsAttributes(aa)) {
|
||||
return aag.getGroupId();
|
||||
return aag.getVolumeGroupId();
|
||||
}
|
||||
}
|
||||
return DEFAULT_GROUP;
|
||||
return AudioVolumeGroups.DEFAULT_VOLUME_GROUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -266,15 +266,14 @@ public final class AudioProductStrategy implements Parcelable {
|
||||
&& ((refFormattedTags.length() == 0) || refFormattedTags.equals(cliFormattedTags));
|
||||
}
|
||||
|
||||
|
||||
private static final class AudioAttributesGroup implements Parcelable {
|
||||
private int mGroupId;
|
||||
private int mVolumeGroupId;
|
||||
private int mLegacyStreamType;
|
||||
private final AudioAttributes[] mAudioAttributes;
|
||||
|
||||
AudioAttributesGroup(int groupId, int streamType,
|
||||
AudioAttributesGroup(int volumeGroupId, int streamType,
|
||||
@NonNull AudioAttributes[] audioAttributes) {
|
||||
mGroupId = groupId;
|
||||
mVolumeGroupId = volumeGroupId;
|
||||
mLegacyStreamType = streamType;
|
||||
mAudioAttributes = audioAttributes;
|
||||
}
|
||||
@@ -286,7 +285,7 @@ public final class AudioProductStrategy implements Parcelable {
|
||||
|
||||
AudioAttributesGroup thatAag = (AudioAttributesGroup) o;
|
||||
|
||||
return mGroupId == thatAag.mGroupId
|
||||
return mVolumeGroupId == thatAag.mVolumeGroupId
|
||||
&& mLegacyStreamType == thatAag.mLegacyStreamType
|
||||
&& mAudioAttributes.equals(thatAag.mAudioAttributes);
|
||||
}
|
||||
@@ -295,8 +294,8 @@ public final class AudioProductStrategy implements Parcelable {
|
||||
return mLegacyStreamType;
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return mGroupId;
|
||||
public int getVolumeGroupId() {
|
||||
return mVolumeGroupId;
|
||||
}
|
||||
|
||||
public @NonNull AudioAttributes getAudioAttributes() {
|
||||
@@ -331,7 +330,7 @@ public final class AudioProductStrategy implements Parcelable {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(mGroupId);
|
||||
dest.writeInt(mVolumeGroupId);
|
||||
dest.writeInt(mLegacyStreamType);
|
||||
dest.writeInt(mAudioAttributes.length);
|
||||
for (AudioAttributes attributes : mAudioAttributes) {
|
||||
@@ -343,14 +342,14 @@ public final class AudioProductStrategy implements Parcelable {
|
||||
new Parcelable.Creator<AudioAttributesGroup>() {
|
||||
@Override
|
||||
public AudioAttributesGroup createFromParcel(@NonNull Parcel in) {
|
||||
int groupId = in.readInt();
|
||||
int volumeGroupId = in.readInt();
|
||||
int streamType = in.readInt();
|
||||
int nbAttributes = in.readInt();
|
||||
AudioAttributes[] aa = new AudioAttributes[nbAttributes];
|
||||
for (int index = 0; index < nbAttributes; index++) {
|
||||
aa[index] = AudioAttributes.CREATOR.createFromParcel(in);
|
||||
}
|
||||
return new AudioAttributesGroup(groupId, streamType, aa);
|
||||
return new AudioAttributesGroup(volumeGroupId, streamType, aa);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -365,8 +364,8 @@ public final class AudioProductStrategy implements Parcelable {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("\n Legacy Stream Type: ");
|
||||
s.append(Integer.toString(mLegacyStreamType));
|
||||
s.append(" Group Id: ");
|
||||
s.append(Integer.toString(mGroupId));
|
||||
s.append(" Volume Group Id: ");
|
||||
s.append(Integer.toString(mVolumeGroupId));
|
||||
|
||||
for (AudioAttributes attribute : mAudioAttributes) {
|
||||
s.append("\n -");
|
||||
|
||||
18
media/java/android/media/audiopolicy/AudioVolumeGroup.aidl
Normal file
18
media/java/android/media/audiopolicy/AudioVolumeGroup.aidl
Normal file
@@ -0,0 +1,18 @@
|
||||
/* Copyright 2018, 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.audiopolicy;
|
||||
|
||||
parcelable AudioVolumeGroup;
|
||||
171
media/java/android/media/audiopolicy/AudioVolumeGroup.java
Normal file
171
media/java/android/media/audiopolicy/AudioVolumeGroup.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.audiopolicy;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.media.AudioAttributes;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class to create the association between different playback attributes
|
||||
* (e.g. media, mapping direction) to a single volume control.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class AudioVolumeGroup implements Parcelable {
|
||||
/**
|
||||
* Unique identifier of a volume group.
|
||||
*/
|
||||
private int mId;
|
||||
/**
|
||||
* human-readable name of this volume group.
|
||||
*/
|
||||
private final String mName;
|
||||
|
||||
private final AudioAttributes[] mAudioAttributes;
|
||||
private int[] mLegacyStreamTypes;
|
||||
|
||||
/**
|
||||
* @param name of the volume group
|
||||
* @param id of the volume group
|
||||
* @param followers {@link AudioProductStrategies} strategy following this volume group
|
||||
*/
|
||||
AudioVolumeGroup(@NonNull String name, int id,
|
||||
@NonNull AudioAttributes[] audioAttributes,
|
||||
@NonNull int[] legacyStreamTypes) {
|
||||
Preconditions.checkNotNull(name, "name must not be null");
|
||||
Preconditions.checkNotNull(audioAttributes, "audioAttributes must not be null");
|
||||
Preconditions.checkNotNull(legacyStreamTypes, "legacyStreamTypes must not be null");
|
||||
mName = name;
|
||||
mId = id;
|
||||
mAudioAttributes = audioAttributes;
|
||||
mLegacyStreamTypes = legacyStreamTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@NonNull Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
AudioVolumeGroup thatAvg = (AudioVolumeGroup) o;
|
||||
|
||||
return mName == thatAvg.mName && mId == thatAvg.mId
|
||||
&& mAudioAttributes.equals(thatAvg.mAudioAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List of {@link AudioAttributes} involved in this {@link AudioVolumeGroup}.
|
||||
*/
|
||||
public List<AudioAttributes> getAudioAttributes() {
|
||||
return Arrays.asList(mAudioAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the stream types involved in this {@link AudioVolumeGroup}.
|
||||
*/
|
||||
public @NonNull int[] getLegacyStreamTypes() {
|
||||
return mLegacyStreamTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return human-readable name of this volume group.
|
||||
*/
|
||||
public @NonNull String name() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the volume group unique identifier id.
|
||||
*/
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeString(mName);
|
||||
dest.writeInt(mId);
|
||||
dest.writeInt(mAudioAttributes.length);
|
||||
for (AudioAttributes attributes : mAudioAttributes) {
|
||||
attributes.writeToParcel(dest, flags | AudioAttributes.FLATTEN_TAGS/*flags*/);
|
||||
}
|
||||
dest.writeInt(mLegacyStreamTypes.length);
|
||||
for (int streamType : mLegacyStreamTypes) {
|
||||
dest.writeInt(streamType);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<AudioVolumeGroup> CREATOR =
|
||||
new Parcelable.Creator<AudioVolumeGroup>() {
|
||||
@Override
|
||||
public @NonNull AudioVolumeGroup createFromParcel(@NonNull Parcel in) {
|
||||
Preconditions.checkNotNull(in, "in Parcel must not be null");
|
||||
String name = in.readString();
|
||||
int id = in.readInt();
|
||||
int nbAttributes = in.readInt();
|
||||
AudioAttributes[] audioAttributes = new AudioAttributes[nbAttributes];
|
||||
for (int index = 0; index < nbAttributes; index++) {
|
||||
audioAttributes[index] = AudioAttributes.CREATOR.createFromParcel(in);
|
||||
}
|
||||
int nbStreamTypes = in.readInt();
|
||||
int[] streamTypes = new int[nbStreamTypes];
|
||||
for (int index = 0; index < nbStreamTypes; index++) {
|
||||
streamTypes[index] = in.readInt();
|
||||
}
|
||||
return new AudioVolumeGroup(name, id, audioAttributes, streamTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull AudioVolumeGroup[] newArray(int size) {
|
||||
return new AudioVolumeGroup[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public @NonNull String toString() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("\n Name: ");
|
||||
s.append(mName);
|
||||
s.append(" Id: ");
|
||||
s.append(Integer.toString(mId));
|
||||
|
||||
s.append("\n Supported Audio Attributes:");
|
||||
for (AudioAttributes attribute : mAudioAttributes) {
|
||||
s.append("\n -");
|
||||
s.append(attribute.toString());
|
||||
}
|
||||
s.append("\n Supported Legacy Stream Types: { ");
|
||||
for (int legacyStreamType : mLegacyStreamTypes) {
|
||||
s.append(Integer.toString(legacyStreamType));
|
||||
s.append(" ");
|
||||
}
|
||||
s.append("}");
|
||||
return s.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.audiopolicy;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Message;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* The AudioVolumeGroupChangeHandler handles AudioManager.OnAudioVolumeGroupChangedListener
|
||||
* callbacks posted from JNI
|
||||
*
|
||||
* TODO: Make use of Executor of callbacks.
|
||||
* @hide
|
||||
*/
|
||||
public class AudioVolumeGroupChangeHandler {
|
||||
private Handler mHandler;
|
||||
private HandlerThread mHandlerThread;
|
||||
private final ArrayList<AudioManager.VolumeGroupCallback> mListeners =
|
||||
new ArrayList<AudioManager.VolumeGroupCallback>();
|
||||
|
||||
private static final String TAG = "AudioVolumeGroupChangeHandler";
|
||||
|
||||
private static final int AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED = 1000;
|
||||
private static final int AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER = 4;
|
||||
|
||||
/**
|
||||
* Accessed by native methods: JNI Callback context.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private long mJniCallback;
|
||||
|
||||
/**
|
||||
* Initialization
|
||||
*/
|
||||
public void init() {
|
||||
synchronized (this) {
|
||||
if (mHandler != null) {
|
||||
return;
|
||||
}
|
||||
// create a new thread for our new event handler
|
||||
mHandlerThread = new HandlerThread(TAG);
|
||||
mHandlerThread.start();
|
||||
|
||||
if (mHandlerThread.getLooper() == null) {
|
||||
mHandler = null;
|
||||
return;
|
||||
}
|
||||
mHandler = new Handler(mHandlerThread.getLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
ArrayList<AudioManager.VolumeGroupCallback> listeners;
|
||||
synchronized (this) {
|
||||
if (msg.what == AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER) {
|
||||
listeners =
|
||||
new ArrayList<AudioManager.VolumeGroupCallback>();
|
||||
if (mListeners.contains(msg.obj)) {
|
||||
listeners.add(
|
||||
(AudioManager.VolumeGroupCallback) msg.obj);
|
||||
}
|
||||
} else {
|
||||
listeners = mListeners;
|
||||
}
|
||||
}
|
||||
if (listeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (msg.what) {
|
||||
case AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED:
|
||||
for (int i = 0; i < listeners.size(); i++) {
|
||||
listeners.get(i).onAudioVolumeGroupChanged((int) msg.arg1,
|
||||
(int) msg.arg2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
native_setup(new WeakReference<AudioVolumeGroupChangeHandler>(this));
|
||||
}
|
||||
}
|
||||
|
||||
private native void native_setup(Object moduleThis);
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
native_finalize();
|
||||
if (mHandlerThread.isAlive()) {
|
||||
mHandlerThread.quit();
|
||||
}
|
||||
}
|
||||
private native void native_finalize();
|
||||
|
||||
/**
|
||||
* @param cb the {@link AudioManager.VolumeGroupCallback} to register
|
||||
*/
|
||||
public void registerListener(@NonNull AudioManager.VolumeGroupCallback cb) {
|
||||
Preconditions.checkNotNull(cb, "volume group callback shall not be null");
|
||||
synchronized (this) {
|
||||
mListeners.add(cb);
|
||||
}
|
||||
if (mHandler != null) {
|
||||
Message m = mHandler.obtainMessage(
|
||||
AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER, 0, 0, cb);
|
||||
mHandler.sendMessage(m);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cb the {@link AudioManager.VolumeGroupCallback} to unregister
|
||||
*/
|
||||
public void unregisterListener(@NonNull AudioManager.VolumeGroupCallback cb) {
|
||||
Preconditions.checkNotNull(cb, "volume group callback shall not be null");
|
||||
synchronized (this) {
|
||||
mListeners.remove(cb);
|
||||
}
|
||||
}
|
||||
|
||||
Handler handler() {
|
||||
return mHandler;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void postEventFromNative(Object moduleRef,
|
||||
int what, int arg1, int arg2, Object obj) {
|
||||
AudioVolumeGroupChangeHandler eventHandler =
|
||||
(AudioVolumeGroupChangeHandler) ((WeakReference) moduleRef).get();
|
||||
if (eventHandler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventHandler != null) {
|
||||
Handler handler = eventHandler.handler();
|
||||
if (handler != null) {
|
||||
Message m = handler.obtainMessage(what, arg1, arg2, obj);
|
||||
if (what != AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER) {
|
||||
handler.removeMessages(what);
|
||||
}
|
||||
handler.sendMessage(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
media/java/android/media/audiopolicy/AudioVolumeGroups.aidl
Normal file
18
media/java/android/media/audiopolicy/AudioVolumeGroups.aidl
Normal file
@@ -0,0 +1,18 @@
|
||||
/* Copyright 2018, 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.audiopolicy;
|
||||
|
||||
parcelable AudioVolumeGroups;
|
||||
135
media/java/android/media/audiopolicy/AudioVolumeGroups.java
Normal file
135
media/java/android/media/audiopolicy/AudioVolumeGroups.java
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.audiopolicy;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.media.AudioSystem;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* A class to encapsulate a collection of {@link AudioVolumeGroup}.
|
||||
*/
|
||||
@SystemApi
|
||||
public final class AudioVolumeGroups implements Iterable<AudioVolumeGroup>, Parcelable {
|
||||
|
||||
private final ArrayList<AudioVolumeGroup> mAudioVolumeGroupList;
|
||||
|
||||
private static final String TAG = "AudioVolumeGroups";
|
||||
|
||||
/**
|
||||
* Volume group value to use when introspection API fails.
|
||||
*/
|
||||
public static final int DEFAULT_VOLUME_GROUP = -1;
|
||||
|
||||
public AudioVolumeGroups() {
|
||||
ArrayList<AudioVolumeGroup> avgList = new ArrayList<AudioVolumeGroup>();
|
||||
int status = native_list_audio_volume_groups(avgList);
|
||||
if (status != AudioSystem.SUCCESS) {
|
||||
Log.w(TAG, ": listAudioVolumeGroups failed");
|
||||
}
|
||||
mAudioVolumeGroupList = avgList;
|
||||
}
|
||||
|
||||
private AudioVolumeGroups(@NonNull ArrayList<AudioVolumeGroup> audioVolumeGroupList) {
|
||||
Preconditions.checkNotNull(audioVolumeGroupList, "audioVolumeGroupList must not be null");
|
||||
mAudioVolumeGroupList = audioVolumeGroupList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of {@link AudioProductStrategy} objects
|
||||
*/
|
||||
public int size() {
|
||||
return mAudioVolumeGroupList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link Iterator}
|
||||
*/
|
||||
@Override
|
||||
public Iterator<AudioVolumeGroup> iterator() {
|
||||
return mAudioVolumeGroupList.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@NonNull Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
AudioVolumeGroups that = (AudioVolumeGroups) o;
|
||||
|
||||
return mAudioVolumeGroupList.equals(that.mAudioVolumeGroupList);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the matching {@link AudioVolumeGroup} objects with the given id,
|
||||
* null object if not found.
|
||||
*/
|
||||
public @Nullable AudioVolumeGroup getById(int volumeGroupId) {
|
||||
for (final AudioVolumeGroup avg : this) {
|
||||
if (avg.getId() == volumeGroupId) {
|
||||
return avg;
|
||||
}
|
||||
}
|
||||
Log.e(TAG, ": invalid volume group id: " + volumeGroupId + " requested");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(size());
|
||||
for (final AudioVolumeGroup volumeGroup : this) {
|
||||
volumeGroup.writeToParcel(dest, flags);
|
||||
}
|
||||
}
|
||||
|
||||
private static native int native_list_audio_volume_groups(
|
||||
ArrayList<AudioVolumeGroup> groups);
|
||||
|
||||
public static final Parcelable.Creator<AudioVolumeGroups> CREATOR =
|
||||
new Parcelable.Creator<AudioVolumeGroups>() {
|
||||
@Override
|
||||
public @NonNull AudioVolumeGroups createFromParcel(@NonNull Parcel in) {
|
||||
Preconditions.checkNotNull(in, "in Parcel must not be null");
|
||||
ArrayList<AudioVolumeGroup> avgList = new ArrayList<AudioVolumeGroup>();
|
||||
int size = in.readInt();
|
||||
for (int index = 0; index < size; index++) {
|
||||
avgList.add(AudioVolumeGroup.CREATOR.createFromParcel(in));
|
||||
}
|
||||
return new AudioVolumeGroups(avgList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull AudioVolumeGroups[] newArray(int size) {
|
||||
return new AudioVolumeGroups[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user