Implement skeleton of new HDMI Control Service.

HdmiCecService is a system service handling HDMI-CEC features
and command. Recently we found out that industry has more
requirements to support HDMI-CEC. Also, MHL is another
standard should be in our pocket. Basically, MHL is
a standard to support communication between mobile device
and TV or Av device. As CEC is a control standard over HDMI
cable, MHL has control channel for peer device.
There behavior is very similiar. Both have commands that
can change Tv's current input and can send/receive key
to other device to control other deivce or TV.

In order to cover both CEC and MHL, current HdmiCecService
implementation has limitation. We had several
session of discussion and decided to refactor
HdmiCecService into HdmiControlService.
For each standard it will have separate controller instance
like HdmiCecController and HdmiMhlController.

In this change I didn't touch original HdmiCecService
because some component, like cast receiver, uses HdmiCecService.
For a while we will keep HdmiCecService until HdmiControlService
accomodates all features of HdmiCecService.

Change-Id: I5485280ab803dbf071d898bfbe34be0b11ce7958
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
new file mode 100644
index 0000000..5f07108
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -0,0 +1,94 @@
+/*
+ * 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.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
+ * and pass it to CEC HAL so that it sends message to other device. For incoming
+ * message it translates the message and delegates it to proper module.
+ *
+ * <p>It can be created only by {@link HdmiCecController#create}
+ *
+ * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
+ */
+class HdmiCecController {
+    private static final String TAG = "HdmiCecController";
+
+    // Handler instance to process synchronous I/O (mainly send) message.
+    private Handler mIoHandler;
+
+    // Handler instance to process various messages coming from other CEC
+    // device or issued by internal state change.
+    private Handler mMessageHandler;
+
+    // Stores the pointer to the native implementation of the service that
+    // interacts with HAL.
+    private long mNativePtr;
+
+    // Private constructor.  Use HdmiCecController.create().
+    private HdmiCecController() {
+    }
+
+    /**
+     * A factory method to get {@link HdmiCecController}. If it fails to initialize
+     * inner device or has no device it will return {@code null}.
+     *
+     * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
+     *
+     * @param ioLooper a Looper instance to handle IO (mainly send message) operation.
+     * @param messageHandler a message handler that processes a message coming from other
+     *                       CEC compatible device or callback of internal state change.
+     * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
+     *         returns {@code null}.
+     */
+    static HdmiCecController create(Looper ioLooper, Handler messageHandler) {
+        HdmiCecController handler = new HdmiCecController();
+        long nativePtr = nativeInit(handler);
+        if (nativePtr == 0L) {
+            handler = null;
+            return null;
+        }
+
+        handler.init(ioLooper, messageHandler, nativePtr);
+        return handler;
+    }
+
+    private void init(Looper ioLooper, Handler messageHandler, long nativePtr) {
+        mIoHandler = new Handler(ioLooper) {
+                @Override
+            public void handleMessage(Message msg) {
+                // TODO: Call native sendMessage.
+            }
+        };
+
+        mMessageHandler = messageHandler;
+        mNativePtr = nativePtr;
+    }
+
+    /**
+     * Called by native when an HDMI-CEC message arrived.
+     */
+    private void handleMessage(int srcAddress, int dstAddres, int opcode, byte[] params) {
+        // TODO: Translate message and delegate it to main message handler.
+    }
+
+    private static native long nativeInit(HdmiCecController handler);
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
new file mode 100644
index 0000000..56c5b49
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -0,0 +1,70 @@
+/*
+ * 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.annotation.Nullable;
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Slog;
+
+import com.android.server.SystemService;
+
+/**
+ * Provides a service for sending and processing HDMI control messages,
+ * HDMI-CEC and MHL control command, and providing the information on both standard.
+ */
+public final class HdmiControlService extends SystemService {
+    private static final String TAG = "HdmiControlService";
+
+    // A thread to handle synchronous IO of CEC and MHL control service.
+    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
+    // and sparse call it shares a thread to handle IO operations.
+    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
+
+    // Main handler class to handle incoming message from each controller.
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            // TODO: Add handler for each message type.
+        }
+    };
+
+    @Nullable
+    private HdmiCecController mCecController;
+
+    @Nullable
+    private HdmiMhlController mMhlController;
+
+    public HdmiControlService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        mCecController = HdmiCecController.create(mIoThread.getLooper(), mHandler);
+        if (mCecController == null) {
+            Slog.i(TAG, "Device does not support HDMI-CEC.");
+        }
+
+        mMhlController = HdmiMhlController.create(mIoThread.getLooper(), mHandler);
+        if (mMhlController == null) {
+            Slog.i(TAG, "Device does not support MHL-control.");
+        }
+    }
+}
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 1b3887c..51583a5 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -11,7 +11,9 @@
     $(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_HdmiCecController.cpp \
     $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecService.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiMhlController.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 \
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
new file mode 100644
index 0000000..f3e8f3c
--- /dev/null
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 "HdmiCecControllerJni"
+
+#define LOG_NDEBUG 1
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <hardware/hdmi_cec.h>
+
+namespace android {
+
+static struct {
+    jmethodID handleMessage;
+} gHdmiCecControllerClassInfo;
+
+
+class HdmiCecController {
+public:
+    HdmiCecController(jobject callbacksObj);
+
+private:
+    static void onReceived(const hdmi_event_t* event, void* arg);
+
+    jobject mCallbacksObj;
+};
+
+HdmiCecController::HdmiCecController(jobject callbacksObj) :
+    mCallbacksObj(callbacksObj) {
+}
+
+// static
+void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) {
+    HdmiCecController* handler = static_cast<HdmiCecController*>(arg);
+    if (handler == NULL) {
+        return;
+    }
+
+    // TODO: propagate message to Java layer.
+}
+
+
+//------------------------------------------------------------------------------
+static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) {
+    // TODO: initialize hal and pass it to controller if ready.
+
+    HdmiCecController* controller = new HdmiCecController(
+            env->NewGlobalRef(callbacksObj));
+
+    return reinterpret_cast<jlong>(controller);
+}
+
+static JNINativeMethod sMethods[] = {
+    /* name, signature, funcPtr */
+    { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecController;)J",
+            (void *) nativeInit },
+};
+
+#define CLASS_PATH "com/android/server/hdmi/HdmiCecController"
+
+int register_android_server_hdmi_HdmiCecController(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 bf9f7f4..1feb325 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -38,7 +38,9 @@
 int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
 int register_android_server_connectivity_Vpn(JNIEnv* env);
 int register_android_server_dreams_McuHal(JNIEnv* env);
+int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
 int register_android_server_hdmi_HdmiCecService(JNIEnv* env);
+int register_android_server_hdmi_HdmiMhlController(JNIEnv* env);
 };
 
 using namespace android;
@@ -72,7 +74,10 @@
     register_android_server_ConsumerIrService(env);
     register_android_server_dreams_McuHal(env);
     register_android_server_BatteryStatsService(env);
+    register_android_server_hdmi_HdmiCecController(env);
+    // TODO: remove this once replaces HdmiCecService with HdmiControlService.
     register_android_server_hdmi_HdmiCecService(env);
+    register_android_server_hdmi_HdmiMhlController(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 f08d69f..259a6d3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -61,6 +61,7 @@
 import com.android.server.content.ContentService;
 import com.android.server.display.DisplayManagerService;
 import com.android.server.dreams.DreamManagerService;
+import com.android.server.hdmi.HdmiControlService;
 import com.android.server.input.InputManagerService;
 import com.android.server.lights.LightsManager;
 import com.android.server.lights.LightsService;
@@ -919,6 +920,12 @@
             }
 
             try {
+                mSystemServiceManager.startService(HdmiControlService.class);
+            } catch (Throwable e) {
+                reportWtf("starting HdmiControlService", e);
+            }
+
+            try {
                 Slog.i(TAG, "TvInputManagerService");
                 mSystemServiceManager.startService(TvInputManagerService.class);
             } catch (Throwable e) {