diff options
| -rw-r--r-- | api/system-current.txt | 3 | ||||
| -rw-r--r-- | core/jni/android_media_AudioSystem.cpp | 51 | ||||
| -rw-r--r-- | media/java/android/media/AudioManager.java | 5 | ||||
| -rw-r--r-- | media/java/android/media/AudioSystem.java | 7 | ||||
| -rw-r--r-- | media/java/android/media/IAudioService.aidl | 5 | ||||
| -rw-r--r-- | media/java/android/media/audiopolicy/AudioMix.java | 14 | ||||
| -rw-r--r-- | media/java/android/media/audiopolicy/AudioPolicy.java | 75 | ||||
| -rw-r--r-- | services/core/java/com/android/server/audio/AudioService.java | 84 |
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); + } + } }; //====================== |