Support status bar and insets on virtual devices.
Allow for the virtual device owner to add TYPE_STATUS_BAR views and
the corresponding insets to the displays owned by them.
Bug: 350007866
Bug: 327742113
Test: presubmit, VDM demo app
Flag: android.companion.virtualdevice.flags.status_bar_and_insets
Change-Id: I15c0d4db1364e13039f978e55e1eacf2a71e63d5
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index b4c36e1..260fcfe 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -110,3 +110,10 @@
description: "Device awareness in power and display APIs"
bug: "285020111"
}
+
+flag {
+ name: "status_bar_and_insets"
+ namespace: "virtual_devices"
+ description: "Allow for status bar and insets on virtual devices"
+ bug: "350007866"
+}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f96706e..04d2de9 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -16,6 +16,7 @@
package com.android.server.policy;
+import static android.Manifest.permission.CREATE_VIRTUAL_DEVICE;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
@@ -59,13 +60,18 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD;
@@ -3058,7 +3064,7 @@
/** {@inheritDoc} */
@Override
public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
- int[] outAppOp) {
+ int[] outAppOp, int displayId) {
if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
!= PERMISSION_GRANTED) {
return ADD_PERMISSION_DENIED;
@@ -3098,6 +3104,12 @@
case TYPE_VOICE_INTERACTION:
case TYPE_QS_DIALOG:
case TYPE_NAVIGATION_BAR_PANEL:
+ case TYPE_STATUS_BAR:
+ case TYPE_NOTIFICATION_SHADE:
+ case TYPE_NAVIGATION_BAR:
+ case TYPE_STATUS_BAR_ADDITIONAL:
+ case TYPE_STATUS_BAR_SUB_PANEL:
+ case TYPE_VOICE_INTERACTION_STARTING:
// The window manager will check these.
return ADD_OKAY;
}
@@ -3141,6 +3153,13 @@
return ADD_OKAY;
}
+ // Allow virtual device owners to add overlays on the displays they own.
+ if (mWindowManagerFuncs.isCallerVirtualDeviceOwner(displayId, callingUid)
+ && mContext.checkCallingOrSelfPermission(CREATE_VIRTUAL_DEVICE)
+ == PERMISSION_GRANTED) {
+ return ADD_OKAY;
+ }
+
// check if user has enabled this operation. SecurityException will be thrown if this app
// has not been allowed by the user. The reason to use "noteOp" (instead of checkOp) is to
// make sure the usage is logged.
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 989c8a8..892af6b 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -362,6 +362,12 @@
* Invoked when a screenshot is taken of the given display to notify registered listeners.
*/
List<ComponentName> notifyScreenshotListeners(int displayId);
+
+ /**
+ * Returns whether the given UID is the owner of a virtual device, which the given display
+ * belongs to.
+ */
+ boolean isCallerVirtualDeviceOwner(int displayId, int callingUid);
}
/**
@@ -421,6 +427,7 @@
* @param packageName package name
* @param outAppOp First element will be filled with the app op corresponding to
* this window, or OP_NONE.
+ * @param displayId The display on which this window is being added.
*
* @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed;
* else an error code, usually
@@ -429,7 +436,7 @@
* @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY
*/
int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
- int[] outAppOp);
+ int[] outAppOp, int displayId);
/**
* After the window manager has computed the current configuration based
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e90a2c9..9a3ad2d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -107,7 +107,6 @@
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
-import android.companion.virtual.VirtualDeviceManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -158,6 +157,7 @@
import com.android.server.am.ActivityManagerService;
import com.android.server.am.HostingRecord;
import com.android.server.am.UserState;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.pm.SaferIntentUtils;
import com.android.server.utils.Slogf;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
@@ -285,7 +285,7 @@
private WindowManagerService mWindowManager;
private AppOpsManager mAppOpsManager;
- private VirtualDeviceManager mVirtualDeviceManager;
+ private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
/** Common synchronization logic used to save things to disks. */
PersisterQueue mPersisterQueue;
@@ -1298,16 +1298,24 @@
if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
return Context.DEVICE_ID_DEFAULT;
}
- if (mVirtualDeviceManager == null) {
+ if (mVirtualDeviceManagerInternal == null) {
if (mService.mHasCompanionDeviceSetupFeature) {
- mVirtualDeviceManager =
- mService.mContext.getSystemService(VirtualDeviceManager.class);
+ mVirtualDeviceManagerInternal =
+ LocalServices.getService(VirtualDeviceManagerInternal.class);
}
- if (mVirtualDeviceManager == null) {
+ if (mVirtualDeviceManagerInternal == null) {
return Context.DEVICE_ID_DEFAULT;
}
}
- return mVirtualDeviceManager.getDeviceIdForDisplayId(displayId);
+ return mVirtualDeviceManagerInternal.getDeviceIdForDisplayId(displayId);
+ }
+
+ boolean isDeviceOwnerUid(int displayId, int callingUid) {
+ final int deviceId = getDeviceIdForDisplayId(displayId);
+ if (deviceId == Context.DEVICE_ID_DEFAULT || deviceId == Context.DEVICE_ID_INVALID) {
+ return false;
+ }
+ return mVirtualDeviceManagerInternal.getDeviceOwnerUid(deviceId) == callingUid;
}
private AppOpsManager getAppOpsManager() {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 745b792..5c62120 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1036,7 +1036,7 @@
/**
* Check if a window can be added to the system.
*
- * Currently enforces that two window types are singletons per display:
+ * Currently enforces that these window types are singletons per display:
* <ul>
* <li>{@link WindowManager.LayoutParams#TYPE_STATUS_BAR}</li>
* <li>{@link WindowManager.LayoutParams#TYPE_NOTIFICATION_SHADE}</li>
@@ -1058,41 +1058,39 @@
ActivityTaskManagerService.enforceTaskPermission("DisplayPolicy");
}
+ final String systemUiPermission =
+ mService.isCallerVirtualDeviceOwner(mDisplayContent.getDisplayId(), callingUid)
+ // Allow virtual device owners to add system windows on their displays.
+ ? android.Manifest.permission.CREATE_VIRTUAL_DEVICE
+ : android.Manifest.permission.STATUS_BAR_SERVICE;
+
switch (attrs.type) {
case TYPE_STATUS_BAR:
- mContext.enforcePermission(
- android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+ mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
"DisplayPolicy");
if (mStatusBar != null && mStatusBar.isAlive()) {
return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
case TYPE_NOTIFICATION_SHADE:
- mContext.enforcePermission(
- android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+ mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
"DisplayPolicy");
- if (mNotificationShade != null) {
- if (mNotificationShade.isAlive()) {
- return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
- }
+ if (mNotificationShade != null && mNotificationShade.isAlive()) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
case TYPE_NAVIGATION_BAR:
- mContext.enforcePermission(android.Manifest.permission.STATUS_BAR_SERVICE,
- callingPid, callingUid, "DisplayPolicy");
+ mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
+ "DisplayPolicy");
if (mNavigationBar != null && mNavigationBar.isAlive()) {
return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
case TYPE_NAVIGATION_BAR_PANEL:
- mContext.enforcePermission(android.Manifest.permission.STATUS_BAR_SERVICE,
- callingPid, callingUid, "DisplayPolicy");
- break;
case TYPE_STATUS_BAR_ADDITIONAL:
case TYPE_STATUS_BAR_SUB_PANEL:
case TYPE_VOICE_INTERACTION_STARTING:
- mContext.enforcePermission(
- android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+ mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
"DisplayPolicy");
break;
case TYPE_STATUS_BAR_PANEL:
@@ -1102,8 +1100,7 @@
if (attrs.providedInsets != null) {
// Recents component is allowed to add inset types.
if (!mService.mAtmService.isCallerRecents(callingUid)) {
- mContext.enforcePermission(
- android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+ mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
"DisplayPolicy");
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 979b3a5..dae5c8d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1526,7 +1526,7 @@
final boolean isRoundedCornerOverlay = (attrs.privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
- appOp);
+ appOp, displayId);
if (res != ADD_OKAY) {
return res;
}
@@ -10123,6 +10123,23 @@
}
}
+ /**
+ * Returns whether the given UID is the owner of a virtual device, which the given display
+ * belongs to.
+ */
+ @Override
+ public boolean isCallerVirtualDeviceOwner(int displayId, int callingUid) {
+ if (!android.companion.virtualdevice.flags.Flags.statusBarAndInsets()) {
+ return false;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mAtmService.mTaskSupervisor.isDeviceOwnerUid(displayId, callingUid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@RequiresPermission(ACCESS_SURFACE_FLINGER)
@Override
public boolean replaceContentOnDisplay(int displayId, SurfaceControl sc) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index 07934ea..e694c0b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -188,7 +188,7 @@
.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_WALLPAPER,
- /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+ /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_NONE);
}
@@ -198,7 +198,7 @@
.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
- /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+ /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY);
}
@@ -208,7 +208,7 @@
.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
- /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+ /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_NONE);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index d62c626..eebb487 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -60,7 +60,7 @@
@Override
public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
- int[] outAppOp) {
+ int[] outAppOp, int displayId) {
return 0;
}