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
This commit is contained in:
Vladimir Komsiyski
2023-03-29 14:38:16 +02:00
parent 3a829d9767
commit eda545b0ea
2 changed files with 502 additions and 324 deletions

View File

@@ -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<VirtualDeviceManager.ActivityListener, ActivityListenerDelegate>
mActivityListeners =
new ArrayMap<>();
private final Object mIntentInterceptorListenersLock = new Object();
@GuardedBy("mIntentInterceptorListenersLock")
private final ArrayMap<VirtualDeviceManager.IntentInterceptorCallback,
IntentInterceptorDelegate> mIntentInterceptorListeners =
new ArrayMap<>();
private final Object mSoundEffectListenersLock = new Object();
@GuardedBy("mSoundEffectListenersLock")
private final ArrayMap<VirtualDeviceManager.SoundEffectListener, SoundEffectListenerDelegate>
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<VirtualSensor> 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));
}
}
}

View File

@@ -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<ActivityListener, ActivityListenerDelegate> mActivityListeners =
new ArrayMap<>();
private final Object mIntentInterceptorListenersLock = new Object();
@GuardedBy("mIntentInterceptorListenersLock")
private final ArrayMap<IntentInterceptorCallback,
VirtualIntentInterceptorDelegate> mIntentInterceptorListeners =
new ArrayMap<>();
private final Object mSoundEffectListenersLock = new Object();
@GuardedBy("mSoundEffectListenersLock")
private final ArrayMap<SoundEffectListener, SoundEffectListenerDelegate>
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<VirtualSensor> 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));
}
}
}