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

Commit e3d643aa authored by Maurice Lam's avatar Maurice Lam Committed by Android (Google) Code Review
Browse files

Merge "Add ActivityListener to VirtualDeviceManager"

parents aeaaa42c 7f512ebc
Loading
Loading
Loading
Loading
+43 −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;

import android.content.ComponentName;

/**
 * Interface to listen for activity changes in a virtual device.
 *
 * @hide
 */
interface IVirtualDeviceActivityListener {

    /**
     * Called when the top activity is changed.
     *
     * @param displayId The display ID on which the activity change happened.
     * @param topActivity The component name of the top activity.
     */
    void onTopActivityChanged(int displayId, in ComponentName topActivity);

    /**
     * Called when the display becomes empty (e.g. if the user hits back on the last
     * activity of the root task).
     *
     * @param displayId The display ID that became empty.
     */
    void onDisplayEmpty(int displayId);
}
+2 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.companion.virtual;

import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.VirtualDeviceParams;

/**
@@ -39,5 +40,5 @@ interface IVirtualDeviceManager {
     */
    IVirtualDevice createVirtualDevice(
            in IBinder token, String packageName, int associationId,
            in VirtualDeviceParams params);
            in VirtualDeviceParams params, in IVirtualDeviceActivityListener activityListener);
}
+132 −6
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.annotation.SystemService;
import android.app.Activity;
import android.app.PendingIntent;
import android.companion.AssociationInfo;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
@@ -40,6 +41,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.util.ArrayMap;
import android.view.Surface;

import java.util.concurrent.Executor;
@@ -87,9 +89,7 @@ public final class VirtualDeviceManager {
            int associationId,
            @NonNull VirtualDeviceParams params) {
        try {
            IVirtualDevice virtualDevice = mService.createVirtualDevice(
                    new Binder(), mContext.getPackageName(), associationId, params);
            return new VirtualDevice(mContext, virtualDevice);
            return new VirtualDevice(mService, mContext, associationId, params);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -106,10 +106,49 @@ public final class VirtualDeviceManager {

        private final Context mContext;
        private final IVirtualDevice mVirtualDevice;
        private final ArrayMap<ActivityListener, ActivityListenerDelegate> mActivityListeners =
                new ArrayMap<>();
        private final IVirtualDeviceActivityListener mActivityListenerBinder =
                new IVirtualDeviceActivityListener.Stub() {

        private VirtualDevice(Context context, IVirtualDevice virtualDevice) {
                    @Override
                    public void onTopActivityChanged(int displayId, ComponentName topActivity) {
                        final long token = Binder.clearCallingIdentity();
                        try {
                            for (int i = 0; i < mActivityListeners.size(); i++) {
                                mActivityListeners.valueAt(i)
                                        .onTopActivityChanged(displayId, topActivity);
                            }
                        } finally {
                            Binder.restoreCallingIdentity(token);
                        }
                    }

                    @Override
                    public void onDisplayEmpty(int displayId) {
                        final long token = Binder.clearCallingIdentity();
                        try {
                            for (int i = 0; i < mActivityListeners.size(); i++) {
                                mActivityListeners.valueAt(i).onDisplayEmpty(displayId);
                            }
                        } finally {
                            Binder.restoreCallingIdentity(token);
                        }
                    }
                };

        private VirtualDevice(
                IVirtualDeviceManager service,
                Context context,
                int associationId,
                VirtualDeviceParams params) throws RemoteException {
            mContext = context.getApplicationContext();
            mVirtualDevice = virtualDevice;
            mVirtualDevice = service.createVirtualDevice(
                    new Binder(),
                    mContext.getPackageName(),
                    associationId,
                    params,
                    mActivityListenerBinder);
        }

        /**
@@ -137,7 +176,7 @@ public final class VirtualDeviceManager {
                mVirtualDevice.launchPendingIntent(
                        displayId,
                        pendingIntent,
                        new ResultReceiver(new Handler(Looper.myLooper())) {
                        new ResultReceiver(new Handler(Looper.getMainLooper())) {
                            @Override
                            protected void onReceiveResult(int resultCode, Bundle resultData) {
                                super.onReceiveResult(resultCode, resultData);
@@ -323,6 +362,47 @@ public final class VirtualDeviceManager {
                throw e.rethrowFromSystemServer();
            }
        }

        /**
         * Adds an activity listener to listen for events such as top activity change or virtual
         * display task stack became empty.
         *
         * @param listener The listener to add.
         * @see #removeActivityListener(ActivityListener)
         * @hide
         */
        // TODO(b/194949534): Unhide this API
        public void addActivityListener(@NonNull ActivityListener listener) {
            addActivityListener(listener, mContext.getMainExecutor());
        }

        /**
         * Adds an activity listener to listen for events such as top activity change or virtual
         * display task stack became empty.
         *
         * @param listener The listener to add.
         * @param executor The executor where the callback is executed on.
         * @see #removeActivityListener(ActivityListener)
         * @hide
         */
        // TODO(b/194949534): Unhide this API
        public void addActivityListener(
                @NonNull ActivityListener listener, @NonNull Executor executor) {
            mActivityListeners.put(listener, new ActivityListenerDelegate(listener, executor));
        }

        /**
         * Removes an activity listener previously added with
         * {@link #addActivityListener}.
         *
         * @param listener The listener to remove.
         * @see #addActivityListener(ActivityListener, Executor)
         * @hide
         */
        // TODO(b/194949534): Unhide this API
        public void removeActivityListener(@NonNull ActivityListener listener) {
            mActivityListeners.remove(listener);
        }
    }

    /**
@@ -342,4 +422,50 @@ public final class VirtualDeviceManager {
         */
        void onLaunchFailed();
    }

    /**
     * Listener for activity changes in this virtual device.
     *
     * @hide
     */
    // TODO(b/194949534): Unhide this API
    public interface ActivityListener {

        /**
         * Called when the top activity is changed.
         *
         * @param displayId The display ID on which the activity change happened.
         * @param topActivity The component name of the top activity.
         */
        void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity);

        /**
         * Called when the display becomes empty (e.g. if the user hits back on the last
         * activity of the root task).
         *
         * @param displayId The display ID that became empty.
         */
        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 onDisplayEmpty(int displayId) {
            mExecutor.execute(() -> mActivityListener.onDisplayEmpty(displayId));
        }
    }
}
+28 −2
Original line number Diff line number Diff line
@@ -23,14 +23,18 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.compat.CompatChanges;
import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
import android.view.Display;
import android.window.DisplayWindowPolicyController;

import java.util.List;
@@ -60,15 +64,29 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {

    @NonNull
    final ArraySet<Integer> mRunningUids = new ArraySet<>();
    @Nullable private final ActivityListener mActivityListener;
    private final Handler mHandler = new Handler(Looper.getMainLooper());

    /**
     * Creates a window policy controller that is generic to the different use cases of virtual
     * device.
     *
     * @param windowFlags The window flags that this controller is interested in.
     * @param systemWindowFlags The system window flags that this controller is interested in.
     * @param allowedUsers The set of users that are allowed to stream in this display.
     * @param activityListener Activity listener to listen for activity changes. The display ID
     *   is not populated in this callback and is always {@link Display#INVALID_DISPLAY}.
     */
    GenericWindowPolicyController(int windowFlags, int systemWindowFlags,
            @NonNull ArraySet<UserHandle> allowedUsers,
            @Nullable Set<ComponentName> allowedActivities,
            @Nullable Set<ComponentName> blockedActivities) {
            @Nullable Set<ComponentName> blockedActivities,
            @NonNull ActivityListener activityListener) {
        mAllowedUsers = allowedUsers;
        mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
        mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
        setInterestedWindowFlags(windowFlags, systemWindowFlags);
        mActivityListener = activityListener;
    }

    @Override
@@ -92,13 +110,21 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {

    @Override
    public void onTopActivityChanged(ComponentName topActivity, int uid) {

        if (mActivityListener != null) {
            // Post callback on the main thread so it doesn't block activity launching
            mHandler.post(() ->
                    mActivityListener.onTopActivityChanged(Display.INVALID_DISPLAY, topActivity));
        }
    }

    @Override
    public void onRunningAppsChanged(ArraySet<Integer> runningUids) {
        mRunningUids.clear();
        mRunningUids.addAll(runningUids);
        if (mActivityListener != null && mRunningUids.isEmpty()) {
            // Post callback on the main thread so it doesn't block activity launching
            mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY));
        }
    }

    /**
+38 −5
Original line number Diff line number Diff line
@@ -29,7 +29,10 @@ import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.companion.virtual.VirtualDeviceParams;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
@@ -75,6 +78,30 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
    private final OnDeviceCloseListener mListener;
    private final IBinder mAppToken;
    private final VirtualDeviceParams mParams;
    private final IVirtualDeviceActivityListener mActivityListener;

    private ActivityListener createListenerAdapter(int displayId) {
        return new ActivityListener() {

            @Override
            public void onTopActivityChanged(int unusedDisplayId, ComponentName topActivity) {
                try {
                    mActivityListener.onTopActivityChanged(displayId, topActivity);
                } catch (RemoteException e) {
                    Slog.w(TAG, "Unable to call mActivityListener", e);
                }
            }

            @Override
            public void onDisplayEmpty(int unusedDisplayId) {
                try {
                    mActivityListener.onDisplayEmpty(displayId);
                } catch (RemoteException e) {
                    Slog.w(TAG, "Unable to call mActivityListener", e);
                }
            }
        };
    }

    /**
     * A mapping from the virtual display ID to its corresponding
@@ -85,18 +112,22 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub

    VirtualDeviceImpl(Context context, AssociationInfo associationInfo,
            IBinder token, int ownerUid, OnDeviceCloseListener listener,
            PendingTrampolineCallback pendingTrampolineCallback, VirtualDeviceParams params) {
            PendingTrampolineCallback pendingTrampolineCallback,
            IVirtualDeviceActivityListener activityListener,
            VirtualDeviceParams params) {
        this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener,
                pendingTrampolineCallback, params);
                pendingTrampolineCallback, activityListener, params);
    }

    @VisibleForTesting
    VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token,
            int ownerUid, InputController inputController, OnDeviceCloseListener listener,
            PendingTrampolineCallback pendingTrampolineCallback, VirtualDeviceParams params) {
            PendingTrampolineCallback pendingTrampolineCallback,
            IVirtualDeviceActivityListener activityListener, VirtualDeviceParams params) {
        mContext = context;
        mAssociationInfo = associationInfo;
        mPendingTrampolineCallback = pendingTrampolineCallback;
        mActivityListener = activityListener;
        mOwnerUid = ownerUid;
        mAppToken = token;
        mParams = params;
@@ -361,9 +392,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
                displayId, false);
        final GenericWindowPolicyController dwpc =
                new GenericWindowPolicyController(FLAG_SECURE,
                        SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, getAllowedUserHandles(),
                        SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
                        getAllowedUserHandles(),
                        mParams.getAllowedActivities(),
                        mParams.getBlockedActivities());
                        mParams.getBlockedActivities(),
                        createListenerAdapter(displayId));
        mWindowPolicyControllers.put(displayId, dwpc);
        return dwpc;
    }
Loading