Merge "Clean up audio resources when VirtualDevice is closed"
This commit is contained in:
@@ -148,6 +148,8 @@ public final class VirtualDeviceManager {
|
||||
}
|
||||
}
|
||||
};
|
||||
@Nullable
|
||||
private VirtualAudioDevice mVirtualAudioDevice;
|
||||
|
||||
private VirtualDevice(
|
||||
IVirtualDeviceManager service,
|
||||
@@ -255,8 +257,8 @@ public final class VirtualDeviceManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the virtual device, stopping and tearing down any virtual displays,
|
||||
* audio policies, and event injection that's currently in progress.
|
||||
* Closes the virtual device, stopping and tearing down any virtual displays, associated
|
||||
* virtual audio device, and event injection that's currently in progress.
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
|
||||
public void close() {
|
||||
@@ -265,6 +267,10 @@ public final class VirtualDeviceManager {
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
if (mVirtualAudioDevice != null) {
|
||||
mVirtualAudioDevice.close();
|
||||
mVirtualAudioDevice = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,8 +357,10 @@ public final class VirtualDeviceManager {
|
||||
* Creates a VirtualAudioDevice, capable of recording audio emanating from this device,
|
||||
* or injecting audio from another device.
|
||||
*
|
||||
* <p>Note: This object does not support capturing privileged playback, such as voice call
|
||||
* audio.
|
||||
* <p>Note: One {@link VirtualDevice} can only create one {@link VirtualAudioDevice}, so
|
||||
* calling this method multiple times will return the same instance. When
|
||||
* {@link VirtualDevice#close()} is called, the associated {@link VirtualAudioDevice} will
|
||||
* also be closed automatically.
|
||||
*
|
||||
* @param display The target virtual display to capture from and inject into.
|
||||
* @param executor The {@link Executor} object for the thread on which to execute
|
||||
@@ -368,7 +376,11 @@ public final class VirtualDeviceManager {
|
||||
@NonNull VirtualDisplay display,
|
||||
@Nullable Executor executor,
|
||||
@Nullable AudioConfigurationChangeCallback callback) {
|
||||
return new VirtualAudioDevice(mContext, mVirtualDevice, display, executor, callback);
|
||||
if (mVirtualAudioDevice == null) {
|
||||
mVirtualAudioDevice = new VirtualAudioDevice(
|
||||
mContext, mVirtualDevice, display, executor, callback);
|
||||
}
|
||||
return mVirtualAudioDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,7 +60,6 @@ final class UserRestrictionsDetector extends BroadcastReceiver {
|
||||
/** Registers user restrictions change. */
|
||||
void register(@NonNull UserRestrictionsCallback callback) {
|
||||
mUserRestrictionsCallback = callback;
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
|
||||
mContext.registerReceiver(/* receiver= */ this, filter);
|
||||
@@ -73,8 +72,10 @@ final class UserRestrictionsDetector extends BroadcastReceiver {
|
||||
|
||||
/** Unregisters user restrictions change. */
|
||||
void unregister() {
|
||||
mUserRestrictionsCallback = null;
|
||||
mContext.unregisterReceiver(/* receiver= */ this);
|
||||
if (mUserRestrictionsCallback != null) {
|
||||
mUserRestrictionsCallback = null;
|
||||
mContext.unregisterReceiver(/* receiver= */ this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.companion.virtual.IVirtualDevice;
|
||||
import android.content.Context;
|
||||
import android.hardware.display.VirtualDisplay;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioPlaybackConfiguration;
|
||||
import android.media.AudioRecordingConfiguration;
|
||||
import android.os.RemoteException;
|
||||
@@ -96,7 +97,7 @@ public final class VirtualAudioDevice implements Closeable {
|
||||
|
||||
if (mOngoingSession != null && mOngoingSession.getAudioInjection() != null) {
|
||||
throw new IllegalStateException("Cannot start an audio injection while a session is "
|
||||
+ "ongoing. Call close() on this device first to end the previous injection.");
|
||||
+ "ongoing. Call close() on this device first to end the previous session.");
|
||||
}
|
||||
if (mOngoingSession == null) {
|
||||
mOngoingSession = new VirtualAudioSession(mContext, mCallback, mExecutor);
|
||||
@@ -114,6 +115,9 @@ public final class VirtualAudioDevice implements Closeable {
|
||||
/**
|
||||
* Begins recording audio emanating from this device.
|
||||
*
|
||||
* <p>Note: This method does not support capturing privileged playback, which means the
|
||||
* application can opt out of capturing by {@link AudioManager#setAllowedCapturePolicy(int)}.
|
||||
*
|
||||
* @return An {@link AudioCapture} containing the recorded audio.
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
|
||||
@@ -150,6 +154,7 @@ public final class VirtualAudioDevice implements Closeable {
|
||||
return mOngoingSession != null ? mOngoingSession.getAudioInjection() : null;
|
||||
}
|
||||
|
||||
/** Stops audio capture and injection then releases all the resources */
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
@@ -120,7 +120,7 @@ public final class VirtualAudioSession extends IAudioSessionCallback.Stub implem
|
||||
@NonNull
|
||||
public AudioInjection startAudioInjection(@NonNull AudioFormat injectionFormat) {
|
||||
Objects.requireNonNull(injectionFormat, "injectionFormat must not be null");
|
||||
mUserRestrictionsDetector.register(/* callback= */ this);
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mAudioInjection != null) {
|
||||
throw new IllegalStateException(
|
||||
@@ -130,6 +130,8 @@ public final class VirtualAudioSession extends IAudioSessionCallback.Stub implem
|
||||
mInjectionFormat = injectionFormat;
|
||||
mAudioInjection = new AudioInjection();
|
||||
mAudioInjection.play();
|
||||
|
||||
mUserRestrictionsDetector.register(/* callback= */ this);
|
||||
mAudioInjection.setSilent(mUserRestrictionsDetector.isUnmuteMicrophoneDisallowed());
|
||||
return mAudioInjection;
|
||||
}
|
||||
|
||||
@@ -235,6 +235,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
|
||||
});
|
||||
mPerDisplayWakelocks.clear();
|
||||
}
|
||||
if (mVirtualAudioController != null) {
|
||||
mVirtualAudioController.stopListening();
|
||||
mVirtualAudioController = null;
|
||||
}
|
||||
}
|
||||
mListener.onClose(mAssociationInfo.getId());
|
||||
mAppToken.unlinkToDeath(this, 0);
|
||||
|
||||
@@ -360,6 +360,16 @@ public class VirtualDeviceManagerServiceTest {
|
||||
assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void close_cleanVirtualAudioController() {
|
||||
mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
|
||||
mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mCallback);
|
||||
|
||||
mDeviceImpl.close();
|
||||
|
||||
assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendKeyEvent_noFd() {
|
||||
assertThrows(
|
||||
|
||||
Reference in New Issue
Block a user