Add per-display Configuration change reporting
Adds reporting per-display configuration changes to
hierarchy listeners. On system-ui's side, the
DisplayWindowController then dispatches to other registrants
within the process.
Bug: 133381284
Test: Added testDisplayWindowListener
Change-Id: I68c4e9e229b97a9e91ad0d4f5276447a947c413b
diff --git a/core/java/android/view/IDisplayWindowListener.aidl b/core/java/android/view/IDisplayWindowListener.aidl
index 725cd6f..973a208 100644
--- a/core/java/android/view/IDisplayWindowListener.aidl
+++ b/core/java/android/view/IDisplayWindowListener.aidl
@@ -16,12 +16,16 @@
package android.view;
+import android.content.res.Configuration;
+
/**
* Interface to listen for changes to display window-containers.
*
- * This differs from DisplayManager's DisplayListener:
+ * This differs from DisplayManager's DisplayListener in a couple ways:
* - onDisplayAdded is always called after the display is actually added to the WM hierarchy.
* This corresponds to the DisplayContent and not the raw Dislay from DisplayManager.
+ * - onDisplayConfigurationChanged is called for all configuration changes, not just changes
+ * to displayinfo (eg. windowing-mode).
*
* @hide
*/
@@ -33,6 +37,11 @@
void onDisplayAdded(int displayId);
/**
+ * Called when a display's window-container configuration has changed.
+ */
+ void onDisplayConfigurationChanged(int displayId, in Configuration newConfig);
+
+ /**
* Called when a display is removed from the hierarchy.
*/
void onDisplayRemoved(int displayId);
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java
index f3487fb..19fff79 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java
@@ -16,8 +16,10 @@
package com.android.systemui.wm;
+import android.content.res.Configuration;
import android.os.Handler;
import android.os.RemoteException;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowListener;
import android.view.IDisplayWindowRotationCallback;
@@ -40,6 +42,8 @@
*/
@Singleton
public class DisplayWindowController {
+ private static final String TAG = "DisplayWindowController";
+
private final Handler mHandler;
private final ArrayList<OnDisplayWindowRotationController> mRotationControllers =
@@ -84,8 +88,26 @@
DisplayRecord record = new DisplayRecord();
record.mDisplayId = displayId;
mDisplays.put(displayId, record);
- for (DisplayWindowListener l : mDisplayChangedListeners) {
- l.onDisplayAdded(displayId);
+ for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
+ mDisplayChangedListeners.get(i).onDisplayAdded(displayId);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ mHandler.post(() -> {
+ synchronized (mDisplays) {
+ DisplayRecord dr = mDisplays.get(displayId);
+ if (dr == null) {
+ Slog.w(TAG, "Skipping Display Configuration change on non-added"
+ + " display.");
+ return;
+ }
+ for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
+ mDisplayChangedListeners.get(i).onDisplayConfigurationChanged(
+ displayId, newConfig);
}
}
});
@@ -118,8 +140,8 @@
}
/**
- * Add a display window-container listener. It will get notified when displays are
- * added/removed from the WM hierarchy.
+ * Add a display window-container listener. It will get notified whenever a display's
+ * configuration changes or when displays are added/removed from the WM hierarchy.
*/
public void addDisplayWindowListener(DisplayWindowListener listener) {
synchronized (mDisplays) {
@@ -165,7 +187,8 @@
}
/**
- * Gets notified when a display is added/removed to the WM hierarchy.
+ * Gets notified when a display is added/removed to the WM hierarchy and when a display's
+ * window-configuration changes.
*
* @see IDisplayWindowListener
*/
@@ -176,6 +199,11 @@
void onDisplayAdded(int displayId);
/**
+ * Called when a display's window-container configuration changes.
+ */
+ void onDisplayConfigurationChanged(int displayId, Configuration newConfig);
+
+ /**
* Called when a display is removed.
*/
void onDisplayRemoved(int displayId);
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 5258357..be7dfe5 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -1080,6 +1080,8 @@
ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
mService.mH.sendMessage(msg);
}
+ mService.mWindowManager.mDisplayNotificationController.dispatchDisplayChanged(
+ this, getConfiguration());
}
return changes;
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
index dbc452f..2da76ea 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import android.content.res.Configuration;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.view.IDisplayWindowListener;
@@ -62,6 +63,28 @@
mDisplayListeners.finishBroadcast();
}
+ void dispatchDisplayChanged(ActivityDisplay display, Configuration newConfig) {
+ // Only report changed if this has actually been added to the hierarchy already.
+ boolean isInHierarchy = false;
+ for (int i = 0; i < display.getParent().getChildCount(); ++i) {
+ if (display.getParent().getChildAt(i) == display) {
+ isInHierarchy = true;
+ }
+ }
+ if (!isInHierarchy) {
+ return;
+ }
+ int count = mDisplayListeners.beginBroadcast();
+ for (int i = 0; i < count; ++i) {
+ try {
+ mDisplayListeners.getBroadcastItem(i).onDisplayConfigurationChanged(
+ display.mDisplayId, newConfig);
+ } catch (RemoteException e) {
+ }
+ }
+ mDisplayListeners.finishBroadcast();
+ }
+
void dispatchDisplayRemoved(ActivityDisplay display) {
int count = mDisplayListeners.beginBroadcast();
for (int i = 0; i < count; ++i) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index e9546a2..dae1052 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -26,7 +26,9 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.content.res.Configuration;
import android.graphics.Rect;
+import android.view.IDisplayWindowListener;
import android.view.WindowContainerTransaction;
import androidx.test.filters.MediumTest;
@@ -35,6 +37,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+
/**
* Tests for the {@link ActivityTaskManagerService} class.
*
@@ -93,5 +97,56 @@
mService.applyContainerTransaction(t);
assertEquals(newBounds, stack.getBounds());
}
+
+ @Test
+ public void testDisplayWindowListener() {
+ final ArrayList<Integer> added = new ArrayList<>();
+ final ArrayList<Integer> changed = new ArrayList<>();
+ final ArrayList<Integer> removed = new ArrayList<>();
+ IDisplayWindowListener listener = new IDisplayWindowListener.Stub() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ added.add(displayId);
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ changed.add(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ removed.add(displayId);
+ }
+ };
+ mService.mWindowManager.registerDisplayWindowListener(listener);
+ // Check that existing displays call added
+ assertEquals(1, added.size());
+ assertEquals(0, changed.size());
+ assertEquals(0, removed.size());
+ added.clear();
+ // Check adding a display
+ ActivityDisplay newDisp1 = new TestActivityDisplay.Builder(mService, 600, 800).build();
+ assertEquals(1, added.size());
+ assertEquals(0, changed.size());
+ assertEquals(0, removed.size());
+ added.clear();
+ // Check that changes are reported
+ Configuration c = new Configuration(newDisp1.getRequestedOverrideConfiguration());
+ c.windowConfiguration.setBounds(new Rect(0, 0, 1000, 1300));
+ newDisp1.onRequestedOverrideConfigurationChanged(c);
+ mService.mRootActivityContainer.ensureVisibilityAndConfig(null /* starting */,
+ newDisp1.mDisplayId, false /* markFrozenIfConfigChanged */,
+ false /* deferResume */);
+ assertEquals(0, added.size());
+ assertEquals(1, changed.size());
+ assertEquals(0, removed.size());
+ changed.clear();
+ // Check that removal is reported
+ newDisp1.remove();
+ assertEquals(0, added.size());
+ assertEquals(0, changed.size());
+ assertEquals(1, removed.size());
+ }
}