EArc AIDL Binding

Add the binding to EARC AIDL in android.hardware.hdmi.earc.

Bug: 240388105
Test: atest VtsHalTvHdmiEArcAidlTargetTest
      atest VtsHalTvHdmiConnectionAidlTargetTest
      atest VtsHalTvHdmiCecAidlTargetTest
Change-Id: I25f6b8ae60858cb51e5d7b3b162e453b04db9e27
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 17b6f5d..2e86eb7 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -101,6 +101,8 @@
     defaults: ["platform_service_defaults"],
     srcs: [
         ":android.hardware.biometrics.face-V3-java-source",
+        ":android.hardware.tv.hdmi.connection-V1-java-source",
+        ":android.hardware.tv.hdmi.earc-V1-java-source",
         ":statslog-art-java-gen",
         ":statslog-contexthub-java-gen",
         ":services.core-sources",
@@ -160,6 +162,7 @@
         "android.hardware.tv.cec-V1.1-java",
         "android.hardware.tv.hdmi.cec-V1-java",
         "android.hardware.tv.hdmi.connection-V1-java",
+        "android.hardware.tv.hdmi.earc-V1-java",
         "android.hardware.weaver-V1.0-java",
         "android.hardware.weaver-V2-java",
         "android.hardware.biometrics.face-V1.0-java",
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 6b5af88..61417ef0 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -19,6 +19,8 @@
 import android.annotation.IntDef;
 import android.annotation.StringDef;
 import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.tv.hdmi.connection.HpdSignal;
+import android.hardware.tv.hdmi.earc.IEArcStatus;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -599,10 +601,13 @@
     })
     @interface RcProfileSource {}
 
-    static final int HDMI_EARC_STATUS_IDLE = 0;             // IDLE1
-    static final int HDMI_EARC_STATUS_EARC_PENDING = 1;     // DISC1 and DISC2
-    static final int HDMI_EARC_STATUS_ARC_PENDING = 2;      // IDLE2 for ARC
-    static final int HDMI_EARC_STATUS_EARC_CONNECTED = 3;   // eARC connected
+    static final int HDMI_EARC_STATUS_IDLE = IEArcStatus.STATUS_IDLE; // IDLE1
+    static final int HDMI_EARC_STATUS_EARC_PENDING =
+            IEArcStatus.STATUS_EARC_PENDING; // DISC1 and DISC2
+    static final int HDMI_EARC_STATUS_ARC_PENDING = IEArcStatus.STATUS_ARC_PENDING; // IDLE2 for ARC
+    static final int HDMI_EARC_STATUS_EARC_CONNECTED =
+            IEArcStatus.STATUS_EARC_CONNECTED; // eARC connected
+
     @IntDef({
             HDMI_EARC_STATUS_IDLE,
             HDMI_EARC_STATUS_EARC_PENDING,
@@ -611,8 +616,11 @@
             })
     @interface EarcStatus {}
 
-    static final int HDMI_HPD_TYPE_PHYSICAL = 0;   // Default. Physical hotplug signal.
-    static final int HDMI_HPD_TYPE_STATUS_BIT = 1; // HDMI_HPD status bit.
+    static final int HDMI_HPD_TYPE_PHYSICAL =
+            HpdSignal.HDMI_HPD_PHYSICAL; // Default. Physical hotplug signal.
+    static final int HDMI_HPD_TYPE_STATUS_BIT =
+            HpdSignal.HDMI_HPD_STATUS_BIT; // HDMI_HPD status bit.
+
     @IntDef({
             HDMI_HPD_TYPE_PHYSICAL,
             HDMI_HPD_TYPE_STATUS_BIT
diff --git a/services/core/java/com/android/server/hdmi/HdmiEarcController.java b/services/core/java/com/android/server/hdmi/HdmiEarcController.java
index 8522509..2c1aa4f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiEarcController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiEarcController.java
@@ -16,8 +16,14 @@
 
 package com.android.server.hdmi;
 
+import android.hardware.tv.hdmi.earc.IEArc;
+import android.hardware.tv.hdmi.earc.IEArcCallback;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -29,6 +35,89 @@
 
     private final HdmiControlService mService;
 
+    private EArcHalWrapper mEArcAidl;
+
+    private final class EArcHalWrapper implements IBinder.DeathRecipient {
+        private IEArc mEArc;
+        private IEArcCallback mEArcCallback;
+
+        @Override
+        public void binderDied() {
+            mEArc.asBinder().unlinkToDeath(this, 0);
+            connectToHal();
+            if (mEArcCallback != null) {
+                nativeSetCallback(mEArcCallback);
+            }
+        }
+
+        boolean connectToHal() {
+            mEArc =
+                    IEArc.Stub.asInterface(
+                            ServiceManager.getService(IEArc.DESCRIPTOR + "/default"));
+            if (mEArc == null) {
+                return false;
+            }
+            try {
+                mEArc.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                HdmiLogger.error("Couldn't link callback object: ", e);
+            }
+            return true;
+        }
+
+        public boolean nativeInit() {
+            return connectToHal();
+        }
+
+        public void nativeSetEArcEnabled(boolean enabled) {
+            try {
+                mEArc.setEArcEnabled(enabled);
+            } catch (ServiceSpecificException sse) {
+                HdmiLogger.error(
+                        "Could not set eARC enabled to " + enabled + ". Error: ", sse.errorCode);
+            } catch (RemoteException re) {
+                HdmiLogger.error("Could not set eARC enabled to " + enabled + ":. Exception: ", re);
+            }
+        }
+
+        public boolean nativeIsEArcEnabled() {
+            try {
+                return mEArc.isEArcEnabled();
+            } catch (RemoteException re) {
+                HdmiLogger.error("Could not read if eARC is enabled. Exception: ", re);
+                return false;
+            }
+        }
+
+        public void nativeSetCallback(IEArcCallback callback) {
+            mEArcCallback = callback;
+            try {
+                mEArc.setCallback(callback);
+            } catch (RemoteException re) {
+                HdmiLogger.error("Could not set callback. Exception: ", re);
+            }
+        }
+
+        public byte nativeGetState(int portId) {
+            try {
+                return mEArc.getState(portId);
+            } catch (RemoteException re) {
+                HdmiLogger.error("Could not get eARC state. Exception: ", re);
+                return -1;
+            }
+        }
+
+        public byte[] nativeGetLastReportedAudioCapabilities(int portId) {
+            try {
+                return mEArc.getLastReportedAudioCapabilities(portId);
+            } catch (RemoteException re) {
+                HdmiLogger.error(
+                        "Could not read last reported audio capabilities. Exception: ", re);
+                return null;
+            }
+        }
+    }
+
     // Private constructor. Use HdmiEarcController.create().
     private HdmiEarcController(HdmiControlService service) {
         mService = service;
@@ -47,12 +136,21 @@
     static HdmiEarcController create(HdmiControlService service) {
         // TODO add the native wrapper and return null if eARC HAL is not present.
         HdmiEarcController controller = new HdmiEarcController(service);
-        controller.init();
+        if (!controller.init()) {
+            HdmiLogger.warning("Could not connect to eARC AIDL HAL.");
+            return null;
+        }
         return controller;
     }
 
-    private void init() {
-        mControlHandler = new Handler(mService.getServiceLooper());
+    private boolean init() {
+        mEArcAidl = new EArcHalWrapper();
+        if (mEArcAidl.nativeInit()) {
+            mControlHandler = new Handler(mService.getServiceLooper());
+            mEArcAidl.nativeSetCallback(new EarcAidlCallback());
+            return true;
+        }
+        return false;
     }
 
     private void assertRunOnServiceThread() {
@@ -73,9 +171,7 @@
     @HdmiAnnotations.ServiceThreadOnly
     void setEarcEnabled(boolean enabled) {
         assertRunOnServiceThread();
-        // Stub.
-        // TODO: bind to native.
-        // TODO: handle error return values here, with logging.
+        mEArcAidl.nativeSetEArcEnabled(enabled);
     }
 
     /**
@@ -86,23 +182,21 @@
     @HdmiAnnotations.ServiceThreadOnly
     @Constants.EarcStatus
     int getState(int portId) {
-        // Stub.
-        // TODO: bind to native.
-        return Constants.HDMI_EARC_STATUS_IDLE;
+        return mEArcAidl.nativeGetState(portId);
     }
 
-     /**
+    /**
      * Ask the HAL to report the last eARC capabilities that the connected audio system reported.
+     *
      * @return the raw eARC capabilities
      */
     @HdmiAnnotations.ServiceThreadOnly
-    byte[] getLastReportedCaps() {
-        // Stub. TODO: bind to native.
-        return new byte[] {};
+    byte[] getLastReportedCaps(int portId) {
+        return mEArcAidl.nativeGetLastReportedAudioCapabilities(portId);
     }
 
-    final class EarcCallback {
-        public void onStateChange(@Constants.EarcStatus int status, int portId) {
+    final class EarcAidlCallback extends IEArcCallback.Stub {
+        public void onStateChange(@Constants.EarcStatus byte status, int portId) {
             runOnServiceThread(
                     () -> mService.handleEarcStateChange(status, portId));
         }
@@ -111,7 +205,15 @@
             runOnServiceThread(
                     () -> mService.handleEarcCapabilitiesReported(rawCapabilities, portId));
         }
-    }
 
-    // TODO: bind to native.
+        @Override
+        public synchronized String getInterfaceHash() throws RemoteException {
+            return IEArcCallback.Stub.HASH;
+        }
+
+        @Override
+        public int getInterfaceVersion() throws RemoteException {
+            return IEArcCallback.Stub.VERSION;
+        }
+    }
 }