Add a remote control mechanism for window containers

This adds a "IWindowContainer" interface which is an opaque
handle to a window-container in WM. It has very minimal
functionality and is intended to be used mostly as an
identifier.

Along with this handle, there is a WindowContainerTransaction
which can collect a set of container + configuration changess
and apply them all together. This will be used for remote
rotation control and other batched operations in the future.

Bug: 124011688
Test: Added some wm/core tests.
Change-Id: I691e7c11cd8c30f1a24fd64c6018a18933047847
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3da8481..222f26e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -227,6 +227,7 @@
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
+import android.view.WindowContainerTransaction;
 import android.view.WindowManager;
 
 import com.android.internal.R;
@@ -289,6 +290,7 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -3249,6 +3251,47 @@
         }
     }
 
+    private void sanitizeAndApplyConfigChange(ConfigurationContainer container,
+            WindowContainerTransaction.Change change) {
+        if (!(container instanceof TaskRecord)) {
+            throw new RuntimeException("Invalid token in task transaction");
+        }
+        // The "client"-facing API should prevent bad changes; however, just in case, sanitize
+        // masks here.
+        int configMask = change.getConfigSetMask();
+        int windowMask = change.getWindowSetMask();
+        configMask &= ActivityInfo.CONFIG_WINDOW_CONFIGURATION
+                | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+        windowMask &= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
+        Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
+        c.setTo(change.getConfiguration(), configMask, windowMask);
+        container.onRequestedOverrideConfigurationChanged(c);
+    }
+
+    @Override
+    public void applyContainerTransaction(WindowContainerTransaction t) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "applyContainerTransaction()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            if (t == null) {
+                return;
+            }
+            synchronized (mGlobalLock) {
+                Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
+                        t.getChanges().entrySet().iterator();
+                while (entries.hasNext()) {
+                    final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
+                            entries.next();
+                    final ConfigurationContainer cc = ConfigurationContainer.RemoteToken.fromBinder(
+                            entry.getKey()).getContainer();
+                    sanitizeAndApplyConfigChange(cc, entry.getValue());
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     @Override
     public boolean releaseActivityInstance(IBinder token) {
         synchronized (mGlobalLock) {