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

Commit 431f916c authored by Vladimir Komsiyski's avatar Vladimir Komsiyski Committed by Android (Google) Code Review
Browse files

Merge "Make canDisplayOnRemoteDevices applicable on all virtual displays." into main

parents 7d708c91 bed4fc65
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -83,6 +83,13 @@ flag {
    is_fixed_read_only: true
}

flag {
     namespace: "virtual_devices"
     name: "enforce_remote_device_opt_out_on_all_virtual_displays"
     description: "Respect canDisplayOnRemoteDevices on all virtual displays"
     bug: "338973239"
}

flag {
    namespace: "virtual_devices"
    name: "virtual_display_multi_window_mode_support"
+1 −1
Original line number Diff line number Diff line
@@ -617,7 +617,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
     */
    public static final int FLAG_ENABLE_VR_MODE = 0x8000;
    /**
     * Bit in {@link #flags} indicating if the activity can be displayed on a remote device.
     * Bit in {@link #flags} indicating if the activity can be displayed on a virtual display.
     * Corresponds to {@link android.R.attr#canDisplayOnRemoteDevices}
     * @hide
     */
+2 −2
Original line number Diff line number Diff line
@@ -3296,8 +3296,8 @@
             usually TVs.
             <p>Requires permission {@code android.permission.DISABLE_SYSTEM_SOUND_EFFECTS}. -->
        <attr name="playHomeTransitionSound" format="boolean"/>
        <!-- Indicates whether the activity can be displayed on a remote device which may or
             may not be running Android. -->
        <!-- Indicates whether the activity can be displayed on a display that may belong to a
             remote device which may or may not be running Android. -->
        <attr name="canDisplayOnRemoteDevices" format="boolean"/>
        <attr name="allowUntrustedActivityEmbedding" />
        <attr name="knownActivityEmbeddingCerts" />
+32 −1
Original line number Diff line number Diff line
@@ -16,9 +16,12 @@

package com.android.server.wm;

import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.companion.virtualdevice.flags.Flags;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -26,6 +29,7 @@ import android.os.Process;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
import android.view.Display;
import android.window.DisplayWindowPolicyController;

import java.io.PrintWriter;
@@ -80,6 +84,9 @@ class DisplayWindowPolicyControllerHelper {
                if (hasDisplayCategory(activities.get(i))) {
                    return false;
                }
                if (!launchAllowedByDisplayPolicy(activities.get(i))) {
                    return false;
                }
            }
            return true;
        }
@@ -95,7 +102,13 @@ class DisplayWindowPolicyControllerHelper {
        if (mDisplayWindowPolicyController == null) {
            // Missing controller means that this display has no categories for activity launch
            // restriction.
            return !hasDisplayCategory(activityInfo);
            if (hasDisplayCategory(activityInfo)) {
                return false;
            }
            if (!launchAllowedByDisplayPolicy(activityInfo)) {
                return false;
            }
            return true;
        }
        return mDisplayWindowPolicyController.canActivityBeLaunched(activityInfo, intent,
            windowingMode, launchingFromDisplayId, isNewTask);
@@ -112,6 +125,24 @@ class DisplayWindowPolicyControllerHelper {
        return false;
    }

    private boolean launchAllowedByDisplayPolicy(ActivityInfo aInfo) {
        if (!Flags.enforceRemoteDeviceOptOutOnAllVirtualDisplays()) {
            return true;
        }
        int displayType = mDisplayContent.getDisplay().getType();
        if (displayType != Display.TYPE_VIRTUAL && displayType != Display.TYPE_WIFI) {
            return true;
        }
        if ((aInfo.flags & FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
            Slog.d(TAG,
                    String.format("Checking activity launch on display %d, activity requires"
                                    + " android:canDisplayOnRemoteDevices=true",
                            mDisplayContent.mDisplayId));
            return false;
        }
        return true;
    }

    /**
     * @see DisplayWindowPolicyController#keepActivityOnWindowFlagsChanged(ActivityInfo, int, int)
     */
+86 −0
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ import android.provider.DeviceConfig;
import android.service.voice.IVoiceInteractionSession;
import android.util.Pair;
import android.util.Size;
import android.view.Display;
import android.view.Gravity;
import android.view.RemoteAnimationAdapter;
import android.window.TaskFragmentOrganizerToken;
@@ -941,6 +942,91 @@ public class ActivityStarterTests extends WindowTestsBase {
                notNull() /* options */);
    }


    /**
     * This test ensures that activity launch on a secondary display is allowed if the activity did
     * not opt out from showing on remote devices.
     */
    @Test
    public void testStartActivityOnVirtualDisplay() {
        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
                false /* mockGetRootTask */);
        starter.mRequest.activityInfo.flags |= ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;

        // Create a virtual display at bottom.
        final TestDisplayContent secondaryDisplay =
                new TestDisplayContent.Builder(mAtm, 1000, 1500)
                        .setType(Display.TYPE_VIRTUAL)
                        .setPosition(POSITION_BOTTOM).build();
        final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea();
        final Task stack = secondaryTaskContainer.createRootTask(
                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);

        // Create an activity record on the top of secondary display.
        final ActivityRecord topActivityOnSecondaryDisplay = createSingleTaskActivityOn(stack);

        // Put an activity on default display as the top focused activity.
        new ActivityBuilder(mAtm).setCreateTask(true).build();

        // Start activity with the same intent as {@code topActivityOnSecondaryDisplay}
        // on secondary display.
        final ActivityOptions options = ActivityOptions.makeBasic()
                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
        final int result = starter.setReason("testStartActivityOnVirtualDisplay")
                .setIntent(topActivityOnSecondaryDisplay.intent)
                .setActivityOptions(options.toBundle())
                .execute();

        // Ensure result is delivering intent to top.
        assertEquals(START_DELIVERED_TO_TOP, result);

        // Ensure secondary display only creates one stack.
        verify(secondaryTaskContainer, times(1)).createRootTask(anyInt(), anyInt(), anyBoolean());
    }

    /**
     * This test ensures that activity launch on a secondary display is disallowed if the activity
     * opted out from showing on remote devices.
     */
    @EnableFlags(android.companion.virtualdevice.flags.Flags
            .FLAG_ENFORCE_REMOTE_DEVICE_OPT_OUT_ON_ALL_VIRTUAL_DISPLAYS)
    @Test
    public void testStartOptedOutActivityOnVirtualDisplay() {
        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
                false /* mockGetRootTask */);
        starter.mRequest.activityInfo.flags &= ~ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;

        // Create a virtual display at bottom.
        final TestDisplayContent secondaryDisplay =
                new TestDisplayContent.Builder(mAtm, 1000, 1500)
                        .setType(Display.TYPE_VIRTUAL)
                        .setPosition(POSITION_BOTTOM).build();
        final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea();
        final Task stack = secondaryTaskContainer.createRootTask(
                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);

        // Create an activity record on the top of secondary display.
        final ActivityRecord topActivityOnSecondaryDisplay = createSingleTaskActivityOn(stack);

        // Put an activity on default display as the top focused activity.
        new ActivityBuilder(mAtm).setCreateTask(true).build();

        // Start activity with the same intent as {@code topActivityOnSecondaryDisplay}
        // on secondary display.
        final ActivityOptions options = ActivityOptions.makeBasic()
                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
        final int result = starter.setReason("testStartOptedOutActivityOnVirtualDisplay")
                .setIntent(topActivityOnSecondaryDisplay.intent)
                .setActivityOptions(options.toBundle())
                .execute();

        // Ensure result is canceled.
        assertEquals(START_CANCELED, result);

        // Ensure secondary display only creates one stack.
        verify(secondaryTaskContainer, times(1)).createRootTask(anyInt(), anyInt(), anyBoolean());
    }

    @Test
    public void testWasVisibleInRestartAttempt() {
        final ActivityStarter starter = prepareStarter(