Merge "Add HDMI-CEC service" into klp-modular-dev

This commit is contained in:
Jinsuk Kim
2014-03-21 02:32:38 +00:00
committed by Android (Google) Code Review
14 changed files with 1751 additions and 5 deletions

View File

@ -130,6 +130,8 @@ LOCAL_SRC_FILES += \
core/java/android/hardware/ISerialManager.aidl \
core/java/android/hardware/display/IDisplayManager.aidl \
core/java/android/hardware/display/IDisplayManagerCallback.aidl \
core/java/android/hardware/hdmi/IHdmiCecListener.aidl \
core/java/android/hardware/hdmi/IHdmiCecService.aidl \
core/java/android/hardware/input/IInputManager.aidl \
core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
core/java/android/hardware/location/IFusedLocationHardware.aidl \

View File

@ -10908,6 +10908,54 @@ package android.hardware.display {
}
package android.hardware.hdmi {
public final class HdmiCec {
method public static java.lang.String getDefaultDeviceName(int);
method public static int getTypeFromAddress(int);
method public static boolean isValidAddress(int);
method public static boolean isValidType(int);
field public static final int ADDR_AUDIO_SYSTEM = 5; // 0x5
field public static final int ADDR_BROADCAST = 15; // 0xf
field public static final int ADDR_FREE_USE = 14; // 0xe
field public static final int ADDR_INVALID = -1; // 0xffffffff
field public static final int ADDR_PLAYBACK_1 = 4; // 0x4
field public static final int ADDR_PLAYBACK_2 = 8; // 0x8
field public static final int ADDR_PLAYBACK_3 = 11; // 0xb
field public static final int ADDR_RECORDER_1 = 1; // 0x1
field public static final int ADDR_RECORDER_2 = 2; // 0x2
field public static final int ADDR_RECORDER_3 = 9; // 0x9
field public static final int ADDR_RESERVED_1 = 12; // 0xc
field public static final int ADDR_RESERVED_2 = 13; // 0xd
field public static final int ADDR_TUNER_1 = 3; // 0x3
field public static final int ADDR_TUNER_2 = 6; // 0x6
field public static final int ADDR_TUNER_3 = 7; // 0x7
field public static final int ADDR_TUNER_4 = 10; // 0xa
field public static final int ADDR_TV = 0; // 0x0
field public static final int ADDR_UNREGISTERED = 15; // 0xf
field public static final int DEVICE_AUDIO_SYSTEM = 5; // 0x5
field public static final int DEVICE_INACTIVE = -1; // 0xffffffff
field public static final int DEVICE_PLAYBACK = 4; // 0x4
field public static final int DEVICE_RECORDER = 1; // 0x1
field public static final int DEVICE_RESERVED = 2; // 0x2
field public static final int DEVICE_TUNER = 3; // 0x3
field public static final int DEVICE_TV = 0; // 0x0
field public static final int MESSAGE_ACTIVE_SOURCE = 157; // 0x9d
}
public final class HdmiCecMessage implements android.os.Parcelable {
ctor public HdmiCecMessage(int, int, int, byte[]);
method public int describeContents();
method public int getDestination();
method public int getOpcode();
method public byte[] getParams();
method public int getSource();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
}
package android.hardware.input {
public final class InputManager {

View File

@ -0,0 +1,190 @@
/*
* 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 android.hardware.hdmi;
/**
* Defines constants and utility methods related to HDMI-CEC protocol.
*/
public final class HdmiCec {
/** TV device type. */
public static final int DEVICE_TV = 0;
/** Recording device type. */
public static final int DEVICE_RECORDER = 1;
/** Device type reserved for future usage. */
public static final int DEVICE_RESERVED = 2;
/** Tuner device type. */
public static final int DEVICE_TUNER = 3;
/** Playback device type. */
public static final int DEVICE_PLAYBACK = 4;
/** Audio system device type. */
public static final int DEVICE_AUDIO_SYSTEM = 5;
// Value indicating the device is not an active source.
public static final int DEVICE_INACTIVE = -1;
/** Logical address for TV */
public static final int ADDR_TV = 0;
/** Logical address for recorder 1 */
public static final int ADDR_RECORDER_1 = 1;
/** Logical address for recorder 2 */
public static final int ADDR_RECORDER_2 = 2;
/** Logical address for tuner 1 */
public static final int ADDR_TUNER_1 = 3;
/** Logical address for playback 1 */
public static final int ADDR_PLAYBACK_1 = 4;
/** Logical address for audio system */
public static final int ADDR_AUDIO_SYSTEM = 5;
/** Logical address for tuner 2 */
public static final int ADDR_TUNER_2 = 6;
/** Logical address for tuner 3 */
public static final int ADDR_TUNER_3 = 7;
/** Logical address for playback 2 */
public static final int ADDR_PLAYBACK_2 = 8;
/** Logical address for recorder 3 */
public static final int ADDR_RECORDER_3 = 9;
/** Logical address for tuner 4 */
public static final int ADDR_TUNER_4 = 10;
/** Logical address for playback 3 */
public static final int ADDR_PLAYBACK_3 = 11;
/** Logical address reserved for future usage */
public static final int ADDR_RESERVED_1 = 12;
/** Logical address reserved for future usage */
public static final int ADDR_RESERVED_2 = 13;
/** Logical address for TV other than the one assigned with {@link #ADDR_TV} */
public static final int ADDR_FREE_USE = 14;
/** Logical address for devices to which address cannot be allocated */
public static final int ADDR_UNREGISTERED = 15;
/** Logical address used in the destination address field for broadcast messages */
public static final int ADDR_BROADCAST = 15;
/** Logical address used to indicate it is not initialized or invalid. */
public static final int ADDR_INVALID = -1;
// TODO: Complete the list of CEC messages definition.
public static final int MESSAGE_ACTIVE_SOURCE = 0x9D;
private static final int[] ADDRESS_TO_TYPE = {
DEVICE_TV, // ADDR_TV
DEVICE_RECORDER, // ADDR_RECORDER_1
DEVICE_RECORDER, // ADDR_RECORDER_2
DEVICE_TUNER, // ADDR_TUNER_1
DEVICE_PLAYBACK, // ADDR_PLAYBACK_1
DEVICE_AUDIO_SYSTEM, // ADDR_AUDIO_SYSTEM
DEVICE_TUNER, // ADDR_TUNER_2
DEVICE_TUNER, // ADDR_TUNER_3
DEVICE_PLAYBACK, // ADDR_PLAYBACK_2
DEVICE_RECORDER, // ADDR_RECORDER_3
DEVICE_TUNER, // ADDR_TUNER_4
DEVICE_PLAYBACK, // ADDR_PLAYBACK_3
};
private static final String[] DEFAULT_NAMES = {
"TV",
"Recorder_1",
"Recorder_2",
"Tuner_1",
"Playback_1",
"AudioSystem",
"Tuner_2",
"Tuner_3",
"Playback_2",
"Recorder_3",
"Tuner_4",
"Playback_3",
};
private HdmiCec() { } // Prevents instantiation.
/**
* Check if the given type is valid. A valid type is one of the actual
* logical device types defined in the standard ({@link #DEVICE_TV},
* {@link #DEVICE_PLAYBACK}, {@link #DEVICE_TUNER}, {@link #DEVICE_RECORDER},
* and {@link #DEVICE_AUDIO_SYSTEM}).
*
* @param type device type
* @return true if the given type is valid
*/
public static boolean isValidType(int type) {
return (DEVICE_TV <= type && type <= DEVICE_AUDIO_SYSTEM)
&& type != DEVICE_RESERVED;
}
/**
* Check if the given logical address is valid. A logical address is valid
* if it is one allocated for an actual device which allows communication
* with other logical devices.
*
* @param address logical address
* @return true if the given address is valid
*/
public static boolean isValidAddress(int address) {
// TODO: We leave out the address 'free use(14)' for now. Check this later
// again to make sure it is a valid address for communication.
return (ADDR_TV <= address && address <= ADDR_PLAYBACK_3);
}
/**
* Return the device type for the given logical address.
*
* @param address logical address
* @return device type for the given logical address; DEVICE_INACTIVE
* if the address is not valid.
*/
public static int getTypeFromAddress(int address) {
if (isValidAddress(address)) {
return ADDRESS_TO_TYPE[address];
}
return DEVICE_INACTIVE;
}
/**
* Return the default device name for a logical address. This is the name
* by which the logical device is known to others until a name is
* set explicitly using HdmiCecService.setOsdName.
*
* @param address logical address
* @return default device name; empty string if the address is not valid
*/
public static String getDefaultDeviceName(int address) {
if (isValidAddress(address)) {
return DEFAULT_NAMES[address];
}
return "";
}
}

View File

@ -0,0 +1,19 @@
/*
* 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 android.hardware.hdmi;
parcelable HdmiCecMessage;

View File

@ -0,0 +1,145 @@
/*
* 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 android.hardware.hdmi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Arrays;
/**
* A class to encapsulate HDMI-CEC message used for the devices connected via
* HDMI cable to communicate with one another. A message is defined by its
* source and destination address, command (or opcode), and optional parameters.
*/
public final class HdmiCecMessage implements Parcelable {
private static final int MAX_MESSAGE_LENGTH = 16;
private final int mSource;
private final int mDestination;
private final int mOpcode;
private final byte[] mParams;
/**
* Constructor.
*/
public HdmiCecMessage(int source, int destination, int opcode, byte[] params) {
mSource = source;
mDestination = destination;
mOpcode = opcode;
mParams = Arrays.copyOf(params, params.length);
}
/**
* Return the source address field of the message. It is the logical address
* of the device which generated the message.
*
* @return source address
*/
public int getSource() {
return mSource;
}
/**
* Return the destination address field of the message. It is the logical address
* of the device to which the message is sent.
*
* @return destination address
*/
public int getDestination() {
return mDestination;
}
/**
* Return the opcode field of the message. It is the type of the message that
* tells the destination device what to do.
*
* @return opcode
*/
public int getOpcode() {
return mOpcode;
}
/**
* Return the parameter field of the message. The contents of parameter varies
* from opcode to opcode, and is used together with opcode to describe
* the action for the destination device to take.
*
* @return parameter
*/
public byte[] getParams() {
return mParams;
}
/**
* Describe the kinds of special objects contained in this Parcelable's
* marshalled representation.
*/
@Override
public int describeContents() {
return 0;
}
/**
* Flatten this object in to a Parcel.
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}.
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mSource);
dest.writeInt(mDestination);
dest.writeInt(mOpcode);
dest.writeInt(mParams.length);
dest.writeByteArray(mParams);
}
public static final Parcelable.Creator<HdmiCecMessage> CREATOR
= new Parcelable.Creator<HdmiCecMessage>() {
/**
* Rebuild a HdmiCecMessage previously stored with writeToParcel().
* @param p HdmiCecMessage object to read the Rating from
* @return a new HdmiCecMessage created from the data in the parcel
*/
public HdmiCecMessage createFromParcel(Parcel p) {
int source = p.readInt();
int destination = p.readInt();
int opcode = p.readInt();
byte[] params = new byte[p.readInt()];
p.readByteArray(params);
return new HdmiCecMessage(source, destination, opcode, params);
}
public HdmiCecMessage[] newArray(int size) {
return new HdmiCecMessage[size];
}
};
@Override
public String toString() {
StringBuffer s = new StringBuffer();
s.append(String.format("src: %d dst: %d op: %2X params: ", mSource, mDestination, mOpcode));
for (byte data : mParams) {
s.append(String.format("%02X ", data));
}
return s.toString();
}
}

View File

@ -0,0 +1,29 @@
/*
* 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 android.hardware.hdmi;
import android.hardware.hdmi.HdmiCecMessage;
/**
* Interface definition for HdmiCecService to do interprocess communcation.
*
* @hide
*/
oneway interface IHdmiCecListener {
void onMessageReceived(in HdmiCecMessage message);
void onCableStatusChanged(in boolean connected);
}

View File

@ -0,0 +1,40 @@
/*
* 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 android.hardware.hdmi;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.IHdmiCecListener;
import android.os.IBinder;
/**
* Binder interface that components running in the appplication process
* will use to enable HDMI-CEC protocol exchange with other devices.
*
* @hide
*/
interface IHdmiCecService {
IBinder allocateLogicalDevice(int type, IHdmiCecListener listener);
void removeServiceListener(IBinder b, IHdmiCecListener listener);
void setOsdName(IBinder b, String name);
void sendActiveSource(IBinder b);
void sendInactiveSource(IBinder b);
void sendImageViewOn(IBinder b);
void sendTextViewOn(IBinder b);
void sendGiveDevicePowerStatus(IBinder b, int address);
void sendMessage(IBinder b, in HdmiCecMessage message);
}

View File

@ -682,6 +682,12 @@
android:label="@string/permlab_installLocationProvider"
android:description="@string/permdesc_installLocationProvider" />
<!-- Allows HDMI-CEC service to access device and configuration files.
@hide This should only be used by HDMI-CEC service.
-->
<permission android:name="android.permission.HDMI_CEC"
android:protectionLevel="signatureOrSystem" />
<!-- Allows an application to use location features in hardware,
such as the geofencing api.
<p>Not for use by third-party applications. -->

View File

@ -0,0 +1,171 @@
/*
* 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.IHdmiCecDevice;
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;
/**
* CecDevice class represents a CEC logical device characterized
* by its device type. 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.
*
* <p>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.
*
* <p>The version of HDMI-CEC protocol supported in this class is 1.3a.
*
* <p>Declared as package-private, accessed by HdmiCecService only.
*/
final 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<IHdmiCecListener> mListeners = new ArrayList<IHdmiCecListener>();
private final Binder mBinder = new Binder();
private String mName;
private boolean mIsActiveSource;
/**
* Constructor.
*/
public HdmiCecDevice(int type) {
mType = type;
mIsActiveSource = false;
}
/**
* Return the binder token that identifies this instance.
*/
public Binder getToken() {
return mBinder;
}
/**
* Return the type of this device.
*/
public int getType() {
return mType;
}
/**
* Set the name of the device. The name will be transferred via the message
* &lt;Set OSD Name&gt; to other HDMI-CEC devices connected through HDMI
* cables and shown on TV screen to identify the devicie.
*
* @param name name of the device
*/
public void setName(String name) {
mName = name;
}
/**
* Return the name of this device.
*/
public String getName() {
return mName;
}
/**
* 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;
}
}

View File

@ -0,0 +1,394 @@
/*
* 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.content.Context;
import android.content.pm.PackageManager;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.IHdmiCecListener;
import android.hardware.hdmi.IHdmiCecService;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Locale;
/**
* Provides a service for sending and processing HDMI-CEC messages, and providing
* the information on HDMI settings in general.
*/
public final class HdmiCecService extends SystemService {
private static final String TAG = "HdmiCecService";
// Maintains the allocated logical devices. Device type, not logical address,
// is used for key as logical address is likely to change over time while
// device type is permanent. Type-address mapping is maintained only at
// native level.
private final SparseArray<HdmiCecDevice> mLogicalDevices = new SparseArray<HdmiCecDevice>();
// List of IBinder.DeathRecipient instances to handle dead IHdmiCecListener
// objects.
private final ArrayList<ListenerRecord> mListenerRecords = new ArrayList<ListenerRecord>();
// Used to synchronize the access to the service.
private final Object mLock = new Object();
// Stores the pointer to the native implementation of the service that
// interacts with HAL.
private long mNativePtr;
private static final String PERMISSION = "android.permission.HDMI_CEC";
// Service name under which it is registered to service manager.
// TODO: Move this to Context once HdmiCecManager is introduced.
private static final String HDMI_CEC_SERVICE = "hdmi_cec";
public HdmiCecService(Context context) {
super(context);
}
private static native long nativeInit(HdmiCecService service);
@Override
public void onStart() {
mNativePtr = nativeInit(this);
publishBinderService(HDMI_CEC_SERVICE, new BinderService());
}
/**
* Called by native when an HDMI-CEC message arrived. Invokes the registered
* listeners to handle the message.
*/
private void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
// TODO: Messages like <Standby> may not need be passed to listener
// but better be handled in service by turning off the screen
// or putting the device into suspend mode. List up such messages
// and handle them here.
int type = HdmiCec.getTypeFromAddress(dstAddress);
synchronized (mLock) {
if (dstAddress == HdmiCec.ADDR_BROADCAST) {
for (int i = 0; i < mLogicalDevices.size(); ++i) {
mLogicalDevices.valueAt(i).handleMessage(srcAddress, dstAddress, opcode,
params);
}
} else {
HdmiCecDevice device = mLogicalDevices.get(type);
if (device == null) {
Log.w(TAG, "logical device not found. type: " + type);
return;
}
device.handleMessage(srcAddress, dstAddress, opcode, params);
}
}
}
/**
* Called by native when internal HDMI hotplug event occurs. Invokes the registered
* listeners to handle the event.
*/
private void handleHotplug(boolean connected) {
synchronized(mLock) {
for (int i = 0; i < mLogicalDevices.size(); ++i) {
mLogicalDevices.valueAt(i).handleHotplug(connected);
}
}
}
/**
* Called by native when it needs to know whether we have an active source.
* The native part uses the return value to respond to &lt;Request Active
* Source &gt;.
*
* @return type of the device which is active; DEVICE_INACTIVE if there is
* no active logical device in the system.
*/
private int getActiveSource() {
synchronized(mLock) {
for (int i = 0; i < mLogicalDevices.size(); ++i) {
if (mLogicalDevices.valueAt(i).isActiveSource()) {
return mLogicalDevices.keyAt(i);
}
}
}
return HdmiCec.DEVICE_INACTIVE;
}
/**
* Called by native when a request for the device OSD name was received.
* The native part uses the return value to generate the message
* &lt;Set Osd Name&gt; in response.
*/
private byte[] getOsdName(int type) {
synchronized (mLock) {
HdmiCecDevice device = mLogicalDevices.get(type);
if (device != null) {
return device.getName().getBytes(Charset.forName("US-ASCII"));
}
}
return null;
}
/**
* Called by native when a request for the menu language of the device was
* received. The native part uses the return value to generate the message
* &lt;Set Menu Language&gt; in response. The language should be of
* the 3-letter format as defined in ISO/FDIS 639-2. We use system default
* locale.
*/
private String getLanguage(int type) {
return Locale.getDefault().getISO3Language();
}
private void enforceAccessPermission() {
getContext().enforceCallingOrSelfPermission(PERMISSION, "HdmiCecService");
}
private void dumpInternal(PrintWriter pw) {
pw.println("HdmiCecService (dumpsys hdmi_cec)");
pw.println("");
synchronized (mLock) {
for (int i = 0; i < mLogicalDevices.size(); ++i) {
HdmiCecDevice device = mLogicalDevices.valueAt(i);
pw.println("Device: name=" + device.getName() +
", type=" + device.getType() +
", active=" + device.isActiveSource());
}
}
}
// Remove logical device of a given type.
private void removeLogicalDeviceLocked(int type) {
ensureValidType(type);
mLogicalDevices.remove(type);
nativeRemoveLogicalAddress(mNativePtr, type);
}
private static void ensureValidType(int type) {
if (!HdmiCec.isValidType(type)) {
throw new IllegalArgumentException("invalid type: " + type);
}
}
// Return the logical device identified by the given binder token.
private HdmiCecDevice getLogicalDeviceLocked(IBinder b) {
for (int i = 0; i < mLogicalDevices.size(); ++i) {
HdmiCecDevice device = mLogicalDevices.valueAt(i);
if (device.getToken() == b) {
return device;
}
}
throw new IllegalArgumentException("Device not found");
}
private final class ListenerRecord implements IBinder.DeathRecipient {
private final IHdmiCecListener mListener;
private final int mType;
public ListenerRecord(IHdmiCecListener listener, int type) {
mListener = listener;
mType = type;
}
@Override
public void binderDied() {
synchronized (mLock) {
mListenerRecords.remove(this);
HdmiCecDevice device = mLogicalDevices.get(mType);
if (device != null) {
device.removeListener(mListener);
if (!device.hasListener()) {
removeLogicalDeviceLocked(mType);
}
}
}
}
}
private final class BinderService extends IHdmiCecService.Stub {
@Override
public IBinder allocateLogicalDevice(int type, IHdmiCecListener listener) {
enforceAccessPermission();
ensureValidType(type);
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
synchronized (mLock) {
HdmiCecDevice device = mLogicalDevices.get(type);
if (device != null) {
Log.v(TAG, "Logical address already allocated. Adding listener only.");
} else {
int address = nativeAllocateLogicalAddress(mNativePtr, type);
if (!HdmiCec.isValidAddress(address)) {
Log.e(TAG, "Logical address was not allocated");
return null;
} else {
device = new HdmiCecDevice(type);
device.setName(HdmiCec.getDefaultDeviceName(address));
mLogicalDevices.put(type, device);
}
}
// Adds the listener and its monitor
ListenerRecord record = new ListenerRecord(listener, type);
try {
listener.asBinder().linkToDeath(record, 0);
} catch (RemoteException e) {
Log.w(TAG, "Listener already died");
if (!device.hasListener()) {
removeLogicalDeviceLocked(type);
}
return null;
}
mListenerRecords.add(record);
device.addListener(listener);
return device.getToken();
}
}
@Override
public void setOsdName(IBinder b, String name) {
enforceAccessPermission();
if (TextUtils.isEmpty(name)) {
throw new IllegalArgumentException("name must not be null");
}
synchronized (mLock) {
HdmiCecDevice device = getLogicalDeviceLocked(b);
device.setName(name);
}
}
@Override
public void sendActiveSource(IBinder b) {
enforceAccessPermission();
synchronized (mLock) {
HdmiCecDevice device = getLogicalDeviceLocked(b);
device.setIsActiveSource(true);
nativeSendActiveSource(mNativePtr, device.getType());
}
}
@Override
public void sendInactiveSource(IBinder b) {
enforceAccessPermission();
synchronized (mLock) {
HdmiCecDevice device = getLogicalDeviceLocked(b);
device.setIsActiveSource(false);
nativeSendInactiveSource(mNativePtr, device.getType());
}
}
@Override
public void sendImageViewOn(IBinder b) {
enforceAccessPermission();
synchronized (mLock) {
HdmiCecDevice device = getLogicalDeviceLocked(b);
nativeSendImageViewOn(mNativePtr, device.getType());
}
}
@Override
public void sendTextViewOn(IBinder b) {
enforceAccessPermission();
synchronized (mLock) {
HdmiCecDevice device = getLogicalDeviceLocked(b);
nativeSendTextViewOn(mNativePtr, device.getType());
}
}
@Override
public void sendGiveDevicePowerStatus(IBinder b, int address) {
enforceAccessPermission();
synchronized (mLock) {
HdmiCecDevice device = getLogicalDeviceLocked(b);
nativeSendGiveDevicePowerStatus(mNativePtr, device.getType(), address);
}
}
@Override
public void removeServiceListener(IBinder b, IHdmiCecListener listener) {
enforceAccessPermission();
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
synchronized (mLock) {
HdmiCecDevice device = getLogicalDeviceLocked(b);
for (ListenerRecord record : mListenerRecords) {
if (record.mType == device.getType()
&& record.mListener.asBinder() == listener.asBinder()) {
mListenerRecords.remove(record);
device.removeListener(record.mListener);
if (!device.hasListener()) {
removeLogicalDeviceLocked(record.mType);
}
break;
}
}
}
}
@Override
public void sendMessage(IBinder b, HdmiCecMessage message) {
enforceAccessPermission();
if (message == null) {
throw new IllegalArgumentException("message must not be null");
}
synchronized (mLock) {
HdmiCecDevice device = getLogicalDeviceLocked(b);
nativeSendMessage(mNativePtr, device.getType(), message.getDestination(),
message.getOpcode(), message.getParams());
}
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission denial: can't dump HdmiCecService from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
return;
}
final long ident = Binder.clearCallingIdentity();
try {
dumpInternal(pw);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
private static native int nativeAllocateLogicalAddress(long handler, int deviceType);
private static native void nativeRemoveLogicalAddress(long handler, int deviceType);
private static native void nativeSendMessage(long handler, int deviceType, int destination,
int opcode, byte[] params);
private static native void nativeSendActiveSource(long handler, int deviceType);
private static native void nativeSendInactiveSource(long handler, int deviceType);
private static native void nativeSendImageViewOn(long handler, int deviceType);
private static native void nativeSendTextViewOn(long handler, int deviceType);
private static native void nativeSendGiveDevicePowerStatus(long handler, int deviceType,
int address);
}

View File

@ -5,23 +5,26 @@ LOCAL_REL_DIR := core/jni
LOCAL_SRC_FILES += \
$(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \
$(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
$(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
$(LOCAL_REL_DIR)/com_android_server_dreams_McuHal.cpp \
$(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecService.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
$(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
$(LOCAL_REL_DIR)/com_android_server_location_GpsLocationProvider.cpp \
$(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \
$(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
$(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
$(LOCAL_REL_DIR)/com_android_server_location_GpsLocationProvider.cpp \
$(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \
$(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
$(LOCAL_REL_DIR)/onload.cpp
include external/stlport/libstlport.mk
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
frameworks/base/services \

View File

@ -0,0 +1,689 @@
/*
* 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.
*/
#define LOG_TAG "HdmiCecJni"
#define LOG_NDEBUG 1
#include "ScopedPrimitiveArray.h"
#include <cstring>
#include <deque>
#include <map>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
#include <hardware/hdmi_cec.h>
namespace android {
static struct {
jmethodID handleMessage;
jmethodID handleHotplug;
jmethodID getActiveSource;
jmethodID getOsdName;
jmethodID getLanguage;
} gHdmiCecServiceClassInfo;
#ifndef min
#define min(a, b) ((a) > (b) ? (b) : (a))
#endif
class HdmiCecHandler {
public:
enum HdmiCecError {
SUCCESS = 0,
FAILED = -1
};
// Data type to hold a CEC message or internal event data.
typedef union {
cec_message_t cec;
hotplug_event_t hotplug;
} queue_item_t;
// Entry used for message queue.
typedef std::pair<int, const queue_item_t> MessageEntry;
HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj);
void initialize();
// initialize individual logical device.
int initLogicalDevice(int type);
void releaseLogicalDevice(int type);
cec_logical_address_t getLogicalAddress(int deviceType);
int getDeviceType(cec_logical_address_t addr);
void queueMessage(const MessageEntry& message);
void queueOutgoingMessage(const cec_message_t& message);
void sendReportPhysicalAddress();
void sendActiveSource(cec_logical_address_t srcAddr);
void sendInactiveSource(cec_logical_address_t srcAddr);
void sendImageViewOn(cec_logical_address_t srcAddr);
void sendTextViewOn(cec_logical_address_t srcAddr);
void sendGiveDevicePowerStatus(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
void sendFeatureAbort(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
int opcode, int reason);
void sendCecVersion(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
int version);
void sendDeviceVendorID(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
void sendGiveDeviceVendorID(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
void sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
const char* name, size_t len);
void sendSetMenuLanguage(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
void sendCecMessage(const cec_message_t& message);
private:
enum {
EVENT_TYPE_RX,
EVENT_TYPE_TX,
EVENT_TYPE_HOTPLUG,
EVENT_TYPE_STANDBY
};
static const unsigned int MAX_BUFFER_SIZE = 256;
static const uint16_t INVALID_PHYSICAL_ADDRESS = 0xFFFF;
static const int INACTIVE_DEVICE_TYPE = -1;
static void onReceived(const hdmi_event_t* event, void* arg);
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
void updatePhysicalAddress();
void dispatchMessage(const MessageEntry& message);
void processIncomingMessage(const cec_message_t& msg);
// Check the message before we pass it up to framework. If true, we proceed.
// otherwise do not propagate it.
bool precheckMessage(const cec_message_t& msg);
// Propagate the message up to Java layer.
void propagateMessage(const cec_message_t& msg);
void propagateHotplug(bool connected);
// Handles incoming <Request Active Source> message. If one of logical
// devices is active, it should reply with <Active Source> message.
void handleRequestActiveSource();
void handleGetOsdName(const cec_message_t& msg);
void handleGiveDeviceVendorID(const cec_message_t& msg);
void handleGetCECVersion(const cec_message_t& msg);
void handleGetMenuLanguage(const cec_message_t& msg);
// Internal thread for message queue handler
class HdmiThread : public Thread {
public:
HdmiThread(HdmiCecHandler* hdmiCecHandler, bool canCallJava) :
Thread(canCallJava),
mHdmiCecHandler(hdmiCecHandler) {
}
private:
virtual bool threadLoop() {
ALOGV("HdmiThread started");
AutoMutex _l(mHdmiCecHandler->mMessageQueueLock);
mHdmiCecHandler->mMessageQueueCondition.wait(mHdmiCecHandler->mMessageQueueLock);
/* Process all messages in the queue */
while (mHdmiCecHandler->mMessageQueue.size() > 0) {
MessageEntry entry = mHdmiCecHandler->mMessageQueue.front();
mHdmiCecHandler->dispatchMessage(entry);
}
return true;
}
HdmiCecHandler* mHdmiCecHandler;
};
// device type -> logical address mapping
std::map<int, cec_logical_address_t> mLogicalDevices;
hdmi_cec_device_t* mDevice;
jobject mCallbacksObj;
Mutex mLock;
Mutex mMessageQueueLock;
Condition mMessageQueueCondition;
sp<HdmiThread> mMessageQueueHandler;
std::deque<MessageEntry> mMessageQueue;
uint16_t mPhysicalAddress;
};
HdmiCecHandler::HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj) :
mDevice(device),
mCallbacksObj(callbacksObj) {
}
void HdmiCecHandler::initialize() {
mDevice->register_event_callback(mDevice, HdmiCecHandler::onReceived, this);
mMessageQueueHandler = new HdmiThread(this, true /* canCallJava */);
mMessageQueueHandler->run("MessageHandler");
updatePhysicalAddress();
}
void HdmiCecHandler::updatePhysicalAddress() {
uint16_t addr;
if (!mDevice->get_physical_address(mDevice, &addr)) {
mPhysicalAddress = addr;
} else {
mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
}
}
int HdmiCecHandler::initLogicalDevice(int type) {
cec_logical_address_t addr;
int res = mDevice->allocate_logical_address(mDevice, type, &addr);
if (res != 0) {
ALOGE("Logical Address Allocation failed: %d", res);
} else {
ALOGV("Logical Address Allocation success: %d", addr);
mLogicalDevices.insert(std::pair<int, cec_logical_address_t>(type, addr));
}
return addr;
}
void HdmiCecHandler::releaseLogicalDevice(int type) {
std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.find(type);
if (it != mLogicalDevices.end()) {
mLogicalDevices.erase(it);
}
// TODO: remove the address monitored in HAL as well.
}
cec_logical_address_t HdmiCecHandler::getLogicalAddress(int mDevicetype) {
std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.find(mDevicetype);
if (it != mLogicalDevices.end()) {
return it->second;
}
return CEC_ADDR_UNREGISTERED;
}
int HdmiCecHandler::getDeviceType(cec_logical_address_t addr) {
std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
for (; it != mLogicalDevices.end(); ++it) {
if (it->second == addr) {
return it->first;
}
}
return INACTIVE_DEVICE_TYPE;
}
void HdmiCecHandler::queueMessage(const MessageEntry& entry) {
AutoMutex _l(mMessageQueueLock);
if (mMessageQueue.size() <= MAX_BUFFER_SIZE) {
mMessageQueue.push_back(entry);
mMessageQueueCondition.signal();
} else {
ALOGW("Queue is full! Message dropped.");
}
}
void HdmiCecHandler::queueOutgoingMessage(const cec_message_t& message) {
queue_item_t item;
item.cec = message;
MessageEntry entry = std::make_pair(EVENT_TYPE_TX, item);
queueMessage(entry);
}
void HdmiCecHandler::sendReportPhysicalAddress() {
if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
ALOGE("Invalid physical address.");
return;
}
// Report physical address for each logical one hosted in it.
std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
while (it != mLogicalDevices.end()) {
cec_message_t msg;
msg.initiator = it->second; // logical address
msg.destination = CEC_ADDR_BROADCAST;
msg.length = 4;
msg.body[0] = CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS;
std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
msg.body[3] = it->first; // device type
queueOutgoingMessage(msg);
++it;
}
}
void HdmiCecHandler::sendActiveSource(cec_logical_address_t srcAddr) {
if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
ALOGE("Error getting physical address.");
return;
}
cec_message_t msg;
msg.initiator = srcAddr;
msg.destination = CEC_ADDR_BROADCAST;
msg.length = 3;
msg.body[0] = CEC_MESSAGE_ACTIVE_SOURCE;
std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
queueOutgoingMessage(msg);
}
void HdmiCecHandler::sendInactiveSource(cec_logical_address_t srcAddr) {
cec_message_t msg;
msg.initiator = srcAddr;
msg.destination = CEC_ADDR_TV;
msg.length = 3;
msg.body[0] = CEC_MESSAGE_INACTIVE_SOURCE;
if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) {
std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
queueOutgoingMessage(msg);
}
}
void HdmiCecHandler::sendImageViewOn(cec_logical_address_t srcAddr) {
cec_message_t msg;
msg.initiator = srcAddr;
msg.destination = CEC_ADDR_TV;
msg.length = 1;
msg.body[0] = CEC_MESSAGE_IMAGE_VIEW_ON;
queueOutgoingMessage(msg);
}
void HdmiCecHandler::sendTextViewOn(cec_logical_address_t srcAddr) {
cec_message_t msg;
msg.initiator = srcAddr;
msg.destination = CEC_ADDR_TV;
msg.length = 1;
msg.body[0] = CEC_MESSAGE_TEXT_VIEW_ON;
queueOutgoingMessage(msg);
}
void HdmiCecHandler::sendGiveDevicePowerStatus(cec_logical_address_t srcAddr,
cec_logical_address_t dstAddr) {
cec_message_t msg;
msg.initiator = srcAddr;
msg.destination = dstAddr;
msg.length = 1;
msg.body[0] = CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS;
queueOutgoingMessage(msg);
}
void HdmiCecHandler::sendFeatureAbort(cec_logical_address_t srcAddr,
cec_logical_address_t dstAddr, int opcode, int reason) {
cec_message_t msg;
msg.initiator = srcAddr;
msg.destination = dstAddr;
msg.length = 3;
msg.body[0] = CEC_MESSAGE_FEATURE_ABORT;
msg.body[1] = opcode;
msg.body[2] = reason;
queueOutgoingMessage(msg);
}
void HdmiCecHandler::sendCecVersion(cec_logical_address_t srcAddr,
cec_logical_address_t dstAddr, int version) {
cec_message_t msg;
msg.initiator = srcAddr;
msg.destination = dstAddr;
msg.length = 2;
msg.body[0] = CEC_MESSAGE_CEC_VERSION;
msg.body[1] = version;
queueOutgoingMessage(msg);
}
void HdmiCecHandler::sendGiveDeviceVendorID(cec_logical_address_t srcAddr,
cec_logical_address_t dstAddr) {
cec_message_t msg;
msg.initiator = srcAddr;
msg.destination = dstAddr;
msg.length = 1;
msg.body[0] = CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID;
queueOutgoingMessage(msg);
}
void HdmiCecHandler::sendDeviceVendorID(cec_logical_address_t srcAddr,
cec_logical_address_t dstAddr) {
cec_message_t msg;
msg.initiator = srcAddr;
msg.destination = dstAddr;
msg.length = 4;
msg.body[0] = CEC_MESSAGE_DEVICE_VENDOR_ID;
uint32_t vendor_id;
mDevice->get_vendor_id(mDevice, &vendor_id);
std::memcpy(msg.body + 1, &vendor_id, 3);
queueOutgoingMessage(msg);
}
void HdmiCecHandler::sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
const char* name, size_t len) {
cec_message_t msg;
msg.initiator = srcAddr;
msg.destination = dstAddr;
msg.body[0] = CEC_MESSAGE_SET_OSD_NAME;
msg.length = min(len + 1, CEC_MESSAGE_BODY_MAX_LENGTH);
std::memcpy(msg.body + 1, name, msg.length - 1);
queueOutgoingMessage(msg);
}
void HdmiCecHandler::sendSetMenuLanguage(cec_logical_address_t srcAddr,
cec_logical_address_t dstAddr) {
char lang[4]; // buffer for 3-letter language code
JNIEnv* env = AndroidRuntime::getJNIEnv();
jstring res = (jstring) env->CallObjectMethod(mCallbacksObj,
gHdmiCecServiceClassInfo.getLanguage,
getDeviceType(srcAddr));
const char *clang = env->GetStringUTFChars(res, NULL);
strlcpy(lang, clang, sizeof(lang));
env->ReleaseStringUTFChars(res, clang);
cec_message_t msg;
msg.initiator = srcAddr;
msg.destination = dstAddr;
msg.length = 4; // opcode (1) + language code (3)
msg.body[0] = CEC_MESSAGE_SET_MENU_LANGUAGE;
std::memcpy(msg.body + 1, lang, 3);
queueOutgoingMessage(msg);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
void HdmiCecHandler::sendCecMessage(const cec_message_t& message) {
AutoMutex _l(mLock);
ALOGV("sendCecMessage");
mDevice->send_message(mDevice, &message);
}
// static
void HdmiCecHandler::onReceived(const hdmi_event_t* event, void* arg) {
HdmiCecHandler* handler = static_cast<HdmiCecHandler*>(arg);
if (handler == NULL) {
return;
}
queue_item_t item;
if (event->type == HDMI_EVENT_CEC_MESSAGE) {
item.cec = event->cec;
MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_RX, item);
handler->queueMessage(entry);
} else if (event->type == HDMI_EVENT_HOT_PLUG) {
item.hotplug = event->hotplug;
MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_HOTPLUG, item);
handler->queueMessage(entry);
}
}
// static
void HdmiCecHandler::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
if (env->ExceptionCheck()) {
ALOGE("An exception was thrown by callback '%s'.", methodName);
LOGE_EX(env);
env->ExceptionClear();
}
}
void HdmiCecHandler::dispatchMessage(const MessageEntry& entry) {
int type = entry.first;
mMessageQueueLock.unlock();
if (type == EVENT_TYPE_RX) {
mMessageQueue.pop_front();
processIncomingMessage(entry.second.cec);
} else if (type == EVENT_TYPE_TX) {
sendCecMessage(entry.second.cec);
mMessageQueue.pop_front();
} else if (type == EVENT_TYPE_HOTPLUG) {
mMessageQueue.pop_front();
bool connected = entry.second.hotplug.connected;
if (connected) {
// TODO: Update logical addresses as well, since they also could have
// changed while the cable was disconnected.
updatePhysicalAddress();
}
propagateHotplug(connected);
}
mMessageQueueLock.lock();
}
void HdmiCecHandler::processIncomingMessage(const cec_message_t& msg) {
int opcode = msg.body[0];
if (opcode == CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS) {
sendReportPhysicalAddress();
} else if (opcode == CEC_MESSAGE_REQUEST_ACTIVE_SOURCE) {
handleRequestActiveSource();
} else if (opcode == CEC_MESSAGE_GET_OSD_NAME) {
handleGetOsdName(msg);
} else if (opcode == CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID) {
handleGiveDeviceVendorID(msg);
} else if (opcode == CEC_MESSAGE_GET_CEC_VERSION) {
handleGetCECVersion(msg);
} else if (opcode == CEC_MESSAGE_GET_MENU_LANGUAGE) {
handleGetMenuLanguage(msg);
} else {
if (precheckMessage(msg)) {
propagateMessage(msg);
}
}
}
bool HdmiCecHandler::precheckMessage(const cec_message_t& msg) {
// Check if this is the broadcast message coming to itself, which need not be passed
// back to framework. This happens because CEC spec specifies that a physical device
// may host multiple logical devices. A broadcast message sent by one of them therefore
// should be able to reach the others by the loopback mechanism.
//
// Currently we don't deal with multiple logical devices, so this is not necessary.
// It should be revisited once we support hosting multiple logical devices.
int opcode = msg.body[0];
if (msg.destination == CEC_ADDR_BROADCAST &&
(opcode == CEC_MESSAGE_ACTIVE_SOURCE ||
opcode == CEC_MESSAGE_SET_STREAM_PATH ||
opcode == CEC_MESSAGE_INACTIVE_SOURCE)) {
uint16_t senderAddr;
std::memcpy(&senderAddr, &msg.body[1], 2);
if (senderAddr == mPhysicalAddress) {
return false;
}
}
return true;
}
void HdmiCecHandler::propagateMessage(const cec_message_t& msg) {
int paramLen = msg.length - 1;
jint srcAddr = msg.initiator;
jint dstAddr = msg.destination;
jint opcode = msg.body[0];
JNIEnv* env = AndroidRuntime::getJNIEnv();
jbyteArray params = env->NewByteArray(paramLen);
const jbyte* body = reinterpret_cast<const jbyte *>(msg.body + 1);
if (paramLen > 0) {
env->SetByteArrayRegion(params, 0, paramLen, body);
}
env->CallVoidMethod(mCallbacksObj,
gHdmiCecServiceClassInfo.handleMessage,
srcAddr, dstAddr, opcode, params);
env->DeleteLocalRef(params);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
void HdmiCecHandler::propagateHotplug(bool connected) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(mCallbacksObj,
gHdmiCecServiceClassInfo.handleHotplug,
connected);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
void HdmiCecHandler::handleRequestActiveSource() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jint activeDeviceType = env->CallIntMethod(mCallbacksObj,
gHdmiCecServiceClassInfo.getActiveSource);
if (activeDeviceType != INACTIVE_DEVICE_TYPE) {
sendActiveSource(getLogicalAddress(activeDeviceType));
}
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
void HdmiCecHandler::handleGetOsdName(const cec_message_t& msg) {
cec_logical_address_t addr = msg.destination;
JNIEnv* env = AndroidRuntime::getJNIEnv();
jbyteArray res = (jbyteArray) env->CallObjectMethod(mCallbacksObj,
gHdmiCecServiceClassInfo.getOsdName,
getDeviceType(addr));
jbyte *name = env->GetByteArrayElements(res, NULL);
if (name != NULL) {
sendSetOsdName(addr, msg.initiator, reinterpret_cast<const char *>(name),
env->GetArrayLength(res));
env->ReleaseByteArrayElements(res, name, JNI_ABORT);
}
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
void HdmiCecHandler::handleGiveDeviceVendorID(const cec_message_t& msg) {
sendDeviceVendorID(msg.destination, msg.initiator);
}
void HdmiCecHandler::handleGetCECVersion(const cec_message_t& msg) {
int version;
mDevice->get_version(mDevice, &version);
sendCecVersion(msg.destination, msg.initiator, version);
}
void HdmiCecHandler::handleGetMenuLanguage(const cec_message_t& msg) {
sendSetMenuLanguage(msg.destination, msg.initiator);
}
//------------------------------------------------------------------------------
#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
var = env->GetMethodID(clazz, methodName, methodDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method " methodName);
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) {
int err;
hw_module_t* module;
err = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID, const_cast<const hw_module_t **>(&module));
if (err != 0) {
ALOGE("Error acquiring hardware module: %d", err);
return 0;
}
hw_device_t* device;
err = module->methods->open(module, HDMI_CEC_HARDWARE_INTERFACE, &device);
if (err != 0) {
ALOGE("Error opening hardware module: %d", err);
return 0;
}
HdmiCecHandler *handler = new HdmiCecHandler(reinterpret_cast<hdmi_cec_device *>(device),
env->NewGlobalRef(callbacksObj));
handler->initialize();
GET_METHOD_ID(gHdmiCecServiceClassInfo.handleMessage, clazz,
"handleMessage", "(III[B)V");
GET_METHOD_ID(gHdmiCecServiceClassInfo.handleHotplug, clazz,
"handleHotplug", "(Z)V");
GET_METHOD_ID(gHdmiCecServiceClassInfo.getActiveSource, clazz,
"getActiveSource", "()I");
GET_METHOD_ID(gHdmiCecServiceClassInfo.getOsdName, clazz,
"getOsdName", "(I)[B");
GET_METHOD_ID(gHdmiCecServiceClassInfo.getLanguage, clazz,
"getLanguage", "(I)Ljava/lang/String;");
return reinterpret_cast<jlong>(handler);
}
static void nativeSendMessage(JNIEnv* env, jclass clazz, jlong handlerPtr, jint deviceType,
jint dstAddr, jint opcode, jbyteArray params) {
HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
jsize len = env->GetArrayLength(params);
ScopedByteArrayRO paramsPtr(env, params);
cec_message_t message;
message.initiator = srcAddr;
message.destination = static_cast<cec_logical_address_t>(dstAddr);
message.length = len + 1;
message.body[0] = opcode;
std::memcpy(message.body + 1, paramsPtr.get(), len);
handler->sendCecMessage(message);
}
static int nativeAllocateLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr,
jint deviceType) {
HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
return handler->initLogicalDevice(deviceType);
}
static void nativeRemoveLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr,
jint deviceType) {
HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
return handler->releaseLogicalDevice(deviceType);
}
static void nativeSendActiveSource(JNIEnv* env, jclass clazz, jlong handlerPtr,
jint deviceType) {
HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
handler->sendActiveSource(srcAddr);
}
static void nativeSendInactiveSource(JNIEnv* env, jclass clazz, jlong handlerPtr,
jint deviceType) {
HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
handler->sendInactiveSource(srcAddr);
}
static void nativeSendImageViewOn(JNIEnv* env, jclass clazz, jlong handlerPtr,
jint deviceType) {
HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
handler->sendImageViewOn(srcAddr);
}
static void nativeSendTextViewOn(JNIEnv* env, jclass clazz, jlong handlerPtr,
jint deviceType) {
HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
handler->sendTextViewOn(srcAddr);
}
static void nativeSendGiveDevicePowerStatus(JNIEnv* env, jclass clazz, jlong handlerPtr,
jint deviceType, jint destination) {
HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
cec_logical_address_t dstAddr = static_cast<cec_logical_address_t>(destination);
handler->sendGiveDevicePowerStatus(srcAddr, dstAddr);
}
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "(Lcom/android/server/hdmi/HdmiCecService;)J",
(void *)nativeInit },
{ "nativeSendMessage", "(JIII[B)V",
(void *)nativeSendMessage },
{ "nativeAllocateLogicalAddress", "(JI)I",
(void *)nativeAllocateLogicalAddress },
{ "nativeRemoveLogicalAddress", "(JI)V",
(void *)nativeRemoveLogicalAddress },
{ "nativeSendActiveSource", "(JI)V",
(void *)nativeSendActiveSource },
{ "nativeSendInactiveSource", "(JI)V",
(void *)nativeSendInactiveSource },
{ "nativeSendImageViewOn", "(JI)V",
(void *)nativeSendImageViewOn },
{ "nativeSendTextViewOn", "(JI)V",
(void *)nativeSendTextViewOn },
{ "nativeSendGiveDevicePowerStatus", "(JII)V",
(void *)nativeSendGiveDevicePowerStatus }
};
#define CLASS_PATH "com/android/server/hdmi/HdmiCecService"
int register_android_server_hdmi_HdmiCecService(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
return 0;
}
} /* namespace android */

View File

@ -21,6 +21,7 @@
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_AssetAtlasService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
@ -28,15 +29,15 @@ int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_AssetAtlasService(JNIEnv* env);
int register_android_server_dreams_McuHal(JNIEnv* env);
int register_android_server_hdmi_HdmiCecService(JNIEnv* env);
};
using namespace android;
@ -69,6 +70,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
register_android_server_AssetAtlasService(env);
register_android_server_ConsumerIrService(env);
register_android_server_dreams_McuHal(env);
register_android_server_hdmi_HdmiCecService(env);
return JNI_VERSION_1_4;
}

View File

@ -115,6 +115,8 @@ public final class SystemServer {
"com.android.server.print.PrintManagerService";
private static final String USB_SERVICE_CLASS =
"com.android.server.usb.UsbService$Lifecycle";
private static final String HDMI_CEC_SERVICE_CLASS =
"com.android.server.hdmi.HdmiCecService";
private final int mFactoryTestMode;
private Timer mProfilerSnapshotTimer;
@ -888,6 +890,12 @@ public final class SystemServer {
reportWtf("starting Print Service", e);
}
try {
mSystemServiceManager.startService(HDMI_CEC_SERVICE_CLASS);
} catch (Throwable e) {
reportWtf("starting HdmiCec Service", e);
}
if (!disableNonCoreServices) {
try {
Slog.i(TAG, "Media Router Service");