From eda545b0eaf16ca9f3904440a2c2721b242a281d Mon Sep 17 00:00:00 2001 From: Vladimir Komsiyski Date: Wed, 29 Mar 2023 14:38:16 +0200 Subject: [PATCH] Move implementation details out of VDM Add a new VirtualDeviceInternal class, which holds all the internal implementation details of VirtualDevice's interaction with other services. All its methods are package private and do not contain unnecessary annotations and documentation. VirtualDeviceManager and VirtualDeviceManager.VirtualDevice now are the single go-to point for API and documentation, without unnecessary clutter. Fix: 261163570 Test: atest CtsVirtualDevicesTestCases Change-Id: Iee610e162744d0cafd0925b3e3492bb4dd273403 --- .../virtual/VirtualDeviceInternal.java | 458 ++++++++++++++++++ .../virtual/VirtualDeviceManager.java | 368 ++------------ 2 files changed, 502 insertions(+), 324 deletions(-) create mode 100644 core/java/android/companion/virtual/VirtualDeviceInternal.java diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java new file mode 100644 index 0000000000000..045e4c6c77b18 --- /dev/null +++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.companion.virtual; + +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.PendingIntent; +import android.companion.virtual.audio.VirtualAudioDevice; +import android.companion.virtual.sensor.VirtualSensor; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.display.DisplayManagerGlobal; +import android.hardware.display.IVirtualDisplayCallback; +import android.hardware.display.VirtualDisplay; +import android.hardware.display.VirtualDisplayConfig; +import android.hardware.input.VirtualDpad; +import android.hardware.input.VirtualDpadConfig; +import android.hardware.input.VirtualKeyboard; +import android.hardware.input.VirtualKeyboardConfig; +import android.hardware.input.VirtualMouse; +import android.hardware.input.VirtualMouseConfig; +import android.hardware.input.VirtualNavigationTouchpad; +import android.hardware.input.VirtualNavigationTouchpadConfig; +import android.hardware.input.VirtualTouchscreen; +import android.hardware.input.VirtualTouchscreenConfig; +import android.media.AudioManager; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.util.ArrayMap; + +import com.android.internal.annotations.GuardedBy; + +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.function.IntConsumer; + +/** + * An internal representation of a virtual device. + * + * @hide + */ +public class VirtualDeviceInternal { + + private final Context mContext; + private final IVirtualDeviceManager mService; + private final IVirtualDevice mVirtualDevice; + private final Object mActivityListenersLock = new Object(); + @GuardedBy("mActivityListenersLock") + private final ArrayMap + mActivityListeners = + new ArrayMap<>(); + private final Object mIntentInterceptorListenersLock = new Object(); + @GuardedBy("mIntentInterceptorListenersLock") + private final ArrayMap mIntentInterceptorListeners = + new ArrayMap<>(); + private final Object mSoundEffectListenersLock = new Object(); + @GuardedBy("mSoundEffectListenersLock") + private final ArrayMap + mSoundEffectListeners = new ArrayMap<>(); + private final IVirtualDeviceActivityListener mActivityListenerBinder = + new IVirtualDeviceActivityListener.Stub() { + + @Override + public void onTopActivityChanged(int displayId, ComponentName topActivity, + @UserIdInt int userId) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mActivityListenersLock) { + for (int i = 0; i < mActivityListeners.size(); i++) { + mActivityListeners.valueAt(i) + .onTopActivityChanged(displayId, topActivity); + mActivityListeners.valueAt(i) + .onTopActivityChanged(displayId, topActivity, userId); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onDisplayEmpty(int displayId) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mActivityListenersLock) { + for (int i = 0; i < mActivityListeners.size(); i++) { + mActivityListeners.valueAt(i).onDisplayEmpty(displayId); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + private final IVirtualDeviceSoundEffectListener mSoundEffectListener = + new IVirtualDeviceSoundEffectListener.Stub() { + @Override + public void onPlaySoundEffect(int soundEffect) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSoundEffectListenersLock) { + for (int i = 0; i < mSoundEffectListeners.size(); i++) { + mSoundEffectListeners.valueAt(i).onPlaySoundEffect(soundEffect); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + @Nullable + private VirtualAudioDevice mVirtualAudioDevice; + + VirtualDeviceInternal( + IVirtualDeviceManager service, + Context context, + int associationId, + VirtualDeviceParams params) throws RemoteException { + mService = service; + mContext = context.getApplicationContext(); + mVirtualDevice = service.createVirtualDevice( + new Binder(), + mContext.getPackageName(), + associationId, + params, + mActivityListenerBinder, + mSoundEffectListener); + } + + int getDeviceId() { + try { + return mVirtualDevice.getDeviceId(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @NonNull Context createContext() { + try { + return mContext.createDeviceContext(mVirtualDevice.getDeviceId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @NonNull + List getVirtualSensorList() { + try { + return mVirtualDevice.getVirtualSensorList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void launchPendingIntent( + int displayId, + @NonNull PendingIntent pendingIntent, + @NonNull Executor executor, + @NonNull IntConsumer listener) { + try { + mVirtualDevice.launchPendingIntent( + displayId, + pendingIntent, + new ResultReceiver(new Handler(Looper.getMainLooper())) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + super.onReceiveResult(resultCode, resultData); + executor.execute(() -> listener.accept(resultCode)); + } + }); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + @Nullable + VirtualDisplay createVirtualDisplay( + @NonNull VirtualDisplayConfig config, + @Nullable @CallbackExecutor Executor executor, + @Nullable VirtualDisplay.Callback callback) { + IVirtualDisplayCallback callbackWrapper = + new DisplayManagerGlobal.VirtualDisplayCallback(callback, executor); + final int displayId; + try { + displayId = mService.createVirtualDisplay(config, callbackWrapper, mVirtualDevice, + mContext.getPackageName()); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance(); + return displayManager.createVirtualDisplayWrapper(config, callbackWrapper, + displayId); + } + + void close() { + try { + // This also takes care of unregistering all virtual sensors. + mVirtualDevice.close(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + if (mVirtualAudioDevice != null) { + mVirtualAudioDevice.close(); + mVirtualAudioDevice = null; + } + } + + @NonNull + VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) { + try { + final IBinder token = new Binder( + "android.hardware.input.VirtualDpad:" + config.getInputDeviceName()); + mVirtualDevice.createVirtualDpad(config, token); + return new VirtualDpad(mVirtualDevice, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @NonNull + VirtualKeyboard createVirtualKeyboard(@NonNull VirtualKeyboardConfig config) { + try { + final IBinder token = new Binder( + "android.hardware.input.VirtualKeyboard:" + config.getInputDeviceName()); + mVirtualDevice.createVirtualKeyboard(config, token); + return new VirtualKeyboard(mVirtualDevice, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @NonNull + VirtualMouse createVirtualMouse(@NonNull VirtualMouseConfig config) { + try { + final IBinder token = new Binder( + "android.hardware.input.VirtualMouse:" + config.getInputDeviceName()); + mVirtualDevice.createVirtualMouse(config, token); + return new VirtualMouse(mVirtualDevice, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @NonNull + VirtualTouchscreen createVirtualTouchscreen( + @NonNull VirtualTouchscreenConfig config) { + try { + final IBinder token = new Binder( + "android.hardware.input.VirtualTouchscreen:" + config.getInputDeviceName()); + mVirtualDevice.createVirtualTouchscreen(config, token); + return new VirtualTouchscreen(mVirtualDevice, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @NonNull + VirtualNavigationTouchpad createVirtualNavigationTouchpad( + @NonNull VirtualNavigationTouchpadConfig config) { + try { + final IBinder token = new Binder( + "android.hardware.input.VirtualNavigationTouchpad:" + + config.getInputDeviceName()); + mVirtualDevice.createVirtualNavigationTouchpad(config, token); + return new VirtualNavigationTouchpad(mVirtualDevice, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @NonNull + VirtualAudioDevice createVirtualAudioDevice( + @NonNull VirtualDisplay display, + @Nullable Executor executor, + @Nullable VirtualAudioDevice.AudioConfigurationChangeCallback callback) { + if (mVirtualAudioDevice == null) { + mVirtualAudioDevice = new VirtualAudioDevice(mContext, mVirtualDevice, display, + executor, callback, () -> mVirtualAudioDevice = null); + } + return mVirtualAudioDevice; + } + + @NonNull + void setShowPointerIcon(boolean showPointerIcon) { + try { + mVirtualDevice.setShowPointerIcon(showPointerIcon); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void addActivityListener( + @CallbackExecutor @NonNull Executor executor, + @NonNull VirtualDeviceManager.ActivityListener listener) { + final ActivityListenerDelegate delegate = new ActivityListenerDelegate( + Objects.requireNonNull(listener), Objects.requireNonNull(executor)); + synchronized (mActivityListenersLock) { + mActivityListeners.put(listener, delegate); + } + } + + void removeActivityListener(@NonNull VirtualDeviceManager.ActivityListener listener) { + synchronized (mActivityListenersLock) { + mActivityListeners.remove(Objects.requireNonNull(listener)); + } + } + + void addSoundEffectListener(@CallbackExecutor @NonNull Executor executor, + @NonNull VirtualDeviceManager.SoundEffectListener soundEffectListener) { + final SoundEffectListenerDelegate delegate = + new SoundEffectListenerDelegate(Objects.requireNonNull(executor), + Objects.requireNonNull(soundEffectListener)); + synchronized (mSoundEffectListenersLock) { + mSoundEffectListeners.put(soundEffectListener, delegate); + } + } + + void removeSoundEffectListener( + @NonNull VirtualDeviceManager.SoundEffectListener soundEffectListener) { + synchronized (mSoundEffectListenersLock) { + mSoundEffectListeners.remove(Objects.requireNonNull(soundEffectListener)); + } + } + + void registerIntentInterceptor( + @NonNull IntentFilter interceptorFilter, + @CallbackExecutor @NonNull Executor executor, + @NonNull VirtualDeviceManager.IntentInterceptorCallback interceptorCallback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(interceptorFilter); + Objects.requireNonNull(interceptorCallback); + final IntentInterceptorDelegate delegate = + new IntentInterceptorDelegate(executor, interceptorCallback); + try { + mVirtualDevice.registerIntentInterceptor(delegate, interceptorFilter); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + synchronized (mIntentInterceptorListenersLock) { + mIntentInterceptorListeners.put(interceptorCallback, delegate); + } + } + + void unregisterIntentInterceptor( + @NonNull VirtualDeviceManager.IntentInterceptorCallback interceptorCallback) { + Objects.requireNonNull(interceptorCallback); + final IntentInterceptorDelegate delegate; + synchronized (mIntentInterceptorListenersLock) { + delegate = mIntentInterceptorListeners.remove(interceptorCallback); + } + if (delegate != null) { + try { + mVirtualDevice.unregisterIntentInterceptor(delegate); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * A wrapper for {@link VirtualDeviceManager.ActivityListener} that executes callbacks on the + * given executor. + */ + private static class ActivityListenerDelegate { + @NonNull private final VirtualDeviceManager.ActivityListener mActivityListener; + @NonNull private final Executor mExecutor; + + ActivityListenerDelegate(@NonNull VirtualDeviceManager.ActivityListener listener, + @NonNull Executor executor) { + mActivityListener = listener; + mExecutor = executor; + } + + public void onTopActivityChanged(int displayId, ComponentName topActivity) { + mExecutor.execute(() -> mActivityListener.onTopActivityChanged(displayId, topActivity)); + } + + public void onTopActivityChanged(int displayId, ComponentName topActivity, + @UserIdInt int userId) { + mExecutor.execute(() -> + mActivityListener.onTopActivityChanged(displayId, topActivity, userId)); + } + + public void onDisplayEmpty(int displayId) { + mExecutor.execute(() -> mActivityListener.onDisplayEmpty(displayId)); + } + } + + /** + * A wrapper for {@link VirtualDeviceManager.IntentInterceptorCallback} that executes callbacks + * on the given executor. + */ + private static class IntentInterceptorDelegate extends IVirtualDeviceIntentInterceptor.Stub { + @NonNull private final VirtualDeviceManager.IntentInterceptorCallback + mIntentInterceptorCallback; + @NonNull private final Executor mExecutor; + + private IntentInterceptorDelegate(Executor executor, + VirtualDeviceManager.IntentInterceptorCallback interceptorCallback) { + mExecutor = executor; + mIntentInterceptorCallback = interceptorCallback; + } + + @Override + public void onIntentIntercepted(Intent intent) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mIntentInterceptorCallback.onIntentIntercepted(intent)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + /** + * A wrapper for {@link VirtualDeviceManager.SoundEffectListener} that executes callbacks on the + * given executor. + */ + private static class SoundEffectListenerDelegate { + @NonNull private final VirtualDeviceManager.SoundEffectListener mSoundEffectListener; + @NonNull private final Executor mExecutor; + + private SoundEffectListenerDelegate(Executor executor, + VirtualDeviceManager.SoundEffectListener soundEffectCallback) { + mSoundEffectListener = soundEffectCallback; + mExecutor = executor; + } + + public void onPlaySoundEffect(@AudioManager.SystemSoundEffect int effectType) { + mExecutor.execute(() -> mSoundEffectListener.onPlaySoundEffect(effectType)); + } + } +} diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index cb9f06c39393f..d3d635e19f2e8 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -40,8 +40,6 @@ import android.content.IntentFilter; import android.graphics.Point; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.VirtualDisplayFlag; -import android.hardware.display.DisplayManagerGlobal; -import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.VirtualDpad; @@ -55,26 +53,17 @@ import android.hardware.input.VirtualNavigationTouchpadConfig; import android.hardware.input.VirtualTouchscreen; import android.hardware.input.VirtualTouchscreenConfig; import android.media.AudioManager; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; -import android.os.ResultReceiver; -import android.util.ArrayMap; import android.util.Log; import android.view.Surface; -import com.android.internal.annotations.GuardedBy; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.IntConsumer; @@ -337,75 +326,7 @@ public final class VirtualDeviceManager { @SystemApi public static class VirtualDevice implements AutoCloseable { - private final Context mContext; - private final IVirtualDeviceManager mService; - private final IVirtualDevice mVirtualDevice; - private final Object mActivityListenersLock = new Object(); - @GuardedBy("mActivityListenersLock") - private final ArrayMap mActivityListeners = - new ArrayMap<>(); - private final Object mIntentInterceptorListenersLock = new Object(); - @GuardedBy("mIntentInterceptorListenersLock") - private final ArrayMap mIntentInterceptorListeners = - new ArrayMap<>(); - private final Object mSoundEffectListenersLock = new Object(); - @GuardedBy("mSoundEffectListenersLock") - private final ArrayMap - mSoundEffectListeners = new ArrayMap<>(); - private final IVirtualDeviceActivityListener mActivityListenerBinder = - new IVirtualDeviceActivityListener.Stub() { - - @Override - public void onTopActivityChanged(int displayId, ComponentName topActivity, - @UserIdInt int userId) { - final long token = Binder.clearCallingIdentity(); - try { - synchronized (mActivityListenersLock) { - for (int i = 0; i < mActivityListeners.size(); i++) { - mActivityListeners.valueAt(i) - .onTopActivityChanged(displayId, topActivity); - mActivityListeners.valueAt(i) - .onTopActivityChanged(displayId, topActivity, userId); - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void onDisplayEmpty(int displayId) { - final long token = Binder.clearCallingIdentity(); - try { - synchronized (mActivityListenersLock) { - for (int i = 0; i < mActivityListeners.size(); i++) { - mActivityListeners.valueAt(i).onDisplayEmpty(displayId); - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - }; - private final IVirtualDeviceSoundEffectListener mSoundEffectListener = - new IVirtualDeviceSoundEffectListener.Stub() { - @Override - public void onPlaySoundEffect(int soundEffect) { - final long token = Binder.clearCallingIdentity(); - try { - synchronized (mSoundEffectListenersLock) { - for (int i = 0; i < mSoundEffectListeners.size(); i++) { - mSoundEffectListeners.valueAt(i).onPlaySoundEffect(soundEffect); - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - }; - @Nullable - private VirtualAudioDevice mVirtualAudioDevice; + private final VirtualDeviceInternal mVirtualDeviceInternal; @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) private VirtualDevice( @@ -413,26 +334,15 @@ public final class VirtualDeviceManager { Context context, int associationId, VirtualDeviceParams params) throws RemoteException { - mService = service; - mContext = context.getApplicationContext(); - mVirtualDevice = service.createVirtualDevice( - new Binder(), - mContext.getPackageName(), - associationId, - params, - mActivityListenerBinder, - mSoundEffectListener); + mVirtualDeviceInternal = + new VirtualDeviceInternal(service, context, associationId, params); } /** * Returns the unique ID of this virtual device. */ public int getDeviceId() { - try { - return mVirtualDevice.getDeviceId(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return mVirtualDeviceInternal.getDeviceId(); } /** @@ -440,11 +350,7 @@ public final class VirtualDeviceManager { * calling {@link Context#createDeviceContext(int)} with the device id of this device. */ public @NonNull Context createContext() { - try { - return mContext.createDeviceContext(mVirtualDevice.getDeviceId()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return mVirtualDeviceInternal.createContext(); } /** @@ -456,11 +362,7 @@ public final class VirtualDeviceManager { */ @NonNull public List getVirtualSensorList() { - try { - return mVirtualDevice.getVirtualSensorList(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return mVirtualDeviceInternal.getVirtualSensorList(); } /** @@ -486,20 +388,8 @@ public final class VirtualDeviceManager { @NonNull PendingIntent pendingIntent, @NonNull Executor executor, @NonNull IntConsumer listener) { - try { - mVirtualDevice.launchPendingIntent( - displayId, - pendingIntent, - new ResultReceiver(new Handler(Looper.getMainLooper())) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - super.onReceiveResult(resultCode, resultData); - executor.execute(() -> listener.accept(resultCode)); - } - }); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } + mVirtualDeviceInternal.launchPendingIntent( + displayId, pendingIntent, executor, listener); } /** @@ -540,7 +430,7 @@ public final class VirtualDeviceManager { @VirtualDisplayFlag int flags, @Nullable @CallbackExecutor Executor executor, @Nullable VirtualDisplay.Callback callback) { - // Currently this just use the device ID, which means all of the virtual displays + // Currently this just uses the device ID, which means all of the virtual displays // created using the same virtual device will have the same name if they use this // deprecated API. The name should only be used for informational purposes, and not for // identifying the display in code. @@ -551,7 +441,7 @@ public final class VirtualDeviceManager { if (surface != null) { builder.setSurface(surface); } - return createVirtualDisplay(builder.build(), executor, callback); + return mVirtualDeviceInternal.createVirtualDisplay(builder.build(), executor, callback); } /** @@ -573,18 +463,7 @@ public final class VirtualDeviceManager { @NonNull VirtualDisplayConfig config, @Nullable @CallbackExecutor Executor executor, @Nullable VirtualDisplay.Callback callback) { - IVirtualDisplayCallback callbackWrapper = - new DisplayManagerGlobal.VirtualDisplayCallback(callback, executor); - final int displayId; - try { - displayId = mService.createVirtualDisplay(config, callbackWrapper, mVirtualDevice, - mContext.getPackageName()); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } - DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance(); - return displayManager.createVirtualDisplayWrapper(config, callbackWrapper, - displayId); + return mVirtualDeviceInternal.createVirtualDisplay(config, executor, callback); } /** @@ -593,16 +472,7 @@ public final class VirtualDeviceManager { */ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close() { - try { - // This also takes care of unregistering all virtual sensors. - mVirtualDevice.close(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - if (mVirtualAudioDevice != null) { - mVirtualAudioDevice.close(); - mVirtualAudioDevice = null; - } + mVirtualDeviceInternal.close(); } /** @@ -613,14 +483,7 @@ public final class VirtualDeviceManager { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) { - try { - final IBinder token = new Binder( - "android.hardware.input.VirtualDpad:" + config.getInputDeviceName()); - mVirtualDevice.createVirtualDpad(config, token); - return new VirtualDpad(mVirtualDevice, token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return mVirtualDeviceInternal.createVirtualDpad(config); } /** @@ -631,14 +494,7 @@ public final class VirtualDeviceManager { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualKeyboard createVirtualKeyboard(@NonNull VirtualKeyboardConfig config) { - try { - final IBinder token = new Binder( - "android.hardware.input.VirtualKeyboard:" + config.getInputDeviceName()); - mVirtualDevice.createVirtualKeyboard(config, token); - return new VirtualKeyboard(mVirtualDevice, token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return mVirtualDeviceInternal.createVirtualKeyboard(config); } /** @@ -664,7 +520,7 @@ public final class VirtualDeviceManager { .setInputDeviceName(inputDeviceName) .setAssociatedDisplayId(display.getDisplay().getDisplayId()) .build(); - return createVirtualKeyboard(keyboardConfig); + return mVirtualDeviceInternal.createVirtualKeyboard(keyboardConfig); } /** @@ -675,14 +531,7 @@ public final class VirtualDeviceManager { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualMouse createVirtualMouse(@NonNull VirtualMouseConfig config) { - try { - final IBinder token = new Binder( - "android.hardware.input.VirtualMouse:" + config.getInputDeviceName()); - mVirtualDevice.createVirtualMouse(config, token); - return new VirtualMouse(mVirtualDevice, token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return mVirtualDeviceInternal.createVirtualMouse(config); } /** @@ -709,7 +558,7 @@ public final class VirtualDeviceManager { .setInputDeviceName(inputDeviceName) .setAssociatedDisplayId(display.getDisplay().getDisplayId()) .build(); - return createVirtualMouse(mouseConfig); + return mVirtualDeviceInternal.createVirtualMouse(mouseConfig); } /** @@ -721,38 +570,7 @@ public final class VirtualDeviceManager { @NonNull public VirtualTouchscreen createVirtualTouchscreen( @NonNull VirtualTouchscreenConfig config) { - try { - final IBinder token = new Binder( - "android.hardware.input.VirtualTouchscreen:" + config.getInputDeviceName()); - mVirtualDevice.createVirtualTouchscreen(config, token); - return new VirtualTouchscreen(mVirtualDevice, token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Creates a virtual touchpad in navigation mode. - * - * A touchpad in navigation mode means that its events are interpreted as navigation events - * (up, down, etc) instead of using them to update a cursor's absolute position. If the - * events are not consumed they are converted to DPAD events. - * - * @param config the configurations of the virtual navigation touchpad. - */ - @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) - @NonNull - public VirtualNavigationTouchpad createVirtualNavigationTouchpad( - @NonNull VirtualNavigationTouchpadConfig config) { - try { - final IBinder token = new Binder( - "android.hardware.input.VirtualNavigationTouchpad:" - + config.getInputDeviceName()); - mVirtualDevice.createVirtualNavigationTouchpad(config, token); - return new VirtualNavigationTouchpad(mVirtualDevice, token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return mVirtualDeviceInternal.createVirtualTouchscreen(config); } /** @@ -781,7 +599,23 @@ public final class VirtualDeviceManager { .setInputDeviceName(inputDeviceName) .setAssociatedDisplayId(display.getDisplay().getDisplayId()) .build(); - return createVirtualTouchscreen(touchscreenConfig); + return mVirtualDeviceInternal.createVirtualTouchscreen(touchscreenConfig); + } + + /** + * Creates a virtual touchpad in navigation mode. + * + * A touchpad in navigation mode means that its events are interpreted as navigation events + * (up, down, etc) instead of using them to update a cursor's absolute position. If the + * events are not consumed they are converted to DPAD events. + * + * @param config the configurations of the virtual navigation touchpad. + */ + @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) + @NonNull + public VirtualNavigationTouchpad createVirtualNavigationTouchpad( + @NonNull VirtualNavigationTouchpadConfig config) { + return mVirtualDeviceInternal.createVirtualNavigationTouchpad(config); } /** @@ -807,11 +641,7 @@ public final class VirtualDeviceManager { @NonNull VirtualDisplay display, @Nullable Executor executor, @Nullable AudioConfigurationChangeCallback callback) { - if (mVirtualAudioDevice == null) { - mVirtualAudioDevice = new VirtualAudioDevice(mContext, mVirtualDevice, display, - executor, callback, () -> mVirtualAudioDevice = null); - } - return mVirtualAudioDevice; + return mVirtualDeviceInternal.createVirtualAudioDevice(display, executor, callback); } /** @@ -823,11 +653,7 @@ public final class VirtualDeviceManager { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public void setShowPointerIcon(boolean showPointerIcon) { - try { - mVirtualDevice.setShowPointerIcon(showPointerIcon); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + mVirtualDeviceInternal.setShowPointerIcon(showPointerIcon); } /** @@ -840,11 +666,7 @@ public final class VirtualDeviceManager { */ public void addActivityListener( @CallbackExecutor @NonNull Executor executor, @NonNull ActivityListener listener) { - final ActivityListenerDelegate delegate = new ActivityListenerDelegate( - Objects.requireNonNull(listener), Objects.requireNonNull(executor)); - synchronized (mActivityListenersLock) { - mActivityListeners.put(listener, delegate); - } + mVirtualDeviceInternal.addActivityListener(executor, listener); } /** @@ -855,9 +677,7 @@ public final class VirtualDeviceManager { * @see #addActivityListener(Executor, ActivityListener) */ public void removeActivityListener(@NonNull ActivityListener listener) { - synchronized (mActivityListenersLock) { - mActivityListeners.remove(Objects.requireNonNull(listener)); - } + mVirtualDeviceInternal.removeActivityListener(listener); } /** @@ -869,12 +689,7 @@ public final class VirtualDeviceManager { */ public void addSoundEffectListener(@CallbackExecutor @NonNull Executor executor, @NonNull SoundEffectListener soundEffectListener) { - final SoundEffectListenerDelegate delegate = - new SoundEffectListenerDelegate(Objects.requireNonNull(executor), - Objects.requireNonNull(soundEffectListener)); - synchronized (mSoundEffectListenersLock) { - mSoundEffectListeners.put(soundEffectListener, delegate); - } + mVirtualDeviceInternal.addSoundEffectListener(executor, soundEffectListener); } /** @@ -884,9 +699,7 @@ public final class VirtualDeviceManager { * @see #addActivityListener(Executor, ActivityListener) */ public void removeSoundEffectListener(@NonNull SoundEffectListener soundEffectListener) { - synchronized (mSoundEffectListenersLock) { - mSoundEffectListeners.remove(Objects.requireNonNull(soundEffectListener)); - } + mVirtualDeviceInternal.removeSoundEffectListener(soundEffectListener); } /** @@ -905,19 +718,8 @@ public final class VirtualDeviceManager { @NonNull IntentFilter interceptorFilter, @CallbackExecutor @NonNull Executor executor, @NonNull IntentInterceptorCallback interceptorCallback) { - Objects.requireNonNull(executor); - Objects.requireNonNull(interceptorFilter); - Objects.requireNonNull(interceptorCallback); - final VirtualIntentInterceptorDelegate delegate = - new VirtualIntentInterceptorDelegate(executor, interceptorCallback); - try { - mVirtualDevice.registerIntentInterceptor(delegate, interceptorFilter); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - synchronized (mIntentInterceptorListenersLock) { - mIntentInterceptorListeners.put(interceptorCallback, delegate); - } + mVirtualDeviceInternal.registerIntentInterceptor( + interceptorFilter, executor, interceptorCallback); } /** @@ -927,18 +729,7 @@ public final class VirtualDeviceManager { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor( @NonNull IntentInterceptorCallback interceptorCallback) { - Objects.requireNonNull(interceptorCallback); - final VirtualIntentInterceptorDelegate delegate; - synchronized (mIntentInterceptorListenersLock) { - delegate = mIntentInterceptorListeners.remove(interceptorCallback); - } - if (delegate != null) { - try { - mVirtualDevice.unregisterIntentInterceptor(delegate); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } + mVirtualDeviceInternal.unregisterIntentInterceptor(interceptorCallback); } } @@ -986,33 +777,6 @@ public final class VirtualDeviceManager { void onDisplayEmpty(int displayId); } - /** - * A wrapper for {@link ActivityListener} that executes callbacks on the given executor. - */ - private static class ActivityListenerDelegate { - @NonNull private final ActivityListener mActivityListener; - @NonNull private final Executor mExecutor; - - ActivityListenerDelegate(@NonNull ActivityListener listener, @NonNull Executor executor) { - mActivityListener = listener; - mExecutor = executor; - } - - public void onTopActivityChanged(int displayId, ComponentName topActivity) { - mExecutor.execute(() -> mActivityListener.onTopActivityChanged(displayId, topActivity)); - } - - public void onTopActivityChanged(int displayId, ComponentName topActivity, - @UserIdInt int userId) { - mExecutor.execute(() -> - mActivityListener.onTopActivityChanged(displayId, topActivity, userId)); - } - - public void onDisplayEmpty(int displayId) { - mExecutor.execute(() -> mActivityListener.onDisplayEmpty(displayId)); - } - } - /** * Interceptor interface to be called when an intent matches the IntentFilter passed into {@link * VirtualDevice#registerIntentInterceptor}. When the interceptor is called after matching the @@ -1034,32 +798,6 @@ public final class VirtualDeviceManager { void onIntentIntercepted(@NonNull Intent intent); } - /** - * A wrapper for {@link IntentInterceptorCallback} that executes callbacks on the - * the given executor. - */ - private static class VirtualIntentInterceptorDelegate - extends IVirtualDeviceIntentInterceptor.Stub { - @NonNull private final IntentInterceptorCallback mIntentInterceptorCallback; - @NonNull private final Executor mExecutor; - - private VirtualIntentInterceptorDelegate(Executor executor, - IntentInterceptorCallback interceptorCallback) { - mExecutor = executor; - mIntentInterceptorCallback = interceptorCallback; - } - - @Override - public void onIntentIntercepted(Intent intent) { - final long token = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mIntentInterceptorCallback.onIntentIntercepted(intent)); - } finally { - Binder.restoreCallingIdentity(token); - } - } - } - /** * Listener for system sound effect playback on virtual device. * @hide @@ -1075,22 +813,4 @@ public final class VirtualDeviceManager { */ void onPlaySoundEffect(@AudioManager.SystemSoundEffect int effectType); } - - /** - * A wrapper for {@link SoundEffectListener} that executes callbacks on the given executor. - */ - private static class SoundEffectListenerDelegate { - @NonNull private final SoundEffectListener mSoundEffectListener; - @NonNull private final Executor mExecutor; - - private SoundEffectListenerDelegate(Executor executor, - SoundEffectListener soundEffectCallback) { - mSoundEffectListener = soundEffectCallback; - mExecutor = executor; - } - - public void onPlaySoundEffect(@AudioManager.SystemSoundEffect int effectType) { - mExecutor.execute(() -> mSoundEffectListener.onPlaySoundEffect(effectType)); - } - } }