/**
* Handles CEC command <Active Source>.
- *
- * <p>Used by feature actions that need to handle the command in their flow.
+ * <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 HdmiCecLocalDevice mSource;
private final HdmiControlService mService;
- private final int mSourceAddress;
- private final int mSourcePath;
- @Nullable private final IHdmiControlCallback mCallback;
+ @Nullable
+ private final IHdmiControlCallback mCallback;
- static ActiveSourceHandler create(HdmiControlService service, int sourceAddress,
- int sourcePath, IHdmiControlCallback callback) {
- if (service == null) {
+ static ActiveSourceHandler create(HdmiCecLocalDevice source,
+ IHdmiControlCallback callback) {
+ if (source == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new ActiveSourceHandler(service, sourceAddress, sourcePath, callback);
+ return new ActiveSourceHandler(source, callback);
}
- private ActiveSourceHandler(HdmiControlService service, int sourceAddress, int sourcePath,
- IHdmiControlCallback callback) {
- mService = service;
- mSourceAddress = sourceAddress;
- mSourcePath = sourcePath;
+ private ActiveSourceHandler(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
+ mSource = source;
+ mService = mSource.getService();
mCallback = callback;
}
* @param routingPath routing path of the device to be the active source
*/
void process(int deviceLogicalAddress, int routingPath) {
- if (mSourcePath == routingPath && mService.getActiveSource() == mSourceAddress) {
+ if (getSourcePath() == routingPath && mSource.getActiveSource() == getSourceAddress()) {
invokeCallback(HdmiCec.RESULT_SUCCESS);
return;
}
if (device == null) {
// "New device action" initiated by <Active Source> does not require
// "Routing change action".
- mService.addAndStartAction(new NewDeviceAction(mService, mSourceAddress,
- deviceLogicalAddress, routingPath, false));
+ mSource.addAndStartAction(new NewDeviceAction(mSource, deviceLogicalAddress,
+ routingPath, false));
}
- if (!mService.isInPresetInstallationMode()) {
- int prevActiveInput = mService.getActiveInput();
- mService.updateActiveDevice(deviceLogicalAddress, routingPath);
- if (prevActiveInput != mService.getActiveInput()) {
+ if (!mSource.isInPresetInstallationMode()) {
+ int prevActiveInput = mSource.getActiveInput();
+ mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
+ if (prevActiveInput != mSource.getActiveInput()) {
// TODO: change port input here.
}
invokeCallback(HdmiCec.RESULT_SUCCESS);
// 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) {
+ if (mSource.getActiveSource() == getSourceAddress()) {
HdmiCecMessage activeSource =
- HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath);
+ HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(),
+ getSourcePath());
mService.sendCecCommand(activeSource);
- mService.updateActiveDevice(deviceLogicalAddress, routingPath);
+ mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
invokeCallback(HdmiCec.RESULT_SUCCESS);
} else {
- int activePath = mService.getActivePath();
- mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
+ int activePath = mSource.getActivePath();
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
routingPath, activePath));
// TODO: Start port select action here
- // PortSelectAction action = new PortSelectAction(mService, mSourceAddress,
- // activePath, mCallback);
+ // PortSelectAction action = new PortSelectAction(mService, getSourceAddress(),
+ // activePath, mCallback);
// mService.addActionAndStart(action);
}
}
}
+ private final int getSourceAddress() {
+ return mSource.getDeviceInfo().getLogicalAddress();
+ }
+
+ private final int getSourcePath() {
+ return mSource.getDeviceInfo().getPhysicalAddress();
+ }
+
private void invokeCallback(int result) {
if (mCallback == null) {
return;
/**
* Constructor.
*
- * @param service an instance of {@link HdmiControlService}.
- * @param sourceAddress a logical address which initiates this action
+ * @param source an instance of {@link HdmiCecLocalDevice}.
*/
- DeviceDiscoveryAction(HdmiControlService service, int sourceAddress,
- DeviceDiscoveryCallback callback) {
- super(service, sourceAddress);
+ DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) {
+ super(source);
mCallback = Preconditions.checkNotNull(callback);
}
mDevices.clear();
mState = STATE_WAITING_FOR_DEVICE_POLLING;
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
if (ackedAddress.isEmpty()) {
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, address));
+ sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_SET_OSD_NAME)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, address));
+ sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress, address));
+ sendCommand(
+ HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(), address));
addTimer(mState, TIMEOUT_MS);
}
private boolean mayProcessMessageIfCached(int address, int opcode) {
- HdmiCecMessage message = mService.getCecMessageCache().getMessage(address, opcode);
+ HdmiCecMessage message = getCecMessageCache().getMessage(address, opcode);
if (message != null) {
processCommand(message);
return true;
* limitations under the License.
*/
-import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
private final int mTargetAddress;
private final IHdmiControlCallback mCallback;
- static DevicePowerStatusAction create(HdmiControlService service, int sourceAddress,
+ static DevicePowerStatusAction create(HdmiCecLocalDevice source,
int targetAddress, IHdmiControlCallback callback) {
- if (service == null || callback == null) {
+ if (source == null || callback == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new DevicePowerStatusAction(service, sourceAddress, targetAddress, callback);
+ return new DevicePowerStatusAction(source, targetAddress, callback);
}
- private DevicePowerStatusAction(HdmiControlService service, int sourceAddress,
+ private DevicePowerStatusAction(HdmiCecLocalDevice localDevice,
int targetAddress, IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ super(localDevice);
mTargetAddress = targetAddress;
mCallback = callback;
}
}
private void queryDevicePowerStatus() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+ mTargetAddress));
}
@Override
package com.android.server.hdmi;
-import android.hardware.hdmi.IHdmiControlCallback;
-import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiTvClient;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
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 source {@link HdmiCecLocalDevice} instance
* @param target target logical device that will be a new active source
* @param callback callback object
*/
- public DeviceSelectAction(HdmiControlService service, int sourceAddress, int sourcePath,
+ public DeviceSelectAction(HdmiCecLocalDevice source,
HdmiCecDeviceInfo target, IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ super(source);
mCallback = callback;
- mSourcePath = sourcePath;
mTarget = target;
}
private void queryDevicePowerStatus() {
sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mSourceAddress, mTarget.getLogicalAddress()));
+ getSourceAddress(), mTarget.getLogicalAddress()));
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, TIMEOUT_MS);
}
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)
+ ActiveSourceHandler
+ .create(localDevice(), mCallback)
.process(cmd.getSource(), activePath);
finish();
return true;
private void sendSetStreamPath() {
sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(
- mSourceAddress, mTarget.getPhysicalAddress()));
+ getSourceAddress(), mTarget.getPhysicalAddress()));
mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
addTimer(mState, TIMEOUT_ACTIVE_SOURCE_MS);
}
private void sendRemoteKeyCommand(int keyCode) {
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
mTarget.getLogicalAddress(), keyCode));
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
mTarget.getLogicalAddress()));
}
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
+
+import java.util.List;
/**
* Encapsulates a sequence of CEC/MHL command exchange for a certain feature.
* of the object. All the actual action classes inherit FeatureAction.
*
* <p>More than one FeatureAction objects can be up and running simultaneously,
- * maintained by {@link HdmiControlService}. Each action is passed a new command
+ * maintained by {@link HdmiCecLocalDevice}. Each action is passed a new command
* arriving from the bus, and either consumes it if the command is what the action expects,
* or yields it to other action.
*
* Declared as package private, accessed by {@link HdmiControlService} only.
*/
abstract class FeatureAction {
-
private static final String TAG = "FeatureAction";
// Timer handler message used for timeout event
// Internal state indicating the progress of action.
protected int mState = STATE_NONE;
- protected final HdmiControlService mService;
-
- // Logical address of the device for which the feature action is taken. The commands
- // generated in an action all use this field as source address.
- protected final int mSourceAddress;
+ private final HdmiControlService mService;
+ private final HdmiCecLocalDevice mSource;
// Timer that manages timeout events.
protected ActionTimer mActionTimer;
- FeatureAction(HdmiControlService service, int sourceAddress) {
- mService = service;
- mSourceAddress = sourceAddress;
- mActionTimer = createActionTimer(service.getServiceLooper());
+ FeatureAction(HdmiCecLocalDevice source) {
+ mSource = source;
+ mService = mSource.getService();
+ mActionTimer = createActionTimer(mService.getServiceLooper());
}
@VisibleForTesting
mService.sendCecCommand(cmd, callback);
}
+ protected final void addAndStartAction(FeatureAction action) {
+ mSource.addAndStartAction(action);
+ }
+
+ protected final <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
+ return mSource.getActions(clazz);
+ }
+
+ protected final HdmiCecMessageCache getCecMessageCache() {
+ return mSource.getCecMessageCache();
+ }
+
+ /**
+ * Remove the action from the action queue. This is called after the action finishes
+ * its role.
+ *
+ * @param action
+ */
+ protected final void removeAction(FeatureAction action) {
+ mSource.removeAction(action);
+ }
+
+ protected final <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+ mSource.removeActionExcept(clazz, null);
+ }
+
+ protected final <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+ final FeatureAction exception) {
+ mSource.removeActionExcept(clazz, exception);
+ }
+
+ protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
+ int retryCount) {
+ mService.pollDevices(callback, pickStrategy, retryCount);
+ }
+
/**
* Clean up action's state.
*
removeAction(this);
}
- /**
- * Remove the action from the action queue. This is called after the action finishes
- * its role.
- *
- * @param action
- */
- private void removeAction(FeatureAction action) {
- mService.removeAction(action);
+ protected final HdmiCecLocalDevice localDevice() {
+ return mSource;
+ }
+
+ protected final HdmiCecLocalDevicePlayback playback() {
+ return (HdmiCecLocalDevicePlayback) mSource;
+ }
+
+ protected final HdmiCecLocalDeviceTv tv() {
+ return (HdmiCecLocalDeviceTv) mSource;
+ }
+
+ protected final int getSourceAddress() {
+ return mSource.getDeviceInfo().getLogicalAddress();
+ }
+
+ protected final int getSourcePath() {
+ return mSource.getDeviceInfo().getPhysicalAddress();
}
}
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCec;
-import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.Handler;
private HdmiControlService mService;
- // Map-like container of all cec devices including local ones.
- // 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. Device type is used for key.
private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
return body;
}
- /**
- * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
- * logical address as new device info's.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
- * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
- * that has the same logical address as new one has.
- */
- HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
- assertRunOnServiceThread();
- HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
- if (oldDeviceInfo != null) {
- removeDeviceInfo(deviceInfo.getLogicalAddress());
- }
- mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
- return oldDeviceInfo;
- }
-
- /**
- * Remove a device info corresponding to the given {@code logicalAddress}.
- * It returns removed {@link HdmiCecDeviceInfo} if exists.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param logicalAddress logical address of device to be removed
- * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
- */
- HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
- assertRunOnServiceThread();
- HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
- if (deviceInfo != null) {
- mDeviceInfos.remove(logicalAddress);
- }
- return deviceInfo;
- }
-
- /**
- * Clear all device info.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- */
- void clearDeviceInfoList() {
- assertRunOnServiceThread();
- mDeviceInfos.clear();
- }
-
- /**
- * Return a list of all {@link HdmiCecDeviceInfo}.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param includeLocalDevice whether to add local device or not
- */
- List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
- assertRunOnServiceThread();
- if (includeLocalDevice) {
- return sparseArrayToList(mDeviceInfos);
- } else {
- ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
- for (int i = 0; i < mDeviceInfos.size(); ++i) {
- HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
- if (mRemoteDeviceAddressPredicate.apply(info.getLogicalAddress())) {
- infoList.add(info);
- }
- }
- return infoList;
- }
- }
-
- /**
- * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param logicalAddress logical address to be retrieved
- * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
- * Returns null if no logical address matched
- */
- HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
- assertRunOnServiceThread();
- return mDeviceInfos.get(logicalAddress);
- }
HdmiPortInfo[] getPortInfos() {
return nativeGetPortInfos(mNativePtr);
*/
List<HdmiCecLocalDevice> getLocalDeviceList() {
assertRunOnServiceThread();
- return sparseArrayToList(mLocalDevices);
+ return HdmiUtils.sparseArrayToList(mLocalDevices);
}
private List<Integer> pickPollCandidates(int pickStrategy) {
return pollingCandidates;
}
- private static <T> List<T> sparseArrayToList(SparseArray<T> array) {
- ArrayList<T> list = new ArrayList<>();
- for (int i = 0; i < array.size(); ++i) {
- list.add(array.valueAt(i));
- }
- return list;
- }
-
private boolean isAllocatedLocalDeviceAddress(int address) {
for (int i = 0; i < mLocalDevices.size(); ++i) {
if (mLocalDevices.valueAt(i).isAddressOf(address)) {
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.os.Looper;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
/**
* Class that models a logical CEC device hosted in this system. Handles initialization,
* CEC commands that call for actions customized per device type.
protected int mPreferredAddress;
protected HdmiCecDeviceInfo mDeviceInfo;
+ // 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. Note that we don't keep
+ // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
+ @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;
+
+ protected final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
+ protected final Object mLock;
+
+ // A collection of FeatureAction.
+ // Note that access to this collection should happen in service thread.
+ private final LinkedList<FeatureAction> mActions = new LinkedList<>();
+
protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
mService = service;
mDeviceType = deviceType;
mAddress = HdmiCec.ADDR_UNREGISTERED;
+ mLock = service.getServiceLock();
+
+ // TODO: Get control flag from persistent storage
+ mInputChangeEnabled = true;
}
// Factory method that returns HdmiCecLocalDevice of corresponding type.
* @return true if consumed a message; otherwise, return false.
*/
final boolean dispatchMessage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
int dest = message.getDestination();
if (dest != mAddress && dest != HdmiCec.ADDR_BROADCAST) {
return false;
}
+ // Cache incoming message. Note that it caches only white-listed one.
+ mCecMessageCache.cacheMessage(message);
return onMessage(message);
}
protected final boolean onMessage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
+ if (dispatchMessageToAction(message)) {
+ return true;
+ }
switch (message.getOpcode()) {
case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
return handleGetMenuLanguage(message);
return handleGetCecVersion(message);
case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
return handleReportPhysicalAddress(message);
+ case HdmiCec.MESSAGE_INITIATE_ARC:
+ return handleInitiateArc(message);
+ case HdmiCec.MESSAGE_TERMINATE_ARC:
+ return handleTerminateArc(message);
+ case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ return handleSetSystemAudioMode(message);
+ case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+ return handleSystemAudioModeStatus(message);
default:
return false;
}
}
+ private boolean dispatchMessageToAction(HdmiCecMessage message) {
+ for (FeatureAction action : mActions) {
+ if (action.processCommand(message)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
protected boolean handleGivePhysicalAddress() {
+ assertRunOnServiceThread();
+
int physicalAddress = mService.getPhysicalAddress();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
mAddress, physicalAddress, mDeviceType);
}
protected boolean handleGiveDeviceVendorId() {
+ assertRunOnServiceThread();
+
int vendorId = mService.getVendorId();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
mAddress, vendorId);
}
protected boolean handleGetCecVersion(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
int version = mService.getCecVersion();
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
message.getSource(), version);
}
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
mService.sendCecCommand(
HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
}
protected boolean handleGiveOsdName(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
// Note that since this method is called after logical address allocation is done,
// mDeviceInfo should not be null.
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
return false;
}
+ protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleTerminateArc(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleInitiateArc(HdmiCecMessage message) {
+ return false;
+ }
+
final void handleAddressAllocated(int logicalAddress) {
+ assertRunOnServiceThread();
+
mAddress = mPreferredAddress = logicalAddress;
onAddressAllocated(logicalAddress);
}
HdmiCecDeviceInfo getDeviceInfo() {
+ assertRunOnServiceThread();
return mDeviceInfo;
}
void setDeviceInfo(HdmiCecDeviceInfo info) {
+ assertRunOnServiceThread();
mDeviceInfo = info;
}
// Returns true if the logical address is same as the argument.
boolean isAddressOf(int addr) {
+ assertRunOnServiceThread();
return addr == mAddress;
}
// Resets the logical address to unregistered(15), meaning the logical device is invalid.
void clearAddress() {
+ assertRunOnServiceThread();
mAddress = HdmiCec.ADDR_UNREGISTERED;
}
void setPreferredAddress(int addr) {
+ assertRunOnServiceThread();
mPreferredAddress = addr;
}
int getPreferredAddress() {
+ assertRunOnServiceThread();
return mPreferredAddress;
}
+
+ void addAndStartAction(final FeatureAction action) {
+ assertRunOnServiceThread();
+ mActions.add(action);
+ action.start();
+ }
+
+ // See if we have an action of a given type in progress.
+ <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
+ assertRunOnServiceThread();
+ for (FeatureAction action : mActions) {
+ if (action.getClass().equals(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Returns all actions matched with given class type.
+ <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
+ assertRunOnServiceThread();
+ ArrayList<T> actions = new ArrayList<>();
+ for (FeatureAction action : mActions) {
+ if (action.getClass().equals(clazz)) {
+ actions.add((T) action);
+ }
+ }
+ return actions;
+ }
+
+ /**
+ * Remove the given {@link FeatureAction} object from the action queue.
+ *
+ * @param action {@link FeatureAction} to remove
+ */
+ void removeAction(final FeatureAction action) {
+ assertRunOnServiceThread();
+ mActions.remove(action);
+ }
+
+ // Remove all actions matched with the given Class type.
+ <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+ removeActionExcept(clazz, null);
+ }
+
+ // Remove all actions matched with the given Class type besides |exception|.
+ <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+ final FeatureAction exception) {
+ assertRunOnServiceThread();
+ Iterator<FeatureAction> iter = mActions.iterator();
+ while (iter.hasNext()) {
+ FeatureAction action = iter.next();
+ if (action != exception && action.getClass().equals(clazz)) {
+ action.clear();
+ mActions.remove(action);
+ }
+ }
+ }
+
+ protected void assertRunOnServiceThread() {
+ if (Looper.myLooper() != mService.getServiceLooper()) {
+ throw new IllegalStateException("Should run on service thread.");
+ }
+ }
+
+ /**
+ * Called when a hot-plug event issued.
+ *
+ * @param portId id of port where a hot-plug event happened
+ * @param connected whether to connected or not on the event
+ */
+ void onHotplug(int portId, boolean connected) {
+ }
+
+ final HdmiControlService getService() {
+ return mService;
+ }
+
+ final boolean isConnectedToArcPort(int path) {
+ return mService.isConnectedToArcPort(path);
+ }
+
+ int getActiveSource() {
+ synchronized (mLock) {
+ return mActiveSource;
+ }
+ }
+
+ /**
+ * Returns the active routing path.
+ */
+ int getActivePath() {
+ synchronized (mLock) {
+ return mActiveRoutingPath;
+ }
+ }
+
+ /**
+ * Returns the ID of the active HDMI port. The active input is the port that has the active
+ * routing path connected directly or indirectly under the device hierarchy.
+ */
+ int getActiveInput() {
+ synchronized (mLock) {
+ return mService.pathToPortId(mActiveRoutingPath);
+ }
+ }
+
+ void updateActiveDevice(int logicalAddress, int physicalAddress) {
+ synchronized (mLock) {
+ mActiveSource = logicalAddress;
+ mActiveRoutingPath = physicalAddress;
+ }
+ }
+
+ void setInputChangeEnabled(boolean enabled) {
+ synchronized (mLock) {
+ mInputChangeEnabled = enabled;
+ }
+ }
+
+ boolean isInPresetInstallationMode() {
+ synchronized (mLock) {
+ return !mInputChangeEnabled;
+ }
+ }
+
+ /**
+ * Whether the given path is located in the tail of current active path.
+ *
+ * @param path to be tested
+ * @return true if the given path is located in the tail of current active path; otherwise,
+ * false
+ */
+ // TODO: move this to local device tv.
+ boolean isTailOfActivePath(int path) {
+ synchronized (mLock) {
+ // If active routing path is internal source, return false.
+ if (mActiveRoutingPath == 0) {
+ return false;
+ }
+ for (int i = 12; i >= 0; i -= 4) {
+ int curActivePath = (mActiveRoutingPath >> i) & 0xF;
+ if (curActivePath == 0) {
+ return true;
+ } else {
+ int curPath = (path >> i) & 0xF;
+ if (curPath != curActivePath) {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ HdmiCecMessageCache getCecMessageCache() {
+ assertRunOnServiceThread();
+ return mCecMessageCache;
+ }
+
+ int pathToPortId(int newPath) {
+ assertRunOnServiceThread();
+ return mService.pathToPortId(newPath);
+ }
}
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.RemoteException;
+import android.util.Slog;
/**
* Represent a logical device of type Playback residing in Android system.
*/
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
+ private static final String TAG = "HdmiCecLocalDevicePlayback";
HdmiCecLocalDevicePlayback(HdmiControlService service) {
super(service, HdmiCec.DEVICE_PLAYBACK);
mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
mAddress, mService.getPhysicalAddress(), mDeviceType));
}
+
+ void oneTouchPlay(IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ if (hasAction(OneTouchPlayAction.class)) {
+ Slog.w(TAG, "oneTouchPlay already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ 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, HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate oneTouchPlay");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
+ }
+
+ void queryDisplayStatus(IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ if (hasAction(DevicePowerStatusAction.class)) {
+ Slog.w(TAG, "queryDisplayStatus already in progress");
+ invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+ return;
+ }
+ DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
+ HdmiCec.ADDR_TV, callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate queryDisplayStatus");
+ invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
+ }
+
+ private void invokeCallback(IHdmiControlCallback callback, int result) {
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Invoking callback failed:" + e);
+ }
+ }
+
+ @Override
+ void onHotplug(int portId, boolean connected) {
+ // TODO: clear devices connected to the given port id.
+ mCecMessageCache.flushAll();
+ }
}
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
private static final String TAG = "HdmiCecLocalDeviceTv";
+ // Whether ARC is "enabled" or not.
+ @GuardedBy("mLock")
+ private boolean mArcStatusEnabled = false;
+
+ @GuardedBy("mLock")
+ // Whether SystemAudioMode is "On" or not.
+ private boolean mSystemAudioMode;
+
+ // Map-like container of all cec devices including local ones.
+ // A logical address of device is used as key of container.
+ private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
+
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiCec.DEVICE_TV);
}
@Override
protected void onAddressAllocated(int logicalAddress) {
+ assertRunOnServiceThread();
// TODO: vendor-specific initialization here.
mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
* @param callback callback object to report the result with
*/
void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
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));
+ removeAction(DeviceSelectAction.class);
+ addAndStartAction(new DeviceSelectAction(this, targetDevice, callback));
}
private static void invokeCallback(IHdmiControlCallback callback, int result) {
@Override
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
mAddress, Locale.getDefault().getISO3Language());
// TODO: figure out how to handle failed to get language code.
@Override
protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
+ assertRunOnServiceThread();
// Ignore if [Device Discovery Action] is going on.
- if (mService.hasAction(DeviceDiscoveryAction.class)) {
+ if (hasAction(DeviceDiscoveryAction.class)) {
Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
+ "because Device Discovery Action is on-going:" + message);
return true;
// If it is a new device and connected to the tail of active path,
// it's required to change routing path.
- boolean requireRoutingChange = !mService.isInDeviceList(physicalAddress, logicalAddress)
- && mService.isTailOfActivePath(physicalAddress);
- mService.addAndStartAction(new NewDeviceAction(mService,
- mAddress, message.getSource(), physicalAddress,
+ boolean requireRoutingChange = !isInDeviceList(physicalAddress, logicalAddress)
+ && isTailOfActivePath(physicalAddress);
+ addAndStartAction(new NewDeviceAction(this, message.getSource(), physicalAddress,
requireRoutingChange));
return true;
}
@Override
protected boolean handleVendorSpecificCommand(HdmiCecMessage message) {
+ assertRunOnServiceThread();
List<VendorSpecificAction> actions = Collections.emptyList();
// TODO: Call mService.getActions(VendorSpecificAction.class) to get all the actions.
}
private void launchDeviceDiscovery() {
- mService.clearAllDeviceInfo();
- // TODO: Move the following callback to HdmiLocalDeviceTv.
- DeviceDiscoveryAction action = new DeviceDiscoveryAction(mService, mAddress,
+ assertRunOnServiceThread();
+ clearDeviceInfoList();
+ DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
new DeviceDiscoveryCallback() {
@Override
public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
for (HdmiCecDeviceInfo info : deviceInfos) {
- mService.addCecDevice(info);
+ addCecDevice(info);
}
// Since we removed all devices when it's start and
// device discovery action does not poll local devices,
// we should put device info of local device manually here
for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
- mService.addCecDevice(device.getDeviceInfo());
+ addCecDevice(device.getDeviceInfo());
}
- mService.addAndStartAction(new HotplugDetectionAction(mService,
- mAddress));
+ addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
}
});
- mService.addAndStartAction(action);
+ addAndStartAction(action);
+ }
+
+ // Clear all device info.
+ private void clearDeviceInfoList() {
+ assertRunOnServiceThread();
+ mDeviceInfos.clear();
+ }
+
+ void setSystemAudioMode(boolean on) {
+ synchronized (mLock) {
+ if (on != mSystemAudioMode) {
+ mSystemAudioMode = on;
+ // TODO: Need to set the preference for SystemAudioMode.
+ // TODO: Need to handle the notification of changing the mode and
+ // to identify the notification should be handled in the service or TvSettings.
+ }
+ }
+ }
+
+ boolean getSystemAudioMode() {
+ synchronized (mLock) {
+ assertRunOnServiceThread();
+ return mSystemAudioMode;
+ }
+ }
+
+ /**
+ * Change ARC status into the given {@code enabled} status.
+ *
+ * @return {@code true} if ARC was in "Enabled" status
+ */
+ boolean setArcStatus(boolean enabled) {
+ synchronized (mLock) {
+ boolean oldStatus = mArcStatusEnabled;
+ // 1. Enable/disable ARC circuit.
+ mService.setAudioReturnChannel(enabled);
+
+ // TODO: notify arc mode change to AudioManager.
+
+ // 2. Update arc status;
+ mArcStatusEnabled = enabled;
+ return oldStatus;
+ }
+ }
+
+ /**
+ * Returns whether ARC is enabled or not.
+ */
+ boolean getArcStatus() {
+ synchronized (mLock) {
+ return mArcStatusEnabled;
+ }
+ }
+
+ void setAudioStatus(boolean mute, int volume) {
+ mService.setAudioStatus(mute, volume);
+ }
+
+ @Override
+ protected boolean handleInitiateArc(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // 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.getSource(), true);
+ addAndStartAction(action);
+ return true;
+ }
+
+ @Override
+ protected boolean handleTerminateArc(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // 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.getSource(), false);
+ addAndStartAction(action);
+ return true;
+ }
+
+ @Override
+ protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ if (!isMessageForSystemAudio(message)) {
+ return false;
+ }
+ SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
+ message.getSource(), HdmiUtils.parseCommandParamSystemAudioStatus(message));
+ addAndStartAction(action);
+ return true;
+ }
+
+ @Override
+ protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ if (!isMessageForSystemAudio(message)) {
+ return false;
+ }
+ setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
+ return true;
+ }
+
+ private boolean isMessageForSystemAudio(HdmiCecMessage message) {
+ if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
+ || message.getDestination() != HdmiCec.ADDR_TV
+ || getAvrDeviceInfo() == null) {
+ Slog.w(TAG, "Skip abnormal CecMessage: " + message);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
+ * logical address as new device info's.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
+ * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
+ * that has the same logical address as new one has.
+ */
+ HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
+ assertRunOnServiceThread();
+ HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
+ if (oldDeviceInfo != null) {
+ removeDeviceInfo(deviceInfo.getLogicalAddress());
+ }
+ mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
+ return oldDeviceInfo;
+ }
+
+ /**
+ * Remove a device info corresponding to the given {@code logicalAddress}.
+ * It returns removed {@link HdmiCecDeviceInfo} if exists.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param logicalAddress logical address of device to be removed
+ * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
+ */
+ HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
+ HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
+ if (deviceInfo != null) {
+ mDeviceInfos.remove(logicalAddress);
+ }
+ return deviceInfo;
+ }
+
+ /**
+ * Return a list of all {@link HdmiCecDeviceInfo}.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ */
+ List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includelLocalDevice) {
+ assertRunOnServiceThread();
+ if (includelLocalDevice) {
+ return HdmiUtils.sparseArrayToList(mDeviceInfos);
+ } else {
+
+ ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
+ for (int i = 0; i < mDeviceInfos.size(); ++i) {
+ HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
+ if (!isLocalDeviceAddress(info.getLogicalAddress())) {
+ infoList.add(info);
+ }
+ }
+ return infoList;
+ }
+ }
+
+ private boolean isLocalDeviceAddress(int address) {
+ assertRunOnServiceThread();
+ for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
+ if (device.isAddressOf(address)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param logicalAddress logical address to be retrieved
+ * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
+ * Returns null if no logical address matched
+ */
+ HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
+ return mDeviceInfos.get(logicalAddress);
+ }
+
+ HdmiCecDeviceInfo getAvrDeviceInfo() {
+ assertRunOnServiceThread();
+ return getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
+ }
+
+ /**
+ * Called when a device is newly added or a new device is detected.
+ *
+ * @param info device info of a new device.
+ */
+ final void addCecDevice(HdmiCecDeviceInfo info) {
+ assertRunOnServiceThread();
+ addDeviceInfo(info);
+
+ // TODO: announce new device detection.
+ }
+
+ /**
+ * Called when a device is removed or removal of device is detected.
+ *
+ * @param address a logical address of a device to be removed
+ */
+ final void removeCecDevice(int address) {
+ assertRunOnServiceThread();
+ removeDeviceInfo(address);
+ mCecMessageCache.flushMessagesFrom(address);
+
+ // TODO: announce a device removal.
+ }
+
+ /**
+ * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches
+ * the given routing path. CEC devices use routing path for its physical address to
+ * describe the hierarchy of the devices in the network.
+ *
+ * @param path routing path or physical address
+ * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null
+ */
+ final HdmiCecDeviceInfo getDeviceInfoByPath(int path) {
+ assertRunOnServiceThread();
+ for (HdmiCecDeviceInfo info : getDeviceInfoList(false)) {
+ if (info.getPhysicalAddress() == path) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Whether a device of the specified physical address and logical address exists
+ * in a device info list. However, both are minimal condition and it could
+ * be different device from the original one.
+ *
+ * @param physicalAddress physical address of a device to be searched
+ * @param logicalAddress logical address of a device to be searched
+ * @return true if exist; otherwise false
+ */
+ boolean isInDeviceList(int physicalAddress, int logicalAddress) {
+ assertRunOnServiceThread();
+ HdmiCecDeviceInfo device = getDeviceInfo(logicalAddress);
+ if (device == null) {
+ return false;
+ }
+ return device.getPhysicalAddress() == physicalAddress;
+ }
+
+ @Override
+ void onHotplug(int portNo, boolean connected) {
+ assertRunOnServiceThread();
+ // TODO: delegate onHotplug event to each local device.
+
+ // Tv device will have permanent HotplugDetectionAction.
+ List<HotplugDetectionAction> hotplugActions = getActions(HotplugDetectionAction.class);
+ if (!hotplugActions.isEmpty()) {
+ // Note that hotplug action is single action running on a machine.
+ // "pollAllDevicesNow" cleans up timer and start poll action immediately.
+ hotplugActions.get(0).pollAllDevicesNow();
+ }
}
}
import android.util.SparseArray;
import android.util.SparseIntArray;
-import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
/**
// 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<>();
-
// Used to synchronize the access to the service.
private final Object mLock = new Object();
// Handler running on service thread. It's used to run a task in service thread.
private final Handler mHandler = new Handler();
- private final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
-
@Nullable
private HdmiCecController mCecController;
// from being modified.
private List<HdmiPortInfo> mPortInfo;
- // 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. Note that we don't keep
- // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
- @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.
- private boolean mArcStatusEnabled = false;
-
- @GuardedBy("mLock")
- // Whether SystemAudioMode is "On" or not.
- private boolean mSystemAudioMode;
-
public HdmiControlService(Context context) {
super(context);
mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
- // TODO: Get control flag from persistent storage
- mInputChangeEnabled = true;
}
@Override
return mHandler.getLooper();
}
- int getActiveSource() {
- synchronized (mLock) {
- return mActiveSource;
- }
- }
-
- /**
- * Returns the active routing path.
- */
- int getActivePath() {
- synchronized (mLock) {
- return mActiveRoutingPath;
- }
- }
-
- /**
- * Returns the ID of the active HDMI port. The active input is the port that has the active
- * routing path connected directly or indirectly under the device hierarchy.
- */
- int getActiveInput() {
- synchronized (mLock) {
- return pathToPortId(mActiveRoutingPath);
- }
- }
-
- 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.
*/
HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
assertRunOnServiceThread();
- return mCecController.getDeviceInfo(logicalAddress);
+ HdmiCecLocalDeviceTv tv = tv();
+ if (tv == null) {
+ return null;
+ }
+ return tv.getDeviceInfo(logicalAddress);
}
/**
}
/**
- * Returns a list of {@link HdmiCecDeviceInfo}.
- *
- * @param includeLocalDevice whether to include local devices
- */
- List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
- assertRunOnServiceThread();
- return mCecController.getDeviceInfoList(includeLocalDevice);
- }
-
- /**
- * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches
- * the given routing path. CEC devices use routing path for its physical address to
- * describe the hierarchy of the devices in the network.
- *
- * @param path routing path or physical address
- * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null
- */
- HdmiCecDeviceInfo getDeviceInfoByPath(int path) {
- assertRunOnServiceThread();
- for (HdmiCecDeviceInfo info : mCecController.getDeviceInfoList(false)) {
- if (info.getPhysicalAddress() == path) {
- return info;
- }
- }
- return null;
- }
-
- /**
- * Add and start a new {@link FeatureAction} to the action queue.
- *
- * @param action {@link FeatureAction} to add and start
- */
- 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();
- }
- });
- }
-
- void setSystemAudioMode(boolean on) {
- synchronized (mLock) {
- if (on != mSystemAudioMode) {
- mSystemAudioMode = on;
- // TODO: Need to set the preference for SystemAudioMode.
- // TODO: Need to handle the notification of changing the mode and
- // to identify the notification should be handled in the service or TvSettings.
- }
- }
- }
-
- boolean getSystemAudioMode() {
- synchronized (mLock) {
- return mSystemAudioMode;
- }
- }
-
- /**
* Whether a device of the specified physical address is connected to ARC enabled port.
*/
boolean isConnectedToArcPort(int physicalAddress) {
return false;
}
- // See if we have an action of a given type in progress.
- <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
- for (FeatureAction action : mActions) {
- if (action.getClass().equals(clazz)) {
- return true;
- }
- }
- return false;
- }
-
- // Returns all actions matched with given class type.
- <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
- ArrayList<T> actions = new ArrayList<>();
- for (FeatureAction action : mActions) {
- if (action.getClass().equals(clazz)) {
- actions.add((T) action);
- }
- }
- return actions;
- }
-
- /**
- * Remove the given {@link FeatureAction} object from the action queue.
- *
- * @param action {@link FeatureAction} to remove
- */
- void removeAction(final FeatureAction action) {
- assertRunOnServiceThread();
- mActions.remove(action);
- }
-
- // Remove all actions matched with the given Class type.
- <T extends FeatureAction> void removeAction(final Class<T> clazz) {
- removeActionExcept(clazz, null);
- }
-
- // Remove all actions matched with the given Class type besides |exception|.
- <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
- final FeatureAction exception) {
- assertRunOnServiceThread();
- Iterator<FeatureAction> iter = mActions.iterator();
- while (iter.hasNext()) {
- FeatureAction action = iter.next();
- if (action != exception && action.getClass().equals(clazz)) {
- action.clear();
- mActions.remove(action);
- }
- }
- }
-
- private void runOnServiceThread(Runnable runnable) {
+ 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) {
- assertRunOnServiceThread();
- synchronized (mLock) {
- boolean oldStatus = mArcStatusEnabled;
- // 1. Enable/disable ARC circuit.
- mCecController.setAudioReturnChannel(enabled);
-
- // TODO: notify arc mode change to AudioManager.
-
- // 2. Update arc status;
- mArcStatusEnabled = enabled;
- return oldStatus;
- }
- }
-
- /**
- * Returns whether ARC is enabled or not.
- */
- boolean getArcStatus() {
- synchronized (mLock) {
- return mArcStatusEnabled;
- }
- }
-
- /**
* Transmit a CEC command to CEC bus.
*
* @param command CEC command to send out
}
boolean handleCecCommand(HdmiCecMessage message) {
- // Cache incoming message. Note that it caches only white-listed one.
- mCecMessageCache.cacheMessage(message);
-
- // Commands that queries system information replies directly instead
- // of creating FeatureAction because they are state-less.
- // TODO: move the leftover message to local device.
- switch (message.getOpcode()) {
- case HdmiCec.MESSAGE_INITIATE_ARC:
- handleInitiateArc(message);
- return true;
- case HdmiCec.MESSAGE_TERMINATE_ARC:
- handleTerminateArc(message);
- return true;
- case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
- handleSetSystemAudioMode(message);
- return true;
- case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
- handleSystemAudioModeStatus(message);
- return true;
- default:
- if (dispatchMessageToAction(message)) {
- return true;
- }
- break;
- }
-
return dispatchMessageToLocalDevice(message);
}
- private boolean dispatchMessageToAction(HdmiCecMessage message) {
- for (FeatureAction action : mActions) {
- if (action.processCommand(message)) {
- return true;
- }
- }
- return false;
+ void setAudioReturnChannel(boolean enabled) {
+ mCecController.setAudioReturnChannel(enabled);
}
private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
- if (device.dispatchMessage(message)) {
+ if (device.dispatchMessage(message)
+ && message.getDestination() != HdmiCec.ADDR_BROADCAST) {
return true;
}
}
*/
void onHotplug(int portNo, boolean connected) {
assertRunOnServiceThread();
- // TODO: delegate onHotplug event to each local device.
-
- // Tv device will have permanent HotplugDetectionAction.
- List<HotplugDetectionAction> hotplugActions = getActions(HotplugDetectionAction.class);
- if (!hotplugActions.isEmpty()) {
- // Note that hotplug action is single action running on a machine.
- // "pollAllDevicesNow" cleans up timer and start poll action immediately.
- hotplugActions.get(0).pollAllDevicesNow();
+
+ for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ device.onHotplug(portNo, connected);
}
announceHotplugEvent(portNo, connected);
return strategy | iterationStrategy;
}
- void clearAllDeviceInfo() {
- assertRunOnServiceThread();
- mCecController.clearDeviceInfoList();
- }
-
List<HdmiCecLocalDevice> getAllLocalDevices() {
assertRunOnServiceThread();
return mCecController.getLocalDeviceList();
}
- /**
- * Whether a device of the specified physical address and logical address exists
- * in a device info list. However, both are minimal condition and it could
- * be different device from the original one.
- *
- * @param physicalAddress physical address of a device to be searched
- * @param logicalAddress logical address of a device to be searched
- * @return true if exist; otherwise false
- */
- boolean isInDeviceList(int physicalAddress, int logicalAddress) {
- assertRunOnServiceThread();
- HdmiCecDeviceInfo device = mCecController.getDeviceInfo(logicalAddress);
- if (device == null) {
- return false;
- }
- return device.getPhysicalAddress() == physicalAddress;
+ Object getServiceLock() {
+ return mLock;
+ }
+
+ void setAudioStatus(boolean mute, int volume) {
+ // TODO: Hook up with AudioManager.
}
private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
getPhysicalAddress(), deviceType, getVendorId(), displayName);
}
- 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 handleSetSystemAudioMode(HdmiCecMessage message) {
- if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) {
- return;
- }
- SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
- message.getDestination(), message.getSource(),
- HdmiUtils.parseCommandParamSystemAudioStatus(message));
- addAndStartAction(action);
- }
-
- private void handleSystemAudioModeStatus(HdmiCecMessage message) {
- if (!isMessageForSystemAudio(message)) {
- return;
- }
- setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
- }
-
- private boolean isMessageForSystemAudio(HdmiCecMessage message) {
- if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
- || message.getDestination() != HdmiCec.ADDR_TV
- || getAvrDeviceInfo() == null) {
- Slog.w(TAG, "Skip abnormal CecMessage: " + message);
- return false;
- }
- return true;
- }
-
// Record class that monitors the event of the caller of being killed. Used to clean up
// the listener list and record list accordingly.
private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
runOnServiceThread(new Runnable() {
@Override
public void run() {
- HdmiCecLocalDeviceTv tv =
- (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
+ HdmiCecLocalDeviceTv tv = tv();
if (tv == null) {
Slog.w(TAG, "Local playback device not available");
invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
}
}
- private void oneTouchPlay(IHdmiControlCallback callback) {
- 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);
+ private void oneTouchPlay(final IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ HdmiCecLocalDevicePlayback source = 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);
+ source.oneTouchPlay(callback);
}
- private void queryDisplayStatus(IHdmiControlCallback callback) {
- 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);
+ private void queryDisplayStatus(final IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ HdmiCecLocalDevicePlayback source = 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);
+ source.queryDisplayStatus(callback);
}
private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
}
}
- HdmiCecDeviceInfo getAvrDeviceInfo() {
- return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
- }
-
- void setAudioStatus(boolean mute, int volume) {
- // TODO: Hook up with AudioManager.
- }
-
- boolean isInPresetInstallationMode() {
- synchronized (mLock) {
- return !mInputChangeEnabled;
- }
- }
-
- /**
- * Called when a device is newly added or a new device is detected.
- *
- * @param info device info of a new device.
- */
- void addCecDevice(HdmiCecDeviceInfo info) {
- mCecController.addDeviceInfo(info);
-
- // TODO: announce new device detection.
- }
-
- /**
- * Called when a device is removed or removal of device is detected.
- *
- * @param address a logical address of a device to be removed
- */
- void removeCecDevice(int address) {
- mCecController.removeDeviceInfo(address);
- mCecMessageCache.flushMessagesFrom(address);
-
- // TODO: announce a device removal.
- }
-
private void announceHotplugEvent(int portNo, boolean connected) {
HdmiHotplugEvent event = new HdmiHotplugEvent(portNo, connected);
synchronized (mLock) {
}
}
- HdmiCecMessageCache getCecMessageCache() {
- return mCecMessageCache;
- }
-
private static boolean hasSameTopPort(int path1, int path2) {
return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK)
== (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK);
}
- /**
- * Whether the given path is located in the tail of current active path.
- *
- * @param path to be tested
- * @return true if the given path is located in the tail of current active path; otherwise,
- * false
- */
- // TODO: move this to local device tv.
- boolean isTailOfActivePath(int path) {
- // If active routing path is internal source, return false.
- if (mActiveRoutingPath == 0) {
- return false;
- }
- for (int i = 12; i >= 0; i -= 4) {
- int curActivePath = (mActiveRoutingPath >> i) & 0xF;
- if (curActivePath == 0) {
- return true;
- } else {
- int curPath = (path >> i) & 0xF;
- if (curPath != curActivePath) {
- return false;
- }
- }
- }
- return false;
+ private HdmiCecLocalDeviceTv tv() {
+ return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
+ }
+
+ private HdmiCecLocalDevicePlayback playback() {
+ return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
}
}
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
import android.util.Slog;
+import android.util.SparseArray;
import java.util.ArrayList;
import java.util.Collections;
static int threeBytesToInt(byte[] data) {
return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
}
+
+ static <T> List<T> sparseArrayToList(SparseArray<T> array) {
+ ArrayList<T> list = new ArrayList<>();
+ for (int i = 0; i < array.size(); ++i) {
+ list.add(array.valueAt(i));
+ }
+ return list;
+ }
+
}
/**
* Constructor
*
- * @param service instance of {@link HdmiControlService}
- * @param sourceAddress logical address of a device that initiate this action
+ * @param source {@link HdmiCecLocalDevice} instance
*/
- HotplugDetectionAction(HdmiControlService service, int sourceAddress) {
- super(service, sourceAddress);
+ HotplugDetectionAction(HdmiCecLocalDevice source) {
+ super(source);
}
@Override
if (mTimeoutCount == 0) {
pollAllDevices();
} else {
- if (mService.getSystemAudioMode()) {
+ if (tv().getSystemAudioMode()) {
pollAudioSystem();
}
}
private void pollAllDevices() {
Slog.v(TAG, "Poll all devices.");
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
checkHotplug(ackedAddress, false);
private void pollAudioSystem() {
Slog.v(TAG, "Poll audio system.");
- mService.pollDevices(new DevicePollingCallback() {
+ pollDevices(new DevicePollingCallback() {
@Override
public void onPollingFinished(List<Integer> ackedAddress) {
checkHotplug(ackedAddress, true);
}
private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
- BitSet currentInfos = infoListToBitSet(mService.getDeviceInfoList(false), audioOnly);
+ BitSet currentInfos = infoListToBitSet(tv().getDeviceInfoList(false), audioOnly);
BitSet polledResult = addressListToBitSet(ackedAddress);
// At first, check removed devices.
private void addDevice(int addedAddress) {
// Send <Give Physical Address>.
- sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, addedAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(),
+ addedAddress));
}
private void removeDevice(int removedAddress) {
mayCancelOneTouchRecord(removedAddress);
mayDisableSystemAudioAndARC(removedAddress);
- mService.removeCecDevice(removedAddress);
+ tv().removeCecDevice(removedAddress);
}
private void mayChangeRoutingPath(int address) {
}
private void mayCancelDeviceSelect(int address) {
- List<DeviceSelectAction> actions = mService.getActions(DeviceSelectAction.class);
+ List<DeviceSelectAction> actions = getActions(DeviceSelectAction.class);
if (actions.isEmpty()) {
return;
}
// Should ave only one Device Select Action
DeviceSelectAction action = actions.get(0);
if (action.getTargetAddress() == address) {
- mService.removeAction(DeviceSelectAction.class);
+ removeAction(DeviceSelectAction.class);
}
}
}
// Turn off system audio mode.
- mService.setSystemAudioMode(false);
- if (mService.getArcStatus()) {
- mService.addAndStartAction(
- new RequestArcTerminationAction(mService, mSourceAddress, address));
+ tv().setSystemAudioMode(false);
+ if (tv().getArcStatus()) {
+ addAndStartAction(new RequestArcTerminationAction(localDevice(), address));
}
-
}
}
/**
* Constructor.
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param deviceLogicalAddress logical address of the device in interest
* @param devicePhysicalAddress physical address of the device in interest
* @param requireRoutingChange whether to initiate routing change or not
*/
- NewDeviceAction(HdmiControlService service, int sourceAddress, int deviceLogicalAddress,
+ NewDeviceAction(HdmiCecLocalDevice source, int deviceLogicalAddress,
int devicePhysicalAddress, boolean requireRoutingChange) {
- super(service, sourceAddress);
+ super(source);
mDeviceLogicalAddress = deviceLogicalAddress;
mDevicePhysicalAddress = devicePhysicalAddress;
mVendorId = HdmiCec.UNKNOWN_VENDOR_ID;
@Override
public boolean start() {
- if (HdmiCec.getTypeFromAddress(mSourceAddress) == HdmiCec.DEVICE_AUDIO_SYSTEM) {
- if (mService.getAvrDeviceInfo() == null) {
+ if (HdmiCec.getTypeFromAddress(getSourceAddress()) == HdmiCec.DEVICE_AUDIO_SYSTEM) {
+ if (tv().getAvrDeviceInfo() == null) {
// TODO: Start system audio initiation action
}
// If new device is connected through ARC enabled port,
// initiates ARC channel establishment.
- if (mService.isConnectedToArcPort(mDevicePhysicalAddress)) {
- mService.addAndStartAction(new RequestArcInitiationAction(mService, mSourceAddress,
- mDeviceLogicalAddress));
+ if (tv().isConnectedToArcPort(mDevicePhysicalAddress)) {
+ addAndStartAction(new RequestArcInitiationAction(localDevice(),
+ mDeviceLogicalAddress));
}
}
return true;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(),
mDeviceLogicalAddress));
addTimer(mState, TIMEOUT_MS);
return true;
private void startRoutingChange() {
// Stop existing routing control.
- mService.removeAction(RoutingControlAction.class);
+ removeAction(RoutingControlAction.class);
// Send routing change. The the address is a path of the active port.
int newPath = toTopMostPortPath(mDevicePhysicalAddress);
- sendCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
- mService.getActivePath(), newPath));
- mService.addAndStartAction(new RoutingControlAction(mService, mSourceAddress,
- mService.pathToPortId(newPath), null));
+ sendCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
+ localDevice().getActivePath(), newPath));
+ addAndStartAction(new RoutingControlAction(localDevice(),
+ localDevice().pathToPortId(newPath), null));
}
private static int toTopMostPortPath(int physicalAddress) {
}
private boolean mayProcessCommandIfCached(int destAddress, int opcode) {
- HdmiCecMessage message = mService.getCecMessageCache().getMessage(destAddress, opcode);
+ HdmiCecMessage message = getCecMessageCache().getMessage(destAddress, opcode);
if (message != null) {
return processCommand(message);
}
if (mayProcessCommandIfCached(mDeviceLogicalAddress, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress,
+ sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(),
mDeviceLogicalAddress));
addTimer(mState, TIMEOUT_MS);
}
if (mDisplayName == null) {
mDisplayName = HdmiCec.getDefaultDeviceName(mDeviceLogicalAddress);
}
- mService.addCecDevice(new HdmiCecDeviceInfo(
+ tv().addCecDevice(new HdmiCecDeviceInfo(
mDeviceLogicalAddress, mDevicePhysicalAddress,
HdmiCec.getTypeFromAddress(mDeviceLogicalAddress),
mVendorId, mDisplayName));
* <p>Package-private, accessed by {@link HdmiControlService} only.
*/
-public final class OneTouchPlayAction extends FeatureAction {
+final class OneTouchPlayAction extends FeatureAction {
private static final String TAG = "OneTouchPlayAction";
// State in which the action is waiting for <Report Power Status>. In normal situation
// We wait up to RESPONSE_TIMEOUT_MS * LOOP_COUNTER_MAX = 20 seconds.
private static final int LOOP_COUNTER_MAX = 10;
- private final int mSourcePath;
private final int mTargetAddress;
private final IHdmiControlCallback mCallback;
private int mPowerStatusCounter = 0;
// Factory method. Ensures arguments are valid.
- static OneTouchPlayAction create(HdmiControlService service, int sourceAddress,
- int sourcePath, int targetAddress, IHdmiControlCallback callback) {
- if (service == null || callback == null) {
+ static OneTouchPlayAction create(HdmiCecLocalDevice source,
+ int targetAddress, IHdmiControlCallback callback) {
+ if (source == null || callback == null) {
Slog.e(TAG, "Wrong arguments");
return null;
}
- return new OneTouchPlayAction(service, sourceAddress, sourcePath, targetAddress, callback);
+ return new OneTouchPlayAction(source, targetAddress,
+ callback);
}
- private OneTouchPlayAction(HdmiControlService service, int sourceAddress, int sourcePath,
- int targetAddress, IHdmiControlCallback callback) {
- super(service, sourceAddress);
- mSourcePath = sourcePath;
+ private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
+ IHdmiControlCallback callback) {
+ super(localDevice);
mTargetAddress = targetAddress;
mCallback = callback;
}
@Override
boolean start() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildTextViewOn(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
broadcastActiveSource();
queryDevicePowerStatus();
mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
}
private void broadcastActiveSource() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath));
+ sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath()));
}
private void queryDevicePowerStatus() {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+ mTargetAddress));
}
@Override
/**
* @Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address. It should
- * TV type
+ * @param source {@link HdmiCecLocalDevice} instance
* @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);
- HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ RequestArcAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
}
protected final void disableArcTransmission() {
// Start Set ARC Transmission State action.
- SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(mService,
- mSourceAddress, mAvrAddress, false);
- mService.addAndStartAction(action);
+ SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(localDevice(),
+ mAvrAddress, false);
+ addAndStartAction(action);
}
@Override
*
* For more details look at {@link RequestArcAction#RequestArcAction}.
*/
- RequestArcInitiationAction(HdmiControlService service, int sourceAddress, int avrAddress) {
- super(service, sourceAddress, avrAddress);
+ RequestArcInitiationAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source, avrAddress);
}
@Override
boolean start() {
- HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress,
- mAvrAddress);
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(
+ getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
*
* @see RequestArcAction#RequestArcAction
*/
- RequestArcTerminationAction(HdmiControlService service, int sourceAddress, int avrAddress) {
- super(service, sourceAddress, avrAddress);
+ RequestArcTerminationAction(HdmiCecLocalDevice source, int avrAddress) {
+ super(source, avrAddress);
}
@Override
boolean start() {
HdmiCecMessage command =
- HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress);
+ HdmiCecMessageBuilder.buildRequestArcTermination(getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
* <li> Routing at CEC enable time
* </ul>
*/
-public class RoutingControlAction extends FeatureAction {
+final class RoutingControlAction extends FeatureAction {
private static final String TAG = "RoutingControlAction";
// State in which we wait for <Routing Information> to arrive. If timed out, we use the
// The latest routing path. Updated by each <Routing Information> from CEC switches.
private int mCurrentRoutingPath;
- RoutingControlAction(HdmiControlService service, int sourceAddress, int path,
- IHdmiControlCallback callback) {
- super(service, sourceAddress);
+ RoutingControlAction(HdmiCecLocalDevice localDevice, int path, IHdmiControlCallback callback) {
+ super(localDevice);
mCallback = callback;
mCurrentRoutingPath = path;
}
}
mCurrentRoutingPath = routingPath;
// Stop possible previous routing change sequence if in progress.
- mService.removeAction(RoutingControlAction.class);
+ removeAction(RoutingControlAction.class);
addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS);
return true;
} else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
}
private void sendSetStreamPath() {
- sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(mSourceAddress, mCurrentRoutingPath));
+ sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(getSourceAddress(),
+ mCurrentRoutingPath));
}
private static boolean isInActiveRoutingPath(int activePath, int newPath) {
}
switch (timeoutState) {
case STATE_WAIT_FOR_ROUTING_INFORMATION:
- HdmiCecDeviceInfo device = mService.getDeviceInfoByPath(mCurrentRoutingPath);
+ HdmiCecDeviceInfo device = tv().getDeviceInfoByPath(mCurrentRoutingPath);
if (device == null) {
- maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
+ maybeChangeActiveInput(tv().pathToPortId(mCurrentRoutingPath));
} else {
// TODO: Also check followings and then proceed:
// if routing change was neither triggered by TV at CEC enable time, nor
case STATE_WAIT_FOR_REPORT_POWER_STATUS:
int tvPowerStatus = getTvPowerStatus();
if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) {
- if (!maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath))) {
+ if (!maybeChangeActiveInput(localDevice().pathToPortId(mCurrentRoutingPath))) {
sendSetStreamPath();
}
}
// Called whenever an HDMI input of the TV shall become the active input.
private boolean maybeChangeActiveInput(int path) {
- if (mService.getActiveInput() == mService.pathToPortId(path)) {
+ if (localDevice().getActiveInput() == localDevice().pathToPortId(path)) {
return false;
}
// TODO: Remember the currently active input
}
private void queryDevicePowerStatus(int address, SendMessageCallback callback) {
- sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, address),
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address),
callback);
}
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
} else {
- maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
+ maybeChangeActiveInput(localDevice().pathToPortId(mCurrentRoutingPath));
}
}
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCecMessage;
-import android.view.KeyEvent;
import android.util.Slog;
-
-import libcore.util.EmptyArray;
+import android.view.KeyEvent;
/**
* Feature action that transmits remote control key command (User Control Press/
/**
* Constructor.
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param targetAddress logical address of the device to send the keys to
* @param keyCode remote control key code as defined in {@link KeyEvent}
*/
- SendKeyAction(HdmiControlService service, int sourceAddress, int targetAddress, int keyCode) {
- super(service, sourceAddress);
+ SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keyCode) {
+ super(source);
mTargetAddress = targetAddress;
mLastKeyCode = keyCode;
}
if (keyCodeAndParam == null) {
return;
}
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress, mTargetAddress,
- keyCodeAndParam));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
+ mTargetAddress, keyCodeAndParam));
}
private void sendKeyUp() {
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress, mTargetAddress));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
+ mTargetAddress));
}
@Override
/**
* @Constructor
*
- * @param service an instance of {@link HdmiControlService}
- * @param sourceAddress logical address to be used as source address
+ * @param source {@link HdmiCecLocalDevice} instance
* @param enabled whether to enable ARC Transmission channel
*/
- SetArcTransmissionStateAction(HdmiControlService service, int sourceAddress, int avrAddress,
+ SetArcTransmissionStateAction(HdmiCecLocalDevice source, int avrAddress,
boolean enabled) {
- super(service, sourceAddress);
- HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+ super(source);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrAddress = avrAddress;
mEnabled = enabled;
private void sendReportArcInitiated() {
HdmiCecMessage command =
- HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress);
+ HdmiCecMessageBuilder.buildReportArcInitiated(getSourceAddress(), mAvrAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
}
private void setArcStatus(boolean enabled) {
- boolean wasEnabled = mService.setArcStatus(enabled);
+ boolean wasEnabled = tv().setArcStatus(enabled);
Slog.i(TAG, "Change arc status [old:" + wasEnabled + " ,new:" + enabled);
// If enabled before and set to "disabled" and send <Report Arc Terminated> to
// av reciever.
if (!enabled && wasEnabled) {
- sendCommand(
- HdmiCecMessageBuilder.buildReportArcTerminated(mSourceAddress, mAvrAddress));
+ sendCommand(HdmiCecMessageBuilder.buildReportArcTerminated(getSourceAddress(),
+ mAvrAddress));
}
}
/**
* Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param sourceAddress logical address of source device (TV or STB).
+ * @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
*/
- SystemAudioAction(HdmiControlService service, int sourceAddress, int avrAddress,
- boolean targetStatus) {
- super(service, sourceAddress);
+ SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus) {
+ super(source);
HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
mAvrLogicalAddress = avrAddress;
mTargetAudioStatus = targetStatus;
}
protected void sendSystemAudioModeRequest() {
- int avrPhysicalAddress = mService.getAvrDeviceInfo().getPhysicalAddress();
- HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(mSourceAddress,
+ int avrPhysicalAddress = tv().getAvrDeviceInfo().getPhysicalAddress();
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ getSourceAddress(),
mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
}
protected void setSystemAudioMode(boolean mode) {
- mService.setSystemAudioMode(mode);
+ tv().setSystemAudioMode(mode);
}
protected void sendGiveAudioStatus() {
- HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(mSourceAddress,
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(),
mAvrLogicalAddress);
sendCommand(command, new HdmiControlService.SendMessageCallback() {
@Override
private void handleSendGiveAudioStatusFailure() {
// TODO: Notify the failure status.
- int uiCommand = mService.getSystemAudioMode()
+ int uiCommand = tv().getSystemAudioMode()
? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION // SystemAudioMode: ON
: HdmiConstants.UI_COMMAND_MUTE_FUNCTION; // SystemAudioMode: OFF
sendUserControlPressedAndReleased(uiCommand);
private void sendUserControlPressedAndReleased(int uiCommand) {
sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
- mSourceAddress, mAvrLogicalAddress, uiCommand));
+ getSourceAddress(), mAvrLogicalAddress, uiCommand));
sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
- mSourceAddress, mAvrLogicalAddress));
+ getSourceAddress(), mAvrLogicalAddress));
}
@Override
if (params.length > 0) {
boolean mute = (params[0] & 0x80) == 0x80;
int volume = params[0] & 0x7F;
- mService.setAudioStatus(mute, volume);
+ tv().setAudioStatus(mute, volume);
if (mTargetAudioStatus && mute || !mTargetAudioStatus && !mute) {
// Toggle AVR's mute status to match with the system audio status.
sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE);
}
protected void removeSystemAudioActionInProgress() {
- mService.removeActionExcept(SystemAudioActionFromTv.class, this);
- mService.removeActionExcept(SystemAudioActionFromAvr.class, this);
+ removeActionExcept(SystemAudioActionFromTv.class, this);
+ removeActionExcept(SystemAudioActionFromAvr.class, this);
}
@Override
/**
* Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param tvAddress logical address of TV device
+ * @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid
*/
- SystemAudioActionFromAvr(HdmiControlService service, int tvAddress, int avrAddress,
+ SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress,
boolean targetStatus) {
- super(service, tvAddress, avrAddress, targetStatus);
- HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ super(source, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
}
@Override
}
private void handleSystemAudioActionFromAvr() {
- if (mTargetAudioStatus == mService.getSystemAudioMode()) {
+ if (mTargetAudioStatus == tv().getSystemAudioMode()) {
finish();
return;
}
- if (mService.isInPresetInstallationMode()) {
+ if (tv().isInPresetInstallationMode()) {
sendCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
- mSourceAddress, mAvrLogicalAddress,
+ getSourceAddress(), mAvrLogicalAddress,
HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE, HdmiConstants.ABORT_REFUSED));
mTargetAudioStatus = false;
sendSystemAudioModeRequest();
/**
* Constructor
*
- * @param service {@link HdmiControlService} instance
- * @param tvAddress logical address of TV device
+ * @param sourceAddress {@link HdmiCecLocalDevice} instance
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @throw IllegalArugmentException if device type of tvAddress is invalid
*/
- SystemAudioActionFromTv(HdmiControlService service, int tvAddress, int avrAddress,
+ SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress,
boolean targetStatus) {
- super(service, tvAddress, avrAddress, targetStatus);
- HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+ super(sourceAddress, avrAddress, targetStatus);
+ HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
}
@Override
private static final int STATE_1 = 1;
private static final int STATE_2 = 2;
- VendorSpecificAction(HdmiControlService service, int sourceAddress) {
- super(service, sourceAddress);
+ VendorSpecificAction(HdmiCecLocalDevice source) {
+ super(source);
// Modify the constructor if additional arguments are necessary.
}