Loading services/core/java/com/android/server/hdmi/FeatureAction.java +24 −3 Original line number Diff line number Diff line Loading @@ -118,6 +118,11 @@ abstract class FeatureAction { * @param delayMillis amount of delay for the timer */ void sendTimerMessage(int state, long delayMillis); /** * Removes any pending timer message. */ void clearTimerMessage(); } private class ActionTimerHandler extends Handler implements ActionTimer { Loading @@ -131,6 +136,11 @@ abstract class FeatureAction { sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state), delayMillis); } @Override public void clearTimerMessage() { removeMessages(MSG_TIMEOUT); } @Override public void handleMessage(Message msg) { switch (msg.what) { Loading @@ -154,15 +164,26 @@ abstract class FeatureAction { mActionTimer.sendTimerMessage(state, delayMillis); } protected final void sendCommand(HdmiCecMessage cmd) { mService.sendCecCommand(cmd); protected final boolean sendCommand(HdmiCecMessage cmd) { return mService.sendCecCommand(cmd); } /** * Clean up action's state. * * <p>Declared as package-private. Only {@link HdmiControlService} can access it. */ void clear() { mState = STATE_NONE; // Clear all timers. mActionTimer.clearTimerMessage(); } /** * Finish up the action. Reset the state, and remove itself from the action queue. */ protected void finish() { mState = STATE_NONE; clear(); removeAction(this); } Loading services/core/java/com/android/server/hdmi/HdmiCecController.java +5 −3 Original line number Diff line number Diff line Loading @@ -442,9 +442,9 @@ final class HdmiCecController { } void sendCommand(HdmiCecMessage cecMessage) { boolean sendCommand(HdmiCecMessage cecMessage) { Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage); mIoHandler.sendMessage(message); return mIoHandler.sendMessage(message); } /** Loading @@ -465,7 +465,9 @@ final class HdmiCecController { * Called by native when a hotplug event issues. */ private void handleHotplug(boolean connected) { // TODO: Delegate event to main message handler. // TODO: once add port number to cec HAL interface, pass port number // to the service. mService.onHotplug(0, connected); } private static native long nativeInit(HdmiCecController handler); Loading services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +44 −0 Original line number Diff line number Diff line Loading @@ -185,6 +185,50 @@ public class HdmiCecMessageBuilder { return buildCommand(src, dest, HdmiCec.MESSAGE_CEC_VERSION, params); } /** * Build <Request Arc Initiation> * * @param src source address of command * @param dest destination address of command * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildRequestArcInitiation(int src, int dest) { return buildCommand(src, dest, HdmiCec.MESSAGE_REQUEST_ARC_INITIATION); } /** * Build <Request Arc Termination> * * @param src source address of command * @param dest destination address of command * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildRequestArcTermination(int src, int dest) { return buildCommand(src, dest, HdmiCec.MESSAGE_REQUEST_ARC_TERMINATION); } /** * Build <Report Arc Initiated> * * @param src source address of command * @param dest destination address of command * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildReportArcInitiated(int src, int dest) { return buildCommand(src, dest, HdmiCec.MESSAGE_REPORT_ARC_INITIATED); } /** * Build <Report Arc Terminated> * * @param src source address of command * @param dest destination address of command * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildReportArcTerminated(int src, int dest) { return buildCommand(src, dest, HdmiCec.MESSAGE_REPORT_ARC_TERMINATED); } /** * Build a {@link HdmiCecMessage} without extra parameter. * Loading services/core/java/com/android/server/hdmi/HdmiControlService.java +109 −11 Original line number Diff line number Diff line Loading @@ -21,12 +21,15 @@ import android.content.Context; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.util.Slog; import com.android.server.SystemService; import java.util.Iterator; import java.util.LinkedList; import java.util.Locale; /** Loading @@ -41,12 +44,23 @@ public final class HdmiControlService extends SystemService { // and sparse call it shares a thread to handle IO operations. private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread"); // A collection of FeatureAction. // Note that access to this collection should happen in service thread. private final LinkedList<FeatureAction> mActions = new LinkedList<>(); @Nullable private HdmiCecController mCecController; @Nullable private HdmiMhlController mMhlController; // Whether ARC is "enabled" or not. // TODO: it may need to hold lock if it's accessed from others. private boolean mArcStatusEnabled = false; // Handler running on service thread. It's used to run a task in service thread. private Handler mHandler = new Handler(); public HdmiControlService(Context context) { super(context); } Loading Loading @@ -84,35 +98,83 @@ public final class HdmiControlService extends SystemService { * <p>Declared as package-private. */ Looper getServiceLooper() { return Looper.myLooper(); return mHandler.getLooper(); } /** * Add a new {@link FeatureAction} to the action queue. * Add and start a new {@link FeatureAction} to the action queue. * * @param action {@link FeatureAction} to add * @param action {@link FeatureAction} to add and start */ void addAction(FeatureAction action) { // TODO: Implement this. void addAndStartAction(final FeatureAction action) { // TODO: may need to check the number of stale actions. runOnServiceThread(new Runnable() { @Override public void run() { mActions.add(action); action.start(); } }); } /** * Remove the given {@link FeatureAction} object from the action queue. * * @param action {@link FeatureAction} to add * @param action {@link FeatureAction} to remove */ void removeAction(FeatureAction action) { // TODO: Implement this. void removeAction(final FeatureAction action) { runOnServiceThread(new Runnable() { @Override public void run() { mActions.remove(action); } }); } // Remove all actions matched with the given Class type. private <T extends FeatureAction> void removeAction(final Class<T> clazz) { runOnServiceThread(new Runnable() { @Override public void run() { Iterator<FeatureAction> iter = mActions.iterator(); while (iter.hasNext()) { FeatureAction action = iter.next(); if (action.getClass().equals(clazz)) { action.clear(); mActions.remove(action); } } } }); } private void runOnServiceThread(Runnable runnable) { mHandler.post(runnable); } /** * Change ARC status into the given {@code enabled} status. * * @return {@code true} if ARC was in "Enabled" status */ boolean setArcStatus(boolean enabled) { boolean oldStatus = mArcStatusEnabled; // 1. Enable/disable ARC circuit. // TODO: call set_audio_return_channel of hal interface. // 2. Update arc status; mArcStatusEnabled = enabled; return oldStatus; } /** * Transmit a CEC command to CEC bus. * * @param command CEC command to send out * @return {@code true} if succeeds to send command */ void sendCecCommand(HdmiCecMessage command) { mCecController.sendCommand(command); boolean sendCecCommand(HdmiCecMessage command) { return mCecController.sendCommand(command); } /** Loading Loading @@ -143,6 +205,12 @@ public final class HdmiControlService extends SystemService { case HdmiCec.MESSAGE_GET_CEC_VERSION: handleGetCecVersion(message); return true; case HdmiCec.MESSAGE_INITIATE_ARC: handleInitiateArc(message); return true; case HdmiCec.MESSAGE_TERMINATE_ARC: handleTerminateArc(message); return true; // TODO: Add remaining system information query such as // <Give Device Power Status> and <Request Active Source> handler. default: Loading @@ -151,6 +219,36 @@ public final class HdmiControlService extends SystemService { } } /** * Called when a new hotplug event is issued. * * @param port hdmi port number where hot plug event issued. * @param connected whether to be plugged in or not */ void onHotplug(int portNo, boolean connected) { // TODO: Start "RequestArcInitiationAction" if ARC port. } private void handleInitiateArc(HdmiCecMessage message){ // In case where <Initiate Arc> is started by <Request ARC Initiation> // need to clean up RequestArcInitiationAction. removeAction(RequestArcInitiationAction.class); SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this, message.getDestination(), message.getSource(), true); addAndStartAction(action); } private void handleTerminateArc(HdmiCecMessage message) { // In case where <Terminate Arc> is started by <Request ARC Termination> // need to clean up RequestArcInitiationAction. // TODO: check conditions of power status by calling is_connected api // to be added soon. removeAction(RequestArcTerminationAction.class); SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this, message.getDestination(), message.getSource(), false); addAndStartAction(action); } private void handleGetCecVersion(HdmiCecMessage message) { int version = mCecController.getVersion(); HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(), Loading services/core/java/com/android/server/hdmi/RequestArcAction.java 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * 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.HdmiCec; import android.hardware.hdmi.HdmiCecMessage; import android.util.Slog; /** * Base feature action class for <Request ARC Initiation>/<Request ARC Termination>. */ abstract class RequestArcAction extends FeatureAction { private static final String TAG = "RequestArcAction"; // State in which waits for ARC response. protected static final int STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE = 1; // Logical address of AV Receiver. protected final int mAvrAddress; /** * @Constructor * * @param service {@link HdmiControlService} instance * @param sourceAddress logical address to be used as source address. It should * TV type * @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type * @throw IllegalArugmentException if device type of sourceAddress and avrAddress * is invalid */ RequestArcAction(HdmiControlService service, int sourceAddress, int avrAddress) { super(service, sourceAddress); verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV); verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); mAvrAddress = avrAddress; } private static void verifyAddressType(int logicalAddress, int deviceType) { int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress); if (actualDeviceType != deviceType) { throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType + ", Actual:" + actualDeviceType); } } @Override boolean processCommand(HdmiCecMessage cmd) { if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) { return false; } int src = cmd.getSource(); if (src != mAvrAddress) { Slog.w(TAG, "Invalid source [Expected:" + mAvrAddress + ", Actual:" + src + "]"); return false; } int opcode = cmd.getOpcode(); switch (opcode) { // Handles only <Feature Abort> here and, both <Initiate ARC> and <Terminate ARC> // are handled in HdmiControlService itself because both can be // received wihtout <Request ARC Initiation> or <Request ARC Termination>. case HdmiCec.MESSAGE_FEATURE_ABORT: disableArcTransmission(); finish(); return true; default: Slog.w(TAG, "Unsupported opcode:" + cmd.toString()); } return false; } protected final void disableArcTransmission() { // Start Set ARC Transmission State action. SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(mService, mSourceAddress, mAvrAddress, false); mService.addAndStartAction(action); } @Override final void handleTimerEvent(int state) { if (mState != state || state != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) { return; } disableArcTransmission(); } } Loading
services/core/java/com/android/server/hdmi/FeatureAction.java +24 −3 Original line number Diff line number Diff line Loading @@ -118,6 +118,11 @@ abstract class FeatureAction { * @param delayMillis amount of delay for the timer */ void sendTimerMessage(int state, long delayMillis); /** * Removes any pending timer message. */ void clearTimerMessage(); } private class ActionTimerHandler extends Handler implements ActionTimer { Loading @@ -131,6 +136,11 @@ abstract class FeatureAction { sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state), delayMillis); } @Override public void clearTimerMessage() { removeMessages(MSG_TIMEOUT); } @Override public void handleMessage(Message msg) { switch (msg.what) { Loading @@ -154,15 +164,26 @@ abstract class FeatureAction { mActionTimer.sendTimerMessage(state, delayMillis); } protected final void sendCommand(HdmiCecMessage cmd) { mService.sendCecCommand(cmd); protected final boolean sendCommand(HdmiCecMessage cmd) { return mService.sendCecCommand(cmd); } /** * Clean up action's state. * * <p>Declared as package-private. Only {@link HdmiControlService} can access it. */ void clear() { mState = STATE_NONE; // Clear all timers. mActionTimer.clearTimerMessage(); } /** * Finish up the action. Reset the state, and remove itself from the action queue. */ protected void finish() { mState = STATE_NONE; clear(); removeAction(this); } Loading
services/core/java/com/android/server/hdmi/HdmiCecController.java +5 −3 Original line number Diff line number Diff line Loading @@ -442,9 +442,9 @@ final class HdmiCecController { } void sendCommand(HdmiCecMessage cecMessage) { boolean sendCommand(HdmiCecMessage cecMessage) { Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage); mIoHandler.sendMessage(message); return mIoHandler.sendMessage(message); } /** Loading @@ -465,7 +465,9 @@ final class HdmiCecController { * Called by native when a hotplug event issues. */ private void handleHotplug(boolean connected) { // TODO: Delegate event to main message handler. // TODO: once add port number to cec HAL interface, pass port number // to the service. mService.onHotplug(0, connected); } private static native long nativeInit(HdmiCecController handler); Loading
services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +44 −0 Original line number Diff line number Diff line Loading @@ -185,6 +185,50 @@ public class HdmiCecMessageBuilder { return buildCommand(src, dest, HdmiCec.MESSAGE_CEC_VERSION, params); } /** * Build <Request Arc Initiation> * * @param src source address of command * @param dest destination address of command * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildRequestArcInitiation(int src, int dest) { return buildCommand(src, dest, HdmiCec.MESSAGE_REQUEST_ARC_INITIATION); } /** * Build <Request Arc Termination> * * @param src source address of command * @param dest destination address of command * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildRequestArcTermination(int src, int dest) { return buildCommand(src, dest, HdmiCec.MESSAGE_REQUEST_ARC_TERMINATION); } /** * Build <Report Arc Initiated> * * @param src source address of command * @param dest destination address of command * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildReportArcInitiated(int src, int dest) { return buildCommand(src, dest, HdmiCec.MESSAGE_REPORT_ARC_INITIATED); } /** * Build <Report Arc Terminated> * * @param src source address of command * @param dest destination address of command * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildReportArcTerminated(int src, int dest) { return buildCommand(src, dest, HdmiCec.MESSAGE_REPORT_ARC_TERMINATED); } /** * Build a {@link HdmiCecMessage} without extra parameter. * Loading
services/core/java/com/android/server/hdmi/HdmiControlService.java +109 −11 Original line number Diff line number Diff line Loading @@ -21,12 +21,15 @@ import android.content.Context; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.util.Slog; import com.android.server.SystemService; import java.util.Iterator; import java.util.LinkedList; import java.util.Locale; /** Loading @@ -41,12 +44,23 @@ public final class HdmiControlService extends SystemService { // and sparse call it shares a thread to handle IO operations. private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread"); // A collection of FeatureAction. // Note that access to this collection should happen in service thread. private final LinkedList<FeatureAction> mActions = new LinkedList<>(); @Nullable private HdmiCecController mCecController; @Nullable private HdmiMhlController mMhlController; // Whether ARC is "enabled" or not. // TODO: it may need to hold lock if it's accessed from others. private boolean mArcStatusEnabled = false; // Handler running on service thread. It's used to run a task in service thread. private Handler mHandler = new Handler(); public HdmiControlService(Context context) { super(context); } Loading Loading @@ -84,35 +98,83 @@ public final class HdmiControlService extends SystemService { * <p>Declared as package-private. */ Looper getServiceLooper() { return Looper.myLooper(); return mHandler.getLooper(); } /** * Add a new {@link FeatureAction} to the action queue. * Add and start a new {@link FeatureAction} to the action queue. * * @param action {@link FeatureAction} to add * @param action {@link FeatureAction} to add and start */ void addAction(FeatureAction action) { // TODO: Implement this. void addAndStartAction(final FeatureAction action) { // TODO: may need to check the number of stale actions. runOnServiceThread(new Runnable() { @Override public void run() { mActions.add(action); action.start(); } }); } /** * Remove the given {@link FeatureAction} object from the action queue. * * @param action {@link FeatureAction} to add * @param action {@link FeatureAction} to remove */ void removeAction(FeatureAction action) { // TODO: Implement this. void removeAction(final FeatureAction action) { runOnServiceThread(new Runnable() { @Override public void run() { mActions.remove(action); } }); } // Remove all actions matched with the given Class type. private <T extends FeatureAction> void removeAction(final Class<T> clazz) { runOnServiceThread(new Runnable() { @Override public void run() { Iterator<FeatureAction> iter = mActions.iterator(); while (iter.hasNext()) { FeatureAction action = iter.next(); if (action.getClass().equals(clazz)) { action.clear(); mActions.remove(action); } } } }); } private void runOnServiceThread(Runnable runnable) { mHandler.post(runnable); } /** * Change ARC status into the given {@code enabled} status. * * @return {@code true} if ARC was in "Enabled" status */ boolean setArcStatus(boolean enabled) { boolean oldStatus = mArcStatusEnabled; // 1. Enable/disable ARC circuit. // TODO: call set_audio_return_channel of hal interface. // 2. Update arc status; mArcStatusEnabled = enabled; return oldStatus; } /** * Transmit a CEC command to CEC bus. * * @param command CEC command to send out * @return {@code true} if succeeds to send command */ void sendCecCommand(HdmiCecMessage command) { mCecController.sendCommand(command); boolean sendCecCommand(HdmiCecMessage command) { return mCecController.sendCommand(command); } /** Loading Loading @@ -143,6 +205,12 @@ public final class HdmiControlService extends SystemService { case HdmiCec.MESSAGE_GET_CEC_VERSION: handleGetCecVersion(message); return true; case HdmiCec.MESSAGE_INITIATE_ARC: handleInitiateArc(message); return true; case HdmiCec.MESSAGE_TERMINATE_ARC: handleTerminateArc(message); return true; // TODO: Add remaining system information query such as // <Give Device Power Status> and <Request Active Source> handler. default: Loading @@ -151,6 +219,36 @@ public final class HdmiControlService extends SystemService { } } /** * Called when a new hotplug event is issued. * * @param port hdmi port number where hot plug event issued. * @param connected whether to be plugged in or not */ void onHotplug(int portNo, boolean connected) { // TODO: Start "RequestArcInitiationAction" if ARC port. } private void handleInitiateArc(HdmiCecMessage message){ // In case where <Initiate Arc> is started by <Request ARC Initiation> // need to clean up RequestArcInitiationAction. removeAction(RequestArcInitiationAction.class); SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this, message.getDestination(), message.getSource(), true); addAndStartAction(action); } private void handleTerminateArc(HdmiCecMessage message) { // In case where <Terminate Arc> is started by <Request ARC Termination> // need to clean up RequestArcInitiationAction. // TODO: check conditions of power status by calling is_connected api // to be added soon. removeAction(RequestArcTerminationAction.class); SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this, message.getDestination(), message.getSource(), false); addAndStartAction(action); } private void handleGetCecVersion(HdmiCecMessage message) { int version = mCecController.getVersion(); HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(), Loading
services/core/java/com/android/server/hdmi/RequestArcAction.java 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * 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.HdmiCec; import android.hardware.hdmi.HdmiCecMessage; import android.util.Slog; /** * Base feature action class for <Request ARC Initiation>/<Request ARC Termination>. */ abstract class RequestArcAction extends FeatureAction { private static final String TAG = "RequestArcAction"; // State in which waits for ARC response. protected static final int STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE = 1; // Logical address of AV Receiver. protected final int mAvrAddress; /** * @Constructor * * @param service {@link HdmiControlService} instance * @param sourceAddress logical address to be used as source address. It should * TV type * @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type * @throw IllegalArugmentException if device type of sourceAddress and avrAddress * is invalid */ RequestArcAction(HdmiControlService service, int sourceAddress, int avrAddress) { super(service, sourceAddress); verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV); verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); mAvrAddress = avrAddress; } private static void verifyAddressType(int logicalAddress, int deviceType) { int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress); if (actualDeviceType != deviceType) { throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType + ", Actual:" + actualDeviceType); } } @Override boolean processCommand(HdmiCecMessage cmd) { if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) { return false; } int src = cmd.getSource(); if (src != mAvrAddress) { Slog.w(TAG, "Invalid source [Expected:" + mAvrAddress + ", Actual:" + src + "]"); return false; } int opcode = cmd.getOpcode(); switch (opcode) { // Handles only <Feature Abort> here and, both <Initiate ARC> and <Terminate ARC> // are handled in HdmiControlService itself because both can be // received wihtout <Request ARC Initiation> or <Request ARC Termination>. case HdmiCec.MESSAGE_FEATURE_ABORT: disableArcTransmission(); finish(); return true; default: Slog.w(TAG, "Unsupported opcode:" + cmd.toString()); } return false; } protected final void disableArcTransmission() { // Start Set ARC Transmission State action. SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(mService, mSourceAddress, mAvrAddress, false); mService.addAndStartAction(action); } @Override final void handleTimerEvent(int state) { if (mState != state || state != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) { return; } disableArcTransmission(); } }