CEC Initialization per device type

CEC initialization may vary from device type to type. Changed logical address
map to HdmiCecLocalDevice map to handle it. These per-type classes for local
device will be extended also to take care of incoming CEC commands that should
require different action based on device type.

Change-Id: Ia59a464607b14e942fcf83e63aa1d6310ca6a575
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 662159d..23044dd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -21,7 +21,6 @@
 import android.hardware.hdmi.HdmiCecMessage;
 import android.os.Handler;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 
 import libcore.util.EmptyArray;
 
@@ -75,14 +74,12 @@
     // used as key of container.
     private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos =
             new SparseArray<HdmiCecDeviceInfo>();
-    // Set-like container for all local devices' logical address.
-    // Key and value are same.
-    private final SparseIntArray mLocalAddresses = new SparseIntArray();
+
+    // Stores the local CEC devices in the system.
+    private final ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
 
     // Private constructor.  Use HdmiCecController.create().
     private HdmiCecController() {
-        // TODO: Consider restoring the local device addresses from persistent storage
-        //       to allocate the same addresses again if possible.
     }
 
     /**
@@ -96,53 +93,41 @@
      *         returns {@code null}.
      */
     static HdmiCecController create(HdmiControlService service) {
-        HdmiCecController handler = new HdmiCecController();
-        long nativePtr = nativeInit(handler);
+        HdmiCecController controller = new HdmiCecController();
+        long nativePtr = nativeInit(controller);
         if (nativePtr == 0L) {
-            handler = null;
+            controller = null;
             return null;
         }
 
-        handler.init(service, nativePtr);
-        return handler;
+        controller.init(service, nativePtr);
+        return controller;
+    }
+
+    private void init(HdmiControlService service, long nativePtr) {
+        mService = service;
+        mIoHandler = new Handler(service.getServiceLooper());
+        mControlHandler = new Handler(service.getServiceLooper());
+        mNativePtr = nativePtr;
     }
 
     /**
-     * Initialize {@link #mLocalAddresses} by allocating logical addresses for each hosted type.
+     * Perform initialization for each hosted device.
      *
-     * @param deviceTypes local device types
+     * @param deviceTypes array of device types
      */
     void initializeLocalDevices(int[] deviceTypes) {
-        for (int deviceType : deviceTypes) {
-            int preferred = getPreferredAddress(deviceType);
-            allocateLogicalAddress(deviceType, preferred, new AllocateLogicalAddressCallback() {
-                @Override
-                public void onAllocated(int deviceType, int logicalAddress) {
-                    addLogicalAddress(logicalAddress);
-                }
-            });
-        }
-    }
-
-    /**
-     * Get the preferred address for a given type.
-     *
-     * @param deviceType logical device type to get the address for
-     * @return preferred address; {@link HdmiCec#ADDR_UNREGISTERED} if not available.
-     */
-    private int getPreferredAddress(int deviceType) {
-        // Uses the data restored from persistent memory at boot up if they are available.
-        // Otherwise we return UNREGISTERED indicating there is no preferred address.
-        // Note that for address SPECIFIC_USE(14), HdmiCec.getTypeFromAddress() returns DEVICE_TV,
-        // meaning that we do not support device type video processor yet.
-        for (int i = 0; i < mLocalAddresses.size(); ++i) {
-            int address = mLocalAddresses.keyAt(i);
-            int type = HdmiCec.getTypeFromAddress(address);
-            if (type == deviceType) {
-                return address;
+        for (int type : deviceTypes) {
+            HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type);
+            if (device == null) {
+                continue;
             }
+            // TODO: Consider restoring the local device addresses from persistent storage
+            //       to allocate the same addresses again if possible.
+            device.setPreferredAddress(HdmiCec.ADDR_UNREGISTERED);
+            mLocalDevices.add(device);
+            device.init();
         }
-        return HdmiCec.ADDR_UNREGISTERED;
     }
 
     /**
@@ -310,7 +295,6 @@
      */
     int addLogicalAddress(int newLogicalAddress) {
         if (HdmiCec.isValidAddress(newLogicalAddress)) {
-            mLocalAddresses.put(newLogicalAddress, newLogicalAddress);
             return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
         } else {
             return -1;
@@ -325,7 +309,9 @@
     void clearLogicalAddress() {
         // TODO: consider to backup logical address so that new logical address
         // allocation can use it as preferred address.
-        mLocalAddresses.clear();
+        for (HdmiCecLocalDevice device : mLocalDevices) {
+            device.clearAddress();
+        }
         nativeClearLogicalAddress(mNativePtr);
     }
 
@@ -367,18 +353,17 @@
         mControlHandler.post(runnable);
     }
 
-    private void init(HdmiControlService service, long nativePtr) {
-        mService = service;
-        mIoHandler = new Handler(service.getServiceLooper());
-        mControlHandler = new Handler(service.getServiceLooper());
-        mNativePtr = nativePtr;
-    }
-
     private boolean isAcceptableAddress(int address) {
-        // Can access command targeting devices available in local device or
-        // broadcast command.
-        return address == HdmiCec.ADDR_BROADCAST
-                || mLocalAddresses.indexOfKey(address) < 0;
+        // Can access command targeting devices available in local device or broadcast command.
+        if (address == HdmiCec.ADDR_BROADCAST) {
+            return true;
+        }
+        for (HdmiCecLocalDevice device : mLocalDevices) {
+            if (device.isAddressOf(address)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private void onReceiveCommand(HdmiCecMessage message) {
@@ -397,6 +382,10 @@
         sendCommand(cecMessage, null);
     }
 
+    void sendCommand(HdmiCecMessage cecMessage) {
+        sendCommand(cecMessage, null);
+    }
+
     void sendCommand(final HdmiCecMessage cecMessage,
             final HdmiControlService.SendMessageCallback callback) {
         runOnIoThread(new Runnable() {