summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java278
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiLogger.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java30
4 files changed, 231 insertions, 85 deletions
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 65e98ac8e684..4bba0d892f3b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -95,7 +95,6 @@ java_library_static {
"android.hardware.light-V2.0-java",
"android.hardware.power-java",
"android.hardware.power-V1.0-java",
- "android.hardware.tv.cec-V1.0-java",
"android.hardware.vibrator-java",
"android.net.ipsec.ike.stubs.module_lib",
"app-compat-annotations",
@@ -117,6 +116,7 @@ java_library_static {
"android.hardware.health-V2.0-java",
"android.hardware.health-V2.1-java",
"android.hardware.light-java",
+ "android.hardware.tv.cec-V1.0-java",
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.0-java",
"android.hardware.biometrics.fingerprint-V2.2-java",
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index b84d3226362b..75ab33dbbfc7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -17,11 +17,18 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.tv.cec.V1_0.CecMessage;
+import android.hardware.tv.cec.V1_0.HotplugEvent;
+import android.hardware.tv.cec.V1_0.IHdmiCec;
+import android.hardware.tv.cec.V1_0.IHdmiCec.getPhysicalAddressCallback;
+import android.hardware.tv.cec.V1_0.IHdmiCecCallback;
import android.hardware.tv.cec.V1_0.Result;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Handler;
+import android.os.IHwBinder;
import android.os.Looper;
import android.os.MessageQueue;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Slog;
import android.util.SparseArray;
@@ -79,6 +86,11 @@ final class HdmiCecController {
private static final int MAX_HDMI_MESSAGE_HISTORY = 250;
+ private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+
+ /** Cookie for matching the right end point. */
+ protected static final int HDMI_CEC_HAL_DEATH_COOKIE = 353;
+
// Predicate for whether the given logical address is remote device's one or not.
private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
@Override
@@ -102,10 +114,6 @@ final class HdmiCecController {
// device or issued by internal state change.
private Handler mControlHandler;
- // Stores the pointer to the native implementation of the service that
- // interacts with HAL.
- private volatile long mNativePtr;
-
private final HdmiControlService mService;
// Stores the local CEC devices in the system. Device type is used for key.
@@ -149,23 +157,21 @@ final class HdmiCecController {
* A factory method with injection of native methods for testing.
*/
static HdmiCecController createWithNativeWrapper(
- HdmiControlService service, NativeWrapper nativeWrapper) {
- HdmiCecController controller = new HdmiCecController(service, nativeWrapper);
- long nativePtr = nativeWrapper
- .nativeInit(controller, service.getServiceLooper().getQueue());
- if (nativePtr == 0L) {
- controller = null;
- return null;
- }
-
- controller.init(nativePtr);
- return controller;
+ HdmiControlService service, NativeWrapper nativeWrapper) {
+ HdmiCecController controller = new HdmiCecController(service, nativeWrapper);
+ String nativePtr = nativeWrapper.nativeInit();
+ if (nativePtr == null) {
+ HdmiLogger.warning("Couldn't get tv.cec service.");
+ return null;
+ }
+ controller.init(nativeWrapper);
+ return controller;
}
- private void init(long nativePtr) {
+ private void init(NativeWrapper nativeWrapper) {
mIoHandler = new Handler(mService.getIoLooper());
mControlHandler = new Handler(mService.getServiceLooper());
- mNativePtr = nativePtr;
+ nativeWrapper.setCallback(new HdmiCecCallback());
}
@ServiceThreadOnly
@@ -261,7 +267,7 @@ final class HdmiCecController {
HdmiPortInfo[] getPortInfos() {
- return mNativeWrapperImpl.nativeGetPortInfos(mNativePtr);
+ return mNativeWrapperImpl.nativeGetPortInfos();
}
/**
@@ -289,7 +295,7 @@ final class HdmiCecController {
int addLogicalAddress(int newLogicalAddress) {
assertRunOnServiceThread();
if (HdmiUtils.isValidAddress(newLogicalAddress)) {
- return mNativeWrapperImpl.nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
+ return mNativeWrapperImpl.nativeAddLogicalAddress(newLogicalAddress);
} else {
return Result.FAILURE_INVALID_ARGS;
}
@@ -306,7 +312,7 @@ final class HdmiCecController {
for (int i = 0; i < mLocalDevices.size(); ++i) {
mLocalDevices.valueAt(i).clearAddress();
}
- mNativeWrapperImpl.nativeClearLogicalAddress(mNativePtr);
+ mNativeWrapperImpl.nativeClearLogicalAddress();
}
@ServiceThreadOnly
@@ -326,7 +332,7 @@ final class HdmiCecController {
@ServiceThreadOnly
int getPhysicalAddress() {
assertRunOnServiceThread();
- return mNativeWrapperImpl.nativeGetPhysicalAddress(mNativePtr);
+ return mNativeWrapperImpl.nativeGetPhysicalAddress();
}
/**
@@ -337,7 +343,7 @@ final class HdmiCecController {
@ServiceThreadOnly
int getVersion() {
assertRunOnServiceThread();
- return mNativeWrapperImpl.nativeGetVersion(mNativePtr);
+ return mNativeWrapperImpl.nativeGetVersion();
}
/**
@@ -348,7 +354,7 @@ final class HdmiCecController {
@ServiceThreadOnly
int getVendorId() {
assertRunOnServiceThread();
- return mNativeWrapperImpl.nativeGetVendorId(mNativePtr);
+ return mNativeWrapperImpl.nativeGetVendorId();
}
/**
@@ -361,7 +367,7 @@ final class HdmiCecController {
void setOption(int flag, boolean enabled) {
assertRunOnServiceThread();
HdmiLogger.debug("setOption: [flag:%d, enabled:%b]", flag, enabled);
- mNativeWrapperImpl.nativeSetOption(mNativePtr, flag, enabled);
+ mNativeWrapperImpl.nativeSetOption(flag, enabled);
}
/**
@@ -375,7 +381,7 @@ final class HdmiCecController {
if (!LanguageTag.isLanguage(language)) {
return;
}
- mNativeWrapperImpl.nativeSetLanguage(mNativePtr, language);
+ mNativeWrapperImpl.nativeSetLanguage(language);
}
/**
@@ -387,7 +393,7 @@ final class HdmiCecController {
@ServiceThreadOnly
void enableAudioReturnChannel(int port, boolean enabled) {
assertRunOnServiceThread();
- mNativeWrapperImpl.nativeEnableAudioReturnChannel(mNativePtr, port, enabled);
+ mNativeWrapperImpl.nativeEnableAudioReturnChannel(port, enabled);
}
/**
@@ -399,7 +405,7 @@ final class HdmiCecController {
@ServiceThreadOnly
boolean isConnected(int port) {
assertRunOnServiceThread();
- return mNativeWrapperImpl.nativeIsConnected(mNativePtr, port);
+ return mNativeWrapperImpl.nativeIsConnected(port);
}
/**
@@ -521,7 +527,7 @@ final class HdmiCecController {
// <Polling Message> is a message which has empty body.
int ret =
mNativeWrapperImpl.nativeSendCecCommand(
- mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY);
+ sourceAddress, destinationAddress, EMPTY_BODY);
if (ret == SendMessageResult.SUCCESS) {
return true;
} else if (ret != SendMessageResult.NACK) {
@@ -627,7 +633,7 @@ final class HdmiCecController {
int i = 0;
int errorCode = SendMessageResult.SUCCESS;
do {
- errorCode = mNativeWrapperImpl.nativeSendCecCommand(mNativePtr,
+ errorCode = mNativeWrapperImpl.nativeSendCecCommand(
cecMessage.getSource(), cecMessage.getDestination(), body);
if (errorCode == SendMessageResult.SUCCESS) {
break;
@@ -651,7 +657,7 @@ final class HdmiCecController {
}
/**
- * Called by native when incoming CEC message arrived.
+ * Called when incoming CEC message arrived.
*/
@ServiceThreadOnly
private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
@@ -663,7 +669,7 @@ final class HdmiCecController {
}
/**
- * Called by native when a hotplug event issues.
+ * Called when a hotplug event issues.
*/
@ServiceThreadOnly
private void handleHotplug(int port, boolean connected) {
@@ -710,18 +716,19 @@ final class HdmiCecController {
}
protected interface NativeWrapper {
- long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
- int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress, byte[] body);
- int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
- void nativeClearLogicalAddress(long controllerPtr);
- int nativeGetPhysicalAddress(long controllerPtr);
- int nativeGetVersion(long controllerPtr);
- int nativeGetVendorId(long controllerPtr);
- HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
- void nativeSetOption(long controllerPtr, int flag, boolean enabled);
- void nativeSetLanguage(long controllerPtr, String language);
- void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag);
- boolean nativeIsConnected(long controllerPtr, int port);
+ String nativeInit();
+ void setCallback(HdmiCecCallback callback);
+ int nativeSendCecCommand(int srcAddress, int dstAddress, byte[] body);
+ int nativeAddLogicalAddress(int logicalAddress);
+ void nativeClearLogicalAddress();
+ int nativeGetPhysicalAddress();
+ int nativeGetVersion();
+ int nativeGetVendorId();
+ HdmiPortInfo[] nativeGetPortInfos();
+ void nativeSetOption(int flag, boolean enabled);
+ void nativeSetLanguage(String language);
+ void nativeEnableAudioReturnChannel(int port, boolean flag);
+ boolean nativeIsConnected(int port);
}
private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
@@ -739,67 +746,200 @@ final class HdmiCecController {
int port, boolean flag);
private static native boolean nativeIsConnected(long controllerPtr, int port);
- private static final class NativeWrapperImpl implements NativeWrapper {
+ private static final class NativeWrapperImpl implements NativeWrapper,
+ IHwBinder.DeathRecipient, getPhysicalAddressCallback {
+ private IHdmiCec mHdmiCec;
+ private final Object mLock = new Object();
+ private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+
+ @Override
+ public String nativeInit() {
+ return (connectToHal() ? mHdmiCec.toString() : null);
+ }
+
+ boolean connectToHal() {
+ try {
+ mHdmiCec = IHdmiCec.getService();
+ try {
+ mHdmiCec.linkToDeath(this, HDMI_CEC_HAL_DEATH_COOKIE);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't link to death : ", e);
+ }
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't get tv.cec service : ", e);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void setCallback(HdmiCecCallback callback) {
+ try {
+ mHdmiCec.setCallback(callback);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't initialise tv.cec callback : ", e);
+ }
+ }
+
+ @Override
+ public int nativeSendCecCommand(int srcAddress, int dstAddress, byte[] body) {
+ CecMessage message = new CecMessage();
+ message.initiator = srcAddress;
+ message.destination = dstAddress;
+ message.body = new ArrayList<>(body.length);
+ for (byte b : body) {
+ message.body.add(b);
+ }
+ try {
+ return mHdmiCec.sendMessage(message);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to send CEC message : ", e);
+ return SendMessageResult.FAIL;
+ }
+ }
+
+ @Override
+ public int nativeAddLogicalAddress(int logicalAddress) {
+ try {
+ return mHdmiCec.addLogicalAddress(logicalAddress);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to add a logical address : ", e);
+ return Result.FAILURE_INVALID_ARGS;
+ }
+ }
+
+ @Override
+ public void nativeClearLogicalAddress() {
+ try {
+ mHdmiCec.clearLogicalAddress();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to clear logical address : ", e);
+ }
+ }
@Override
- public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
- return HdmiCecController.nativeInit(handler, messageQueue);
+ public int nativeGetPhysicalAddress() {
+ try {
+ mHdmiCec.getPhysicalAddress(this);
+ return mPhysicalAddress;
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get physical address : ", e);
+ return INVALID_PHYSICAL_ADDRESS;
+ }
}
@Override
- public int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress,
- byte[] body) {
- return HdmiCecController.nativeSendCecCommand(controllerPtr, srcAddress, dstAddress, body);
+ public int nativeGetVersion() {
+ try {
+ return mHdmiCec.getCecVersion();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get cec version : ", e);
+ return Result.FAILURE_UNKNOWN;
+ }
}
@Override
- public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) {
- return HdmiCecController.nativeAddLogicalAddress(controllerPtr, logicalAddress);
+ public int nativeGetVendorId() {
+ try {
+ return mHdmiCec.getVendorId();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get vendor id : ", e);
+ return Result.FAILURE_UNKNOWN;
+ }
}
@Override
- public void nativeClearLogicalAddress(long controllerPtr) {
- HdmiCecController.nativeClearLogicalAddress(controllerPtr);
+ public HdmiPortInfo[] nativeGetPortInfos() {
+ try {
+ ArrayList<android.hardware.tv.cec.V1_0.HdmiPortInfo> hdmiPortInfos =
+ mHdmiCec.getPortInfo();
+ HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.size()];
+ int i = 0;
+ for (android.hardware.tv.cec.V1_0.HdmiPortInfo portInfo : hdmiPortInfos) {
+ hdmiPortInfo[i] = new HdmiPortInfo(portInfo.portId,
+ portInfo.type,
+ portInfo.physicalAddress,
+ portInfo.cecSupported,
+ false,
+ portInfo.arcSupported);
+ i++;
+ }
+ return hdmiPortInfo;
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get port information : ", e);
+ return null;
+ }
}
@Override
- public int nativeGetPhysicalAddress(long controllerPtr) {
- return HdmiCecController.nativeGetPhysicalAddress(controllerPtr);
+ public void nativeSetOption(int flag, boolean enabled) {
+ try {
+ mHdmiCec.setOption(flag, enabled);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to set option : ", e);
+ }
}
@Override
- public int nativeGetVersion(long controllerPtr) {
- return HdmiCecController.nativeGetVersion(controllerPtr);
+ public void nativeSetLanguage(String language) {
+ try {
+ mHdmiCec.setLanguage(language);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to set language : ", e);
+ }
}
@Override
- public int nativeGetVendorId(long controllerPtr) {
- return HdmiCecController.nativeGetVendorId(controllerPtr);
+ public void nativeEnableAudioReturnChannel(int port, boolean flag) {
+ try {
+ mHdmiCec.enableAudioReturnChannel(port, flag);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to enable/disable ARC : ", e);
+ }
}
@Override
- public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
- return HdmiCecController.nativeGetPortInfos(controllerPtr);
+ public boolean nativeIsConnected(int port) {
+ try {
+ return mHdmiCec.isConnected(port);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get connection info : ", e);
+ return false;
+ }
}
@Override
- public void nativeSetOption(long controllerPtr, int flag, boolean enabled) {
- HdmiCecController.nativeSetOption(controllerPtr, flag, enabled);
+ public void serviceDied(long cookie) {
+ if (cookie == HDMI_CEC_HAL_DEATH_COOKIE) {
+ HdmiLogger.error(TAG, "Service died cokkie : " + cookie + "; reconnecting");
+ connectToHal();
+ }
}
@Override
- public void nativeSetLanguage(long controllerPtr, String language) {
- HdmiCecController.nativeSetLanguage(controllerPtr, language);
+ public void onValues(int result, short addr) {
+ if (result == Result.SUCCESS) {
+ synchronized (mLock) {
+ mPhysicalAddress = new Short(addr).intValue();
+ }
+ }
}
+ }
+ final class HdmiCecCallback extends IHdmiCecCallback.Stub {
@Override
- public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) {
- HdmiCecController.nativeEnableAudioReturnChannel(controllerPtr, port, flag);
+ public void onCecMessage(CecMessage message) throws RemoteException {
+ byte[] body = new byte[message.body.size()];
+ for (int i = 0; i < message.body.size(); i++) {
+ body[i] = message.body.get(i);
+ }
+ runOnServiceThread(
+ () -> handleIncomingCecCommand(message.initiator, message.destination, body));
}
@Override
- public boolean nativeIsConnected(long controllerPtr, int port) {
- return HdmiCecController.nativeIsConnected(controllerPtr, port);
+ public void onHotplugEvent(HotplugEvent event) throws RemoteException {
+ runOnServiceThread(() -> handleHotplug(event.portId, event.connected));
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java
index 2309293dcbd7..8da3c93de360 100644
--- a/services/core/java/com/android/server/hdmi/HdmiLogger.java
+++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java
@@ -18,9 +18,9 @@ package com.android.server.hdmi;
import android.annotation.Nullable;
import android.os.SystemClock;
+import android.util.Log;
import android.util.Pair;
import android.util.Slog;
-import android.util.Log;
import java.util.HashMap;
@@ -71,6 +71,10 @@ final class HdmiLogger {
getLogger().errorInternal(toLogString(logMessage, objs));
}
+ static void error(String logMessage, Exception e, Object... objs) {
+ getLogger().errorInternal(toLogString(logMessage + e, objs));
+ }
+
private void errorInternal(String logMessage) {
String log = updateLog(mErrorTimingCache, logMessage);
if (!log.isEmpty()) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 8607ec66a5ba..7538468fbe31 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -17,7 +17,6 @@ package com.android.server.hdmi;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
-import android.os.MessageQueue;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiCecController.NativeWrapper;
@@ -53,13 +52,16 @@ final class FakeNativeWrapper implements NativeWrapper {
private HdmiPortInfo[] mHdmiPortInfo = null;
@Override
- public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
- return 1L;
+ public String nativeInit() {
+ return "[class or subclass of IHdmiCec]@Proxy";
}
@Override
+ public void setCallback(HdmiCecController.HdmiCecCallback callback) {}
+
+ @Override
public int nativeSendCecCommand(
- long controllerPtr, int srcAddress, int dstAddress, byte[] body) {
+ int srcAddress, int dstAddress, byte[] body) {
if (body.length == 0) {
return mPollAddressResponse[dstAddress];
} else {
@@ -69,30 +71,30 @@ final class FakeNativeWrapper implements NativeWrapper {
}
@Override
- public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) {
+ public int nativeAddLogicalAddress(int logicalAddress) {
return 0;
}
@Override
- public void nativeClearLogicalAddress(long controllerPtr) {}
+ public void nativeClearLogicalAddress() {}
@Override
- public int nativeGetPhysicalAddress(long controllerPtr) {
+ public int nativeGetPhysicalAddress() {
return mMyPhysicalAddress;
}
@Override
- public int nativeGetVersion(long controllerPtr) {
+ public int nativeGetVersion() {
return 0;
}
@Override
- public int nativeGetVendorId(long controllerPtr) {
+ public int nativeGetVendorId() {
return 0;
}
@Override
- public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
+ public HdmiPortInfo[] nativeGetPortInfos() {
if (mHdmiPortInfo == null) {
mHdmiPortInfo = new HdmiPortInfo[1];
mHdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
@@ -101,16 +103,16 @@ final class FakeNativeWrapper implements NativeWrapper {
}
@Override
- public void nativeSetOption(long controllerPtr, int flag, boolean enabled) {}
+ public void nativeSetOption(int flag, boolean enabled) {}
@Override
- public void nativeSetLanguage(long controllerPtr, String language) {}
+ public void nativeSetLanguage(String language) {}
@Override
- public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) {}
+ public void nativeEnableAudioReturnChannel(int port, boolean flag) {}
@Override
- public boolean nativeIsConnected(long controllerPtr, int port) {
+ public boolean nativeIsConnected(int port) {
return false;
}