content: package com.genymobile.scrcpy.wrappers;
  
  import com.genymobile.scrcpy.Ln;
  
  import android.annotation.SuppressLint;
  import android.graphics.Rect;
  import android.os.Build;
  import android.os.IBinder;
  import android.view.Surface;
  
  import java.lang.reflect.Method;
  
  @SuppressLint("PrivateApi")
  public final class SurfaceControl {
  
      private static final Class<?> CLASS;
  
      // see <https://android.googlesource.com/platform/frameworks/base.git/+/pie-release-2/core/java/android/view/SurfaceControl.java#305>
      public static final int POWER_MODE_OFF = 0;
      public static final int POWER_MODE_NORMAL = 2;
  
      static {
          try {
              CLASS = Class.forName("android.view.SurfaceControl");
          } catch (ClassNotFoundException e) {
              throw new AssertionError(e);
          }
      }
  
      private static Method getBuiltInDisplayMethod;
      private static Method setDisplayPowerModeMethod;
      private static Method getPhysicalDisplayTokenMethod;
      private static Method getPhysicalDisplayIdsMethod;
  
      private SurfaceControl() {
          // only static methods
      }
  
      public static void openTransaction() {
          try {
              CLASS.getMethod("openTransaction").invoke(null);
          } catch (Exception e) {
              throw new AssertionError(e);
          }
      }
  
      public static void closeTransaction() {
          try {
              CLASS.getMethod("closeTransaction").invoke(null);
          } catch (Exception e) {
              throw new AssertionError(e);
          }
      }
  
      public static void setDisplayProjection(IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect) {
          try {
              CLASS.getMethod("setDisplayProjection", IBinder.class, int.class, Rect.class, Rect.class)
                      .invoke(null, displayToken, orientation, layerStackRect, displayRect);
          } catch (Exception e) {
              throw new AssertionError(e);
          }
      }
  
      public static void setDisplayLayerStack(IBinder displayToken, int layerStack) {
          try {
              CLASS.getMethod("setDisplayLayerStack", IBinder.class, int.class).invoke(null, displayToken, layerStack);
          } catch (Exception e) {
              throw new AssertionError(e);
          }
      }
  
      public static void setDisplaySurface(IBinder displayToken, Surface surface) {
          try {
              CLASS.getMethod("setDisplaySurface", IBinder.class, Surface.class).invoke(null, displayToken, surface);
          } catch (Exception e) {
              throw new AssertionError(e);
          }
      }
  
      public static IBinder createDisplay(String name, boolean secure) throws Exception {
          return (IBinder) CLASS.getMethod("createDisplay", String.class, boolean.class).invoke(null, name, secure);
      }
  
      private static Method getGetBuiltInDisplayMethod() throws NoSuchMethodException {
          if (getBuiltInDisplayMethod == null) {
              // the method signature has changed in Android Q
              // <https://github.com/Genymobile/scrcpy/issues/586>
              if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                  getBuiltInDisplayMethod = CLASS.getMethod("getBuiltInDisplay", int.class);
              } else {
                  getBuiltInDisplayMethod = CLASS.getMethod("getInternalDisplayToken");
              }
          }
          return getBuiltInDisplayMethod;
      }
  
      public static IBinder getBuiltInDisplay() {
          try {
              Method method = getGetBuiltInDisplayMethod();
              if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                  // call getBuiltInDisplay(0)
                  return (IBinder) method.invoke(null, 0);
              }
  
              // call getInternalDisplayToken()
              return (IBinder) method.invoke(null);
          } catch (ReflectiveOperationException e) {
              Ln.e("Could not invoke method", e);
              return null;
          }
      }
  
      private static Method getGetPhysicalDisplayTokenMethod() throws NoSuchMethodException {
          if (getPhysicalDisplayTokenMethod == null) {
              getPhysicalDisplayTokenMethod = CLASS.getMethod("getPhysicalDisplayToken", long.class);
          }
          return getPhysicalDisplayTokenMethod;
      }
  
      public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
          try {
              Method method = getGetPhysicalDisplayTokenMethod();
              return (IBinder) method.invoke(null, physicalDisplayId);
          } catch (ReflectiveOperationException e) {
              Ln.e("Could not invoke method", e);
              return null;
          }
      }
  
      private static Method getGetPhysicalDisplayIdsMethod() throws NoSuchMethodException {
          if (getPhysicalDisplayIdsMethod == null) {
              getPhysicalDisplayIdsMethod = CLASS.getMethod("getPhysicalDisplayIds");
          }
          return getPhysicalDisplayIdsMethod;
      }
  
      public static boolean hasPhysicalDisplayIdsMethod() {
          try {
              getGetPhysicalDisplayIdsMethod();
              return true;
          } catch (NoSuchMethodException e) {
              return false;
          }
      }
  
      public static long[] getPhysicalDisplayIds() {
          try {
              Method method = getGetPhysicalDisplayIdsMethod();
              return (long[]) method.invoke(null);
          } catch (ReflectiveOperationException e) {
              Ln.e("Could not invoke method", e);
              return null;
          }
      }
  
      private static Method getSetDisplayPowerModeMethod() throws NoSuchMethodException {
          if (setDisplayPowerModeMethod == null) {
              setDisplayPowerModeMethod = CLASS.getMethod("setDisplayPowerMode", IBinder.class, int.class);
          }
          return setDisplayPowerModeMethod;
      }
  
      public static boolean setDisplayPowerMode(IBinder displayToken, int mode) {
          try {
              Method method = getSetDisplayPowerModeMethod();
              method.invoke(null, displayToken, mode);
              return true;
          } catch (ReflectiveOperationException e) {
              Ln.e("Could not invoke method", e);
              return false;
          }
      }
  
      public static void destroyDisplay(IBinder displayToken) {
          try {
              CLASS.getMethod("destroyDisplay", IBinder.class).invoke(null, displayToken);
          } catch (Exception e) {
              throw new AssertionError(e);
          }
      }
  }
  