Merge "Move implementation details out of VDM" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b151cfde0b
458
core/java/android/companion/virtual/VirtualDeviceInternal.java
Normal file
458
core/java/android/companion/virtual/VirtualDeviceInternal.java
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user