diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 8c3297d67d736..5efd0d79fb347 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3023,6 +3023,8 @@ package android.companion.virtual { method public int describeContents(); method @NonNull public java.util.Set getAllowedActivities(); method @NonNull public java.util.Set getAllowedCrossTaskNavigations(); + method public int getAudioPlaybackSessionId(); + method public int getAudioRecordingSessionId(); method @NonNull public java.util.Set getBlockedActivities(); method @NonNull public java.util.Set getBlockedCrossTaskNavigations(); method public int getDefaultActivityPolicy(); @@ -3054,6 +3056,8 @@ package android.companion.virtual { method @NonNull public android.companion.virtual.VirtualDeviceParams build(); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedCrossTaskNavigations(@NonNull java.util.Set); + method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAudioPlaybackSessionId(int); + method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAudioRecordingSessionId(int); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDefaultRecentsPolicy(int); diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl index 6e784b2e607d9..f0d23ac8374fd 100644 --- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl +++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl @@ -72,4 +72,16 @@ interface IVirtualDeviceManager { int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig, in IVirtualDisplayCallback callback, in IVirtualDevice virtualDevice, String packageName); + + /** + * Returns device-specific session id for playback, or AUDIO_SESSION_ID_GENERATE + * if there's none. + */ + int getAudioPlaybackSessionId(int deviceId); + + /** + * Returns device-specific session id for recording, or AUDIO_SESSION_ID_GENERATE + * if there's none. + */ + int getAudioRecordingSessionId(int deviceId); } diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index f851c319580ef..088ac069dd7a6 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -269,9 +269,14 @@ public final class VirtualDeviceManager { * @hide */ public int getAudioPlaybackSessionId(int deviceId) { - //TODO - Return session id rerouted to VirtualAudioDevice if the VirtualAudioDevice - //is configured to operate in context-aware mode. - return AUDIO_SESSION_ID_GENERATE; + if (mService == null) { + return AUDIO_SESSION_ID_GENERATE; + } + try { + return mService.getAudioPlaybackSessionId(deviceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -287,9 +292,14 @@ public final class VirtualDeviceManager { * @hide */ public int getAudioRecordingSessionId(int deviceId) { - //TODO - Return session id corresponding to VirtualAudioDevice injection if the - // VirtualAudioDevice is configured to operate in context-aware mode. - return AUDIO_SESSION_ID_GENERATE; + if (mService == null) { + return AUDIO_SESSION_ID_GENERATE; + } + try { + return mService.getAudioRecordingSessionId(deviceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index f59a7a9b66683..597b0f51fb82f 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -17,6 +17,7 @@ package android.companion.virtual; import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY; +import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE; import android.annotation.IntDef; import android.annotation.NonNull; @@ -191,6 +192,8 @@ public final class VirtualDeviceParams implements Parcelable { @NonNull private final List mVirtualSensorConfigs; @RecentsPolicy private final int mDefaultRecentsPolicy; + private final int mAudioPlaybackSessionId; + private final int mAudioRecordingSessionId; private VirtualDeviceParams( @LockState int lockState, @@ -204,7 +207,9 @@ public final class VirtualDeviceParams implements Parcelable { @Nullable String name, @NonNull SparseIntArray devicePolicies, @NonNull List virtualSensorConfigs, - @RecentsPolicy int defaultRecentsPolicy) { + @RecentsPolicy int defaultRecentsPolicy, + int audioPlaybackSessionId, + int audioRecordingSessionId) { mLockState = lockState; mUsersWithMatchingAccounts = new ArraySet<>(Objects.requireNonNull(usersWithMatchingAccounts)); @@ -220,6 +225,9 @@ public final class VirtualDeviceParams implements Parcelable { mDevicePolicies = Objects.requireNonNull(devicePolicies); mVirtualSensorConfigs = Objects.requireNonNull(virtualSensorConfigs); mDefaultRecentsPolicy = defaultRecentsPolicy; + mAudioPlaybackSessionId = audioPlaybackSessionId; + mAudioRecordingSessionId = audioRecordingSessionId; + } @SuppressWarnings("unchecked") @@ -237,6 +245,8 @@ public final class VirtualDeviceParams implements Parcelable { mVirtualSensorConfigs = new ArrayList<>(); parcel.readTypedList(mVirtualSensorConfigs, VirtualSensorConfig.CREATOR); mDefaultRecentsPolicy = parcel.readInt(); + mAudioPlaybackSessionId = parcel.readInt(); + mAudioRecordingSessionId = parcel.readInt(); } /** @@ -371,6 +381,24 @@ public final class VirtualDeviceParams implements Parcelable { return mDefaultRecentsPolicy; } + /** + * Returns device-specific audio session id for playback. + * + * @see Builder#setAudioPlaybackSessionId(int) + */ + public int getAudioPlaybackSessionId() { + return mAudioPlaybackSessionId; + } + + /** + * Returns device-specific audio session id for recording. + * + * @see Builder#setAudioRecordingSessionId(int) + */ + public int getAudioRecordingSessionId() { + return mAudioRecordingSessionId; + } + @Override public int describeContents() { return 0; @@ -390,6 +418,8 @@ public final class VirtualDeviceParams implements Parcelable { dest.writeSparseIntArray(mDevicePolicies); dest.writeTypedList(mVirtualSensorConfigs); dest.writeInt(mDefaultRecentsPolicy); + dest.writeInt(mAudioPlaybackSessionId); + dest.writeInt(mAudioRecordingSessionId); } @Override @@ -422,7 +452,9 @@ public final class VirtualDeviceParams implements Parcelable { && Objects.equals(mBlockedActivities, that.mBlockedActivities) && mDefaultActivityPolicy == that.mDefaultActivityPolicy && Objects.equals(mName, that.mName) - && mDefaultRecentsPolicy == that.mDefaultRecentsPolicy; + && mDefaultRecentsPolicy == that.mDefaultRecentsPolicy + && mAudioPlaybackSessionId == that.mAudioPlaybackSessionId + && mAudioRecordingSessionId == that.mAudioRecordingSessionId; } @Override @@ -431,7 +463,7 @@ public final class VirtualDeviceParams implements Parcelable { mLockState, mUsersWithMatchingAccounts, mAllowedCrossTaskNavigations, mBlockedCrossTaskNavigations, mDefaultNavigationPolicy, mAllowedActivities, mBlockedActivities, mDefaultActivityPolicy, mName, mDevicePolicies, - mDefaultRecentsPolicy); + mDefaultRecentsPolicy, mAudioPlaybackSessionId, mAudioRecordingSessionId); for (int i = 0; i < mDevicePolicies.size(); i++) { hashCode = 31 * hashCode + mDevicePolicies.keyAt(i); hashCode = 31 * hashCode + mDevicePolicies.valueAt(i); @@ -454,6 +486,8 @@ public final class VirtualDeviceParams implements Parcelable { + " mName=" + mName + " mDevicePolicies=" + mDevicePolicies + " mDefaultRecentsPolicy=" + mDefaultRecentsPolicy + + " mAudioPlaybackSessionId=" + mAudioPlaybackSessionId + + " mAudioRecordingSessionId=" + mAudioRecordingSessionId + ")"; } @@ -490,6 +524,8 @@ public final class VirtualDeviceParams implements Parcelable { @NonNull private SparseIntArray mDevicePolicies = new SparseIntArray(); @NonNull private List mVirtualSensorConfigs = new ArrayList<>(); private int mDefaultRecentsPolicy; + private int mAudioPlaybackSessionId = AUDIO_SESSION_ID_GENERATE; + private int mAudioRecordingSessionId = AUDIO_SESSION_ID_GENERATE; /** * Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY} @@ -705,6 +741,54 @@ public final class VirtualDeviceParams implements Parcelable { return this; } + /** + * Sets audio playback session id specific for this virtual device. + * + *

Audio players constructed within context associated with this virtual device + * will be automatically assigned provided session id. + * + *

Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_AUDIO}, + * otherwise {@link #build()} method will throw {@link IllegalArgumentException} if + * the playback session id is set to value other than + * {@link android.media.AudioManager.AUDIO_SESSION_ID_GENERATE}. + * + * @param playbackSessionId requested device-specific audio session id for playback + * @see android.media.AudioManager.generateAudioSessionId() + * @see android.media.AudioTrack.Builder.setContext(Context) + */ + @NonNull + public Builder setAudioPlaybackSessionId(int playbackSessionId) { + if (playbackSessionId != AUDIO_SESSION_ID_GENERATE || playbackSessionId < 0) { + throw new IllegalArgumentException("Invalid playback audio session id"); + } + mAudioPlaybackSessionId = playbackSessionId; + return this; + } + + /** + * Sets audio recording session id specific for this virtual device. + * + *

{@link android.media.AudioRecord} constructed within context associated with this + * virtual device will be automatically assigned provided session id. + * + *

Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_AUDIO}, + * otherwise {@link #build()} method will throw {@link IllegalArgumentException} if + * the recording session id is set to value other than + * {@link android.media.AudioManager.AUDIO_SESSION_ID_GENERATE}. + * + * @param recordingSessionId requested device-specific audio session id for playback + * @see android.media.AudioManager.generateAudioSessionId() + * @see android.media.AudioRecord.Builder.setContext(Context) + */ + @NonNull + public Builder setAudioRecordingSessionId(int recordingSessionId) { + if (recordingSessionId != AUDIO_SESSION_ID_GENERATE || recordingSessionId < 0) { + throw new IllegalArgumentException("Invalid recording audio session id"); + } + mAudioRecordingSessionId = recordingSessionId; + return this; + } + /** * Builds the {@link VirtualDeviceParams} instance. * @@ -721,6 +805,15 @@ public final class VirtualDeviceParams implements Parcelable { "DEVICE_POLICY_CUSTOM for POLICY_TYPE_SENSORS is required for creating " + "virtual sensors."); } + + if ((mAudioPlaybackSessionId != AUDIO_SESSION_ID_GENERATE + || mAudioRecordingSessionId != AUDIO_SESSION_ID_GENERATE) + && mDevicePolicies.get(POLICY_TYPE_AUDIO, DEVICE_POLICY_DEFAULT) + != DEVICE_POLICY_CUSTOM) { + throw new IllegalArgumentException("DEVICE_POLICY_CUSTOM for POLICY_TYPE_AUDIO is " + + "required for configuration of device-specific audio session ids."); + } + SparseArray> sensorNameByType = new SparseArray(); for (int i = 0; i < mVirtualSensorConfigs.size(); ++i) { VirtualSensorConfig config = mVirtualSensorConfigs.get(i); @@ -744,7 +837,9 @@ public final class VirtualDeviceParams implements Parcelable { mName, mDevicePolicies, mVirtualSensorConfigs, - mDefaultRecentsPolicy); + mDefaultRecentsPolicy, + mAudioPlaybackSessionId, + mAudioRecordingSessionId); } } } diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index b6cd160af80e1..195fee1ad7f97 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -253,6 +253,16 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return mParams.getDevicePolicy(policyType); } + /** Returns device-specific audio session id for playback. */ + public int getAudioPlaybackSessionId() { + return mParams.getAudioPlaybackSessionId(); + } + + /** Returns device-specific audio session id for recording. */ + public int getAudioRecordingSessionId() { + return mParams.getAudioRecordingSessionId(); + } + /** Returns the unique device ID of this device. */ @Override // Binder call public int getDeviceId() { diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index da2c5162e6e18..63739718e0f51 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -16,6 +16,8 @@ package com.android.server.companion.virtual; +import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE; + import static com.android.server.wm.ActivityInterceptorCallback.VIRTUAL_DEVICE_SERVICE_ORDERED_ID; import android.annotation.NonNull; @@ -388,6 +390,24 @@ public class VirtualDeviceManagerService extends SystemService { return VirtualDeviceManager.DEVICE_ID_DEFAULT; } + @Override // Binder call + public int getAudioPlaybackSessionId(int deviceId) { + synchronized (mVirtualDeviceManagerLock) { + VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId); + return virtualDevice != null + ? virtualDevice.getAudioPlaybackSessionId() : AUDIO_SESSION_ID_GENERATE; + } + } + + @Override // Binder call + public int getAudioRecordingSessionId(int deviceId) { + synchronized (mVirtualDeviceManagerLock) { + VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId); + return virtualDevice != null + ? virtualDevice.getAudioRecordingSessionId() : AUDIO_SESSION_ID_GENERATE; + } + } + @Nullable private AssociationInfo getAssociationInfo(String packageName, int associationId) { final int callingUserId = getCallingUserHandle().getIdentifier(); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java index 0f9523122199f..3a27e3bcfdb61 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java @@ -41,6 +41,8 @@ public class VirtualDeviceParamsTest { private static final String SENSOR_NAME = "VirtualSensorName"; private static final String SENSOR_VENDOR = "VirtualSensorVendor"; + private static final int PLAYBACK_SESSION_ID = 42; + private static final int RECORDING_SESSION_ID = 77; @Test public void parcelable_shouldRecreateSuccessfully() { @@ -49,6 +51,8 @@ public class VirtualDeviceParamsTest { .setUsersWithMatchingAccounts(Set.of(UserHandle.of(123), UserHandle.of(456))) .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM) .setDevicePolicy(POLICY_TYPE_AUDIO, DEVICE_POLICY_CUSTOM) + .setAudioPlaybackSessionId(PLAYBACK_SESSION_ID) + .setAudioRecordingSessionId(RECORDING_SESSION_ID) .addVirtualSensorConfig( new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) .setVendor(SENSOR_VENDOR) @@ -65,6 +69,8 @@ public class VirtualDeviceParamsTest { .containsExactly(UserHandle.of(123), UserHandle.of(456)); assertThat(params.getDevicePolicy(POLICY_TYPE_SENSORS)).isEqualTo(DEVICE_POLICY_CUSTOM); assertThat(params.getDevicePolicy(POLICY_TYPE_AUDIO)).isEqualTo(DEVICE_POLICY_CUSTOM); + assertThat(params.getAudioPlaybackSessionId()).isEqualTo(PLAYBACK_SESSION_ID); + assertThat(params.getAudioRecordingSessionId()).isEqualTo(RECORDING_SESSION_ID); List sensorConfigs = params.getVirtualSensorConfigs(); assertThat(sensorConfigs).hasSize(1);