/*
* 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.hardware.hdmi.IHdmiCecListener;
import android.os.Binder;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* HdmiCecDevice class represents a CEC logical device characterized
* by its device type. It is a superclass of those serving concrete device type.
* Currently we're interested in playback(one of sources), display(sink) device type
* only. The support for the other types like recorder, audio system will come later.
*
*
A physical device can contain the functions of
* more than one logical device, in which case it should create
* as many logical devices as necessary.
*
*
Note that if a physical device has multiple instances of a particular
* functionality, it should advertize only one instance. For instance, if
* a device has multiple tuners, it should only expose one for control
* via CEC. In this case, it is up to the device itself to manage multiple tuners.
*
*
The version of HDMI-CEC protocol supported in this class is 1.3a.
*
*
Declared as package-private, accessed by HdmiCecService only.
*/
abstract class HdmiCecDevice {
private static final String TAG = "HdmiCecDevice";
private final int mType;
// List of listeners to the message/event coming to the device.
private final List mListeners = new ArrayList();
private final Binder mBinder = new Binder();
private final HdmiCecService mService;
private boolean mIsActiveSource;
/**
* Factory method that creates HdmiCecDevice instance to the device type.
*/
public static HdmiCecDevice create(HdmiCecService service, int type) {
if (type == HdmiCec.DEVICE_PLAYBACK) {
return new HdmiCecDevicePlayback(service, type);
} else if (type == HdmiCec.DEVICE_TV) {
return new HdmiCecDeviceTv(service, type);
}
return null;
}
/**
* Constructor.
*/
public HdmiCecDevice(HdmiCecService service, int type) {
mService = service;
mType = type;
mIsActiveSource = false;
}
/**
* Called right after the class is instantiated. This method can be used to
* implement any initialization tasks for the instance.
*/
abstract public void initialize();
/**
* Return the binder token that identifies this instance.
*/
public Binder getToken() {
return mBinder;
}
/**
* Return the service instance.
*/
public HdmiCecService getService() {
return mService;
}
/**
* Return the type of this device.
*/
public int getType() {
return mType;
}
/**
* Register a listener to be invoked when events occur.
*
* @param listener the listern that will run
*/
public void addListener(IHdmiCecListener listener) {
mListeners.add(listener);
}
/**
* Remove the listener that was previously registered.
*
* @param listener IHdmiCecListener instance to be removed
*/
public void removeListener(IHdmiCecListener listener) {
mListeners.remove(listener);
}
/**
* Indicate if the device has listeners.
*
* @return true if there are listener instances for this device
*/
public boolean hasListener() {
return !mListeners.isEmpty();
}
/**
* Handle HDMI-CEC message coming to the device by invoking the registered
* listeners.
*/
public void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE) {
mIsActiveSource = false;
}
if (mListeners.size() == 0) {
return;
}
HdmiCecMessage message = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
for (IHdmiCecListener listener : mListeners) {
try {
listener.onMessageReceived(message);
} catch (RemoteException e) {
Log.e(TAG, "listener.onMessageReceived failed.");
}
}
}
public void handleHotplug(boolean connected) {
for (IHdmiCecListener listener : mListeners) {
try {
listener.onCableStatusChanged(connected);
} catch (RemoteException e) {
Log.e(TAG, "listener.onCableStatusChanged failed.");
}
}
}
/**
* Return the active status of the device.
*
* @return true if the device is the active source among the connected
* HDMI-CEC-enabled devices; otherwise false.
*/
public boolean isActiveSource() {
return mIsActiveSource;
}
/**
* Update the active source state of the device.
*/
public void setIsActiveSource(boolean state) {
mIsActiveSource = state;
}
/**
* Send <Active Source> command. The default implementation does nothing. Should be
* overriden by subclass.
*/
public void sendActiveSource(int physicalAddress) {
logWarning(" not valid for the device type: " + mType
+ " address:" + physicalAddress);
}
/**
* Send <Inactive Source> command. The default implementation does nothing. Should be
* overriden by subclass.
*/
public void sendInactiveSource(int physicalAddress) {
logWarning(" not valid for the device type: " + mType
+ " address:" + physicalAddress);
}
/**
* Send <Image View On> command. The default implementation does nothing. Should be
* overriden by subclass.
*/
public void sendImageViewOn() {
logWarning(" not valid for the device type: " + mType);
}
/**
* Send <Text View On> command. The default implementation does nothing. Should be
* overriden by subclass.
*/
public void sendTextViewOn() {
logWarning(" not valid for the device type: " + mType);
}
/**
* Check if the connected sink device is in powered-on state. The default implementation
* simply returns false. Should be overriden by subclass to report the correct state.
*/
public boolean isSinkDeviceOn() {
logWarning("isSinkDeviceOn() not valid for the device type: " + mType);
return false;
}
private void logWarning(String msg) {
Log.w(TAG, msg);
}
}