Loading services/core/java/com/android/server/hdmi/Constants.java +4 −0 Original line number Diff line number Diff line Loading @@ -173,6 +173,10 @@ final class Constants { static final int SYSTEM_AUDIO_STATUS_OFF = 0; static final int SYSTEM_AUDIO_STATUS_ON = 1; // [Menu State] static final int MENU_STATE_ACTIVATED = 0; static final int MENU_STATE_DEACTIVATED = 1; // 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; Loading services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +71 −0 Original line number Diff line number Diff line Loading @@ -17,10 +17,15 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.input.InputManager; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.util.Slog; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; Loading @@ -39,15 +44,21 @@ abstract class HdmiCecLocalDevice { private static final String TAG = "HdmiCecLocalDevice"; private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1; private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2; // Timeout in millisecond for device clean up (5s). // Normal actions timeout is 2s but some of them would have several sequence of timeout. private static final int DEVICE_CLEANUP_TIMEOUT = 5000; // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior. // When it expires, we can assume <User Control Release> is received. private static final int FOLLOWER_SAFETY_TIMEOUT = 550; protected final HdmiControlService mService; protected final int mDeviceType; protected int mAddress; protected int mPreferredAddress; protected HdmiDeviceInfo mDeviceInfo; protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE; protected int mLastKeyRepeatCount = 0; static class ActiveSource { int logicalAddress; Loading Loading @@ -111,6 +122,9 @@ abstract class HdmiCecLocalDevice { case MSG_DISABLE_DEVICE_TIMEOUT: handleDisableDeviceTimeout(); break; case MSG_USER_CONTROL_RELEASE_TIMEOUT: handleUserControlReleased(); break; } } }; Loading Loading @@ -230,10 +244,14 @@ abstract class HdmiCecLocalDevice { return handleImageViewOn(message); case Constants.MESSAGE_USER_CONTROL_PRESSED: return handleUserControlPressed(message); case Constants.MESSAGE_USER_CONTROL_RELEASED: return handleUserControlReleased(); case Constants.MESSAGE_SET_STREAM_PATH: return handleSetStreamPath(message); case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS: return handleGiveDevicePowerStatus(message); case Constants.MESSAGE_MENU_REQUEST: return handleGiveDeviceMenuStatus(message); case Constants.MESSAGE_VENDOR_COMMAND: return handleVendorCommand(message); case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID: Loading Loading @@ -376,6 +394,7 @@ abstract class HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleUserControlPressed(HdmiCecMessage message) { assertRunOnServiceThread(); mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT); if (mService.isPowerOnOrTransient() && isPowerOffOrToggleCommand(message)) { mService.standby(); return true; Loading @@ -383,9 +402,54 @@ abstract class HdmiCecLocalDevice { mService.wakeUp(); return true; } final long downTime = SystemClock.uptimeMillis(); final byte[] params = message.getParams(); final int keycode = HdmiCecKeycode.cecKeyToAndroidKey(params[0], params.length > 1 ? params[1] : HdmiCecKeycode.NO_PARAM); int keyRepeatCount = 0; if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) { if (keycode == mLastKeycode) { keyRepeatCount = mLastKeyRepeatCount + 1; } else { injectKeyEvent(downTime, KeyEvent.ACTION_UP, mLastKeycode, 0); } } mLastKeycode = keycode; mLastKeyRepeatCount = keyRepeatCount; if (keycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) { injectKeyEvent(downTime, KeyEvent.ACTION_DOWN, keycode, keyRepeatCount); mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT), FOLLOWER_SAFETY_TIMEOUT); return true; } return false; } @ServiceThreadOnly protected boolean handleUserControlReleased() { assertRunOnServiceThread(); mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT); mLastKeyRepeatCount = 0; if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) { final long upTime = SystemClock.uptimeMillis(); injectKeyEvent(upTime, KeyEvent.ACTION_UP, mLastKeycode, 0); mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE; return true; } return false; } static void injectKeyEvent(long time, int action, int keycode, int repeat) { KeyEvent keyEvent = KeyEvent.obtain(time, time, action, keycode, repeat, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_HDMI, null); InputManager.getInstance().injectInputEvent(keyEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); keyEvent.recycle(); } static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) { byte[] params = message.getParams(); return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED Loading Loading @@ -420,6 +484,13 @@ abstract class HdmiCecLocalDevice { return true; } protected boolean handleGiveDeviceMenuStatus(HdmiCecMessage message) { // Always report menu active to receive Remote Control. mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus( mAddress, message.getSource(), Constants.MENU_STATE_ACTIVATED)); return true; } protected boolean handleVendorCommand(HdmiCecMessage message) { mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), message.getParams(), false); Loading services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +15 −0 Original line number Diff line number Diff line Loading @@ -337,6 +337,21 @@ public class HdmiCecMessageBuilder { return buildCommand(src, dest, Constants.MESSAGE_REPORT_POWER_STATUS, param); } /** * Build <Report Menu Status> command. * * @param src source address of command * @param dest destination address of command * @param menuStatus menu status of the device * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildReportMenuStatus(int src, int dest, int menuStatus) { byte[] param = new byte[] { (byte) (menuStatus) }; return buildCommand(src, dest, Constants.MESSAGE_MENU_STATUS, param); } /** * Build <System Audio Mode Request> command. * Loading Loading
services/core/java/com/android/server/hdmi/Constants.java +4 −0 Original line number Diff line number Diff line Loading @@ -173,6 +173,10 @@ final class Constants { static final int SYSTEM_AUDIO_STATUS_OFF = 0; static final int SYSTEM_AUDIO_STATUS_ON = 1; // [Menu State] static final int MENU_STATE_ACTIVATED = 0; static final int MENU_STATE_DEACTIVATED = 1; // 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; Loading
services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +71 −0 Original line number Diff line number Diff line Loading @@ -17,10 +17,15 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.input.InputManager; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.util.Slog; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; Loading @@ -39,15 +44,21 @@ abstract class HdmiCecLocalDevice { private static final String TAG = "HdmiCecLocalDevice"; private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1; private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2; // Timeout in millisecond for device clean up (5s). // Normal actions timeout is 2s but some of them would have several sequence of timeout. private static final int DEVICE_CLEANUP_TIMEOUT = 5000; // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior. // When it expires, we can assume <User Control Release> is received. private static final int FOLLOWER_SAFETY_TIMEOUT = 550; protected final HdmiControlService mService; protected final int mDeviceType; protected int mAddress; protected int mPreferredAddress; protected HdmiDeviceInfo mDeviceInfo; protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE; protected int mLastKeyRepeatCount = 0; static class ActiveSource { int logicalAddress; Loading Loading @@ -111,6 +122,9 @@ abstract class HdmiCecLocalDevice { case MSG_DISABLE_DEVICE_TIMEOUT: handleDisableDeviceTimeout(); break; case MSG_USER_CONTROL_RELEASE_TIMEOUT: handleUserControlReleased(); break; } } }; Loading Loading @@ -230,10 +244,14 @@ abstract class HdmiCecLocalDevice { return handleImageViewOn(message); case Constants.MESSAGE_USER_CONTROL_PRESSED: return handleUserControlPressed(message); case Constants.MESSAGE_USER_CONTROL_RELEASED: return handleUserControlReleased(); case Constants.MESSAGE_SET_STREAM_PATH: return handleSetStreamPath(message); case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS: return handleGiveDevicePowerStatus(message); case Constants.MESSAGE_MENU_REQUEST: return handleGiveDeviceMenuStatus(message); case Constants.MESSAGE_VENDOR_COMMAND: return handleVendorCommand(message); case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID: Loading Loading @@ -376,6 +394,7 @@ abstract class HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleUserControlPressed(HdmiCecMessage message) { assertRunOnServiceThread(); mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT); if (mService.isPowerOnOrTransient() && isPowerOffOrToggleCommand(message)) { mService.standby(); return true; Loading @@ -383,9 +402,54 @@ abstract class HdmiCecLocalDevice { mService.wakeUp(); return true; } final long downTime = SystemClock.uptimeMillis(); final byte[] params = message.getParams(); final int keycode = HdmiCecKeycode.cecKeyToAndroidKey(params[0], params.length > 1 ? params[1] : HdmiCecKeycode.NO_PARAM); int keyRepeatCount = 0; if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) { if (keycode == mLastKeycode) { keyRepeatCount = mLastKeyRepeatCount + 1; } else { injectKeyEvent(downTime, KeyEvent.ACTION_UP, mLastKeycode, 0); } } mLastKeycode = keycode; mLastKeyRepeatCount = keyRepeatCount; if (keycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) { injectKeyEvent(downTime, KeyEvent.ACTION_DOWN, keycode, keyRepeatCount); mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT), FOLLOWER_SAFETY_TIMEOUT); return true; } return false; } @ServiceThreadOnly protected boolean handleUserControlReleased() { assertRunOnServiceThread(); mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT); mLastKeyRepeatCount = 0; if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) { final long upTime = SystemClock.uptimeMillis(); injectKeyEvent(upTime, KeyEvent.ACTION_UP, mLastKeycode, 0); mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE; return true; } return false; } static void injectKeyEvent(long time, int action, int keycode, int repeat) { KeyEvent keyEvent = KeyEvent.obtain(time, time, action, keycode, repeat, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_HDMI, null); InputManager.getInstance().injectInputEvent(keyEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); keyEvent.recycle(); } static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) { byte[] params = message.getParams(); return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED Loading Loading @@ -420,6 +484,13 @@ abstract class HdmiCecLocalDevice { return true; } protected boolean handleGiveDeviceMenuStatus(HdmiCecMessage message) { // Always report menu active to receive Remote Control. mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus( mAddress, message.getSource(), Constants.MENU_STATE_ACTIVATED)); return true; } protected boolean handleVendorCommand(HdmiCecMessage message) { mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), message.getParams(), false); Loading
services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +15 −0 Original line number Diff line number Diff line Loading @@ -337,6 +337,21 @@ public class HdmiCecMessageBuilder { return buildCommand(src, dest, Constants.MESSAGE_REPORT_POWER_STATUS, param); } /** * Build <Report Menu Status> command. * * @param src source address of command * @param dest destination address of command * @param menuStatus menu status of the device * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildReportMenuStatus(int src, int dest, int menuStatus) { byte[] param = new byte[] { (byte) (menuStatus) }; return buildCommand(src, dest, Constants.MESSAGE_MENU_STATUS, param); } /** * Build <System Audio Mode Request> command. * Loading