DO NOT MERGE: Hook up the CEC playback API to service internal logic.
This change enables CEC playback API (oneTouchPlay, queryDisplayStatus).
Also updated local device list type to SparseArray to make it easy
to get one based on device type.
Change-Id: I6f88b2dac2d873c493a90411549a4e5719a5e460
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 8d0696b..68ce607 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -80,8 +80,8 @@
// A logical address of device is used as key of container.
private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
- // Stores the local CEC devices in the system.
- private final ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ // Stores the local CEC devices in the system. Device type is used for key.
+ private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
// Private constructor. Use HdmiCecController.create().
private HdmiCecController() {
@@ -131,7 +131,7 @@
// TODO: Consider restoring the local device addresses from persistent storage
// to allocate the same addresses again if possible.
device.setPreferredAddress(HdmiCec.ADDR_UNREGISTERED);
- mLocalDevices.add(device);
+ mLocalDevices.put(type, device);
device.init();
}
}
@@ -290,6 +290,17 @@
}
/**
+ * Return the locally hosted logical device of a given type.
+ *
+ * @param deviceType logical device type
+ * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
+ * otherwise null.
+ */
+ HdmiCecLocalDevice getLocalDevice(int deviceType) {
+ return mLocalDevices.get(deviceType);
+ }
+
+ /**
* Add a new logical address to the device. Device's HW should be notified
* when a new logical address is assigned to a device, so that it can accept
* a command having available destinations.
@@ -317,8 +328,8 @@
assertRunOnServiceThread();
// TODO: consider to backup logical address so that new logical address
// allocation can use it as preferred address.
- for (HdmiCecLocalDevice device : mLocalDevices) {
- device.clearAddress();
+ for (int i = 0; i < mLocalDevices.size(); ++i) {
+ mLocalDevices.valueAt(i).clearAddress();
}
nativeClearLogicalAddress(mNativePtr);
}
@@ -379,8 +390,8 @@
}
private boolean isAllocatedLocalDeviceAddress(int address) {
- for (HdmiCecLocalDevice device : mLocalDevices) {
- if (device.isAddressOf(address)) {
+ for (int i = 0; i < mLocalDevices.size(); ++i) {
+ if (mLocalDevices.valueAt(i).isAddressOf(address)) {
return true;
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 74961fd..475852f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -178,6 +178,16 @@
});
}
+ // See if we have an action of a given type in progress.
+ private <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
+ for (FeatureAction action : mActions) {
+ if (action.getClass().equals(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Remove the given {@link FeatureAction} object from the action queue.
*
@@ -415,36 +425,95 @@
}
@Override
- public void oneTouchPlay(IHdmiControlCallback callback) {
+ public void oneTouchPlay(final IHdmiControlCallback callback) {
enforceAccessPermission();
- // TODO: Post a message for HdmiControlService#oneTouchPlay()
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiControlService.this.oneTouchPlay(callback);
+ }
+ });
}
@Override
- public void queryDisplayStatus(IHdmiControlCallback callback) {
+ public void queryDisplayStatus(final IHdmiControlCallback callback) {
enforceAccessPermission();
- // TODO: Post a message for HdmiControlService#queryDisplayStatus()
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiControlService.this.queryDisplayStatus(callback);
+ }
+ });
}
@Override
- public void addHotplugEventListener(IHdmiHotplugEventListener listener) {
+ public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
enforceAccessPermission();
- // TODO: Post a message for HdmiControlService#addHotplugEventListener()
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiControlService.this.addHotplugEventListener(listener);
+ }
+ });
}
@Override
- public void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
+ public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
enforceAccessPermission();
- // TODO: Post a message for HdmiControlService#removeHotplugEventListener()
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiControlService.this.removeHotplugEventListener(listener);
+ }
+ });
}
}
private void oneTouchPlay(IHdmiControlCallback callback) {
- // TODO: Create a new action
+ if (hasAction(OneTouchPlayAction.class)) {
+ Slog.w(TAG, "oneTouchPlay already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ return;
+ }
+ HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+ if (source == null) {
+ Slog.w(TAG, "Local playback device not available");
+ invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
+ return;
+ }
+ // TODO: Consider the case of multiple TV sets. For now we always direct the command
+ // to the primary one.
+ OneTouchPlayAction action = OneTouchPlayAction.create(this,
+ source.getDeviceInfo().getLogicalAddress(),
+ source.getDeviceInfo().getPhysicalAddress(), HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate oneTouchPlay");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
}
private void queryDisplayStatus(IHdmiControlCallback callback) {
- // TODO: Create a new action
+ if (hasAction(DevicePowerStatusAction.class)) {
+ Slog.w(TAG, "queryDisplayStatus already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ return;
+ }
+ HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+ if (source == null) {
+ Slog.w(TAG, "Local playback device not available");
+ invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
+ return;
+ }
+ DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
+ source.getDeviceInfo().getLogicalAddress(), HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate queryDisplayStatus");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
}
private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
@@ -473,4 +542,12 @@
mHotplugEventListeners.remove(listener);
}
}
+
+ private void invokeCallback(IHdmiControlCallback callback, int result) {
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Invoking callback failed:" + e);
+ }
+ }
}