diff options
| author | 2024-01-31 17:18:25 +0100 | |
|---|---|---|
| committer | 2024-03-05 19:02:35 +0100 | |
| commit | 95b405a526c1354bab265bac45deb590ee01b7a9 (patch) | |
| tree | b310e59b6e1c3319280cbca6405bf1a3082be7ee | |
| parent | dad0d1c5ab8dd5e4d621bccb819f1c659a8e0a1d (diff) | |
Add (virtual) device ID to AudioPolicy/AudioMix
This adds a clear signal to AudioMix as to which (virtual) deviceId the
mix belongs to. This signal is then used in AudioServer to determine if
a record is permitted and has access to the mix via device-aware
permissions.
Bug: 291737188
Test: atest VirtualAudioPermissionTest
Change-Id: Iaf492814fe10059accf38174f655610ebe780e7d
| -rw-r--r-- | core/jni/android_media_AudioSystem.cpp | 7 | ||||
| -rw-r--r-- | media/java/android/media/AudioManager.java | 3 | ||||
| -rw-r--r-- | media/java/android/media/AudioRecord.java | 4 | ||||
| -rw-r--r-- | media/java/android/media/AudioTrack.java | 3 | ||||
| -rw-r--r-- | media/java/android/media/IAudioService.aidl | 4 | ||||
| -rw-r--r-- | media/java/android/media/audiopolicy/AudioMix.java | 39 | ||||
| -rw-r--r-- | media/java/android/media/audiopolicy/AudioPolicy.java | 20 | ||||
| -rw-r--r-- | services/core/java/com/android/server/audio/AudioService.java | 20 |
8 files changed, 88 insertions, 12 deletions
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 52237989f059..d48cdc4645c6 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -161,6 +161,7 @@ static struct { jfieldID mMixType; jfieldID mCallbackFlags; jfieldID mToken; + jfieldID mVirtualDeviceId; } gAudioMixFields; static jclass gAudioFormatClass; @@ -2312,7 +2313,7 @@ static jint convertAudioMixFromNative(JNIEnv *env, jobject *jAudioMix, const Aud jstring deviceAddress = env->NewStringUTF(nAudioMix.mDeviceAddress.c_str()); *jAudioMix = env->NewObject(gAudioMixClass, gAudioMixCstor, jAudioMixingRule, jAudioFormat, nAudioMix.mRouteFlags, nAudioMix.mCbFlags, nAudioMix.mDeviceType, - deviceAddress, jBinderToken); + deviceAddress, jBinderToken, nAudioMix.mVirtualDeviceId); return AUDIO_JAVA_SUCCESS; } @@ -2347,6 +2348,7 @@ static jint convertAudioMixToNative(JNIEnv *env, AudioMix *nAudioMix, const jobj aiBinder(AIBinder_fromJavaBinder(env, jToken), &AIBinder_decStrong); nAudioMix->mToken = AIBinder_toPlatformBinder(aiBinder.get()); + nAudioMix->mVirtualDeviceId = env->GetIntField(jAudioMix, gAudioMixFields.mVirtualDeviceId); jint status = convertAudioMixingRuleToNative(env, jRule, &(nAudioMix->mCriteria)); env->DeleteLocalRef(jRule); @@ -3676,7 +3678,7 @@ int register_android_media_AudioSystem(JNIEnv *env) gAudioMixCstor = GetMethodIDOrDie(env, audioMixClass, "<init>", "(Landroid/media/audiopolicy/AudioMixingRule;Landroid/" - "media/AudioFormat;IIILjava/lang/String;Landroid/os/IBinder;)V"); + "media/AudioFormat;IIILjava/lang/String;Landroid/os/IBinder;I)V"); } gAudioMixFields.mRule = GetFieldIDOrDie(env, audioMixClass, "mRule", "Landroid/media/audiopolicy/AudioMixingRule;"); @@ -3689,6 +3691,7 @@ int register_android_media_AudioSystem(JNIEnv *env) gAudioMixFields.mMixType = GetFieldIDOrDie(env, audioMixClass, "mMixType", "I"); gAudioMixFields.mCallbackFlags = GetFieldIDOrDie(env, audioMixClass, "mCallbackFlags", "I"); gAudioMixFields.mToken = GetFieldIDOrDie(env, audioMixClass, "mToken", "Landroid/os/IBinder;"); + gAudioMixFields.mVirtualDeviceId = GetFieldIDOrDie(env, audioMixClass, "mVirtualDeviceId", "I"); jclass audioFormatClass = FindClassOrDie(env, "android/media/AudioFormat"); gAudioFormatClass = MakeGlobalRefOrDie(env, audioFormatClass); diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 6f7024ae76b4..1fe3c2ecec29 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -5453,7 +5453,8 @@ public class AudioManager { String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(), policy.hasFocusListener(), policy.isFocusPolicy(), policy.isTestFocusPolicy(), policy.isVolumeController(), - projection == null ? null : projection.getProjection()); + projection == null ? null : projection.getProjection(), + policy.getAttributionSource()); if (regId == null) { return ERROR; } else { diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 447d3bbddceb..80e57193d0dc 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -789,7 +789,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, private @NonNull AudioRecord buildAudioPlaybackCaptureRecord() { AudioMix audioMix = mAudioPlaybackCaptureConfiguration.createAudioMix(mFormat); MediaProjection projection = mAudioPlaybackCaptureConfiguration.getMediaProjection(); - AudioPolicy audioPolicy = new AudioPolicy.Builder(/*context=*/ null) + AudioPolicy audioPolicy = new AudioPolicy.Builder(/*context=*/ mContext) .setMediaProjection(projection) .addMix(audioMix).build(); @@ -853,7 +853,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, .setFormat(mFormat) .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK) .build(); - AudioPolicy audioPolicy = new AudioPolicy.Builder(null).addMix(audioMix).build(); + AudioPolicy audioPolicy = new AudioPolicy.Builder(mContext).addMix(audioMix).build(); if (AudioManager.registerAudioPolicyStatic(audioPolicy) != 0) { throw new UnsupportedOperationException("Error: could not register audio policy"); } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 194da217a121..73deb17d0055 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -1353,7 +1353,8 @@ public class AudioTrack extends PlayerBase .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK) .build(); AudioPolicy audioPolicy = - new AudioPolicy.Builder(/*context=*/ null).addMix(audioMix).build(); + new AudioPolicy.Builder(/*context=*/ mContext).addMix(audioMix).build(); + if (AudioManager.registerAudioPolicyStatic(audioPolicy) != 0) { throw new UnsupportedOperationException("Error: could not register audio policy"); } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 98bd3caf3f7d..e612645fb4d7 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -18,6 +18,7 @@ package android.media; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; +import android.content.AttributionSource; import android.media.AudioAttributes; import android.media.AudioDeviceAttributes; import android.media.AudioFormat; @@ -361,7 +362,8 @@ interface IAudioService { String registerAudioPolicy(in AudioPolicyConfig policyConfig, in IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy, boolean isTestFocusPolicy, - boolean isVolumeController, in IMediaProjection projection); + boolean isVolumeController, in IMediaProjection projection, + in AttributionSource attributionSource); oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb); diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java index 60ab1a4e42ac..f673c9060301 100644 --- a/media/java/android/media/audiopolicy/AudioMix.java +++ b/media/java/android/media/audiopolicy/AudioMix.java @@ -24,6 +24,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; import android.media.AudioDeviceInfo; import android.media.AudioFormat; import android.media.AudioSystem; @@ -67,12 +68,19 @@ public class AudioMix implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) final int mDeviceSystemType; // an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* + // The (virtual) device ID that this AudioMix was registered for. This value is overwritten + // when registering this AudioMix with an AudioPolicy or attaching this AudioMix to an + // AudioPolicy to match the AudioPolicy attribution. Does not imply that it only modifies + // audio routing for this device ID. + private int mVirtualDeviceId; + /** * All parameters are guaranteed valid through the Builder. */ private AudioMix(@NonNull AudioMixingRule rule, @NonNull AudioFormat format, int routeFlags, int callbackFlags, - int deviceType, @Nullable String deviceAddress, IBinder token) { + int deviceType, @Nullable String deviceAddress, IBinder token, + int virtualDeviceId) { mRule = Objects.requireNonNull(rule); mFormat = Objects.requireNonNull(format); mRouteFlags = routeFlags; @@ -81,6 +89,7 @@ public class AudioMix implements Parcelable { mDeviceSystemType = deviceType; mDeviceAddress = (deviceAddress == null) ? new String("") : deviceAddress; mToken = token; + mVirtualDeviceId = virtualDeviceId; } // CALLBACK_FLAG_* values: keep in sync with AudioMix::kCbFlag* values defined @@ -269,6 +278,11 @@ public class AudioMix implements Parcelable { } /** @hide */ + public boolean matchesVirtualDeviceId(int deviceId) { + return mVirtualDeviceId == deviceId; + } + + /** @hide */ @Override public boolean equals(Object o) { if (this == o) return true; @@ -305,6 +319,7 @@ public class AudioMix implements Parcelable { mFormat.writeToParcel(dest, flags); mRule.writeToParcel(dest, flags); dest.writeStrongBinder(mToken); + dest.writeInt(mVirtualDeviceId); } public static final @NonNull Parcelable.Creator<AudioMix> CREATOR = new Parcelable.Creator<>() { @@ -325,6 +340,7 @@ public class AudioMix implements Parcelable { mixBuilder.setFormat(AudioFormat.CREATOR.createFromParcel(p)); mixBuilder.setMixingRule(AudioMixingRule.CREATOR.createFromParcel(p)); mixBuilder.setToken(p.readStrongBinder()); + mixBuilder.setVirtualDeviceId(p.readInt()); return mixBuilder.build(); } @@ -333,6 +349,15 @@ public class AudioMix implements Parcelable { } }; + /** + * Updates the deviceId of the AudioMix to match with the AudioPolicy the mix is registered + * through. + * @hide + */ + public void setVirtualDeviceId(int virtualDeviceId) { + mVirtualDeviceId = virtualDeviceId; + } + /** @hide */ @IntDef(flag = true, value = { ROUTE_FLAG_RENDER, ROUTE_FLAG_LOOP_BACK } ) @@ -348,6 +373,7 @@ public class AudioMix implements Parcelable { private int mRouteFlags = 0; private int mCallbackFlags = 0; private IBinder mToken = null; + private int mVirtualDeviceId = Context.DEVICE_ID_DEFAULT; // an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* private int mDeviceSystemType = AudioSystem.DEVICE_NONE; private String mDeviceAddress = null; @@ -398,6 +424,15 @@ public class AudioMix implements Parcelable { /** * @hide + * Only used by AudioMix internally. + */ + Builder setVirtualDeviceId(int virtualDeviceId) { + mVirtualDeviceId = virtualDeviceId; + return this; + } + + /** + * @hide * Only used by AudioPolicyConfig, not a public API. * @param callbackFlags which callbacks are called from native * @return the same Builder instance. @@ -564,7 +599,7 @@ public class AudioMix implements Parcelable { } return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceSystemType, - mDeviceAddress, mToken); + mDeviceAddress, mToken, mVirtualDeviceId); } private int getLoopbackDeviceSystemTypeForAudioMixingRule(AudioMixingRule rule) { diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index 508c0a2b9a21..293a8f89fbca 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -27,6 +27,7 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.ActivityManager; +import android.content.AttributionSource; import android.content.Context; import android.content.pm.PackageManager; import android.media.AudioAttributes; @@ -146,6 +147,16 @@ public class AudioPolicy { return mProjection; } + /** @hide */ + public AttributionSource getAttributionSource() { + return getAttributionSource(mContext); + } + + private static AttributionSource getAttributionSource(Context context) { + return context == null + ? AttributionSource.myAttributionSource() : context.getAttributionSource(); + } + /** * The parameters are guaranteed non-null through the Builder */ @@ -208,6 +219,9 @@ public class AudioPolicy { if (mix == null) { throw new IllegalArgumentException("Illegal null AudioMix argument"); } + if (android.permission.flags.Flags.deviceAwarePermissionApisEnabled()) { + mix.setVirtualDeviceId(getAttributionSource(mContext).getDeviceId()); + } mMixes.add(mix); return this; } @@ -358,6 +372,9 @@ public class AudioPolicy { if (mix == null) { throw new IllegalArgumentException("Illegal null AudioMix in attachMixes"); } else { + if (android.permission.flags.Flags.deviceAwarePermissionApisEnabled()) { + mix.setVirtualDeviceId(getAttributionSource(mContext).getDeviceId()); + } zeMixes.add(mix); } } @@ -400,6 +417,9 @@ public class AudioPolicy { if (mix == null) { throw new IllegalArgumentException("Illegal null AudioMix in detachMixes"); } else { + if (android.permission.flags.Flags.deviceAwarePermissionApisEnabled()) { + mix.setVirtualDeviceId(getAttributionSource(mContext).getDeviceId()); + } zeMixes.add(mix); } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d4f04b5ad760..1fd6c0faa1b2 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -73,6 +73,7 @@ import android.app.role.RoleManager; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; +import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -12206,7 +12207,9 @@ public class AudioService extends IAudioService.Stub //========================================================================================== public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy, boolean isTestFocusPolicy, - boolean isVolumeController, IMediaProjection projection) { + boolean isVolumeController, IMediaProjection projection, + AttributionSource attributionSource) { + Objects.requireNonNull(attributionSource); AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback); if (!isPolicyRegisterAllowed(policyConfig, @@ -12227,7 +12230,8 @@ public class AudioService extends IAudioService.Stub } try { AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener, - isFocusPolicy, isTestFocusPolicy, isVolumeController, projection); + isFocusPolicy, isTestFocusPolicy, isVolumeController, projection, + attributionSource); pcb.asBinder().linkToDeath(app, 0/*flags*/); // logging after registration so we have the registration id @@ -13199,6 +13203,7 @@ public class AudioService extends IAudioService.Stub public class AudioPolicyProxy extends AudioPolicyConfig implements IBinder.DeathRecipient { private static final String TAG = "AudioPolicyProxy"; final IAudioPolicyCallback mPolicyCallback; + final AttributionSource mAttributionSource; final boolean mHasFocusListener; final boolean mIsVolumeController; final HashMap<Integer, AudioDeviceArray> mUidDeviceAffinities = @@ -13238,10 +13243,12 @@ public class AudioService extends IAudioService.Stub AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token, boolean hasFocusListener, boolean isFocusPolicy, boolean isTestFocusPolicy, - boolean isVolumeController, IMediaProjection projection) { + boolean isVolumeController, IMediaProjection projection, + AttributionSource attributionSource) { super(config); setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++)); mPolicyCallback = token; + mAttributionSource = attributionSource; mHasFocusListener = hasFocusListener; mIsVolumeController = isVolumeController; mProjection = projection; @@ -13369,6 +13376,7 @@ public class AudioService extends IAudioService.Stub if (android.media.audiopolicy.Flags.audioMixOwnership()) { for (AudioMix mix : mixes) { setMixRegistration(mix); + mix.setVirtualDeviceId(mAttributionSource.getDeviceId()); } int result = mAudioSystem.registerPolicyMixes(mixes, true); @@ -13392,6 +13400,9 @@ public class AudioService extends IAudioService.Stub @AudioSystem.AudioSystemError int connectMixes() { final long identity = Binder.clearCallingIdentity(); try { + for (AudioMix mix : mMixes) { + mix.setVirtualDeviceId(mAttributionSource.getDeviceId()); + } return mAudioSystem.registerPolicyMixes(mMixes, true); } finally { Binder.restoreCallingIdentity(identity); @@ -13405,6 +13416,9 @@ public class AudioService extends IAudioService.Stub Objects.requireNonNull(mixesToUpdate); Objects.requireNonNull(updatedMixingRules); + for (AudioMix mix : mixesToUpdate) { + mix.setVirtualDeviceId(mAttributionSource.getDeviceId()); + } if (mixesToUpdate.length != updatedMixingRules.length) { Log.e(TAG, "Provided list of audio mixes to update and corresponding mixing rules " + "have mismatching length (mixesToUpdate.length = " + mixesToUpdate.length |