multidisplay: call surfaceflinger to set display

The resizable emulator needs to change display frequently,
and multidisplay is enhanced to do that.

This cl adds "SET_DISPLAY" command to multidisplay and
calls surfaceflinger to do that.

necessary permissions are added to achieve this.

Bug: 308443736

Change-Id: I1c0c54eb152d26a79a91115a826c64131da1bb5e
diff --git a/MultiDisplayProvider/AndroidManifest.xml b/MultiDisplayProvider/AndroidManifest.xml
index 3b3b1e3..5c203bd 100644
--- a/MultiDisplayProvider/AndroidManifest.xml
+++ b/MultiDisplayProvider/AndroidManifest.xml
@@ -20,7 +20,11 @@
           package="com.android.emulator.multidisplay"
           android:sharedUserId="android.uid.system" >
 
+    <uses-permission android:name="android.permission.HARDWARE_TEST" />
+    <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
     <uses-sdk android:minSdkVersion="19" />
+
     <application android:label="@string/app_name"
                  android:persistent="true">
         <receiver android:name=".MultiDisplayServiceReceiver"
diff --git a/MultiDisplayProvider/jni/com_android_emulator_multidisplay.cpp b/MultiDisplayProvider/jni/com_android_emulator_multidisplay.cpp
index 10e9282..26c7a43 100644
--- a/MultiDisplayProvider/jni/com_android_emulator_multidisplay.cpp
+++ b/MultiDisplayProvider/jni/com_android_emulator_multidisplay.cpp
@@ -43,6 +43,7 @@
 static const uint8_t DEL = 2;
 static const uint8_t QUERY = 3;
 static const uint8_t BIND = 4;
+static const uint8_t SET_DISPLAY = 0x10;
 
 static void fillMsg(std::vector<uint8_t>& buf, uint8_t cmd, uint8_t* data, uint32_t size) {
     // msg format is size(4B) + cmd(1B) + data(size B)
@@ -125,6 +126,14 @@
     std::vector<uint8_t> args(length, 0);
     qemu_pipe_read_fully(gFd, args.data(), (size_t)length);
     switch(args[0]) {
+        case SET_DISPLAY:
+            ALOGV("received setdisplay event");
+            *arrp = SET_DISPLAY;
+            for (int i = 1; i < 6; i++) {
+                *(arrp + i) = *(uint32_t*)(&args[(i - 1) * 4 + 1]);
+            }
+            env->ReleaseIntArrayElements(arr, arrp, JNI_COMMIT);
+            break;
         case ADD: {
             ALOGV("received add event");
             *arrp = ADD;
diff --git a/MultiDisplayProvider/src/com/android/emulator/multidisplay/MultiDisplayService.java b/MultiDisplayProvider/src/com/android/emulator/multidisplay/MultiDisplayService.java
index 41da923..d78cc4e 100644
--- a/MultiDisplayProvider/src/com/android/emulator/multidisplay/MultiDisplayService.java
+++ b/MultiDisplayProvider/src/com/android/emulator/multidisplay/MultiDisplayService.java
@@ -17,15 +17,20 @@
 package com.android.emulator.multidisplay;
 
 import android.app.Service;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.util.DebugUtils;
 import android.util.Log;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
 import android.view.Surface;
 
+import java.lang.Thread;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -33,8 +38,6 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
-import android.os.Messenger;
-
 public final class MultiDisplayService extends Service {
     private static final String TAG = "MultiDisplayService";
     private static final String DISPLAY_NAME = "Emulator 2D Display";
@@ -47,6 +50,10 @@
     private static final int MAX_DISPLAYS = 10;
     private static final int ADD = 1;
     private static final int DEL = 2;
+    // the following is used by resizabel to set display
+    // intentionally shifted 4 bits to avoid conflicting
+    // with existing multidisplay functions
+    private static final int SET_DISPLAY = 0x10;
 
     private static final int FLAGS = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
                                       DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY |
@@ -55,6 +62,8 @@
                                       1 << 6 |//DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH
                                       1 << 9; //DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
 
+    private static final String SURFACE_COMPOSER_INTERFACE_KEY = "android.ui.ISurfaceComposer";
+    private IBinder mSurfaceFlinger;
     private DisplayManager mDisplayManager;
     private VirtualDisplay mVirtualDisplay[];
     private Surface mSurface[];
@@ -130,7 +139,7 @@
 
     @Override
     public void onCreate() {
-        Log.d(TAG, "Creating service");
+        Log.i(TAG, "Creating service");
 
         super.onCreate();
 
@@ -359,12 +368,16 @@
             while(nativeOpen() <= 0) {
                 Log.e(TAG, "failed to open multiDisplay pipe, retry");
             }
+            Log.d(TAG, "success open multiDisplay pipe");
             while(true) {
+                Log.d(TAG, "waiting to read pipe");
                 int[] array = {0, 0, 0, 0, 0, 0};
                 if (!nativeReadPipe(array)) {
+                    Log.e(TAG, "failed and try again");
                     continue;
                 }
-                Log.v(TAG, "run(): array= " + Arrays.toString(array));
+                Log.d(TAG, "have read something from pipe");
+                Log.d(TAG, "run(): array= " + Arrays.toString(array));
                 switch (array[0]) {
                     case ADD: {
                         for (int j = 0; j < 6; j++) {
@@ -392,6 +405,34 @@
                         deleteVirtualDisplay(i);
                         break;
                     }
+                    case SET_DISPLAY: {
+                         for (int j = 0; j < 6; j++) {
+                             Log.d(TAG, "SET_DISPLAY received " + array[j]);
+                         }
+                         if (mSurfaceFlinger == null) {
+                             Log.d(TAG, "obtain surfaceflinger " );
+                             mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger");
+                         }
+                         if (mSurfaceFlinger != null) {
+                             int i = array[1];
+                             Parcel data = Parcel.obtain();
+                             data.writeInterfaceToken(SURFACE_COMPOSER_INTERFACE_KEY);
+                             data.writeInt(i);
+                             try {
+                                 if (i >=0) {
+                                    mSurfaceFlinger.transact(1035, data, null, 0 /* flags */);
+                                    Log.d(TAG, "setting display to " + i);
+                                 } else {
+                                    Log.e(TAG, "invalid display id " + i);
+                                 }
+                             } catch (RemoteException e) {
+                                 Log.e(TAG, "Could not set display:" + e.toString());
+                             }
+                         } else {
+                             Log.e(TAG, "cannot get SurfaceFlinger service");
+                         }
+                        break;
+                    }
                     // TODO(b/231763427): implement LIST
                 }
             }
diff --git a/data/etc/permissions/privapp-permissions-multidisplay.xml b/data/etc/permissions/privapp-permissions-multidisplay.xml
new file mode 100644
index 0000000..7e79a60
--- /dev/null
+++ b/data/etc/permissions/privapp-permissions-multidisplay.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2023 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License
+  -->
+<!--
+This XML file declares which signature|privileged permissions should be granted to privileged
+applications on GMS or Google-branded devices.
+It allows additional grants on top of privapp-permissions-platform.xml
+-->
+<permissions>
+    <privapp-permissions package="com.android.emulator.multidisplay">
+        <permission name="android.permission.HARDWARE_TEST"/>
+        <permission name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
+        <permission name="android.permission.ACCESS_SURFACE_FLINGER"/>
+    </privapp-permissions>
+</permissions>
+
diff --git a/vendor_common.mk b/vendor_common.mk
index d4cbb31..a0d5479 100644
--- a/vendor_common.mk
+++ b/vendor_common.mk
@@ -338,6 +338,7 @@
     frameworks/native/data/etc/android.software.autofill.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.autofill.xml \
     frameworks/native/data/etc/android.software.verified_boot.xml:${TARGET_COPY_OUT_PRODUCT}/etc/permissions/android.software.verified_boot.xml \
     device/generic/goldfish/data/etc/permissions/privapp-permissions-goldfish.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/permissions/privapp-permissions-goldfish.xml \
+    device/generic/goldfish/data/etc/permissions/privapp-permissions-multidisplay.xml:$(TARGET_COPY_OUT_SYSTEM_EXT)/etc/permissions/privapp-permissions-multidisplay.xml \
 
 ifneq ($(EMULATOR_DISABLE_RADIO),true)
 # Android TV ingests this file, but declares its own set of hardware permissions.