Merge "Add listener to sound effects played on VirtualDevice."
This commit is contained in:
committed by
Android (Google) Code Review
commit
2ffa2cc070
@@ -3010,8 +3010,13 @@ package android.companion.virtual {
|
||||
method public void onIntentIntercepted(@NonNull android.content.Intent);
|
||||
}
|
||||
|
||||
public static interface VirtualDeviceManager.SoundEffectListener {
|
||||
method public void onPlaySoundEffect(int);
|
||||
}
|
||||
|
||||
public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
|
||||
method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
|
||||
method public void addSoundEffectListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
|
||||
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
|
||||
method @NonNull public android.content.Context createContext();
|
||||
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
|
||||
@@ -3030,6 +3035,7 @@ package android.companion.virtual {
|
||||
method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
|
||||
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
|
||||
method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
|
||||
method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
|
||||
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
|
||||
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.companion.virtual;
|
||||
|
||||
import android.companion.virtual.IVirtualDevice;
|
||||
import android.companion.virtual.IVirtualDeviceActivityListener;
|
||||
import android.companion.virtual.IVirtualDeviceSoundEffectListener;
|
||||
import android.companion.virtual.VirtualDevice;
|
||||
import android.companion.virtual.VirtualDeviceParams;
|
||||
import android.hardware.display.IVirtualDisplayCallback;
|
||||
@@ -41,10 +42,12 @@ interface IVirtualDeviceManager {
|
||||
* @param params The parameters for creating this virtual device. See {@link
|
||||
* VirtualDeviceManager.VirtualDeviceParams}.
|
||||
* @param activityListener The listener to listen for activity changes in a virtual device.
|
||||
* @param soundEffectListener The listener to listen for sound effect playback requests.
|
||||
*/
|
||||
IVirtualDevice createVirtualDevice(
|
||||
in IBinder token, String packageName, int associationId,
|
||||
in VirtualDeviceParams params, in IVirtualDeviceActivityListener activityListener);
|
||||
in VirtualDeviceParams params, in IVirtualDeviceActivityListener activityListener,
|
||||
in IVirtualDeviceSoundEffectListener soundEffectListener);
|
||||
|
||||
/**
|
||||
* Returns the details of all available virtual devices.
|
||||
@@ -92,4 +95,13 @@ interface IVirtualDeviceManager {
|
||||
* if there's none.
|
||||
*/
|
||||
int getAudioRecordingSessionId(int deviceId);
|
||||
|
||||
/**
|
||||
* Triggers sound effect playback on virtual device.
|
||||
*
|
||||
* @param deviceId id of the virtual device.
|
||||
* @param sound effect type corresponding to
|
||||
* {@code android.media.AudioManager.SystemSoundEffect}
|
||||
*/
|
||||
void playSoundEffect(int deviceId, int effectType);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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;
|
||||
|
||||
/**
|
||||
* Interface to listen for sound effect playback on Virtual Device.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IVirtualDeviceSoundEffectListener {
|
||||
|
||||
|
||||
/**
|
||||
* Called when there's sound effect to be played on Virtual Device.
|
||||
*
|
||||
* @param sound effect type corresponding to
|
||||
* {@code android.media.AudioManager.SystemSoundEffect}
|
||||
*/
|
||||
void onPlaySoundEffect(int effectType);
|
||||
}
|
||||
@@ -69,6 +69,8 @@ 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;
|
||||
@@ -333,10 +335,15 @@ public final class VirtualDeviceManager {
|
||||
* @hide
|
||||
*/
|
||||
public void playSoundEffect(int deviceId, @AudioManager.SystemSoundEffect int effectType) {
|
||||
//TODO - handle requests to play sound effects by custom callbacks or SoundPool asociated
|
||||
// with device session id.
|
||||
// For now, this is intentionally left empty and effectively disables sound effects for
|
||||
// virtual devices with custom device audio policy.
|
||||
if (mService == null) {
|
||||
Log.w(TAG, "Failed to dispatch sound effect; no virtual device manager service.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mService.playSoundEffect(deviceId, effectType);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -359,6 +366,10 @@ public final class VirtualDeviceManager {
|
||||
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() {
|
||||
|
||||
@@ -387,6 +398,22 @@ public final class VirtualDeviceManager {
|
||||
}
|
||||
}
|
||||
};
|
||||
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 VirtualCameraDevice mVirtualCameraDevice;
|
||||
@NonNull
|
||||
@@ -407,7 +434,8 @@ public final class VirtualDeviceManager {
|
||||
mContext.getPackageName(),
|
||||
associationId,
|
||||
params,
|
||||
mActivityListenerBinder);
|
||||
mActivityListenerBinder,
|
||||
mSoundEffectListener);
|
||||
final List<VirtualSensorConfig> virtualSensorConfigs = params.getVirtualSensorConfigs();
|
||||
for (int i = 0; i < virtualSensorConfigs.size(); ++i) {
|
||||
mVirtualSensors.add(createVirtualSensor(virtualSensorConfigs.get(i)));
|
||||
@@ -946,6 +974,35 @@ public final class VirtualDeviceManager {
|
||||
mActivityListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a sound effect listener.
|
||||
*
|
||||
* @param executor The executor where the listener is executed on.
|
||||
* @param soundEffectListener The listener to add.
|
||||
* @see #removeActivityListener(ActivityListener)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a sound effect listener previously added with {@link #addActivityListener}.
|
||||
*
|
||||
* @param soundEffectListener The listener to remove.
|
||||
* @see #addActivityListener(Executor, ActivityListener)
|
||||
*/
|
||||
public void removeSoundEffectListener(@NonNull SoundEffectListener soundEffectListener) {
|
||||
synchronized (mSoundEffectListenersLock) {
|
||||
mSoundEffectListeners.remove(soundEffectListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an intent interceptor that will intercept an intent attempting to launch
|
||||
* when matching the provided IntentFilter and calls the callback with the intercepted
|
||||
@@ -1090,4 +1147,38 @@ public final class VirtualDeviceManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for system sound effect playback on virtual device.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public interface SoundEffectListener {
|
||||
|
||||
/**
|
||||
* Called when there's a system sound effect to be played on virtual device.
|
||||
*
|
||||
* @param effectType - system sound effect type, see
|
||||
* {@code android.media.AudioManager.SystemSoundEffect}
|
||||
*/
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import android.companion.AssociationInfo;
|
||||
import android.companion.virtual.IVirtualDevice;
|
||||
import android.companion.virtual.IVirtualDeviceActivityListener;
|
||||
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
|
||||
import android.companion.virtual.IVirtualDeviceSoundEffectListener;
|
||||
import android.companion.virtual.VirtualDeviceManager;
|
||||
import android.companion.virtual.VirtualDeviceManager.ActivityListener;
|
||||
import android.companion.virtual.VirtualDeviceParams;
|
||||
@@ -119,6 +120,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
|
||||
private final VirtualDeviceParams mParams;
|
||||
private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>();
|
||||
private final IVirtualDeviceActivityListener mActivityListener;
|
||||
private final IVirtualDeviceSoundEffectListener mSoundEffectListener;
|
||||
@GuardedBy("mVirtualDeviceLock")
|
||||
private final Map<IBinder, IntentFilter> mIntentInterceptors = new ArrayMap<>();
|
||||
@NonNull
|
||||
@@ -170,6 +172,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
|
||||
OnDeviceCloseListener onDeviceCloseListener,
|
||||
PendingTrampolineCallback pendingTrampolineCallback,
|
||||
IVirtualDeviceActivityListener activityListener,
|
||||
IVirtualDeviceSoundEffectListener soundEffectListener,
|
||||
Consumer<ArraySet<Integer>> runningAppsChangedCallback,
|
||||
VirtualDeviceParams params) {
|
||||
this(
|
||||
@@ -184,6 +187,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
|
||||
onDeviceCloseListener,
|
||||
pendingTrampolineCallback,
|
||||
activityListener,
|
||||
soundEffectListener,
|
||||
runningAppsChangedCallback,
|
||||
params);
|
||||
}
|
||||
@@ -201,6 +205,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
|
||||
OnDeviceCloseListener onDeviceCloseListener,
|
||||
PendingTrampolineCallback pendingTrampolineCallback,
|
||||
IVirtualDeviceActivityListener activityListener,
|
||||
IVirtualDeviceSoundEffectListener soundEffectListener,
|
||||
Consumer<ArraySet<Integer>> runningAppsChangedCallback,
|
||||
VirtualDeviceParams params) {
|
||||
super(PermissionEnforcer.fromContext(context));
|
||||
@@ -209,6 +214,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
|
||||
mAssociationInfo = associationInfo;
|
||||
mPendingTrampolineCallback = pendingTrampolineCallback;
|
||||
mActivityListener = activityListener;
|
||||
mSoundEffectListener = soundEffectListener;
|
||||
mRunningAppsChangedCallback = runningAppsChangedCallback;
|
||||
mOwnerUid = ownerUid;
|
||||
mDeviceId = deviceId;
|
||||
@@ -937,6 +943,14 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
|
||||
Toast.LENGTH_LONG, mContext.getMainLooper());
|
||||
}
|
||||
|
||||
void playSoundEffect(int effectType) {
|
||||
try {
|
||||
mSoundEffectListener.onPlaySoundEffect(effectType);
|
||||
} catch (RemoteException exception) {
|
||||
Slog.w(TAG, "Unable to invoke sound effect listener", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true if
|
||||
* the intent matches any filter notifying the DisplayPolicyController to abort the
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.companion.CompanionDeviceManager;
|
||||
import android.companion.virtual.IVirtualDevice;
|
||||
import android.companion.virtual.IVirtualDeviceActivityListener;
|
||||
import android.companion.virtual.IVirtualDeviceManager;
|
||||
import android.companion.virtual.IVirtualDeviceSoundEffectListener;
|
||||
import android.companion.virtual.VirtualDevice;
|
||||
import android.companion.virtual.VirtualDeviceManager;
|
||||
import android.companion.virtual.VirtualDeviceParams;
|
||||
@@ -222,7 +223,8 @@ public class VirtualDeviceManagerService extends SystemService {
|
||||
String packageName,
|
||||
int associationId,
|
||||
@NonNull VirtualDeviceParams params,
|
||||
@NonNull IVirtualDeviceActivityListener activityListener) {
|
||||
@NonNull IVirtualDeviceActivityListener activityListener,
|
||||
@NonNull IVirtualDeviceSoundEffectListener soundEffectListener) {
|
||||
getContext().enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
|
||||
"createVirtualDevice");
|
||||
@@ -246,7 +248,7 @@ public class VirtualDeviceManagerService extends SystemService {
|
||||
VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
|
||||
associationInfo, token, callingUid, deviceId, cameraAccessController,
|
||||
this::onDeviceClosed, mPendingTrampolineCallback, activityListener,
|
||||
runningAppsChangedCallback, params);
|
||||
soundEffectListener, runningAppsChangedCallback, params);
|
||||
mVirtualDevices.put(deviceId, virtualDevice);
|
||||
return virtualDevice;
|
||||
}
|
||||
@@ -364,6 +366,18 @@ public class VirtualDeviceManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void playSoundEffect(int deviceId, int effectType) {
|
||||
VirtualDeviceImpl virtualDevice;
|
||||
synchronized (mVirtualDeviceManagerLock) {
|
||||
virtualDevice = mVirtualDevices.get(deviceId);
|
||||
}
|
||||
|
||||
if (virtualDevice != null) {
|
||||
virtualDevice.playSoundEffect(effectType);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AssociationInfo getAssociationInfo(String packageName, int associationId) {
|
||||
final UserHandle userHandle = getCallingUserHandle();
|
||||
|
||||
@@ -51,6 +51,7 @@ import android.app.admin.DevicePolicyManager;
|
||||
import android.companion.AssociationInfo;
|
||||
import android.companion.virtual.IVirtualDeviceActivityListener;
|
||||
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
|
||||
import android.companion.virtual.IVirtualDeviceSoundEffectListener;
|
||||
import android.companion.virtual.VirtualDeviceParams;
|
||||
import android.companion.virtual.audio.IAudioConfigChangedCallback;
|
||||
import android.companion.virtual.audio.IAudioRoutingCallback;
|
||||
@@ -75,6 +76,7 @@ import android.hardware.input.VirtualMouseScrollEvent;
|
||||
import android.hardware.input.VirtualNavigationTouchpadConfig;
|
||||
import android.hardware.input.VirtualTouchEvent;
|
||||
import android.hardware.input.VirtualTouchscreenConfig;
|
||||
import android.media.AudioManager;
|
||||
import android.net.MacAddress;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
@@ -223,6 +225,8 @@ public class VirtualDeviceManagerServiceTest {
|
||||
@Mock
|
||||
private IVirtualDeviceActivityListener mActivityListener;
|
||||
@Mock
|
||||
private IVirtualDeviceSoundEffectListener mSoundEffectListener;
|
||||
@Mock
|
||||
private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
|
||||
@Mock
|
||||
private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
|
||||
@@ -1572,6 +1576,13 @@ public class VirtualDeviceManagerServiceTest {
|
||||
intent.filterEquals(blockedAppIntent)), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playSoundEffect_callsSoundEffectListener() throws Exception {
|
||||
mVdm.playSoundEffect(mDeviceImpl.getDeviceId(), AudioManager.FX_KEY_CLICK);
|
||||
|
||||
verify(mSoundEffectListener).onPlaySoundEffect(AudioManager.FX_KEY_CLICK);
|
||||
}
|
||||
|
||||
private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid) {
|
||||
VirtualDeviceParams params = new VirtualDeviceParams.Builder()
|
||||
.setBlockedActivities(getBlockedActivities())
|
||||
@@ -1585,7 +1596,8 @@ public class VirtualDeviceManagerServiceTest {
|
||||
mAssociationInfo, new Binder(), ownerUid, virtualDeviceId,
|
||||
mInputController, mSensorController, mCameraAccessController,
|
||||
/* onDeviceCloseListener= */ deviceId -> mVdms.removeVirtualDevice(deviceId),
|
||||
mPendingTrampolineCallback, mActivityListener, mRunningAppsChangedCallback, params);
|
||||
mPendingTrampolineCallback, mActivityListener, mSoundEffectListener,
|
||||
mRunningAppsChangedCallback, params);
|
||||
mVdms.addVirtualDevice(virtualDeviceImpl);
|
||||
return virtualDeviceImpl;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user