OSDN Git Service

Rearrange ownership between Hdmi control modules.
authorJungshik Jang <jayjang@google.com>
Mon, 16 Jun 2014 07:45:36 +0000 (16:45 +0900)
committerJungshik Jang <jayjang@google.com>
Tue, 17 Jun 2014 04:03:55 +0000 (13:03 +0900)
Here is a list of changes on this. (R: rationale)
1. HdmiCecLocalDeviceTv takes over responsibilty of device info
management.
 R: All devices infos are added or removed by only Tv's
device discovery and hot plug detection mecanism

2. Each HdmiCecLocalDevice manages FeatureAction and Cec
 message cache.
 R: There is no direct connection between actions that
 are created in different device action. If there is
 an same actions created from different local device,
 they should be managed independently.

3. Active path and logical address is managed by
   HdmiCecLocalDevice.
 R: All device should know active path of current source.

4. All system audio & ARC features are handled by
   HdmiCecLocalDeviceTv
 R: In terms of ARC, theoretically, any device can be transmiter of
   ARC but TV is the de facto device
   On other hands, for system audio Tv is the recipeint
    of request.

Change-Id: Iac9ff43fb41798ed4f94c61d23345fe5fe777fbb

24 files changed:
services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
services/core/java/com/android/server/hdmi/DeviceSelectAction.java
services/core/java/com/android/server/hdmi/FeatureAction.java
services/core/java/com/android/server/hdmi/HdmiCecController.java
services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
services/core/java/com/android/server/hdmi/HdmiControlService.java
services/core/java/com/android/server/hdmi/HdmiUtils.java
services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
services/core/java/com/android/server/hdmi/NewDeviceAction.java
services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
services/core/java/com/android/server/hdmi/RequestArcAction.java
services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
services/core/java/com/android/server/hdmi/RoutingControlAction.java
services/core/java/com/android/server/hdmi/SendKeyAction.java
services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
services/core/java/com/android/server/hdmi/SystemAudioAction.java
services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
services/core/java/com/android/server/hdmi/VendorSpecificAction.java

index bcd08eb..23cf40b 100644 (file)
@@ -26,31 +26,29 @@ import android.util.Slog;
 
 /**
  * Handles CEC command &lt;Active Source&gt;.
- *
- * <p>Used by feature actions that need to handle the command in their flow.
+ * <p>
+ * Used by feature actions that need to handle the command in their flow.
  */
 final class ActiveSourceHandler {
     private static final String TAG = "ActiveSourceHandler";
 
+    private final HdmiCecLocalDevice mSource;
     private final HdmiControlService mService;
-    private final int mSourceAddress;
-    private final int mSourcePath;
-    @Nullable private final IHdmiControlCallback mCallback;
+    @Nullable
+    private final IHdmiControlCallback mCallback;
 
-    static ActiveSourceHandler create(HdmiControlService service, int sourceAddress,
-            int sourcePath, IHdmiControlCallback callback) {
-        if (service == null) {
+    static ActiveSourceHandler create(HdmiCecLocalDevice source,
+            IHdmiControlCallback callback) {
+        if (source == null) {
             Slog.e(TAG, "Wrong arguments");
             return null;
         }
-        return new ActiveSourceHandler(service, sourceAddress, sourcePath, callback);
+        return new ActiveSourceHandler(source, callback);
     }
 
-    private ActiveSourceHandler(HdmiControlService service, int sourceAddress, int sourcePath,
-            IHdmiControlCallback callback) {
-        mService = service;
-        mSourceAddress = sourceAddress;
-        mSourcePath = sourcePath;
+    private ActiveSourceHandler(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
+        mSource = source;
+        mService = mSource.getService();
         mCallback = callback;
     }
 
@@ -61,7 +59,7 @@ final class ActiveSourceHandler {
      * @param routingPath routing path of the device to be the active source
      */
     void process(int deviceLogicalAddress, int routingPath) {
-        if (mSourcePath == routingPath && mService.getActiveSource() == mSourceAddress) {
+        if (getSourcePath() == routingPath && mSource.getActiveSource() == getSourceAddress()) {
             invokeCallback(HdmiCec.RESULT_SUCCESS);
             return;
         }
@@ -69,14 +67,14 @@ final class ActiveSourceHandler {
         if (device == null) {
             // "New device action" initiated by <Active Source> does not require
             // "Routing change action".
-            mService.addAndStartAction(new NewDeviceAction(mService, mSourceAddress,
-                    deviceLogicalAddress, routingPath, false));
+            mSource.addAndStartAction(new NewDeviceAction(mSource, deviceLogicalAddress,
+                    routingPath, false));
         }
 
-        if (!mService.isInPresetInstallationMode()) {
-            int prevActiveInput = mService.getActiveInput();
-            mService.updateActiveDevice(deviceLogicalAddress, routingPath);
-            if (prevActiveInput != mService.getActiveInput()) {
+        if (!mSource.isInPresetInstallationMode()) {
+            int prevActiveInput = mSource.getActiveInput();
+            mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
+            if (prevActiveInput != mSource.getActiveInput()) {
                 // TODO: change port input here.
             }
             invokeCallback(HdmiCec.RESULT_SUCCESS);
@@ -84,24 +82,33 @@ final class ActiveSourceHandler {
             // TV is in a mode that should keep its current source/input from
             // being changed for its operation. Reclaim the active source
             // or switch the port back to the one used for the current mode.
-            if (mService.getActiveSource() == mSourceAddress) {
+            if (mSource.getActiveSource() == getSourceAddress()) {
                 HdmiCecMessage activeSource =
-                        HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath);
+                        HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(),
+                                getSourcePath());
                 mService.sendCecCommand(activeSource);
-                mService.updateActiveDevice(deviceLogicalAddress, routingPath);
+                mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
                 invokeCallback(HdmiCec.RESULT_SUCCESS);
             } else {
-                int activePath = mService.getActivePath();
-                mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
+                int activePath = mSource.getActivePath();
+                mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
                         routingPath, activePath));
                 // TODO: Start port select action here
-                // PortSelectAction action = new PortSelectAction(mService, mSourceAddress,
-                //        activePath, mCallback);
+                // PortSelectAction action = new PortSelectAction(mService, getSourceAddress(),
+                // activePath, mCallback);
                 // mService.addActionAndStart(action);
             }
         }
     }
 
+    private final int getSourceAddress() {
+        return mSource.getDeviceInfo().getLogicalAddress();
+    }
+
+    private final int getSourcePath() {
+        return mSource.getDeviceInfo().getPhysicalAddress();
+    }
+
     private void invokeCallback(int result) {
         if (mCallback == null) {
             return;
index f7392e9..daeff3c 100644 (file)
@@ -94,12 +94,10 @@ final class DeviceDiscoveryAction extends FeatureAction {
     /**
      * Constructor.
      *
-     * @param service an instance of {@link HdmiControlService}.
-     * @param sourceAddress a logical address which initiates this action
+     * @param source an instance of {@link HdmiCecLocalDevice}.
      */
-    DeviceDiscoveryAction(HdmiControlService service, int sourceAddress,
-            DeviceDiscoveryCallback callback) {
-        super(service, sourceAddress);
+    DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) {
+        super(source);
         mCallback = Preconditions.checkNotNull(callback);
     }
 
@@ -108,7 +106,7 @@ final class DeviceDiscoveryAction extends FeatureAction {
         mDevices.clear();
         mState = STATE_WAITING_FOR_DEVICE_POLLING;
 
-        mService.pollDevices(new DevicePollingCallback() {
+        pollDevices(new DevicePollingCallback() {
             @Override
             public void onPollingFinished(List<Integer> ackedAddress) {
                 if (ackedAddress.isEmpty()) {
@@ -156,7 +154,7 @@ final class DeviceDiscoveryAction extends FeatureAction {
         if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS)) {
             return;
         }
-        sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, address));
+        sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(), address));
         addTimer(mState, TIMEOUT_MS);
     }
 
@@ -179,7 +177,7 @@ final class DeviceDiscoveryAction extends FeatureAction {
         if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_SET_OSD_NAME)) {
             return;
         }
-        sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, address));
+        sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(), address));
         addTimer(mState, TIMEOUT_MS);
     }
 
@@ -203,12 +201,13 @@ final class DeviceDiscoveryAction extends FeatureAction {
         if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
             return;
         }
-        sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress, address));
+        sendCommand(
+                HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(), address));
         addTimer(mState, TIMEOUT_MS);
     }
 
     private boolean mayProcessMessageIfCached(int address, int opcode) {
-        HdmiCecMessage message = mService.getCecMessageCache().getMessage(address, opcode);
+        HdmiCecMessage message = getCecMessageCache().getMessage(address, opcode);
         if (message != null) {
             processCommand(message);
             return true;
index 63c2182..51df473 100644 (file)
@@ -16,9 +16,10 @@ package com.android.server.hdmi;
  * limitations under the License.
  */
 
-import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.hdmi.HdmiCec;
 import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
 import android.os.RemoteException;
 import android.util.Slog;
 
@@ -40,18 +41,18 @@ final class DevicePowerStatusAction extends FeatureAction {
     private final int mTargetAddress;
     private final IHdmiControlCallback mCallback;
 
-    static DevicePowerStatusAction create(HdmiControlService service, int sourceAddress,
+    static DevicePowerStatusAction create(HdmiCecLocalDevice source,
             int targetAddress, IHdmiControlCallback callback) {
-        if (service == null || callback == null) {
+        if (source == null || callback == null) {
             Slog.e(TAG, "Wrong arguments");
             return null;
         }
-        return new DevicePowerStatusAction(service, sourceAddress, targetAddress, callback);
+        return new DevicePowerStatusAction(source, targetAddress, callback);
     }
 
-    private DevicePowerStatusAction(HdmiControlService service, int sourceAddress,
+    private DevicePowerStatusAction(HdmiCecLocalDevice localDevice,
             int targetAddress, IHdmiControlCallback callback) {
-        super(service, sourceAddress);
+        super(localDevice);
         mTargetAddress = targetAddress;
         mCallback = callback;
     }
@@ -65,8 +66,8 @@ final class DevicePowerStatusAction extends FeatureAction {
     }
 
     private void queryDevicePowerStatus() {
-        mService.sendCecCommand(
-                HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, mTargetAddress));
+        sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+                mTargetAddress));
     }
 
     @Override
index a8696a1..dbe3b80 100644 (file)
 
 package com.android.server.hdmi;
 
-import android.hardware.hdmi.IHdmiControlCallback;
-import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiTvClient;
+import android.hardware.hdmi.IHdmiControlCallback;
 import android.os.RemoteException;
 import android.util.Slog;
 
@@ -66,24 +67,20 @@ final class DeviceSelectAction extends FeatureAction {
 
     private final HdmiCecDeviceInfo mTarget;
     private final IHdmiControlCallback mCallback;
-    private final int mSourcePath;
 
     private int mPowerStatusCounter = 0;
 
     /**
      * Constructor.
      *
-     * @param service {@link HdmiControlService} instance
-     * @param sourceAddress logical address of TV initiating this action
-     * @param sourcePath physical address of TV
+     * @param source {@link HdmiCecLocalDevice} instance
      * @param target target logical device that will be a new active source
      * @param callback callback object
      */
-    public DeviceSelectAction(HdmiControlService service, int sourceAddress, int sourcePath,
+    public DeviceSelectAction(HdmiCecLocalDevice source,
             HdmiCecDeviceInfo target, IHdmiControlCallback callback) {
-        super(service, sourceAddress);
+        super(source);
         mCallback = callback;
-        mSourcePath = sourcePath;
         mTarget = target;
     }
 
@@ -96,7 +93,7 @@ final class DeviceSelectAction extends FeatureAction {
 
     private void queryDevicePowerStatus() {
         sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
-                mSourceAddress, mTarget.getLogicalAddress()));
+                getSourceAddress(), mTarget.getLogicalAddress()));
         mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
         addTimer(mState, TIMEOUT_MS);
     }
@@ -118,7 +115,8 @@ final class DeviceSelectAction extends FeatureAction {
             case STATE_WAIT_FOR_ACTIVE_SOURCE:
                 if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) {
                     int activePath = HdmiUtils.twoBytesToInt(params);
-                    ActiveSourceHandler.create(mService, mSourceAddress, mSourcePath, mCallback)
+                    ActiveSourceHandler
+                            .create(localDevice(), mCallback)
                             .process(cmd.getSource(), activePath);
                     finish();
                     return true;
@@ -174,15 +172,15 @@ final class DeviceSelectAction extends FeatureAction {
 
     private void sendSetStreamPath() {
         sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(
-                mSourceAddress, mTarget.getPhysicalAddress()));
+                getSourceAddress(), mTarget.getPhysicalAddress()));
         mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
         addTimer(mState, TIMEOUT_ACTIVE_SOURCE_MS);
     }
 
     private void sendRemoteKeyCommand(int keyCode) {
-        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress,
+        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
                 mTarget.getLogicalAddress(), keyCode));
-        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress,
+        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
                 mTarget.getLogicalAddress()));
     }
 
index ae272b4..0ec17f6 100644 (file)
@@ -22,6 +22,9 @@ import android.os.Message;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
+
+import java.util.List;
 
 /**
  * Encapsulates a sequence of CEC/MHL command exchange for a certain feature.
@@ -33,14 +36,13 @@ import com.android.internal.annotations.VisibleForTesting;
  * of the object. All the actual action classes inherit FeatureAction.
  *
  * <p>More than one FeatureAction objects can be up and running simultaneously,
- * maintained by {@link HdmiControlService}. Each action is passed a new command
+ * maintained by {@link HdmiCecLocalDevice}. Each action is passed a new command
  * arriving from the bus, and either consumes it if the command is what the action expects,
  * or yields it to other action.
  *
  * Declared as package private, accessed by {@link HdmiControlService} only.
  */
 abstract class FeatureAction {
-
     private static final String TAG = "FeatureAction";
 
     // Timer handler message used for timeout event
@@ -56,19 +58,16 @@ abstract class FeatureAction {
     // Internal state indicating the progress of action.
     protected int mState = STATE_NONE;
 
-    protected final HdmiControlService mService;
-
-    // Logical address of the device for which the feature action is taken. The commands
-    // generated in an action all use this field as source address.
-    protected final int mSourceAddress;
+    private final HdmiControlService mService;
+    private final HdmiCecLocalDevice mSource;
 
     // Timer that manages timeout events.
     protected ActionTimer mActionTimer;
 
-    FeatureAction(HdmiControlService service, int sourceAddress) {
-        mService = service;
-        mSourceAddress = sourceAddress;
-        mActionTimer = createActionTimer(service.getServiceLooper());
+    FeatureAction(HdmiCecLocalDevice source) {
+        mSource = source;
+        mService = mSource.getService();
+        mActionTimer = createActionTimer(mService.getServiceLooper());
     }
 
     @VisibleForTesting
@@ -175,6 +174,42 @@ abstract class FeatureAction {
         mService.sendCecCommand(cmd, callback);
     }
 
+    protected final void addAndStartAction(FeatureAction action) {
+        mSource.addAndStartAction(action);
+    }
+
+    protected final <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
+        return mSource.getActions(clazz);
+    }
+
+    protected final HdmiCecMessageCache getCecMessageCache() {
+        return mSource.getCecMessageCache();
+    }
+
+    /**
+     * Remove the action from the action queue. This is called after the action finishes
+     * its role.
+     *
+     * @param action
+     */
+    protected final void removeAction(FeatureAction action) {
+        mSource.removeAction(action);
+    }
+
+    protected final <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+        mSource.removeActionExcept(clazz, null);
+    }
+
+    protected final <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+            final FeatureAction exception) {
+        mSource.removeActionExcept(clazz, exception);
+    }
+
+    protected final void pollDevices(DevicePollingCallback callback, int pickStrategy,
+            int retryCount) {
+        mService.pollDevices(callback, pickStrategy, retryCount);
+    }
+
     /**
      * Clean up action's state.
      *
@@ -194,13 +229,23 @@ abstract class FeatureAction {
         removeAction(this);
     }
 
-    /**
-     * Remove the action from the action queue. This is called after the action finishes
-     * its role.
-     *
-     * @param action
-     */
-    private void removeAction(FeatureAction action) {
-        mService.removeAction(action);
+    protected final HdmiCecLocalDevice localDevice() {
+        return mSource;
+    }
+
+    protected final HdmiCecLocalDevicePlayback playback() {
+        return (HdmiCecLocalDevicePlayback) mSource;
+    }
+
+    protected final HdmiCecLocalDeviceTv tv() {
+        return (HdmiCecLocalDeviceTv) mSource;
+    }
+
+    protected final int getSourceAddress() {
+        return mSource.getDeviceInfo().getLogicalAddress();
+    }
+
+    protected final int getSourcePath() {
+        return mSource.getDeviceInfo().getPhysicalAddress();
     }
 }
index a0c635d..fe16869 100644 (file)
@@ -17,7 +17,6 @@
 package com.android.server.hdmi;
 
 import android.hardware.hdmi.HdmiCec;
-import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.hardware.hdmi.HdmiCecMessage;
 import android.hardware.hdmi.HdmiPortInfo;
 import android.os.Handler;
@@ -109,10 +108,6 @@ final class HdmiCecController {
 
     private HdmiControlService mService;
 
-    // Map-like container of all cec devices including local ones.
-    // A logical address of device is used as key of container.
-    private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
-
     // Stores the local CEC devices in the system. Device type is used for key.
     private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
 
@@ -224,90 +219,6 @@ final class HdmiCecController {
         return body;
     }
 
-    /**
-     * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
-     * logical address as new device info's.
-     *
-     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
-     *
-     * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
-     * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
-     *         that has the same logical address as new one has.
-     */
-    HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
-        assertRunOnServiceThread();
-        HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
-        if (oldDeviceInfo != null) {
-            removeDeviceInfo(deviceInfo.getLogicalAddress());
-        }
-        mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
-        return oldDeviceInfo;
-    }
-
-    /**
-     * Remove a device info corresponding to the given {@code logicalAddress}.
-     * It returns removed {@link HdmiCecDeviceInfo} if exists.
-     *
-     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
-     *
-     * @param logicalAddress logical address of device to be removed
-     * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
-     */
-    HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
-        assertRunOnServiceThread();
-        HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
-        if (deviceInfo != null) {
-            mDeviceInfos.remove(logicalAddress);
-        }
-        return deviceInfo;
-    }
-
-    /**
-     * Clear all device info.
-     *
-     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
-     */
-    void clearDeviceInfoList() {
-        assertRunOnServiceThread();
-        mDeviceInfos.clear();
-    }
-
-    /**
-     * Return a list of all {@link HdmiCecDeviceInfo}.
-     *
-     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
-     *
-     * @param includeLocalDevice whether to add local device or not
-     */
-    List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
-        assertRunOnServiceThread();
-        if (includeLocalDevice) {
-            return sparseArrayToList(mDeviceInfos);
-        } else {
-            ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
-            for (int i = 0; i < mDeviceInfos.size(); ++i) {
-                HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
-                if (mRemoteDeviceAddressPredicate.apply(info.getLogicalAddress())) {
-                    infoList.add(info);
-                }
-            }
-            return infoList;
-        }
-    }
-
-    /**
-     * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
-     *
-     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
-     *
-     * @param logicalAddress logical address to be retrieved
-     * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
-     *         Returns null if no logical address matched
-     */
-    HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
-        assertRunOnServiceThread();
-        return mDeviceInfos.get(logicalAddress);
-    }
 
     HdmiPortInfo[] getPortInfos() {
         return nativeGetPortInfos(mNativePtr);
@@ -451,7 +362,7 @@ final class HdmiCecController {
      */
     List<HdmiCecLocalDevice> getLocalDeviceList() {
         assertRunOnServiceThread();
-        return sparseArrayToList(mLocalDevices);
+        return HdmiUtils.sparseArrayToList(mLocalDevices);
     }
 
     private List<Integer> pickPollCandidates(int pickStrategy) {
@@ -489,14 +400,6 @@ final class HdmiCecController {
         return pollingCandidates;
     }
 
-    private static <T> List<T> sparseArrayToList(SparseArray<T> array) {
-        ArrayList<T> list = new ArrayList<>();
-        for (int i = 0; i < array.size(); ++i) {
-            list.add(array.valueAt(i));
-        }
-        return list;
-    }
-
     private boolean isAllocatedLocalDeviceAddress(int address) {
         for (int i = 0; i < mLocalDevices.size(); ++i) {
             if (mLocalDevices.valueAt(i).isAddressOf(address)) {
index 7a2a6cc..08d7786 100644 (file)
@@ -19,8 +19,16 @@ package com.android.server.hdmi;
 import android.hardware.hdmi.HdmiCec;
 import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.hardware.hdmi.HdmiCecMessage;
+import android.os.Looper;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
 /**
  * Class that models a logical CEC device hosted in this system. Handles initialization,
  * CEC commands that call for actions customized per device type.
@@ -34,10 +42,37 @@ abstract class HdmiCecLocalDevice {
     protected int mPreferredAddress;
     protected HdmiCecDeviceInfo mDeviceInfo;
 
+    // Logical address of the active source.
+    @GuardedBy("mLock")
+    private int mActiveSource;
+
+    // Active routing path. Physical address of the active source but not all the time, such as
+    // when the new active source does not claim itself to be one. Note that we don't keep
+    // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
+    @GuardedBy("mLock")
+    private int mActiveRoutingPath;
+
+    // Set to true while the service is in normal mode. While set to false, no input change is
+    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
+    // system upgrade, etc., a.k.a. "prohibit mode".
+    @GuardedBy("mLock")
+    private boolean mInputChangeEnabled;
+
+    protected final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
+    protected final Object mLock;
+
+    // A collection of FeatureAction.
+    // Note that access to this collection should happen in service thread.
+    private final LinkedList<FeatureAction> mActions = new LinkedList<>();
+
     protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
         mService = service;
         mDeviceType = deviceType;
         mAddress = HdmiCec.ADDR_UNREGISTERED;
+        mLock = service.getServiceLock();
+
+        // TODO: Get control flag from persistent storage
+        mInputChangeEnabled = true;
     }
 
     // Factory method that returns HdmiCecLocalDevice of corresponding type.
@@ -69,14 +104,23 @@ abstract class HdmiCecLocalDevice {
      * @return true if consumed a message; otherwise, return false.
      */
     final boolean dispatchMessage(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+
         int dest = message.getDestination();
         if (dest != mAddress && dest != HdmiCec.ADDR_BROADCAST) {
             return false;
         }
+        // Cache incoming message. Note that it caches only white-listed one.
+        mCecMessageCache.cacheMessage(message);
         return onMessage(message);
     }
 
     protected final boolean onMessage(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+
+        if (dispatchMessageToAction(message)) {
+            return true;
+        }
         switch (message.getOpcode()) {
             case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
                 return handleGetMenuLanguage(message);
@@ -90,12 +134,31 @@ abstract class HdmiCecLocalDevice {
                 return handleGetCecVersion(message);
             case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
                 return handleReportPhysicalAddress(message);
+            case HdmiCec.MESSAGE_INITIATE_ARC:
+                return handleInitiateArc(message);
+            case HdmiCec.MESSAGE_TERMINATE_ARC:
+                return handleTerminateArc(message);
+            case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+                return handleSetSystemAudioMode(message);
+            case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+                return handleSystemAudioModeStatus(message);
             default:
                 return false;
         }
     }
 
+    private boolean dispatchMessageToAction(HdmiCecMessage message) {
+        for (FeatureAction action : mActions) {
+            if (action.processCommand(message)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     protected boolean handleGivePhysicalAddress() {
+        assertRunOnServiceThread();
+
         int physicalAddress = mService.getPhysicalAddress();
         HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                 mAddress, physicalAddress, mDeviceType);
@@ -104,6 +167,8 @@ abstract class HdmiCecLocalDevice {
     }
 
     protected boolean handleGiveDeviceVendorId() {
+        assertRunOnServiceThread();
+
         int vendorId = mService.getVendorId();
         HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
                 mAddress, vendorId);
@@ -112,6 +177,8 @@ abstract class HdmiCecLocalDevice {
     }
 
     protected boolean handleGetCecVersion(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+
         int version = mService.getCecVersion();
         HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
                 message.getSource(), version);
@@ -120,6 +187,8 @@ abstract class HdmiCecLocalDevice {
     }
 
     protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+
         Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
         mService.sendCecCommand(
                 HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
@@ -129,6 +198,8 @@ abstract class HdmiCecLocalDevice {
     }
 
     protected boolean handleGiveOsdName(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+
         // Note that since this method is called after logical address allocation is done,
         // mDeviceInfo should not be null.
         HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
@@ -149,34 +220,222 @@ abstract class HdmiCecLocalDevice {
         return false;
     }
 
+    protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+        return false;
+    }
+
+    protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+        return false;
+    }
+
+    protected boolean handleTerminateArc(HdmiCecMessage message) {
+        return false;
+    }
+
+    protected boolean handleInitiateArc(HdmiCecMessage message) {
+        return false;
+    }
+
     final void handleAddressAllocated(int logicalAddress) {
+        assertRunOnServiceThread();
+
         mAddress = mPreferredAddress = logicalAddress;
         onAddressAllocated(logicalAddress);
     }
 
     HdmiCecDeviceInfo getDeviceInfo() {
+        assertRunOnServiceThread();
         return mDeviceInfo;
     }
 
     void setDeviceInfo(HdmiCecDeviceInfo info) {
+        assertRunOnServiceThread();
         mDeviceInfo = info;
     }
 
     // Returns true if the logical address is same as the argument.
     boolean isAddressOf(int addr) {
+        assertRunOnServiceThread();
         return addr == mAddress;
     }
 
     // Resets the logical address to unregistered(15), meaning the logical device is invalid.
     void clearAddress() {
+        assertRunOnServiceThread();
         mAddress = HdmiCec.ADDR_UNREGISTERED;
     }
 
     void setPreferredAddress(int addr) {
+        assertRunOnServiceThread();
         mPreferredAddress = addr;
     }
 
     int getPreferredAddress() {
+        assertRunOnServiceThread();
         return mPreferredAddress;
     }
+
+    void addAndStartAction(final FeatureAction action) {
+        assertRunOnServiceThread();
+        mActions.add(action);
+        action.start();
+    }
+
+    // See if we have an action of a given type in progress.
+    <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
+        assertRunOnServiceThread();
+        for (FeatureAction action : mActions) {
+            if (action.getClass().equals(clazz)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // Returns all actions matched with given class type.
+    <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
+        assertRunOnServiceThread();
+        ArrayList<T> actions = new ArrayList<>();
+        for (FeatureAction action : mActions) {
+            if (action.getClass().equals(clazz)) {
+                actions.add((T) action);
+            }
+        }
+        return actions;
+    }
+
+    /**
+     * Remove the given {@link FeatureAction} object from the action queue.
+     *
+     * @param action {@link FeatureAction} to remove
+     */
+    void removeAction(final FeatureAction action) {
+        assertRunOnServiceThread();
+        mActions.remove(action);
+    }
+
+    // Remove all actions matched with the given Class type.
+    <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+        removeActionExcept(clazz, null);
+    }
+
+    // Remove all actions matched with the given Class type besides |exception|.
+    <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+            final FeatureAction exception) {
+        assertRunOnServiceThread();
+        Iterator<FeatureAction> iter = mActions.iterator();
+        while (iter.hasNext()) {
+            FeatureAction action = iter.next();
+            if (action != exception && action.getClass().equals(clazz)) {
+                action.clear();
+                mActions.remove(action);
+            }
+        }
+    }
+
+    protected void assertRunOnServiceThread() {
+        if (Looper.myLooper() != mService.getServiceLooper()) {
+            throw new IllegalStateException("Should run on service thread.");
+        }
+    }
+
+    /**
+     * Called when a hot-plug event issued.
+     *
+     * @param portId id of port where a hot-plug event happened
+     * @param connected whether to connected or not on the event
+     */
+    void onHotplug(int portId, boolean connected) {
+    }
+
+    final HdmiControlService getService() {
+        return mService;
+    }
+
+    final boolean isConnectedToArcPort(int path) {
+        return mService.isConnectedToArcPort(path);
+    }
+
+    int getActiveSource() {
+        synchronized (mLock) {
+            return mActiveSource;
+        }
+    }
+
+    /**
+     * Returns the active routing path.
+     */
+    int getActivePath() {
+        synchronized (mLock) {
+            return mActiveRoutingPath;
+        }
+    }
+
+    /**
+     * Returns the ID of the active HDMI port. The active input is the port that has the active
+     * routing path connected directly or indirectly under the device hierarchy.
+     */
+    int getActiveInput() {
+        synchronized (mLock) {
+            return mService.pathToPortId(mActiveRoutingPath);
+        }
+    }
+
+    void updateActiveDevice(int logicalAddress, int physicalAddress) {
+        synchronized (mLock) {
+            mActiveSource = logicalAddress;
+            mActiveRoutingPath = physicalAddress;
+        }
+    }
+
+    void setInputChangeEnabled(boolean enabled) {
+        synchronized (mLock) {
+            mInputChangeEnabled = enabled;
+        }
+    }
+
+    boolean isInPresetInstallationMode() {
+        synchronized (mLock) {
+            return !mInputChangeEnabled;
+        }
+    }
+
+    /**
+     * Whether the given path is located in the tail of current active path.
+     *
+     * @param path to be tested
+     * @return true if the given path is located in the tail of current active path; otherwise,
+     *         false
+     */
+    // TODO: move this to local device tv.
+    boolean isTailOfActivePath(int path) {
+        synchronized (mLock) {
+            // If active routing path is internal source, return false.
+            if (mActiveRoutingPath == 0) {
+                return false;
+            }
+            for (int i = 12; i >= 0; i -= 4) {
+                int curActivePath = (mActiveRoutingPath >> i) & 0xF;
+                if (curActivePath == 0) {
+                    return true;
+                } else {
+                    int curPath = (path >> i) & 0xF;
+                    if (curPath != curActivePath) {
+                        return false;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    HdmiCecMessageCache getCecMessageCache() {
+        assertRunOnServiceThread();
+        return mCecMessageCache;
+    }
+
+    int pathToPortId(int newPath) {
+        assertRunOnServiceThread();
+        return mService.pathToPortId(newPath);
+    }
 }
index d79e283..01345ef 100644 (file)
 package com.android.server.hdmi;
 
 import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.RemoteException;
+import android.util.Slog;
 
 /**
  * Represent a logical device of type Playback residing in Android system.
  */
 final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
+    private static final String TAG = "HdmiCecLocalDevicePlayback";
 
     HdmiCecLocalDevicePlayback(HdmiControlService service) {
         super(service, HdmiCec.DEVICE_PLAYBACK);
@@ -32,4 +36,54 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
         mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                 mAddress, mService.getPhysicalAddress(), mDeviceType));
     }
+
+    void oneTouchPlay(IHdmiControlCallback callback) {
+        assertRunOnServiceThread();
+        if (hasAction(OneTouchPlayAction.class)) {
+            Slog.w(TAG, "oneTouchPlay already in progress");
+            invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+            return;
+        }
+
+        // TODO: Consider the case of multiple TV sets. For now we always direct the command
+        //       to the primary one.
+        OneTouchPlayAction action = OneTouchPlayAction.create(this, HdmiCec.ADDR_TV, callback);
+        if (action == null) {
+            Slog.w(TAG, "Cannot initiate oneTouchPlay");
+            invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+            return;
+        }
+        addAndStartAction(action);
+    }
+
+    void queryDisplayStatus(IHdmiControlCallback callback) {
+        assertRunOnServiceThread();
+        if (hasAction(DevicePowerStatusAction.class)) {
+            Slog.w(TAG, "queryDisplayStatus already in progress");
+            invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
+            return;
+        }
+        DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
+                HdmiCec.ADDR_TV, callback);
+        if (action == null) {
+            Slog.w(TAG, "Cannot initiate queryDisplayStatus");
+            invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
+            return;
+        }
+        addAndStartAction(action);
+    }
+
+    private void invokeCallback(IHdmiControlCallback callback, int result) {
+        try {
+            callback.onComplete(result);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Invoking callback failed:" + e);
+        }
+    }
+
+    @Override
+    void onHotplug(int portId, boolean connected) {
+        // TODO: clear devices connected to the given port id.
+        mCecMessageCache.flushAll();
+    }
 }
index 625b256..92ddd3d 100644 (file)
@@ -22,9 +22,12 @@ import android.hardware.hdmi.HdmiCecMessage;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -35,12 +38,25 @@ import java.util.Locale;
 final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
     private static final String TAG = "HdmiCecLocalDeviceTv";
 
+    // Whether ARC is "enabled" or not.
+    @GuardedBy("mLock")
+    private boolean mArcStatusEnabled = false;
+
+    @GuardedBy("mLock")
+    // Whether SystemAudioMode is "On" or not.
+    private boolean mSystemAudioMode;
+
+    // Map-like container of all cec devices including local ones.
+    // A logical address of device is used as key of container.
+    private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
+
     HdmiCecLocalDeviceTv(HdmiControlService service) {
         super(service, HdmiCec.DEVICE_TV);
     }
 
     @Override
     protected void onAddressAllocated(int logicalAddress) {
+        assertRunOnServiceThread();
         // TODO: vendor-specific initialization here.
 
         mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
@@ -59,14 +75,14 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
      * @param callback callback object to report the result with
      */
     void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
+        assertRunOnServiceThread();
         HdmiCecDeviceInfo targetDevice = mService.getDeviceInfo(targetAddress);
         if (targetDevice == null) {
             invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE);
             return;
         }
-        mService.removeAction(DeviceSelectAction.class);
-        mService.addAndStartAction(new DeviceSelectAction(mService, mAddress,
-                mService.getPhysicalAddress(), targetDevice, callback));
+        removeAction(DeviceSelectAction.class);
+        addAndStartAction(new DeviceSelectAction(this, targetDevice, callback));
     }
 
     private static void invokeCallback(IHdmiControlCallback callback, int result) {
@@ -79,6 +95,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
 
     @Override
     protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+        assertRunOnServiceThread();
         HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
                 mAddress, Locale.getDefault().getISO3Language());
         // TODO: figure out how to handle failed to get language code.
@@ -92,8 +109,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
 
     @Override
     protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
+        assertRunOnServiceThread();
         // Ignore if [Device Discovery Action] is going on.
-        if (mService.hasAction(DeviceDiscoveryAction.class)) {
+        if (hasAction(DeviceDiscoveryAction.class)) {
             Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
                     + "because Device Discovery Action is on-going:" + message);
             return true;
@@ -104,16 +122,16 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
 
         // If it is a new device and connected to the tail of active path,
         // it's required to change routing path.
-        boolean requireRoutingChange = !mService.isInDeviceList(physicalAddress, logicalAddress)
-                && mService.isTailOfActivePath(physicalAddress);
-        mService.addAndStartAction(new NewDeviceAction(mService,
-                mAddress, message.getSource(), physicalAddress,
+        boolean requireRoutingChange = !isInDeviceList(physicalAddress, logicalAddress)
+                && isTailOfActivePath(physicalAddress);
+        addAndStartAction(new NewDeviceAction(this, message.getSource(), physicalAddress,
                 requireRoutingChange));
         return true;
     }
 
     @Override
     protected boolean handleVendorSpecificCommand(HdmiCecMessage message) {
+        assertRunOnServiceThread();
         List<VendorSpecificAction> actions = Collections.emptyList();
         // TODO: Call mService.getActions(VendorSpecificAction.class) to get all the actions.
 
@@ -138,27 +156,304 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
     }
 
     private void launchDeviceDiscovery() {
-        mService.clearAllDeviceInfo();
-        // TODO: Move the following callback to HdmiLocalDeviceTv.
-        DeviceDiscoveryAction action = new DeviceDiscoveryAction(mService, mAddress,
+        assertRunOnServiceThread();
+        clearDeviceInfoList();
+        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
                 new DeviceDiscoveryCallback() {
                     @Override
                     public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
                         for (HdmiCecDeviceInfo info : deviceInfos) {
-                            mService.addCecDevice(info);
+                            addCecDevice(info);
                         }
 
                         // Since we removed all devices when it's start and
                         // device discovery action does not poll local devices,
                         // we should put device info of local device manually here
                         for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
-                            mService.addCecDevice(device.getDeviceInfo());
+                            addCecDevice(device.getDeviceInfo());
                         }
 
-                        mService.addAndStartAction(new HotplugDetectionAction(mService,
-                                mAddress));
+                        addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
                     }
                 });
-        mService.addAndStartAction(action);
+        addAndStartAction(action);
+    }
+
+    // Clear all device info.
+    private void clearDeviceInfoList() {
+        assertRunOnServiceThread();
+        mDeviceInfos.clear();
+    }
+
+    void setSystemAudioMode(boolean on) {
+        synchronized (mLock) {
+            if (on != mSystemAudioMode) {
+                mSystemAudioMode = on;
+                // TODO: Need to set the preference for SystemAudioMode.
+                // TODO: Need to handle the notification of changing the mode and
+                // to identify the notification should be handled in the service or TvSettings.
+            }
+        }
+    }
+
+    boolean getSystemAudioMode() {
+        synchronized (mLock) {
+            assertRunOnServiceThread();
+            return mSystemAudioMode;
+        }
+    }
+
+    /**
+     * Change ARC status into the given {@code enabled} status.
+     *
+     * @return {@code true} if ARC was in "Enabled" status
+     */
+    boolean setArcStatus(boolean enabled) {
+        synchronized (mLock) {
+            boolean oldStatus = mArcStatusEnabled;
+            // 1. Enable/disable ARC circuit.
+            mService.setAudioReturnChannel(enabled);
+
+            // TODO: notify arc mode change to AudioManager.
+
+            // 2. Update arc status;
+            mArcStatusEnabled = enabled;
+            return oldStatus;
+        }
+    }
+
+    /**
+     * Returns whether ARC is enabled or not.
+     */
+    boolean getArcStatus() {
+        synchronized (mLock) {
+            return mArcStatusEnabled;
+        }
+    }
+
+    void setAudioStatus(boolean mute, int volume) {
+        mService.setAudioStatus(mute, volume);
+    }
+
+    @Override
+    protected boolean handleInitiateArc(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        // In case where <Initiate Arc> is started by <Request ARC Initiation>
+        // need to clean up RequestArcInitiationAction.
+        removeAction(RequestArcInitiationAction.class);
+        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
+                message.getSource(), true);
+        addAndStartAction(action);
+        return true;
+    }
+
+    @Override
+    protected boolean handleTerminateArc(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        // In case where <Terminate Arc> is started by <Request ARC Termination>
+        // need to clean up RequestArcInitiationAction.
+        // TODO: check conditions of power status by calling is_connected api
+        // to be added soon.
+        removeAction(RequestArcTerminationAction.class);
+        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
+                message.getSource(), false);
+        addAndStartAction(action);
+        return true;
+    }
+
+    @Override
+    protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        if (!isMessageForSystemAudio(message)) {
+            return false;
+        }
+        SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
+                message.getSource(), HdmiUtils.parseCommandParamSystemAudioStatus(message));
+        addAndStartAction(action);
+        return true;
+    }
+
+    @Override
+    protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        if (!isMessageForSystemAudio(message)) {
+            return false;
+        }
+        setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
+        return true;
+    }
+
+    private boolean isMessageForSystemAudio(HdmiCecMessage message) {
+        if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
+                || message.getDestination() != HdmiCec.ADDR_TV
+                || getAvrDeviceInfo() == null) {
+            Slog.w(TAG, "Skip abnormal CecMessage: " + message);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
+     * logical address as new device info's.
+     *
+     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+     *
+     * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
+     * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
+     *         that has the same logical address as new one has.
+     */
+    HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
+        assertRunOnServiceThread();
+        HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
+        if (oldDeviceInfo != null) {
+            removeDeviceInfo(deviceInfo.getLogicalAddress());
+        }
+        mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
+        return oldDeviceInfo;
+    }
+
+    /**
+     * Remove a device info corresponding to the given {@code logicalAddress}.
+     * It returns removed {@link HdmiCecDeviceInfo} if exists.
+     *
+     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+     *
+     * @param logicalAddress logical address of device to be removed
+     * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
+     */
+    HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
+        assertRunOnServiceThread();
+        HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
+        if (deviceInfo != null) {
+            mDeviceInfos.remove(logicalAddress);
+        }
+        return deviceInfo;
+    }
+
+    /**
+     * Return a list of all {@link HdmiCecDeviceInfo}.
+     *
+     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+     */
+    List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includelLocalDevice) {
+        assertRunOnServiceThread();
+        if (includelLocalDevice) {
+                return HdmiUtils.sparseArrayToList(mDeviceInfos);
+        } else {
+
+            ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
+            for (int i = 0; i < mDeviceInfos.size(); ++i) {
+                HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
+                if (!isLocalDeviceAddress(info.getLogicalAddress())) {
+                    infoList.add(info);
+                }
+            }
+            return infoList;
+        }
+    }
+
+    private boolean isLocalDeviceAddress(int address) {
+        assertRunOnServiceThread();
+        for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
+            if (device.isAddressOf(address)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
+     *
+     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+     *
+     * @param logicalAddress logical address to be retrieved
+     * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
+     *         Returns null if no logical address matched
+     */
+    HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
+        assertRunOnServiceThread();
+        return mDeviceInfos.get(logicalAddress);
+    }
+
+    HdmiCecDeviceInfo getAvrDeviceInfo() {
+        assertRunOnServiceThread();
+        return getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
+    }
+
+    /**
+     * Called when a device is newly added or a new device is detected.
+     *
+     * @param info device info of a new device.
+     */
+    final void addCecDevice(HdmiCecDeviceInfo info) {
+        assertRunOnServiceThread();
+        addDeviceInfo(info);
+
+        // TODO: announce new device detection.
+    }
+
+    /**
+     * Called when a device is removed or removal of device is detected.
+     *
+     * @param address a logical address of a device to be removed
+     */
+    final void removeCecDevice(int address) {
+        assertRunOnServiceThread();
+        removeDeviceInfo(address);
+        mCecMessageCache.flushMessagesFrom(address);
+
+        // TODO: announce a device removal.
+    }
+
+    /**
+     * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches
+     * the given routing path. CEC devices use routing path for its physical address to
+     * describe the hierarchy of the devices in the network.
+     *
+     * @param path routing path or physical address
+     * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null
+     */
+    final HdmiCecDeviceInfo getDeviceInfoByPath(int path) {
+        assertRunOnServiceThread();
+        for (HdmiCecDeviceInfo info : getDeviceInfoList(false)) {
+            if (info.getPhysicalAddress() == path) {
+                return info;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Whether a device of the specified physical address and logical address exists
+     * in a device info list. However, both are minimal condition and it could
+     * be different device from the original one.
+     *
+     * @param physicalAddress physical address of a device to be searched
+     * @param logicalAddress logical address of a device to be searched
+     * @return true if exist; otherwise false
+     */
+    boolean isInDeviceList(int physicalAddress, int logicalAddress) {
+        assertRunOnServiceThread();
+        HdmiCecDeviceInfo device = getDeviceInfo(logicalAddress);
+        if (device == null) {
+            return false;
+        }
+        return device.getPhysicalAddress() == physicalAddress;
+    }
+
+    @Override
+    void onHotplug(int portNo, boolean connected) {
+        assertRunOnServiceThread();
+        // TODO: delegate onHotplug event to each local device.
+
+        // Tv device will have permanent HotplugDetectionAction.
+        List<HotplugDetectionAction> hotplugActions = getActions(HotplugDetectionAction.class);
+        if (!hotplugActions.isEmpty()) {
+            // Note that hotplug action is single action running on a machine.
+            // "pollAllDevicesNow" cleans up timer and start poll action immediately.
+            hotplugActions.get(0).pollAllDevicesNow();
+        }
     }
 }
index bccfa36..c93db88 100644 (file)
@@ -36,14 +36,11 @@ import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.server.SystemService;
 import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -100,10 +97,6 @@ public final class HdmiControlService extends SystemService {
     // and sparse call it shares a thread to handle IO operations.
     private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
 
-    // A collection of FeatureAction.
-    // Note that access to this collection should happen in service thread.
-    private final LinkedList<FeatureAction> mActions = new LinkedList<>();
-
     // Used to synchronize the access to the service.
     private final Object mLock = new Object();
 
@@ -121,8 +114,6 @@ public final class HdmiControlService extends SystemService {
     // Handler running on service thread. It's used to run a task in service thread.
     private final Handler mHandler = new Handler();
 
-    private final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
-
     @Nullable
     private HdmiCecController mCecController;
 
@@ -133,37 +124,10 @@ public final class HdmiControlService extends SystemService {
     // from being modified.
     private List<HdmiPortInfo> mPortInfo;
 
-    // Logical address of the active source.
-    @GuardedBy("mLock")
-    private int mActiveSource;
-
-    // Active routing path. Physical address of the active source but not all the time, such as
-    // when the new active source does not claim itself to be one. Note that we don't keep
-    // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
-    @GuardedBy("mLock")
-    private int mActiveRoutingPath;
-
-    // Set to true while the service is in normal mode. While set to false, no input change is
-    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
-    // system upgrade, etc., a.k.a. "prohibit mode".
-    @GuardedBy("mLock")
-    private boolean mInputChangeEnabled;
-
-    @GuardedBy("mLock")
-    // Whether ARC is "enabled" or not.
-    // TODO: it may need to hold lock if it's accessed from others.
-    private boolean mArcStatusEnabled = false;
-
-    @GuardedBy("mLock")
-    // Whether SystemAudioMode is "On" or not.
-    private boolean mSystemAudioMode;
-
     public HdmiControlService(Context context) {
         super(context);
         mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
                 com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
-        // TODO: Get control flag from persistent storage
-        mInputChangeEnabled = true;
     }
 
     @Override
@@ -336,44 +300,6 @@ public final class HdmiControlService extends SystemService {
         return mHandler.getLooper();
     }
 
-    int getActiveSource() {
-        synchronized (mLock) {
-            return mActiveSource;
-        }
-    }
-
-    /**
-     * Returns the active routing path.
-     */
-    int getActivePath() {
-        synchronized (mLock) {
-            return mActiveRoutingPath;
-        }
-    }
-
-    /**
-     * Returns the ID of the active HDMI port. The active input is the port that has the active
-     * routing path connected directly or indirectly under the device hierarchy.
-     */
-    int getActiveInput() {
-        synchronized (mLock) {
-            return pathToPortId(mActiveRoutingPath);
-        }
-    }
-
-    void updateActiveDevice(int logicalAddress, int physicalAddress) {
-        synchronized (mLock) {
-            mActiveSource = logicalAddress;
-            mActiveRoutingPath = physicalAddress;
-        }
-    }
-
-    void setInputChangeEnabled(boolean enabled) {
-        synchronized (mLock) {
-            mInputChangeEnabled = enabled;
-        }
-    }
-
     /**
      * Returns physical address of the device.
      */
@@ -390,7 +316,11 @@ public final class HdmiControlService extends SystemService {
 
     HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
         assertRunOnServiceThread();
-        return mCecController.getDeviceInfo(logicalAddress);
+        HdmiCecLocalDeviceTv tv = tv();
+        if (tv == null) {
+            return null;
+        }
+        return tv.getDeviceInfo(logicalAddress);
     }
 
     /**
@@ -401,67 +331,6 @@ public final class HdmiControlService extends SystemService {
     }
 
     /**
-     * Returns a list of {@link HdmiCecDeviceInfo}.
-     *
-     * @param includeLocalDevice whether to include local devices
-     */
-    List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
-        assertRunOnServiceThread();
-        return mCecController.getDeviceInfoList(includeLocalDevice);
-    }
-
-    /**
-     * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches
-     * the given routing path. CEC devices use routing path for its physical address to
-     * describe the hierarchy of the devices in the network.
-     *
-     * @param path routing path or physical address
-     * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null
-     */
-    HdmiCecDeviceInfo getDeviceInfoByPath(int path) {
-        assertRunOnServiceThread();
-        for (HdmiCecDeviceInfo info : mCecController.getDeviceInfoList(false)) {
-            if (info.getPhysicalAddress() == path) {
-                return info;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Add and start a new {@link FeatureAction} to the action queue.
-     *
-     * @param action {@link FeatureAction} to add and start
-     */
-    void addAndStartAction(final FeatureAction action) {
-        // TODO: may need to check the number of stale actions.
-        runOnServiceThread(new Runnable() {
-            @Override
-            public void run() {
-                mActions.add(action);
-                action.start();
-            }
-        });
-    }
-
-    void setSystemAudioMode(boolean on) {
-        synchronized (mLock) {
-            if (on != mSystemAudioMode) {
-                mSystemAudioMode = on;
-                // TODO: Need to set the preference for SystemAudioMode.
-                // TODO: Need to handle the notification of changing the mode and
-                // to identify the notification should be handled in the service or TvSettings.
-            }
-        }
-    }
-
-    boolean getSystemAudioMode() {
-        synchronized (mLock) {
-            return mSystemAudioMode;
-        }
-    }
-
-    /**
      * Whether a device of the specified physical address is connected to ARC enabled port.
      */
     boolean isConnectedToArcPort(int physicalAddress) {
@@ -474,57 +343,7 @@ public final class HdmiControlService extends SystemService {
         return false;
     }
 
-    // See if we have an action of a given type in progress.
-    <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
-        for (FeatureAction action : mActions) {
-            if (action.getClass().equals(clazz)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // Returns all actions matched with given class type.
-    <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
-        ArrayList<T> actions = new ArrayList<>();
-        for (FeatureAction action : mActions) {
-            if (action.getClass().equals(clazz)) {
-                actions.add((T) action);
-            }
-        }
-        return actions;
-    }
-
-    /**
-     * Remove the given {@link FeatureAction} object from the action queue.
-     *
-     * @param action {@link FeatureAction} to remove
-     */
-    void removeAction(final FeatureAction action) {
-        assertRunOnServiceThread();
-        mActions.remove(action);
-    }
-
-    // Remove all actions matched with the given Class type.
-    <T extends FeatureAction> void removeAction(final Class<T> clazz) {
-        removeActionExcept(clazz, null);
-    }
-
-    // Remove all actions matched with the given Class type besides |exception|.
-    <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
-            final FeatureAction exception) {
-        assertRunOnServiceThread();
-        Iterator<FeatureAction> iter = mActions.iterator();
-        while (iter.hasNext()) {
-            FeatureAction action = iter.next();
-            if (action != exception && action.getClass().equals(clazz)) {
-                action.clear();
-                mActions.remove(action);
-            }
-        }
-    }
-
-    private void runOnServiceThread(Runnable runnable) {
+    void runOnServiceThread(Runnable runnable) {
         mHandler.post(runnable);
     }
 
@@ -539,35 +358,6 @@ public final class HdmiControlService extends SystemService {
     }
 
     /**
-     * Change ARC status into the given {@code enabled} status.
-     *
-     * @return {@code true} if ARC was in "Enabled" status
-     */
-    boolean setArcStatus(boolean enabled) {
-        assertRunOnServiceThread();
-        synchronized (mLock) {
-            boolean oldStatus = mArcStatusEnabled;
-            // 1. Enable/disable ARC circuit.
-            mCecController.setAudioReturnChannel(enabled);
-
-            // TODO: notify arc mode change to AudioManager.
-
-            // 2. Update arc status;
-            mArcStatusEnabled = enabled;
-            return oldStatus;
-        }
-    }
-
-    /**
-     * Returns whether ARC is enabled or not.
-     */
-    boolean getArcStatus() {
-        synchronized (mLock) {
-            return mArcStatusEnabled;
-        }
-    }
-
-    /**
      * Transmit a CEC command to CEC bus.
      *
      * @param command CEC command to send out
@@ -582,47 +372,17 @@ public final class HdmiControlService extends SystemService {
     }
 
     boolean handleCecCommand(HdmiCecMessage message) {
-        // Cache incoming message. Note that it caches only white-listed one.
-        mCecMessageCache.cacheMessage(message);
-
-        // Commands that queries system information replies directly instead
-        // of creating FeatureAction because they are state-less.
-        // TODO: move the leftover message to local device.
-        switch (message.getOpcode()) {
-            case HdmiCec.MESSAGE_INITIATE_ARC:
-                handleInitiateArc(message);
-                return true;
-            case HdmiCec.MESSAGE_TERMINATE_ARC:
-                handleTerminateArc(message);
-                return true;
-            case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
-                handleSetSystemAudioMode(message);
-                return true;
-            case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
-                handleSystemAudioModeStatus(message);
-                return true;
-            default:
-                if (dispatchMessageToAction(message)) {
-                    return true;
-                }
-                break;
-        }
-
         return dispatchMessageToLocalDevice(message);
     }
 
-    private boolean dispatchMessageToAction(HdmiCecMessage message) {
-        for (FeatureAction action : mActions) {
-            if (action.processCommand(message)) {
-                return true;
-            }
-        }
-        return false;
+    void setAudioReturnChannel(boolean enabled) {
+        mCecController.setAudioReturnChannel(enabled);
     }
 
     private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
         for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
-            if (device.dispatchMessage(message)) {
+            if (device.dispatchMessage(message)
+                    && message.getDestination() != HdmiCec.ADDR_BROADCAST) {
                 return true;
             }
         }
@@ -639,14 +399,9 @@ public final class HdmiControlService extends SystemService {
      */
     void onHotplug(int portNo, boolean connected) {
         assertRunOnServiceThread();
-        // TODO: delegate onHotplug event to each local device.
-
-        // Tv device will have permanent HotplugDetectionAction.
-        List<HotplugDetectionAction> hotplugActions = getActions(HotplugDetectionAction.class);
-        if (!hotplugActions.isEmpty()) {
-            // Note that hotplug action is single action running on a machine.
-            // "pollAllDevicesNow" cleans up timer and start poll action immediately.
-            hotplugActions.get(0).pollAllDevicesNow();
+
+        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+            device.onHotplug(portNo, connected);
         }
 
         announceHotplugEvent(portNo, connected);
@@ -677,32 +432,17 @@ public final class HdmiControlService extends SystemService {
         return strategy | iterationStrategy;
     }
 
-    void clearAllDeviceInfo() {
-        assertRunOnServiceThread();
-        mCecController.clearDeviceInfoList();
-    }
-
     List<HdmiCecLocalDevice> getAllLocalDevices() {
         assertRunOnServiceThread();
         return mCecController.getLocalDeviceList();
     }
 
-    /**
-     * Whether a device of the specified physical address and logical address exists
-     * in a device info list. However, both are minimal condition and it could
-     * be different device from the original one.
-     *
-     * @param physicalAddress physical address of a device to be searched
-     * @param logicalAddress logical address of a device to be searched
-     * @return true if exist; otherwise false
-     */
-    boolean isInDeviceList(int physicalAddress, int logicalAddress) {
-        assertRunOnServiceThread();
-        HdmiCecDeviceInfo device = mCecController.getDeviceInfo(logicalAddress);
-        if (device == null) {
-            return false;
-        }
-        return device.getPhysicalAddress() == physicalAddress;
+    Object getServiceLock() {
+        return mLock;
+    }
+
+    void setAudioStatus(boolean mute, int volume) {
+        // TODO: Hook up with AudioManager.
     }
 
     private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
@@ -712,53 +452,6 @@ public final class HdmiControlService extends SystemService {
                 getPhysicalAddress(), deviceType, getVendorId(), displayName);
     }
 
-    private void handleInitiateArc(HdmiCecMessage message){
-        // In case where <Initiate Arc> is started by <Request ARC Initiation>
-        // need to clean up RequestArcInitiationAction.
-        removeAction(RequestArcInitiationAction.class);
-        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
-                message.getDestination(), message.getSource(), true);
-        addAndStartAction(action);
-    }
-
-    private void handleTerminateArc(HdmiCecMessage message) {
-        // In case where <Terminate Arc> is started by <Request ARC Termination>
-        // need to clean up RequestArcInitiationAction.
-        // TODO: check conditions of power status by calling is_connected api
-        // to be added soon.
-        removeAction(RequestArcTerminationAction.class);
-        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
-                message.getDestination(), message.getSource(), false);
-        addAndStartAction(action);
-    }
-
-    private void handleSetSystemAudioMode(HdmiCecMessage message) {
-        if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) {
-            return;
-        }
-        SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
-                message.getDestination(), message.getSource(),
-                HdmiUtils.parseCommandParamSystemAudioStatus(message));
-        addAndStartAction(action);
-    }
-
-    private void handleSystemAudioModeStatus(HdmiCecMessage message) {
-        if (!isMessageForSystemAudio(message)) {
-            return;
-        }
-        setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
-    }
-
-    private boolean isMessageForSystemAudio(HdmiCecMessage message) {
-        if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
-                || message.getDestination() != HdmiCec.ADDR_TV
-                || getAvrDeviceInfo() == null) {
-            Slog.w(TAG, "Skip abnormal CecMessage: " + message);
-            return false;
-        }
-        return true;
-    }
-
     // Record class that monitors the event of the caller of being killed. Used to clean up
     // the listener list and record list accordingly.
     private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
@@ -799,8 +492,7 @@ public final class HdmiControlService extends SystemService {
             runOnServiceThread(new Runnable() {
                 @Override
                 public void run() {
-                    HdmiCecLocalDeviceTv tv =
-                            (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
+                    HdmiCecLocalDeviceTv tv = tv();
                     if (tv == null) {
                         Slog.w(TAG, "Local playback device not available");
                         invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
@@ -856,51 +548,26 @@ public final class HdmiControlService extends SystemService {
         }
     }
 
-    private void oneTouchPlay(IHdmiControlCallback callback) {
-        if (hasAction(OneTouchPlayAction.class)) {
-            Slog.w(TAG, "oneTouchPlay already in progress");
-            invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
-            return;
-        }
-        HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+    private void oneTouchPlay(final IHdmiControlCallback callback) {
+        assertRunOnServiceThread();
+        HdmiCecLocalDevicePlayback source = playback();
         if (source == null) {
             Slog.w(TAG, "Local playback device not available");
             invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
             return;
         }
-        // TODO: Consider the case of multiple TV sets. For now we always direct the command
-        //       to the primary one.
-        OneTouchPlayAction action = OneTouchPlayAction.create(this,
-                source.getDeviceInfo().getLogicalAddress(),
-                source.getDeviceInfo().getPhysicalAddress(), HdmiCec.ADDR_TV, callback);
-        if (action == null) {
-            Slog.w(TAG, "Cannot initiate oneTouchPlay");
-            invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
-            return;
-        }
-        addAndStartAction(action);
+        source.oneTouchPlay(callback);
     }
 
-    private void queryDisplayStatus(IHdmiControlCallback callback) {
-        if (hasAction(DevicePowerStatusAction.class)) {
-            Slog.w(TAG, "queryDisplayStatus already in progress");
-            invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
-            return;
-        }
-        HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
+    private void queryDisplayStatus(final IHdmiControlCallback callback) {
+        assertRunOnServiceThread();
+        HdmiCecLocalDevicePlayback source = playback();
         if (source == null) {
             Slog.w(TAG, "Local playback device not available");
             invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
             return;
         }
-        DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
-                source.getDeviceInfo().getLogicalAddress(), HdmiCec.ADDR_TV, callback);
-        if (action == null) {
-            Slog.w(TAG, "Cannot initiate queryDisplayStatus");
-            invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
-            return;
-        }
-        addAndStartAction(action);
+        source.queryDisplayStatus(callback);
     }
 
     private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
@@ -938,43 +605,6 @@ public final class HdmiControlService extends SystemService {
         }
     }
 
-    HdmiCecDeviceInfo getAvrDeviceInfo() {
-        return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
-    }
-
-    void setAudioStatus(boolean mute, int volume) {
-        // TODO: Hook up with AudioManager.
-    }
-
-    boolean isInPresetInstallationMode() {
-        synchronized (mLock) {
-            return !mInputChangeEnabled;
-        }
-    }
-
-    /**
-     * Called when a device is newly added or a new device is detected.
-     *
-     * @param info device info of a new device.
-     */
-    void addCecDevice(HdmiCecDeviceInfo info) {
-        mCecController.addDeviceInfo(info);
-
-        // TODO: announce new device detection.
-    }
-
-    /**
-     * Called when a device is removed or removal of device is detected.
-     *
-     * @param address a logical address of a device to be removed
-     */
-    void removeCecDevice(int address) {
-        mCecController.removeDeviceInfo(address);
-        mCecMessageCache.flushMessagesFrom(address);
-
-        // TODO: announce a device removal.
-    }
-
     private void announceHotplugEvent(int portNo, boolean connected) {
         HdmiHotplugEvent event = new HdmiHotplugEvent(portNo, connected);
         synchronized (mLock) {
@@ -993,39 +623,16 @@ public final class HdmiControlService extends SystemService {
         }
     }
 
-    HdmiCecMessageCache getCecMessageCache() {
-        return mCecMessageCache;
-    }
-
     private static boolean hasSameTopPort(int path1, int path2) {
         return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK)
                 == (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK);
     }
 
-    /**
-     * Whether the given path is located in the tail of current active path.
-     *
-     * @param path to be tested
-     * @return true if the given path is located in the tail of current active path; otherwise,
-     *         false
-     */
-    // TODO: move this to local device tv.
-    boolean isTailOfActivePath(int path) {
-        // If active routing path is internal source, return false.
-        if (mActiveRoutingPath == 0) {
-            return false;
-        }
-        for (int i = 12; i >= 0; i -= 4) {
-            int curActivePath = (mActiveRoutingPath >> i) & 0xF;
-            if (curActivePath == 0) {
-                return true;
-            } else {
-                int curPath = (path >> i) & 0xF;
-                if (curPath != curActivePath) {
-                    return false;
-                }
-            }
-        }
-        return false;
+    private HdmiCecLocalDeviceTv tv() {
+        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
+    }
+
+    private HdmiCecLocalDevicePlayback playback() {
+        return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
     }
 }
index b534377..9b7cc8d 100644 (file)
@@ -19,6 +19,7 @@ package com.android.server.hdmi;
 import android.hardware.hdmi.HdmiCec;
 import android.hardware.hdmi.HdmiCecMessage;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -111,4 +112,13 @@ final class HdmiUtils {
     static int threeBytesToInt(byte[] data) {
         return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
     }
+
+    static <T> List<T> sparseArrayToList(SparseArray<T> array) {
+        ArrayList<T> list = new ArrayList<>();
+        for (int i = 0; i < array.size(); ++i) {
+            list.add(array.valueAt(i));
+        }
+        return list;
+    }
+
 }
index ae20eda..74f40a6 100644 (file)
@@ -52,11 +52,10 @@ final class HotplugDetectionAction extends FeatureAction {
     /**
      * Constructor
      *
-     * @param service instance of {@link HdmiControlService}
-     * @param sourceAddress logical address of a device that initiate this action
+     * @param source {@link HdmiCecLocalDevice} instance
      */
-    HotplugDetectionAction(HdmiControlService service, int sourceAddress) {
-        super(service, sourceAddress);
+    HotplugDetectionAction(HdmiCecLocalDevice source) {
+        super(source);
     }
 
     @Override
@@ -110,7 +109,7 @@ final class HotplugDetectionAction extends FeatureAction {
         if (mTimeoutCount == 0) {
             pollAllDevices();
         } else {
-            if (mService.getSystemAudioMode()) {
+            if (tv().getSystemAudioMode()) {
                 pollAudioSystem();
             }
         }
@@ -121,7 +120,7 @@ final class HotplugDetectionAction extends FeatureAction {
     private void pollAllDevices() {
         Slog.v(TAG, "Poll all devices.");
 
-        mService.pollDevices(new DevicePollingCallback() {
+        pollDevices(new DevicePollingCallback() {
             @Override
             public void onPollingFinished(List<Integer> ackedAddress) {
                 checkHotplug(ackedAddress, false);
@@ -133,7 +132,7 @@ final class HotplugDetectionAction extends FeatureAction {
     private void pollAudioSystem() {
         Slog.v(TAG, "Poll audio system.");
 
-        mService.pollDevices(new DevicePollingCallback() {
+        pollDevices(new DevicePollingCallback() {
             @Override
             public void onPollingFinished(List<Integer> ackedAddress) {
                 checkHotplug(ackedAddress, true);
@@ -143,7 +142,7 @@ final class HotplugDetectionAction extends FeatureAction {
     }
 
     private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
-        BitSet currentInfos = infoListToBitSet(mService.getDeviceInfoList(false), audioOnly);
+        BitSet currentInfos = infoListToBitSet(tv().getDeviceInfoList(false), audioOnly);
         BitSet polledResult = addressListToBitSet(ackedAddress);
 
         // At first, check removed devices.
@@ -195,7 +194,8 @@ final class HotplugDetectionAction extends FeatureAction {
 
     private void addDevice(int addedAddress) {
         // Send <Give Physical Address>.
-        sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, addedAddress));
+        sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(),
+                addedAddress));
     }
 
     private void removeDevice(int removedAddress) {
@@ -206,7 +206,7 @@ final class HotplugDetectionAction extends FeatureAction {
         mayCancelOneTouchRecord(removedAddress);
         mayDisableSystemAudioAndARC(removedAddress);
 
-        mService.removeCecDevice(removedAddress);
+        tv().removeCecDevice(removedAddress);
     }
 
     private void mayChangeRoutingPath(int address) {
@@ -217,7 +217,7 @@ final class HotplugDetectionAction extends FeatureAction {
     }
 
     private void mayCancelDeviceSelect(int address) {
-        List<DeviceSelectAction> actions = mService.getActions(DeviceSelectAction.class);
+        List<DeviceSelectAction> actions = getActions(DeviceSelectAction.class);
         if (actions.isEmpty()) {
             return;
         }
@@ -225,7 +225,7 @@ final class HotplugDetectionAction extends FeatureAction {
         // Should ave only one Device Select Action
         DeviceSelectAction action = actions.get(0);
         if (action.getTargetAddress() == address) {
-            mService.removeAction(DeviceSelectAction.class);
+            removeAction(DeviceSelectAction.class);
         }
     }
 
@@ -239,11 +239,9 @@ final class HotplugDetectionAction extends FeatureAction {
         }
 
         // Turn off system audio mode.
-        mService.setSystemAudioMode(false);
-        if (mService.getArcStatus()) {
-            mService.addAndStartAction(
-                    new RequestArcTerminationAction(mService, mSourceAddress, address));
+        tv().setSystemAudioMode(false);
+        if (tv().getArcStatus()) {
+            addAndStartAction(new RequestArcTerminationAction(localDevice(), address));
         }
-
     }
 }
index 2cae507..4a49f09 100644 (file)
@@ -56,15 +56,14 @@ final class NewDeviceAction extends FeatureAction {
     /**
      * Constructor.
      *
-     * @param service {@link HdmiControlService} instance
-     * @param sourceAddress logical address to be used as source address
+     * @param source {@link HdmiCecLocalDevice} instance
      * @param deviceLogicalAddress logical address of the device in interest
      * @param devicePhysicalAddress physical address of the device in interest
      * @param requireRoutingChange whether to initiate routing change or not
      */
-    NewDeviceAction(HdmiControlService service, int sourceAddress, int deviceLogicalAddress,
+    NewDeviceAction(HdmiCecLocalDevice source, int deviceLogicalAddress,
             int devicePhysicalAddress, boolean requireRoutingChange) {
-        super(service, sourceAddress);
+        super(source);
         mDeviceLogicalAddress = deviceLogicalAddress;
         mDevicePhysicalAddress = devicePhysicalAddress;
         mVendorId = HdmiCec.UNKNOWN_VENDOR_ID;
@@ -73,16 +72,16 @@ final class NewDeviceAction extends FeatureAction {
 
     @Override
     public boolean start() {
-        if (HdmiCec.getTypeFromAddress(mSourceAddress) == HdmiCec.DEVICE_AUDIO_SYSTEM) {
-            if (mService.getAvrDeviceInfo() == null) {
+        if (HdmiCec.getTypeFromAddress(getSourceAddress()) == HdmiCec.DEVICE_AUDIO_SYSTEM) {
+            if (tv().getAvrDeviceInfo() == null) {
                 // TODO: Start system audio initiation action
             }
 
             // If new device is connected through ARC enabled port,
             // initiates ARC channel establishment.
-            if (mService.isConnectedToArcPort(mDevicePhysicalAddress)) {
-                mService.addAndStartAction(new RequestArcInitiationAction(mService, mSourceAddress,
-                        mDeviceLogicalAddress));
+            if (tv().isConnectedToArcPort(mDevicePhysicalAddress)) {
+                addAndStartAction(new RequestArcInitiationAction(localDevice(),
+                                mDeviceLogicalAddress));
             }
         }
 
@@ -95,7 +94,7 @@ final class NewDeviceAction extends FeatureAction {
             return true;
         }
 
-        sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress,
+        sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(),
                 mDeviceLogicalAddress));
         addTimer(mState, TIMEOUT_MS);
         return true;
@@ -155,14 +154,14 @@ final class NewDeviceAction extends FeatureAction {
 
     private void startRoutingChange() {
         // Stop existing routing control.
-        mService.removeAction(RoutingControlAction.class);
+        removeAction(RoutingControlAction.class);
 
         // Send routing change. The the address is a path of the active port.
         int newPath = toTopMostPortPath(mDevicePhysicalAddress);
-        sendCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
-                mService.getActivePath(), newPath));
-        mService.addAndStartAction(new RoutingControlAction(mService, mSourceAddress,
-                mService.pathToPortId(newPath), null));
+        sendCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(),
+                localDevice().getActivePath(), newPath));
+        addAndStartAction(new RoutingControlAction(localDevice(),
+                localDevice().pathToPortId(newPath), null));
     }
 
     private static int toTopMostPortPath(int physicalAddress) {
@@ -170,7 +169,7 @@ final class NewDeviceAction extends FeatureAction {
     }
 
     private boolean mayProcessCommandIfCached(int destAddress, int opcode) {
-        HdmiCecMessage message = mService.getCecMessageCache().getMessage(destAddress, opcode);
+        HdmiCecMessage message = getCecMessageCache().getMessage(destAddress, opcode);
         if (message != null) {
             return processCommand(message);
         }
@@ -184,7 +183,7 @@ final class NewDeviceAction extends FeatureAction {
         if (mayProcessCommandIfCached(mDeviceLogicalAddress, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
             return;
         }
-        sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress,
+        sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(),
                 mDeviceLogicalAddress));
         addTimer(mState, TIMEOUT_MS);
     }
@@ -193,7 +192,7 @@ final class NewDeviceAction extends FeatureAction {
         if (mDisplayName == null) {
             mDisplayName = HdmiCec.getDefaultDeviceName(mDeviceLogicalAddress);
         }
-        mService.addCecDevice(new HdmiCecDeviceInfo(
+        tv().addCecDevice(new HdmiCecDeviceInfo(
                 mDeviceLogicalAddress, mDevicePhysicalAddress,
                 HdmiCec.getTypeFromAddress(mDeviceLogicalAddress),
                 mVendorId, mDisplayName));
index 69fad13..e0a3a8b 100644 (file)
@@ -30,7 +30,7 @@ import android.util.Slog;
  * <p>Package-private, accessed by {@link HdmiControlService} only.
  */
 
-public final class OneTouchPlayAction extends FeatureAction {
+final class OneTouchPlayAction extends FeatureAction {
     private static final String TAG = "OneTouchPlayAction";
 
     // State in which the action is waiting for <Report Power Status>. In normal situation
@@ -48,34 +48,32 @@ public final class OneTouchPlayAction extends FeatureAction {
     // We wait up to RESPONSE_TIMEOUT_MS * LOOP_COUNTER_MAX = 20 seconds.
     private static final int LOOP_COUNTER_MAX = 10;
 
-    private final int mSourcePath;
     private final int mTargetAddress;
     private final IHdmiControlCallback mCallback;
 
     private int mPowerStatusCounter = 0;
 
     // Factory method. Ensures arguments are valid.
-    static OneTouchPlayAction create(HdmiControlService service, int sourceAddress,
-            int sourcePath, int targetAddress, IHdmiControlCallback callback) {
-        if (service == null || callback == null) {
+    static OneTouchPlayAction create(HdmiCecLocalDevice source,
+            int targetAddress, IHdmiControlCallback callback) {
+        if (source == null || callback == null) {
             Slog.e(TAG, "Wrong arguments");
             return null;
         }
-        return new OneTouchPlayAction(service, sourceAddress, sourcePath, targetAddress, callback);
+        return new OneTouchPlayAction(source, targetAddress,
+                callback);
     }
 
-    private OneTouchPlayAction(HdmiControlService service, int sourceAddress, int sourcePath,
-            int targetAddress, IHdmiControlCallback callback) {
-        super(service, sourceAddress);
-        mSourcePath = sourcePath;
+    private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
+            IHdmiControlCallback callback) {
+        super(localDevice);
         mTargetAddress = targetAddress;
         mCallback = callback;
     }
 
     @Override
     boolean start() {
-        mService.sendCecCommand(
-                HdmiCecMessageBuilder.buildTextViewOn(mSourceAddress, mTargetAddress));
+        sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
         broadcastActiveSource();
         queryDevicePowerStatus();
         mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
@@ -84,13 +82,12 @@ public final class OneTouchPlayAction extends FeatureAction {
     }
 
     private void broadcastActiveSource() {
-        mService.sendCecCommand(
-                HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath));
+        sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath()));
     }
 
     private void queryDevicePowerStatus() {
-        mService.sendCecCommand(
-                HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, mTargetAddress));
+        sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+                mTargetAddress));
     }
 
     @Override
index 08ca306..a2e08f1 100644 (file)
@@ -35,16 +35,14 @@ abstract class RequestArcAction extends FeatureAction {
     /**
      * @Constructor
      *
-     * @param service {@link HdmiControlService} instance
-     * @param sourceAddress logical address to be used as source address. It should
-     *                      TV type
+     * @param source {@link HdmiCecLocalDevice} instance
      * @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type
      * @throw IllegalArugmentException if device type of sourceAddress and avrAddress
      *                      is invalid
      */
-    RequestArcAction(HdmiControlService service, int sourceAddress, int avrAddress) {
-        super(service, sourceAddress);
-        HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+    RequestArcAction(HdmiCecLocalDevice source, int avrAddress) {
+        super(source);
+        HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
         HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
         mAvrAddress = avrAddress;
     }
@@ -72,9 +70,9 @@ abstract class RequestArcAction extends FeatureAction {
 
     protected final void disableArcTransmission() {
         // Start Set ARC Transmission State action.
-        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(mService,
-                mSourceAddress, mAvrAddress, false);
-        mService.addAndStartAction(action);
+        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(localDevice(),
+                mAvrAddress, false);
+        addAndStartAction(action);
     }
 
     @Override
index 343aff7..98ec953 100644 (file)
@@ -31,14 +31,14 @@ final class RequestArcInitiationAction extends RequestArcAction {
      *
      * For more details look at {@link RequestArcAction#RequestArcAction}.
      */
-    RequestArcInitiationAction(HdmiControlService service, int sourceAddress, int avrAddress) {
-        super(service, sourceAddress, avrAddress);
+    RequestArcInitiationAction(HdmiCecLocalDevice source, int avrAddress) {
+        super(source, avrAddress);
     }
 
     @Override
     boolean start() {
-        HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress,
-                mAvrAddress);
+        HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(
+                getSourceAddress(), mAvrAddress);
         sendCommand(command, new HdmiControlService.SendMessageCallback() {
             @Override
             public void onSendCompleted(int error) {
index d4a35f8..19fa22b 100644 (file)
@@ -31,14 +31,14 @@ final class RequestArcTerminationAction extends RequestArcAction {
      *
      * @see RequestArcAction#RequestArcAction
      */
-    RequestArcTerminationAction(HdmiControlService service, int sourceAddress, int avrAddress) {
-        super(service, sourceAddress, avrAddress);
+    RequestArcTerminationAction(HdmiCecLocalDevice source, int avrAddress) {
+        super(source, avrAddress);
     }
 
     @Override
     boolean start() {
         HdmiCecMessage command =
-                HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress);
+                HdmiCecMessageBuilder.buildRequestArcTermination(getSourceAddress(), mAvrAddress);
         sendCommand(command, new HdmiControlService.SendMessageCallback() {
             @Override
             public void onSendCompleted(int error) {
index 19974ea..2eec846 100644 (file)
@@ -39,7 +39,7 @@ import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
  * <li> Routing at CEC enable time
  * </ul>
  */
-public class RoutingControlAction extends FeatureAction {
+final class RoutingControlAction extends FeatureAction {
     private static final String TAG = "RoutingControlAction";
 
     // State in which we wait for <Routing Information> to arrive. If timed out, we use the
@@ -63,9 +63,8 @@ public class RoutingControlAction extends FeatureAction {
     // The latest routing path. Updated by each <Routing Information> from CEC switches.
     private int mCurrentRoutingPath;
 
-    RoutingControlAction(HdmiControlService service, int sourceAddress, int path,
-            IHdmiControlCallback callback) {
-        super(service, sourceAddress);
+    RoutingControlAction(HdmiCecLocalDevice localDevice, int path, IHdmiControlCallback callback) {
+        super(localDevice);
         mCallback = callback;
         mCurrentRoutingPath = path;
     }
@@ -92,7 +91,7 @@ public class RoutingControlAction extends FeatureAction {
             }
             mCurrentRoutingPath = routingPath;
             // Stop possible previous routing change sequence if in progress.
-            mService.removeAction(RoutingControlAction.class);
+            removeAction(RoutingControlAction.class);
             addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS);
             return true;
         } else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
@@ -130,7 +129,8 @@ public class RoutingControlAction extends FeatureAction {
     }
 
     private void sendSetStreamPath() {
-        sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(mSourceAddress, mCurrentRoutingPath));
+        sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(getSourceAddress(),
+                mCurrentRoutingPath));
     }
 
     private static boolean isInActiveRoutingPath(int activePath, int newPath) {
@@ -164,9 +164,9 @@ public class RoutingControlAction extends FeatureAction {
         }
         switch (timeoutState) {
             case STATE_WAIT_FOR_ROUTING_INFORMATION:
-                HdmiCecDeviceInfo device = mService.getDeviceInfoByPath(mCurrentRoutingPath);
+                HdmiCecDeviceInfo device = tv().getDeviceInfoByPath(mCurrentRoutingPath);
                 if (device == null) {
-                    maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
+                    maybeChangeActiveInput(tv().pathToPortId(mCurrentRoutingPath));
                 } else {
                     // TODO: Also check followings and then proceed:
                     //       if routing change was neither triggered by TV at CEC enable time, nor
@@ -184,7 +184,7 @@ public class RoutingControlAction extends FeatureAction {
             case STATE_WAIT_FOR_REPORT_POWER_STATUS:
                 int tvPowerStatus = getTvPowerStatus();
                 if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) {
-                    if (!maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath))) {
+                    if (!maybeChangeActiveInput(localDevice().pathToPortId(mCurrentRoutingPath))) {
                         sendSetStreamPath();
                     }
                 }
@@ -196,7 +196,7 @@ public class RoutingControlAction extends FeatureAction {
 
     // Called whenever an HDMI input of the TV shall become the active input.
     private boolean maybeChangeActiveInput(int path) {
-        if (mService.getActiveInput() == mService.pathToPortId(path)) {
+        if (localDevice().getActiveInput() == localDevice().pathToPortId(path)) {
             return false;
         }
         // TODO: Remember the currently active input
@@ -207,7 +207,7 @@ public class RoutingControlAction extends FeatureAction {
     }
 
     private void queryDevicePowerStatus(int address, SendMessageCallback callback) {
-        sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, address),
+        sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address),
                 callback);
     }
 
@@ -216,7 +216,7 @@ public class RoutingControlAction extends FeatureAction {
             mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
             addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
         } else {
-            maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
+            maybeChangeActiveInput(localDevice().pathToPortId(mCurrentRoutingPath));
         }
     }
 
index 8e6998f..ddcda92 100644 (file)
 package com.android.server.hdmi;
 
 import android.hardware.hdmi.HdmiCecMessage;
-import android.view.KeyEvent;
 import android.util.Slog;
-
-import libcore.util.EmptyArray;
+import android.view.KeyEvent;
 
 /**
  * Feature action that transmits remote control key command (User Control Press/
@@ -56,13 +54,12 @@ final class SendKeyAction extends FeatureAction {
     /**
      * Constructor.
      *
-     * @param service {@link HdmiControlService} instance
-     * @param sourceAddress logical address to be used as source address
+     * @param source {@link HdmiCecLocalDevice} instance
      * @param targetAddress logical address of the device to send the keys to
      * @param keyCode remote control key code as defined in {@link KeyEvent}
      */
-    SendKeyAction(HdmiControlService service, int sourceAddress, int targetAddress, int keyCode) {
-        super(service, sourceAddress);
+    SendKeyAction(HdmiCecLocalDevice source, int targetAddress, int keyCode) {
+        super(source);
         mTargetAddress = targetAddress;
         mLastKeyCode = keyCode;
     }
@@ -112,12 +109,13 @@ final class SendKeyAction extends FeatureAction {
         if (keyCodeAndParam == null) {
             return;
         }
-        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress, mTargetAddress,
-                keyCodeAndParam));
+        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
+                mTargetAddress, keyCodeAndParam));
     }
 
     private void sendKeyUp() {
-        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress, mTargetAddress));
+        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
+                mTargetAddress));
     }
 
     @Override
index d53d88d..da841c6 100644 (file)
@@ -39,14 +39,13 @@ final class SetArcTransmissionStateAction extends FeatureAction {
     /**
      * @Constructor
      *
-     * @param service an instance of {@link HdmiControlService}
-     * @param sourceAddress logical address to be used as source address
+     * @param source {@link HdmiCecLocalDevice} instance
      * @param enabled whether to enable ARC Transmission channel
      */
-    SetArcTransmissionStateAction(HdmiControlService service, int sourceAddress, int avrAddress,
+    SetArcTransmissionStateAction(HdmiCecLocalDevice source, int avrAddress,
             boolean enabled) {
-        super(service, sourceAddress);
-        HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+        super(source);
+        HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
         HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
         mAvrAddress = avrAddress;
         mEnabled = enabled;
@@ -65,7 +64,7 @@ final class SetArcTransmissionStateAction extends FeatureAction {
 
     private void sendReportArcInitiated() {
         HdmiCecMessage command =
-                HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress);
+                HdmiCecMessageBuilder.buildReportArcInitiated(getSourceAddress(), mAvrAddress);
         sendCommand(command, new HdmiControlService.SendMessageCallback() {
             @Override
             public void onSendCompleted(int error) {
@@ -93,14 +92,14 @@ final class SetArcTransmissionStateAction extends FeatureAction {
     }
 
     private void setArcStatus(boolean enabled) {
-        boolean wasEnabled = mService.setArcStatus(enabled);
+        boolean wasEnabled = tv().setArcStatus(enabled);
         Slog.i(TAG, "Change arc status [old:" + wasEnabled + " ,new:" + enabled);
 
         // If enabled before and set to "disabled" and send <Report Arc Terminated> to
         // av reciever.
         if (!enabled && wasEnabled) {
-            sendCommand(
-                    HdmiCecMessageBuilder.buildReportArcTerminated(mSourceAddress, mAvrAddress));
+            sendCommand(HdmiCecMessageBuilder.buildReportArcTerminated(getSourceAddress(),
+                    mAvrAddress));
         }
     }
 
index dde3342..cf7ff90 100644 (file)
@@ -47,23 +47,22 @@ abstract class SystemAudioAction extends FeatureAction {
     /**
      * Constructor
      *
-     * @param service {@link HdmiControlService} instance
-     * @param sourceAddress logical address of source device (TV or STB).
+     * @param source {@link HdmiCecLocalDevice} instance
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
      * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
      */
-    SystemAudioAction(HdmiControlService service, int sourceAddress, int avrAddress,
-            boolean targetStatus) {
-        super(service, sourceAddress);
+    SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus) {
+        super(source);
         HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
         mAvrLogicalAddress = avrAddress;
         mTargetAudioStatus = targetStatus;
     }
 
     protected void sendSystemAudioModeRequest() {
-        int avrPhysicalAddress = mService.getAvrDeviceInfo().getPhysicalAddress();
-        HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(mSourceAddress,
+        int avrPhysicalAddress = tv().getAvrDeviceInfo().getPhysicalAddress();
+        HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+                getSourceAddress(),
                 mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus);
         sendCommand(command, new HdmiControlService.SendMessageCallback() {
             @Override
@@ -90,11 +89,11 @@ abstract class SystemAudioAction extends FeatureAction {
     }
 
     protected void setSystemAudioMode(boolean mode) {
-        mService.setSystemAudioMode(mode);
+        tv().setSystemAudioMode(mode);
     }
 
     protected void sendGiveAudioStatus() {
-        HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(mSourceAddress,
+        HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(),
                 mAvrLogicalAddress);
         sendCommand(command, new HdmiControlService.SendMessageCallback() {
             @Override
@@ -112,7 +111,7 @@ abstract class SystemAudioAction extends FeatureAction {
     private void handleSendGiveAudioStatusFailure() {
         // TODO: Notify the failure status.
 
-        int uiCommand = mService.getSystemAudioMode()
+        int uiCommand = tv().getSystemAudioMode()
                 ? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION  // SystemAudioMode: ON
                 : HdmiConstants.UI_COMMAND_MUTE_FUNCTION;           // SystemAudioMode: OFF
         sendUserControlPressedAndReleased(uiCommand);
@@ -121,9 +120,9 @@ abstract class SystemAudioAction extends FeatureAction {
 
     private void sendUserControlPressedAndReleased(int uiCommand) {
         sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
-                mSourceAddress, mAvrLogicalAddress, uiCommand));
+                getSourceAddress(), mAvrLogicalAddress, uiCommand));
         sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
-                mSourceAddress, mAvrLogicalAddress));
+                getSourceAddress(), mAvrLogicalAddress));
     }
 
     @Override
@@ -158,7 +157,7 @@ abstract class SystemAudioAction extends FeatureAction {
                 if (params.length > 0) {
                     boolean mute = (params[0] & 0x80) == 0x80;
                     int volume = params[0] & 0x7F;
-                    mService.setAudioStatus(mute, volume);
+                    tv().setAudioStatus(mute, volume);
                     if (mTargetAudioStatus && mute || !mTargetAudioStatus && !mute) {
                         // Toggle AVR's mute status to match with the system audio status.
                         sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE);
@@ -171,8 +170,8 @@ abstract class SystemAudioAction extends FeatureAction {
     }
 
     protected void removeSystemAudioActionInProgress() {
-        mService.removeActionExcept(SystemAudioActionFromTv.class, this);
-        mService.removeActionExcept(SystemAudioActionFromAvr.class, this);
+        removeActionExcept(SystemAudioActionFromTv.class, this);
+        removeActionExcept(SystemAudioActionFromAvr.class, this);
     }
 
     @Override
index c5eb44b..3907b71 100644 (file)
@@ -25,16 +25,15 @@ final class SystemAudioActionFromAvr extends SystemAudioAction {
     /**
      * Constructor
      *
-     * @param service {@link HdmiControlService} instance
-     * @param tvAddress logical address of TV device
+     * @param source {@link HdmiCecLocalDevice} instance
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
      * @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid
      */
-    SystemAudioActionFromAvr(HdmiControlService service, int tvAddress, int avrAddress,
+    SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress,
             boolean targetStatus) {
-        super(service, tvAddress, avrAddress, targetStatus);
-        HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+        super(source, avrAddress, targetStatus);
+        HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
     }
 
     @Override
@@ -45,13 +44,13 @@ final class SystemAudioActionFromAvr extends SystemAudioAction {
     }
 
     private void handleSystemAudioActionFromAvr() {
-        if (mTargetAudioStatus == mService.getSystemAudioMode()) {
+        if (mTargetAudioStatus == tv().getSystemAudioMode()) {
             finish();
             return;
         }
-        if (mService.isInPresetInstallationMode()) {
+        if (tv().isInPresetInstallationMode()) {
             sendCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
-                    mSourceAddress, mAvrLogicalAddress,
+                    getSourceAddress(), mAvrLogicalAddress,
                     HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE, HdmiConstants.ABORT_REFUSED));
             mTargetAudioStatus = false;
             sendSystemAudioModeRequest();
index 9994de6..e0c4ff4 100644 (file)
@@ -26,16 +26,15 @@ final class SystemAudioActionFromTv extends SystemAudioAction {
     /**
      * Constructor
      *
-     * @param service {@link HdmiControlService} instance
-     * @param tvAddress logical address of TV device
+     * @param sourceAddress {@link HdmiCecLocalDevice} instance
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
      * @throw IllegalArugmentException if device type of tvAddress is invalid
      */
-    SystemAudioActionFromTv(HdmiControlService service, int tvAddress, int avrAddress,
+    SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress,
             boolean targetStatus) {
-        super(service, tvAddress, avrAddress, targetStatus);
-        HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+        super(sourceAddress, avrAddress, targetStatus);
+        HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
     }
 
     @Override
index 9d45702..c954b50 100644 (file)
@@ -12,8 +12,8 @@ public class VendorSpecificAction extends FeatureAction {
     private static final int STATE_1 = 1;
     private static final int STATE_2 = 2;
 
-    VendorSpecificAction(HdmiControlService service, int sourceAddress) {
-        super(service, sourceAddress);
+    VendorSpecificAction(HdmiCecLocalDevice source) {
+        super(source);
         // Modify the constructor if additional arguments are necessary.
     }