Add HDMI-CEC service

This CL adds a system service handling HDMI-CEC protocol. The service
is equipped with the capability sending/receiving HDMI-CEC messages

Not all the messages are in place. Currently it has messages to support
a few features only, as follows:

- One touch play
- System information
- Routing control (partially - active source status maintenance only)
- Device OSD transfer
- Power status

It will be extended to cover the wider usages in the follow up CLs.

The CEC standard version referenced in the implementation is 1.3a.

Change-Id: Ifed0b02f52ebf098eddb3bd0987efbf353b7e8fe
diff --git a/Android.mk b/Android.mk
index 7cbc339..0355c62 100644
--- a/Android.mk
+++ b/Android.mk
@@ -130,6 +130,8 @@
 	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 \
diff --git a/api/current.txt b/api/current.txt
index 67eb6ea..e98432a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10898,6 +10898,54 @@
 
 }
 
+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 {
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
new file mode 100644
index 0000000..c95431a
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -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 "";
+    }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.aidl b/core/java/android/hardware/hdmi/HdmiCecMessage.aidl
new file mode 100644
index 0000000..6687ba4
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.aidl
@@ -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;
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java
new file mode 100644
index 0000000..be94d97
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java
@@ -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();
+    }
+}
+
diff --git a/core/java/android/hardware/hdmi/IHdmiCecListener.aidl b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl
new file mode 100644
index 0000000..d281ce6
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl
@@ -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);
+}
diff --git a/core/java/android/hardware/hdmi/IHdmiCecService.aidl b/core/java/android/hardware/hdmi/IHdmiCecService.aidl
new file mode 100644
index 0000000..6fefcf8
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiCecService.aidl
@@ -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);
+}
+
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 05c9b17..317e307 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -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. -->
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
new file mode 100644
index 0000000..d30edf5
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
@@ -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;
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecService.java b/services/core/java/com/android/server/hdmi/HdmiCecService.java
new file mode 100644
index 0000000..01f2ec3
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecService.java
@@ -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);
+}
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index d1cfff4..0843e94 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -5,23 +5,26 @@
 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 \
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
new file mode 100644
index 0000000..6170c09
--- /dev/null
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
@@ -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 */
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 00986d5..904966a 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -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_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 @@
     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;
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index db40cbe..7fb591b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -114,6 +114,8 @@
             "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;
@@ -887,6 +889,12 @@
                 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");