summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJinsuk Kim <jinsukkim@google.com>2014-05-11 06:54:49 +0900
committerJinsuk Kim <jinsukkim@google.com>2014-06-12 23:06:29 +0000
commita6ce7708d6124224399241503fadcafe0c4684d4 (patch)
tree2a9a02085a02b19a3656d79525e9d1f34ff63c12
parent85c262772de33a12a20bbcc11eb8349738e550d7 (diff)
downloadframeworks_base-a6ce7708d6124224399241503fadcafe0c4684d4.zip
frameworks_base-a6ce7708d6124224399241503fadcafe0c4684d4.tar.gz
frameworks_base-a6ce7708d6124224399241503fadcafe0c4684d4.tar.bz2
DeviceSelectAction for HdmiControlService
DeviceSelectAction is the main handler for the API deviceSelect() which is used to choose a new active source among logical devices on the bus. Change-Id: I77582a1f873423fc316d89f67a89a867461a76b2
-rw-r--r--core/java/android/hardware/hdmi/HdmiTvClient.java44
-rw-r--r--core/java/android/hardware/hdmi/IHdmiControlService.aidl1
-rw-r--r--services/core/java/com/android/server/hdmi/ActiveSourceHandler.java112
-rw-r--r--services/core/java/com/android/server/hdmi/DeviceSelectAction.java223
-rw-r--r--services/core/java/com/android/server/hdmi/FeatureAction.java5
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java28
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java32
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiConstants.java6
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java82
-rw-r--r--services/core/jni/com_android_server_hdmi_HdmiCecController.cpp55
10 files changed, 549 insertions, 39 deletions
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index 6dc4a4f..85af3d1 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -16,6 +16,8 @@
package android.hardware.hdmi;
import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.util.Log;
/**
* HdmiTvClient represents HDMI-CEC logical device of type TV in the Android system
@@ -33,4 +35,46 @@ public final class HdmiTvClient {
HdmiTvClient(IHdmiControlService service) {
mService = service;
}
+
+ // Factory method for HdmiTvClient.
+ // Declared package-private. Accessed by HdmiControlManager only.
+ static HdmiTvClient create(IHdmiControlService service) {
+ return new HdmiTvClient(service);
+ }
+
+ /**
+ * Callback interface used to get the result of {@link #deviceSelect}.
+ */
+ public interface SelectCallback {
+ /**
+ * Called when the operation is finished.
+ *
+ * @param result the result value of {@link #deviceSelect}
+ */
+ void onComplete(int result);
+ }
+
+ /**
+ * Select a CEC logical device to be a new active source.
+ *
+ * @param logicalAddress
+ * @param callback
+ */
+ public void deviceSelect(int logicalAddress, SelectCallback callback) {
+ // TODO: Replace SelectCallback with PartialResult.
+ try {
+ mService.deviceSelect(logicalAddress, getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to select device: ", e);
+ }
+ }
+
+ private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) {
+ return new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ callback.onComplete(result);
+ }
+ };
+ }
}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 8da38e1..8d7c638 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -33,4 +33,5 @@ interface IHdmiControlService {
void queryDisplayStatus(IHdmiControlCallback callback);
void addHotplugEventListener(IHdmiHotplugEventListener listener);
void removeHotplugEventListener(IHdmiHotplugEventListener listener);
+ void deviceSelect(int logicalAddress, IHdmiControlCallback callback);
}
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
new file mode 100644
index 0000000..32bcb69
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+package com.android.server.hdmi;
+
+import android.annotation.Nullable;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Handles CEC command &lt;Active Source&gt;.
+ *
+ * <p>Used by feature actions that need to handle the command in their flow.
+ */
+final class ActiveSourceHandler {
+ private static final String TAG = "ActiveSourceHandler";
+
+ private final HdmiControlService mService;
+ private final int mSourceAddress;
+ private final int mSourcePath;
+ @Nullable private final IHdmiControlCallback mCallback;
+
+ static ActiveSourceHandler create(HdmiControlService service, int sourceAddress,
+ int sourcePath, IHdmiControlCallback callback) {
+ if (service == null) {
+ Slog.e(TAG, "Wrong arguments");
+ return null;
+ }
+ return new ActiveSourceHandler(service, sourceAddress, sourcePath, callback);
+ }
+
+ private ActiveSourceHandler(HdmiControlService service, int sourceAddress, int sourcePath,
+ IHdmiControlCallback callback) {
+ mService = service;
+ mSourceAddress = sourceAddress;
+ mSourcePath = sourcePath;
+ mCallback = callback;
+ }
+
+ /**
+ * Handles the incoming active source command.
+ *
+ * @param deviceLogicalAddress logical address of the device to be the active source
+ * @param routingPath routing path of the device to be the active source
+ */
+ void process(int deviceLogicalAddress, int routingPath) {
+ if (mSourcePath == routingPath && mService.getActiveSource() == mSourceAddress) {
+ invokeCallback(HdmiCec.RESULT_SUCCESS);
+ return;
+ }
+ HdmiCecDeviceInfo device = mService.getDeviceInfo(deviceLogicalAddress);
+ if (device == null) {
+ // TODO: Start new device action (Device Discovery) sequence 5.
+ }
+
+ if (!mService.isInPresetInstallationMode()) {
+ int prevActiveInput = mService.getActiveInput();
+ mService.updateActiveDevice(deviceLogicalAddress, routingPath);
+ if (prevActiveInput != mService.getActiveInput()) {
+ // TODO: change port input here.
+ }
+ invokeCallback(HdmiCec.RESULT_SUCCESS);
+ } else {
+ // TV is in a mode that should keep its current source/input from
+ // being changed for its operation. Reclaim the active source
+ // or switch the port back to the one used for the current mode.
+ if (mService.getActiveSource() == mSourceAddress) {
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath);
+ mService.sendCecCommand(activeSource);
+ mService.updateActiveDevice(deviceLogicalAddress, routingPath);
+ invokeCallback(HdmiCec.RESULT_SUCCESS);
+ } else {
+ int activePath = mService.getActivePath();
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
+ routingPath, activePath));
+ // TODO: Start port select action here
+ // PortSelectAction action = new PortSelectAction(mService, mSourceAddress,
+ // activePath, mCallback);
+ // mService.addActionAndStart(action);
+ }
+ }
+ }
+
+ private void invokeCallback(int result) {
+ if (mCallback == null) {
+ return;
+ }
+ try {
+ mCallback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Callback failed:" + e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
new file mode 100644
index 0000000..f170de0
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Handles an action that selects a logical device as a new active source.
+ *
+ * Triggered by {@link HdmiTvClient}, attempts to select the given target device
+ * for a new active source. It does its best to wake up the target in standby mode
+ * before issuing the command &gt;Set Stream path&lt;.
+ */
+final class DeviceSelectAction extends FeatureAction {
+ private static final String TAG = "DeviceSelect";
+
+ // Time in milliseconds we wait for the device power status to switch to 'Standby'
+ private static final int TIMEOUT_TRANSIT_TO_STANDBY_MS = 5 * 1000;
+
+ // Time in milliseconds we wait for the device power status to turn to 'On'.
+ private static final int TIMEOUT_POWER_ON_MS = 5 * 1000;
+
+ // Time in milliseconds we wait for <Active Source>.
+ private static final int TIMEOUT_ACTIVE_SOURCE_MS = 20 * 1000;
+
+ // The number of times we try to wake up the target device before we give up
+ // and just send <Set Stream Path>.
+ private static final int LOOP_COUNTER_MAX = 20;
+
+ // State in which we wait for <Report Power Status> to come in response to the command
+ // <Give Device Power Status> we have sent.
+ private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1;
+
+ // State in which we wait for the device power status to switch to 'Standby'.
+ // We wait till the status becomes 'Standby' before we send <Set Stream Path>
+ // to wake up the device again.
+ private static final int STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY = 2;
+
+ // State in which we wait for the device power status to switch to 'on'. We wait
+ // maximum 100 seconds (20 * 5) before we give up and just send <Set Stream Path>.
+ private static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3;
+
+ // State in which we wait for the <Active Source> in response to the command
+ // <Set Stream Path> we have sent. We wait as much as TIMEOUT_ACTIVE_SOURCE_MS
+ // before we give up and mark the action as failure.
+ private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 4;
+
+ private final HdmiCecDeviceInfo mTarget;
+ private final IHdmiControlCallback mCallback;
+ private final int mSourcePath;
+
+ private int mPowerStatusCounter = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param service {@link HdmiControlService} instance
+ * @param sourceAddress logical address of TV initiating this action
+ * @param sourcePath physical address of TV
+ * @param target target logical device that will be a new active source
+ * @param callback callback object
+ */
+ public DeviceSelectAction(HdmiControlService service, int sourceAddress, int sourcePath,
+ HdmiCecDeviceInfo target, IHdmiControlCallback callback) {
+ super(service, sourceAddress);
+ mCallback = callback;
+ mSourcePath = sourcePath;
+ mTarget = target;
+ }
+
+ @Override
+ public boolean start() {
+ // TODO: Call the logic that display a banner saying the select action got started.
+ queryDevicePowerStatus();
+ return true;
+ }
+
+ private void queryDevicePowerStatus() {
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mSourceAddress, mTarget.getLogicalAddress()));
+ mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
+ addTimer(mState, TIMEOUT_MS);
+ }
+
+ @Override
+ public boolean processCommand(HdmiCecMessage cmd) {
+ if (cmd.getSource() != mTarget.getLogicalAddress()) {
+ return false;
+ }
+ int opcode = cmd.getOpcode();
+ byte[] params = cmd.getParams();
+
+ switch (mState) {
+ case STATE_WAIT_FOR_REPORT_POWER_STATUS:
+ if (opcode == HdmiCec.MESSAGE_REPORT_POWER_STATUS && params.length == 1) {
+ return handleReportPowerStatus(params[0]);
+ }
+ return false;
+ case STATE_WAIT_FOR_ACTIVE_SOURCE:
+ if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) {
+ int activePath = HdmiUtils.twoBytesToInt(params);
+ ActiveSourceHandler.create(mService, mSourceAddress, mSourcePath, mCallback)
+ .process(cmd.getSource(), activePath);
+ finish();
+ return true;
+ }
+ return false;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ private boolean handleReportPowerStatus(int powerStatus) {
+ // TODO: Check TV's own status which might have been updated during the action.
+ // If in 'Standby' or 'Transit to standby', remove the banner
+ // and stop this action. Otherwise, send <Set Stream Path>
+ switch (powerStatus) {
+ case HdmiCec.POWER_STATUS_ON:
+ sendSetStreamPath();
+ return true;
+ case HdmiCec.POWER_STATUS_TRANSIENT_TO_STANDBY:
+ if (mPowerStatusCounter < 4) {
+ mState = STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY;
+ addTimer(mState, TIMEOUT_TRANSIT_TO_STANDBY_MS);
+ } else {
+ sendSetStreamPath();
+ }
+ return true;
+ case HdmiCec.POWER_STATUS_STANDBY:
+ if (mPowerStatusCounter == 0) {
+ turnOnDevice();
+ } else {
+ sendSetStreamPath();
+ }
+ return true;
+ case HdmiCec.POWER_STATUS_TRANSIENT_TO_ON:
+ if (mPowerStatusCounter < LOOP_COUNTER_MAX) {
+ mState = STATE_WAIT_FOR_DEVICE_POWER_ON;
+ addTimer(mState, TIMEOUT_POWER_ON_MS);
+ } else {
+ sendSetStreamPath();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void turnOnDevice() {
+ sendRemoteKeyCommand(HdmiConstants.UI_COMMAND_POWER);
+ sendRemoteKeyCommand(HdmiConstants.UI_COMMAND_POWER_ON_FUNCTION);
+ mState = STATE_WAIT_FOR_DEVICE_POWER_ON;
+ addTimer(mState, TIMEOUT_POWER_ON_MS);
+ }
+
+ private void sendSetStreamPath() {
+ sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(
+ mSourceAddress, mTarget.getPhysicalAddress()));
+ mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
+ addTimer(mState, TIMEOUT_ACTIVE_SOURCE_MS);
+ }
+
+ private void sendRemoteKeyCommand(int keyCode) {
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress,
+ mTarget.getLogicalAddress(), keyCode));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress,
+ mTarget.getLogicalAddress()));
+ }
+
+ @Override
+ public void handleTimerEvent(int timeoutState) {
+ if (mState != timeoutState) {
+ Slog.w(TAG, "Timer in a wrong state. Ignored.");
+ return;
+ }
+ switch (mState) {
+ case STATE_WAIT_FOR_REPORT_POWER_STATUS:
+ sendSetStreamPath();
+ break;
+ case STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY:
+ case STATE_WAIT_FOR_DEVICE_POWER_ON:
+ mPowerStatusCounter++;
+ queryDevicePowerStatus();
+ break;
+ case STATE_WAIT_FOR_ACTIVE_SOURCE:
+ // TODO: Remove the banner
+ // Display banner "Communication failed. Please check your cable or connection"
+ invokeCallback(HdmiCec.RESULT_TIMEOUT);
+ finish();
+ break;
+ }
+ }
+
+ private void invokeCallback(int result) {
+ if (mCallback == null) {
+ return;
+ }
+ try {
+ mCallback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Callback failed:" + e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java
index 0ba7773..ae272b4 100644
--- a/services/core/java/com/android/server/hdmi/FeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/FeatureAction.java
@@ -46,8 +46,9 @@ abstract class FeatureAction {
// Timer handler message used for timeout event
protected static final int MSG_TIMEOUT = 100;
- // Default timeout for the incoming command to arrive in response to a request
- protected static final int TIMEOUT_MS = 1000;
+ // Default timeout for the incoming command to arrive in response to a request.
+ // TODO: Consider reading this value from configuration to allow customization.
+ protected static final int TIMEOUT_MS = 2000;
// Default state used in common by all the feature actions.
protected static final int STATE_NONE = 0;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 8bd81ea..6394fe7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -16,8 +16,11 @@
package com.android.server.hdmi;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.os.RemoteException;
import android.util.Slog;
import java.util.Locale;
@@ -55,6 +58,31 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
}
+ /**
+ * Performs the action 'device select', or 'one touch play' initiated by TV.
+ *
+ * @param targetAddress logical address of the device to select
+ * @param callback callback object to report the result with
+ */
+ void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
+ HdmiCecDeviceInfo targetDevice = mService.getDeviceInfo(targetAddress);
+ if (targetDevice == null) {
+ invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE);
+ return;
+ }
+ mService.removeAction(DeviceSelectAction.class);
+ mService.addAndStartAction(new DeviceSelectAction(mService, mAddress,
+ mService.getPhysicalAddress(), targetDevice, callback));
+ }
+
+ private static void invokeCallback(IHdmiControlCallback callback, int result) {
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Invoking callback failed:" + e);
+ }
+ }
+
@Override
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 1fcb32f..8dbfd85 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -270,6 +270,38 @@ public class HdmiCecMessageBuilder {
}
/**
+ * Build &lt;Set Stream Path&gt; command.
+ *
+ * <p>This is a broadcast message sent to all devices on the bus.
+ *
+ * @param src source address of command
+ * @param streamPath physical address of the device to start streaming
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildSetStreamPath(int src, int streamPath) {
+ return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_SET_STREAM_PATH,
+ physicalAddressToParam(streamPath));
+ }
+
+ /**
+ * Build &lt;Routing Change&gt; command.
+ *
+ * <p>This is a broadcast message sent to all devices on the bus.
+ *
+ * @param src source address of command
+ * @param oldPath physical address of the currently active routing path
+ * @param newPath physical address of the new active routing path
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildRoutingChange(int src, int oldPath, int newPath) {
+ byte[] param = new byte[] {
+ (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF),
+ (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF)
+ };
+ return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_ROUTING_CHANGE, param);
+ }
+
+ /**
* Build &lt;Give Device Power Status&gt; command.
*
* @param src source address of command
diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java
index 54b5dcb..8f319ea 100644
--- a/services/core/java/com/android/server/hdmi/HdmiConstants.java
+++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java
@@ -39,9 +39,15 @@ final class HdmiConstants {
// Constants related to UI Command Codes.
// Refer to CEC Table 30 in HDMI Spec v1.4b.
+ static final int UI_COMMAND_POWER = 0x40;
static final int UI_COMMAND_MUTE = 0x43;
static final int UI_COMMAND_MUTE_FUNCTION = 0x65;
static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66;
+ static final int UI_COMMAND_POWER_ON_FUNCTION = 0x6D;
+
+ // Bit mask used to get the routing path of the top level device.
+ // When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0.
+ static final int ROUTING_PATH_TOP_MASK = 0xF000;
// Flags used for setOption to CEC HAL.
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index d41da30..5eb0db7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -125,6 +125,21 @@ public final class HdmiControlService extends SystemService {
@Nullable
private HdmiMhlController mMhlController;
+ // Logical address of the active source.
+ @GuardedBy("mLock")
+ private int mActiveSource;
+
+ // Active routing path. Physical address of the active source but not all the time, such as
+ // when the new active source does not claim itself to be one.
+ @GuardedBy("mLock")
+ private int mActiveRoutingPath;
+
+ // Set to true while the service is in normal mode. While set to false, no input change is
+ // allowed. Used for situations where input change can confuse users such as channel auto-scan,
+ // system upgrade, etc., a.k.a. "prohibit mode".
+ @GuardedBy("mLock")
+ private boolean mInputChangeEnabled;
+
@GuardedBy("mLock")
// Whether ARC is "enabled" or not.
// TODO: it may need to hold lock if it's accessed from others.
@@ -141,6 +156,8 @@ public final class HdmiControlService extends SystemService {
super(context);
mLocalDevices = getContext().getResources().getIntArray(
com.android.internal.R.array.config_hdmiCecLogicalDeviceType);
+ // TODO: Get control flag from persistent storage
+ mInputChangeEnabled = true;
}
@Override
@@ -224,6 +241,41 @@ public final class HdmiControlService extends SystemService {
return mHandler.getLooper();
}
+ int getActiveSource() {
+ synchronized (mLock) {
+ return mActiveSource;
+ }
+ }
+
+ int getActivePath() {
+ synchronized (mLock) {
+ return mActiveRoutingPath;
+ }
+ }
+
+ /**
+ * Returns the path (physical address) of the device at the top of the currently active
+ * routing path. Used to get the corresponding port address of the HDMI input of the TV.
+ */
+ int getActiveInput() {
+ synchronized (mLock) {
+ return mActiveRoutingPath & HdmiConstants.ROUTING_PATH_TOP_MASK;
+ }
+ }
+
+ void updateActiveDevice(int logicalAddress, int physicalAddress) {
+ synchronized (mLock) {
+ mActiveSource = logicalAddress;
+ mActiveRoutingPath = physicalAddress;
+ }
+ }
+
+ void setInputChangeEnabled(boolean enabled) {
+ synchronized (mLock) {
+ mInputChangeEnabled = enabled;
+ }
+ }
+
/**
* Returns physical address of the device.
*/
@@ -238,6 +290,10 @@ public final class HdmiControlService extends SystemService {
return mCecController.getVendorId();
}
+ HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
+ return mCecController.getDeviceInfo(logicalAddress);
+ }
+
/**
* Returns version of CEC.
*/
@@ -304,7 +360,7 @@ public final class HdmiControlService extends SystemService {
}
// Remove all actions matched with the given Class type.
- private <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+ <T extends FeatureAction> void removeAction(final Class<T> clazz) {
removeActionExcept(clazz, null);
}
@@ -575,6 +631,25 @@ public final class HdmiControlService extends SystemService {
}
@Override
+ public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
+ enforceAccessPermission();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiCecLocalDeviceTv tv =
+ (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
+ if (tv == null) {
+ Slog.w(TAG, "Local playback device not available");
+ invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
+ return;
+ }
+ tv.deviceSelect(logicalAddress, callback);
+ }
+ });
+ }
+
+
+ @Override
public void oneTouchPlay(final IHdmiControlCallback callback) {
enforceAccessPermission();
runOnServiceThread(new Runnable() {
@@ -710,8 +785,9 @@ public final class HdmiControlService extends SystemService {
}
boolean isInPresetInstallationMode() {
- // TODO: Implement this.
- return false;
+ synchronized (mLock) {
+ return !mInputChangeEnabled;
+ }
}
/**
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index cbc853d..5bfabd9 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -69,6 +69,7 @@ public:
}
private:
+ static const int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
static void onReceived(const hdmi_event_t* event, void* arg);
hdmi_cec_device_t* mDevice;
@@ -209,11 +210,11 @@ void HdmiCecController::clearLogicaladdress() {
}
int HdmiCecController::getPhysicalAddress() {
- uint16_t physicalAddress = 0xFFFF;
- if (mDevice->get_physical_address(mDevice, &physicalAddress) == 0) {
- return physicalAddress;
+ uint16_t addr;
+ if (!mDevice->get_physical_address(mDevice, &addr)) {
+ return addr;
}
- return -1;
+ return INVALID_PHYSICAL_ADDRESS;
}
int HdmiCecController::getVersion() {
@@ -311,58 +312,45 @@ static jint nativeSendCecCommand(JNIEnv* env, jclass clazz, jlong controllerPtr,
return controller->sendMessage(message);
}
-static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz,
- jlong controllerPtr, jint logicalAddress) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
- return controller->addLogicalAddress(
- static_cast<cec_logical_address_t>(logicalAddress));
+static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr,
+ jint logicalAddress) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
+ return controller->addLogicalAddress(static_cast<cec_logical_address_t>(logicalAddress));
}
-static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz,
- jlong controllerPtr) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
+static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
controller->clearLogicaladdress();
}
-static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz,
- jlong controllerPtr) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
+static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
return controller->getPhysicalAddress();
}
-static jint nativeGetVersion(JNIEnv* env, jclass clazz,
- jlong controllerPtr) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
+static jint nativeGetVersion(JNIEnv* env, jclass clazz, jlong controllerPtr) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
return controller->getVersion();
}
static jint nativeGetVendorId(JNIEnv* env, jclass clazz, jlong controllerPtr) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
return controller->getVendorId();
}
-static void nativeSetOption(JNIEnv* env, jclass clazz, jlong controllerPtr, jint flag,
- jint value) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
+static void nativeSetOption(JNIEnv* env, jclass clazz, jlong controllerPtr, jint flag, jint value) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
controller->setOption(flag, value);
}
static void nativeSetAudioReturnChannel(JNIEnv* env, jclass clazz, jlong controllerPtr,
jboolean enabled) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
controller->setAudioReturnChannel(enabled == JNI_TRUE);
}
static jboolean nativeIsConnected(JNIEnv* env, jclass clazz, jlong controllerPtr, jint port) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
return controller->isConnected(port) ? JNI_TRUE : JNI_FALSE ;
}
@@ -385,8 +373,7 @@ static JNINativeMethod sMethods[] = {
#define CLASS_PATH "com/android/server/hdmi/HdmiCecController"
int register_android_server_hdmi_HdmiCecController(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods,
- NELEM(sMethods));
+ int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
return 0;
}