Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6f97640b authored by Jan Sebechlebsky's avatar Jan Sebechlebsky
Browse files

Add listener to sound effects played on VirtualDevice.

Bug: 261698699
Test: atest VirtualDeviceManagerBasicTest
Change-Id: I074c7ce90d0e47dbcfb48171af87388ddb620c52
parent a48bf36a
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -3003,8 +3003,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);
@@ -3023,6 +3028,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);
  }
+13 −1
Original line number Diff line number Diff line
@@ -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);
}
+34 −0
Original line number Diff line number Diff line
/*
 * 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);
}
+96 −5
Original line number Diff line number Diff line
@@ -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));
        }
    }
}
+14 −0
Original line number Diff line number Diff line
@@ -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
Loading