diff options
| author | 2023-08-26 14:13:57 +0200 | |
|---|---|---|
| committer | 2023-09-21 09:29:28 +0200 | |
| commit | 71f58eb5334089fc65e4a0dce18bc5ce1e703323 (patch) | |
| tree | 3c3b0dcd3b81535c6566d090ab635137f8dbbcab | |
| parent | 6182bc4ad4415c9a2cc018ec8baae9ec37ba3a67 (diff) | |
VDM API for specifying custom home component.
That component is respected whenever a virtual display is created
by the device and that display supports showing home activities.
Using `VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS` for now
and adding a TODO to reference whatever API lands for creating
displays with home support.
Test: atest VirtualDeviceHomeTest
Fix: 297168328
Change-Id: Iced6d176fac39b7cdf375c40de1898c9090a79a3
11 files changed, 122 insertions, 6 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 5c48b21ad35e..fc2063e9e8a5 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3232,6 +3232,7 @@ package android.companion.virtual { method public int getDefaultActivityPolicy(); method public int getDefaultNavigationPolicy(); method public int getDevicePolicy(int); + method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @Nullable public android.content.ComponentName getHomeComponent(); method public int getLockState(); method @Nullable public String getName(); method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts(); @@ -3263,6 +3264,7 @@ package android.companion.virtual { method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int); + method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName); method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>); diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index b4c740ec5599..0fa78c88c863 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -229,6 +229,7 @@ public final class VirtualDeviceParams implements Parcelable { @Nullable private final String mName; // Mapping of @PolicyType to @DevicePolicy @NonNull private final SparseIntArray mDevicePolicies; + @Nullable private final ComponentName mHomeComponent; @NonNull private final List<VirtualSensorConfig> mVirtualSensorConfigs; @Nullable private final IVirtualSensorCallback mVirtualSensorCallback; private final int mAudioPlaybackSessionId; @@ -243,6 +244,7 @@ public final class VirtualDeviceParams implements Parcelable { @NonNull Set<ComponentName> activityPolicyExemptions, @Nullable String name, @NonNull SparseIntArray devicePolicies, + @Nullable ComponentName homeComponent, @NonNull List<VirtualSensorConfig> virtualSensorConfigs, @Nullable IVirtualSensorCallback virtualSensorCallback, int audioPlaybackSessionId, @@ -258,6 +260,7 @@ public final class VirtualDeviceParams implements Parcelable { new ArraySet<>(Objects.requireNonNull(activityPolicyExemptions)); mName = name; mDevicePolicies = Objects.requireNonNull(devicePolicies); + mHomeComponent = homeComponent; mVirtualSensorConfigs = Objects.requireNonNull(virtualSensorConfigs); mVirtualSensorCallback = virtualSensorCallback; mAudioPlaybackSessionId = audioPlaybackSessionId; @@ -280,6 +283,7 @@ public final class VirtualDeviceParams implements Parcelable { IVirtualSensorCallback.Stub.asInterface(parcel.readStrongBinder()); mAudioPlaybackSessionId = parcel.readInt(); mAudioRecordingSessionId = parcel.readInt(); + mHomeComponent = parcel.readTypedObject(ComponentName.CREATOR); } /** @@ -291,6 +295,19 @@ public final class VirtualDeviceParams implements Parcelable { } /** + * Returns the custom component used as home on all displays owned by this virtual device that + * support home activities. + * + * @see Builder#setHomeComponent + */ + // TODO(b/297168328): Link to the relevant API for creating displays with home support. + @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) + @Nullable + public ComponentName getHomeComponent() { + return mHomeComponent; + } + + /** * Returns the user handles with matching managed accounts on the remote device to which * this virtual device is streaming. * @@ -468,6 +485,7 @@ public final class VirtualDeviceParams implements Parcelable { mVirtualSensorCallback != null ? mVirtualSensorCallback.asBinder() : null); dest.writeInt(mAudioPlaybackSessionId); dest.writeInt(mAudioRecordingSessionId); + dest.writeTypedObject(mHomeComponent, flags); } @Override @@ -508,7 +526,7 @@ public final class VirtualDeviceParams implements Parcelable { int hashCode = Objects.hash( mLockState, mUsersWithMatchingAccounts, mCrossTaskNavigationExemptions, mDefaultNavigationPolicy, mActivityPolicyExemptions, mDefaultActivityPolicy, mName, - mDevicePolicies, mAudioPlaybackSessionId, mAudioRecordingSessionId); + mDevicePolicies, mHomeComponent, mAudioPlaybackSessionId, mAudioRecordingSessionId); for (int i = 0; i < mDevicePolicies.size(); i++) { hashCode = 31 * hashCode + mDevicePolicies.keyAt(i); hashCode = 31 * hashCode + mDevicePolicies.valueAt(i); @@ -528,6 +546,7 @@ public final class VirtualDeviceParams implements Parcelable { + " mActivityPolicyExemptions=" + mActivityPolicyExemptions + " mName=" + mName + " mDevicePolicies=" + mDevicePolicies + + " mHomeComponent=" + mHomeComponent + " mAudioPlaybackSessionId=" + mAudioPlaybackSessionId + " mAudioRecordingSessionId=" + mAudioRecordingSessionId + ")"; @@ -588,6 +607,7 @@ public final class VirtualDeviceParams implements Parcelable { @Nullable private VirtualSensorCallback mVirtualSensorCallback; @Nullable private Executor mVirtualSensorDirectChannelCallbackExecutor; @Nullable private VirtualSensorDirectChannelCallback mVirtualSensorDirectChannelCallback; + @Nullable private ComponentName mHomeComponent; private static class VirtualSensorCallbackDelegate extends IVirtualSensorCallback.Stub { @NonNull @@ -665,6 +685,23 @@ public final class VirtualDeviceParams implements Parcelable { } /** + * Specifies a component to be used as home on all displays owned by this virtual device + * that support home activities. + * * + * <p>Note: Only relevant for virtual displays that support home activities.</p> + * + * @param homeComponent The component name to be used as home. If unset, then the system- + * default secondary home activity will be used. + */ + // TODO(b/297168328): Link to the relevant API for creating displays with home support. + @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) + @NonNull + public Builder setHomeComponent(@Nullable ComponentName homeComponent) { + mHomeComponent = homeComponent; + return this; + } + + /** * Sets the user handles with matching managed accounts on the remote device to which * this virtual device is streaming. The caller is responsible for verifying the presence * and legitimacy of a matching managed account on the remote device. @@ -1031,6 +1068,7 @@ public final class VirtualDeviceParams implements Parcelable { mActivityPolicyExemptions, mName, mDevicePolicies, + mHomeComponent, mVirtualSensorConfigs, virtualSensorCallbackDelegate, mAudioPlaybackSessionId, diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig index ee36f187b729..3e96c96d8d17 100644 --- a/core/java/android/companion/virtual/flags.aconfig +++ b/core/java/android/companion/virtual/flags.aconfig @@ -15,6 +15,13 @@ flag { } flag { + name: "vdm_custom_home" + namespace: "virtual_devices" + description: "Enable custom home API" + bug: "297168328" +} + +flag { name: "vdm_public_apis" namespace: "virtual_devices" description: "Enable public VDM API for device capabilities" diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java index 2747233a4bf1..8d71a8e998bd 100644 --- a/core/java/android/window/DisplayWindowPolicyController.java +++ b/core/java/android/window/DisplayWindowPolicyController.java @@ -109,6 +109,12 @@ public abstract class DisplayWindowPolicyController { } /** + * @return the custom home component specified for the relevant display, if any. + */ + @Nullable + public abstract ComponentName getCustomHomeComponent(); + + /** * Returns {@code true} if all of the given activities can be launched on this virtual display * in the configuration defined by the rest of the arguments. * diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index 102c26245dc3..a3ccb168aa4e 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -133,6 +133,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @GuardedBy("mGenericWindowPolicyControllerLock") private boolean mShowTasksInHostDeviceRecents; + @Nullable private final ComponentName mCustomHomeComponent; /** * Creates a window policy controller that is generic to the different use cases of virtual @@ -157,6 +158,10 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController * @param intentListenerCallback Callback that is called to intercept intents when matching * passed in filters. * @param showTasksInHostDeviceRecents whether to show activities in recents on the host device. + * @param customHomeComponent The component acting as a home activity on the virtual display. If + * {@code null}, then the system-default secondary home activity will be used. This is only + * applicable to displays that support home activities, i.e. they're created with the relevant + * virtual display flag. */ public GenericWindowPolicyController( int windowFlags, @@ -172,7 +177,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @Nullable SecureWindowCallback secureWindowCallback, @Nullable IntentListenerCallback intentListenerCallback, @NonNull Set<String> displayCategories, - boolean showTasksInHostDeviceRecents) { + boolean showTasksInHostDeviceRecents, + @Nullable ComponentName customHomeComponent) { super(); mAllowedUsers = allowedUsers; mActivityLaunchAllowedByDefault = activityLaunchAllowedByDefault; @@ -187,6 +193,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mIntentListenerCallback = intentListenerCallback; mDisplayCategories = displayCategories; mShowTasksInHostDeviceRecents = showTasksInHostDeviceRecents; + mCustomHomeComponent = customHomeComponent; } /** @@ -384,6 +391,11 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController return false; } + @Override + public @Nullable ComponentName getCustomHomeComponent() { + return mCustomHomeComponent; + } + /** * Returns true if an app with the given UID has an activity running on the virtual display for * this controller. diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 3b13410720af..78d55df9e935 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -905,6 +905,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mParams.getDefaultNavigationPolicy() == NAVIGATION_POLICY_DEFAULT_ALLOWED; final boolean showTasksInHostDeviceRecents = getDevicePolicy(POLICY_TYPE_RECENTS) == DEVICE_POLICY_DEFAULT; + final ComponentName homeComponent = + Flags.vdmCustomHome() ? mParams.getHomeComponent() : null; final GenericWindowPolicyController gwpc = new GenericWindowPolicyController( FLAG_SECURE, @@ -922,7 +924,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub this::onSecureWindowShown, this::shouldInterceptIntent, displayCategories, - showTasksInHostDeviceRecents); + showTasksInHostDeviceRecents, + homeComponent); gwpc.registerRunningAppsChangedListener(/* listener= */ this); return gwpc; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4309e72c30d7..ca42400dad26 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2160,6 +2160,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } /** + * @see DisplayWindowPolicyController#getCustomHomeComponent() () + */ + @Nullable ComponentName getCustomHomeComponent() { + if (!supportsSystemDecorations() || mDwpcHelper == null) { + return null; + } + return mDwpcHelper.getCustomHomeComponent(); + } + + /** * Applies the rotation transaction. This must be called after {@link #updateRotationUnchecked} * (if it returned {@code true}) to actually finish the rotation. * diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java index 6b33746ad3c4..e0d69b063573 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java +++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java @@ -19,6 +19,7 @@ package com.android.server.wm; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; +import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.Process; @@ -200,6 +201,17 @@ class DisplayWindowPolicyControllerHelper { return mDisplayWindowPolicyController.isEnteringPipAllowed(uid); } + /** + * @see DisplayWindowPolicyController#getCustomHomeComponent + */ + @Nullable + public ComponentName getCustomHomeComponent() { + if (mDisplayWindowPolicyController == null) { + return null; + } + return mDisplayWindowPolicyController.getCustomHomeComponent(); + } + void dump(String prefix, PrintWriter pw) { if (mDisplayWindowPolicyController != null) { pw.println(); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 2fdfec04b895..2a3391807a2c 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1513,10 +1513,30 @@ class RootWindowContainer extends WindowContainer<DisplayContent> throw new IllegalArgumentException( "resolveSecondaryHomeActivity: Should not be default task container"); } - // Resolve activities in the same package as currently selected primary home activity. + Intent homeIntent = mService.getHomeIntent(); ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent); - if (aInfo != null) { + boolean lookForSecondaryHomeActivityInPrimaryHomePackage = aInfo != null; + + if (android.companion.virtual.flags.Flags.vdmCustomHome()) { + // Resolve the externally set home activity for this display, if any. If it is unset or + // we fail to resolve it, fallback to the default secondary home activity. + final ComponentName customHomeComponent = + taskDisplayArea.getDisplayContent() != null + ? taskDisplayArea.getDisplayContent().getCustomHomeComponent() + : null; + if (customHomeComponent != null) { + homeIntent.setComponent(customHomeComponent); + ActivityInfo customHomeActivityInfo = resolveHomeActivity(userId, homeIntent); + if (customHomeActivityInfo != null) { + aInfo = customHomeActivityInfo; + lookForSecondaryHomeActivityInPrimaryHomePackage = false; + } + } + } + + if (lookForSecondaryHomeActivityInPrimaryHomePackage) { + // Resolve activities in the same package as currently selected primary home activity. if (ResolverActivity.class.getName().equals(aInfo.name)) { // Always fallback to secondary home component if default home is not set. aInfo = null; diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java index c40ad2840501..1c48b8aa79f9 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java @@ -88,7 +88,8 @@ public class VirtualAudioControllerTest { /* secureWindowCallback= */ null, /* intentListenerCallback= */ null, /* displayCategories= */ new ArraySet<>(), - /* showTasksInHostDeviceRecents= */ true); + /* showTasksInHostDeviceRecents= */ true, + /* customHomeComponent= */ null); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java index 30a89412d946..cf620fe605c6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java @@ -275,5 +275,10 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase { public boolean isEnteringPipAllowed(int uid) { return true; } + + @Override + public ComponentName getCustomHomeComponent() { + return null; + } } } |