summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/system-current.txt3
-rw-r--r--core/jni/android_media_AudioSystem.cpp51
-rw-r--r--media/java/android/media/AudioManager.java5
-rw-r--r--media/java/android/media/AudioSystem.java7
-rw-r--r--media/java/android/media/IAudioService.aidl5
-rw-r--r--media/java/android/media/audiopolicy/AudioMix.java14
-rw-r--r--media/java/android/media/audiopolicy/AudioPolicy.java75
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java84
8 files changed, 241 insertions, 3 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index 7f3c152364b0..364d2de2d55e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3001,6 +3001,7 @@ package android.media {
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
field public static final int FLAG_FROM_KEY = 65536; // 0x10000
+ field public static final int SUCCESS = 0; // 0x0
}
public static abstract class AudioManager.AudioServerStateCallback {
@@ -3117,8 +3118,10 @@ package android.media.audiopolicy {
method public int detachMixes(java.util.List<android.media.audiopolicy.AudioMix>);
method public int getFocusDuckingBehavior();
method public int getStatus();
+ method public int removeUidDeviceAffinity(int);
method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setRegistration(java.lang.String);
+ method public int setUidDeviceAffinity(int, java.util.List<android.media.AudioDeviceInfo>);
method public java.lang.String toLogFriendlyString();
field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 29d8f30543e6..80560f826235 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1906,6 +1906,53 @@ exit:
return jStatus;
}
+static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobject clazz,
+ jint uid, jintArray deviceTypes, jobjectArray deviceAddresses) {
+ if (deviceTypes == nullptr || deviceAddresses == nullptr) {
+ return (jint) AUDIO_JAVA_BAD_VALUE;
+ }
+ jsize nb = env->GetArrayLength(deviceTypes);
+ if (nb == 0 || nb != env->GetArrayLength(deviceAddresses)) {
+ return (jint) AUDIO_JAVA_BAD_VALUE;
+ }
+ // retrieve all device types
+ std::vector<audio_devices_t> deviceTypesVector;
+ jint* typesPtr = nullptr;
+ typesPtr = env->GetIntArrayElements(deviceTypes, 0);
+ if (typesPtr == nullptr) {
+ return (jint) AUDIO_JAVA_BAD_VALUE;
+ }
+ for (jint i = 0; i < nb; i++) {
+ deviceTypesVector.push_back((audio_devices_t) typesPtr[i]);
+ }
+ env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
+
+ // check each address is a string and add device type/address to list for device affinity
+ Vector<AudioDeviceTypeAddr> deviceVector;
+ jclass stringClass = FindClassOrDie(env, "java/lang/String");
+ for (jint i = 0; i < nb; i++) {
+ jobject addrJobj = env->GetObjectArrayElement(deviceAddresses, i);
+ if (!env->IsInstanceOf(addrJobj, stringClass)) {
+ return (jint) AUDIO_JAVA_BAD_VALUE;
+ }
+ String8 address = String8(env->GetStringUTFChars((jstring) addrJobj, NULL));
+ AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
+ deviceVector.add(dev);
+ }
+
+ status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector);
+ return (jint) nativeToJavaStatus(status);
+}
+
+static jint android_media_AudioSystem_removeUidDeviceAffinities(JNIEnv *env, jobject clazz,
+ jint uid) {
+
+ //###
+ status_t status = NO_ERROR;//AudioSystem::removeUidDeviceAffinities();
+ return (jint) nativeToJavaStatus(status);
+}
+
+
static jint
android_media_AudioSystem_systemReady(JNIEnv *env, jobject thiz)
{
@@ -2133,6 +2180,10 @@ static const JNINativeMethod gMethods[] = {
(void *)android_media_AudioSystem_getAudioHwSyncForSession},
{"registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
(void *)android_media_AudioSystem_registerPolicyMixes},
+ {"setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_setUidDeviceAffinities},
+ {"removeUidDeviceAffinities", "(I)I",
+ (void *)android_media_AudioSystem_removeUidDeviceAffinities},
{"native_register_dynamic_policy_callback", "()V",
(void *)android_media_AudioSystem_registerDynPolicyCallback},
{"native_register_recording_callback", "()V",
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 63eefe04c1d1..30b5480fba55 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4280,9 +4280,8 @@ public class AudioManager {
* Return codes for listAudioPorts(), createAudioPatch() ...
*/
- /** @hide
- * CANDIDATE FOR PUBLIC API
- */
+ /** @hide */
+ @SystemApi
public static final int SUCCESS = AudioSystem.SUCCESS;
/**
* A default error code.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 58fc1ab1c4dd..45cde0ffecc4 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -916,6 +916,13 @@ public class AudioSystem
public static native int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register);
+ /** see AudioPolicy.setUidDeviceAffinities() */
+ public static native int setUidDeviceAffinities(int uid, @NonNull int[] types,
+ @NonNull String[] addresses);
+
+ /** see AudioPolicy.removeUidDeviceAffinities() */
+ public static native int removeUidDeviceAffinities(int uid);
+
public static native int systemReady();
public static native float getStreamVolumeDB(int stream, int index, int device);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index abd64119de61..9fbd7eac6e8a 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -223,6 +223,11 @@ interface IAudioService {
boolean isAudioServerRunning();
+ int setUidDeviceAffinity(in IAudioPolicyCallback pcb, in int uid, in int[] deviceTypes,
+ in String[] deviceAddresses);
+
+ int removeUidDeviceAffinity(in IAudioPolicyCallback pcb, in int uid);
+
// WARNING: read warning at top of file, new methods that need to be used by native
// code via IAudioManager.h need to be added to the top section.
}
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 7fb3aa6d79f9..1c6210e0c060 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -165,6 +165,20 @@ public class AudioMix {
}
/** @hide */
+ public boolean isRoutedToDevice(int deviceType, @NonNull String deviceAddress) {
+ if ((mRouteFlags & ROUTE_FLAG_RENDER) != ROUTE_FLAG_RENDER) {
+ return false;
+ }
+ if (deviceType != mDeviceSystemType) {
+ return false;
+ }
+ if (!deviceAddress.equals(mDeviceAddress)) {
+ return false;
+ }
+ return true;
+ }
+
+ /** @hide */
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 6103f55745f9..65f3294800f5 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioAttributes;
+import android.media.AudioDeviceInfo;
import android.media.AudioFocusInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
@@ -323,6 +324,80 @@ public class AudioPolicy {
}
}
+ /**
+ * @hide
+ * Configures the audio framework so that all audio stream originating from the given UID
+ * can only come from a set of audio devices.
+ * For this routing to be operational, a number of {@link AudioMix} instances must have been
+ * previously registered on this policy, and routed to a super-set of the given audio devices
+ * with {@link AudioMix.Builder#setDevice(android.media.AudioDeviceInfo)}. Note that having
+ * multiple devices in the list doesn't imply the signals will be duplicated on the different
+ * audio devices, final routing will depend on the {@link AudioAttributes} of the sounds being
+ * played.
+ * @param uid UID of the application to affect.
+ * @param devices list of devices to which the audio stream of the application may be routed.
+ * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+ * otherwise.
+ */
+ @SystemApi
+ public int setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) {
+ if (devices == null) {
+ throw new IllegalArgumentException("Illegal null list of audio devices");
+ }
+ synchronized (mLock) {
+ if (mStatus != POLICY_STATUS_REGISTERED) {
+ throw new IllegalStateException("Cannot use unregistered AudioPolicy");
+ }
+ final int[] deviceTypes = new int[devices.size()];
+ final String[] deviceAdresses = new String[devices.size()];
+ int i = 0;
+ for (AudioDeviceInfo device : devices) {
+ if (device == null) {
+ throw new IllegalArgumentException(
+ "Illegal null AudioDeviceInfo in setUidDeviceAffinity");
+ }
+ deviceTypes[i] =
+ AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
+ deviceAdresses[i] = device.getAddress();
+ i++;
+ }
+ final IAudioService service = getService();
+ try {
+ final int status = service.setUidDeviceAffinity(this.cb(),
+ uid, deviceTypes, deviceAdresses);
+ return status;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setUidDeviceAffinity", e);
+ return AudioManager.ERROR;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Removes audio device affinity previously set by
+ * {@link #setUidDeviceAffinity(int, java.util.List)}.
+ * @param uid UID of the application affected.
+ * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+ * otherwise.
+ */
+ @SystemApi
+ public int removeUidDeviceAffinity(int uid) {
+ synchronized (mLock) {
+ if (mStatus != POLICY_STATUS_REGISTERED) {
+ throw new IllegalStateException("Cannot use unregistered AudioPolicy");
+ }
+ final IAudioService service = getService();
+ try {
+ final int status = service.removeUidDeviceAffinity(this.cb(), uid);
+ return status;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in removeUidDeviceAffinity", e);
+ return AudioManager.ERROR;
+ }
+ }
+ }
+
public void setRegistration(String regId) {
synchronized (mLock) {
mRegistrationId = regId;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6cde4adb9f11..d704a3ea8eb1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7822,6 +7822,36 @@ public class AudioService extends IAudioService.Stub
return AudioManager.SUCCESS;
}
+ /** see AudioPolicy.setUidDeviceAffinity() */
+ public int setUidDeviceAffinity(IAudioPolicyCallback pcb, int uid,
+ @NonNull int[] deviceTypes,
+ @NonNull String[] deviceAddresses) {
+ synchronized (mAudioPolicies) {
+ final AudioPolicyProxy app =
+ checkUpdateForPolicy(pcb, "Cannot change device affinity in audio policy");
+ if (app == null) {
+ return AudioManager.ERROR;
+ }
+ if (!app.hasMixRoutedToDevices(deviceTypes, deviceAddresses)) {
+ return AudioManager.ERROR;
+ }
+ }
+ return AudioManager.SUCCESS;
+ }
+
+ /** see AudioPolicy.removeUidDeviceAffinity() */
+ public int removeUidDeviceAffinity(IAudioPolicyCallback pcb, int uid) {
+ synchronized (mAudioPolicies) {
+ final AudioPolicyProxy app =
+ checkUpdateForPolicy(pcb, "Cannot remove device affinity in audio policy");
+ if (app == null) {
+ return AudioManager.ERROR;
+ }
+
+ }
+ return AudioManager.SUCCESS;
+ }
+
public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) {
if (DEBUG_AP) Log.d(TAG, "setFocusPropertiesForPolicy() duck behavior=" + duckingBehavior
+ " policy " + pcb.asBinder());
@@ -7994,6 +8024,15 @@ public class AudioService extends IAudioService.Stub
//======================
// Audio policy proxy
//======================
+ private static final class AudioDeviceArray {
+ final @NonNull int[] mDeviceTypes;
+ final @NonNull String[] mDeviceAddresses;
+ AudioDeviceArray(@NonNull int[] types, @NonNull String[] addresses) {
+ mDeviceTypes = types;
+ mDeviceAddresses = addresses;
+ }
+ }
+
/**
* This internal class inherits from AudioPolicyConfig, each instance contains all the
* mixes of an AudioPolicy and their configurations.
@@ -8003,6 +8042,8 @@ public class AudioService extends IAudioService.Stub
final IAudioPolicyCallback mPolicyCallback;
final boolean mHasFocusListener;
final boolean mIsVolumeController;
+ final HashMap<Integer, AudioDeviceArray> mUidDeviceAffinities =
+ new HashMap<Integer, AudioDeviceArray>();
/**
* Audio focus ducking behavior for an audio policy.
* This variable reflects the value that was successfully set in
@@ -8075,6 +8116,26 @@ public class AudioService extends IAudioService.Stub
return false;
}
+ // Verify all the devices in the array are served by mixes defined in this policy
+ boolean hasMixRoutedToDevices(@NonNull int[] deviceTypes,
+ @NonNull String[] deviceAddresses) {
+ for (int i = 0; i < deviceTypes.length; i++) {
+ boolean hasDevice = false;
+ for (AudioMix mix : mMixes) {
+ // this will check both that the mix has ROUTE_FLAG_RENDER and the device
+ // is reached by this mix
+ if (mix.isRoutedToDevice(deviceTypes[i], deviceAddresses[i])) {
+ hasDevice = true;
+ break;
+ }
+ }
+ if (!hasDevice) {
+ return false;
+ }
+ }
+ return true;
+ }
+
void addMixes(@NonNull ArrayList<AudioMix> mixes) {
// TODO optimize to not have to unregister the mixes already in place
synchronized (mMixes) {
@@ -8098,6 +8159,29 @@ public class AudioService extends IAudioService.Stub
AudioSystem.registerPolicyMixes(mMixes, true);
Binder.restoreCallingIdentity(identity);
}
+
+ void setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+ final Integer Uid = new Integer(uid);
+ if (mUidDeviceAffinities.remove(Uid) != null) {
+ final long identity = Binder.clearCallingIdentity();
+ AudioSystem.removeUidDeviceAffinities(uid);
+ Binder.restoreCallingIdentity(identity);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ final int res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
+ Binder.restoreCallingIdentity(identity);
+ if (res == AudioSystem.SUCCESS) {
+ mUidDeviceAffinities.put(Uid, new AudioDeviceArray(types, addresses));
+ }
+ }
+
+ void removeUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+ if (mUidDeviceAffinities.remove(new Integer(uid)) != null) {
+ final long identity = Binder.clearCallingIdentity();
+ AudioSystem.removeUidDeviceAffinities(uid);
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
};
//======================