OSDN Git Service

Give clear distinction between active source/active routing path
authorJinsuk Kim <jinsukkim@google.com>
Thu, 24 Jul 2014 00:15:35 +0000 (09:15 +0900)
committerJinsuk Kim <jinsukkim@google.com>
Mon, 28 Jul 2014 05:48:29 +0000 (14:48 +0900)
Previously in HdmiControlSerivce, active routing path was mixed with
the physical address of the active source, but these two concepts needs
clearer distinction.

Defined a new variable for active source that contains both logical/physical
address to keep active routing path separate. This change makes it possible
to write the flow more closely to the guideline.

Also added to this CL is flag notifyInputChange, which allows the device/
routing change triggered by external event to trigger InputChangeListener
to get TIF notified. For routing control, the device info passed to TIF
has the path information in the field mPhysicalAddress (other fields are
invalid).

Bug: 16519939
Change-Id: I8b400bc48e874b0866500655773aea38ab945fe4

services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
services/core/java/com/android/server/hdmi/DeviceSelectAction.java
services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.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/NewDeviceAction.java
services/core/java/com/android/server/hdmi/RoutingControlAction.java

index 432424b..f3d570e 100644 (file)
@@ -23,6 +23,8 @@ import android.hardware.hdmi.HdmiControlManager;
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
+
 /**
  * Handles CEC command &lt;Active Source&gt;.
  * <p>
@@ -54,44 +56,45 @@ final class ActiveSourceHandler {
     /**
      * Handles the incoming active source command.
      *
-     * @param activeAddress logical address of the device to be the active source
-     * @param activePath routing path of the device to be the active source
+     * @param newActive new active source information
      */
-    void process(int activeAddress, int activePath) {
+    void process(ActiveSource newActive) {
         // Seq #17
         HdmiCecLocalDeviceTv tv = mSource;
-        if (getSourcePath() == activePath && tv.getActiveSource() == getSourceAddress()) {
+        ActiveSource activeSource = tv.getActiveSource();
+        if (activeSource.equals(newActive)) {
             invokeCallback(HdmiControlManager.RESULT_SUCCESS);
             return;
         }
-        HdmiCecDeviceInfo device = mService.getDeviceInfo(activeAddress);
+        HdmiCecDeviceInfo device = mService.getDeviceInfo(newActive.logicalAddress);
         if (device == null) {
-            tv.startNewDeviceAction(activeAddress, activePath);
+            tv.startNewDeviceAction(newActive);
         }
 
-        int currentActive = tv.getActiveSource();
-        int currentPath = tv.getActivePath();
+        ActiveSource current = tv.getActiveSource();
         if (!tv.isProhibitMode()) {
-            tv.updateActiveSource(activeAddress, activePath);
-            if (currentActive != activeAddress && currentPath != activePath) {
-                tv.updateActivePortId(mService.pathToPortId(activePath));
+            tv.updateActiveSource(newActive);
+            if (!current.equals(newActive)) {
+                boolean notifyInputChange = (mCallback == null);
+                tv.updateActiveInput(newActive.physicalAddress, notifyInputChange);
             }
             invokeCallback(HdmiControlManager.RESULT_SUCCESS);
         } else {
             // 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 (currentActive == getSourceAddress()) {
-                HdmiCecMessage activeSource =
-                        HdmiCecMessageBuilder.buildActiveSource(currentActive, currentPath);
-                mService.sendCecCommand(activeSource);
-                tv.updateActiveSource(currentActive, currentPath);
+            if (current.logicalAddress == getSourceAddress()) {
+                HdmiCecMessage activeSourceCommand = HdmiCecMessageBuilder.buildActiveSource(
+                        current.logicalAddress, current.physicalAddress);
+                mService.sendCecCommand(activeSourceCommand);
+                tv.updateActiveSource(current);
                 invokeCallback(HdmiControlManager.RESULT_SUCCESS);
             } else {
                 HdmiCecMessage routingChange = HdmiCecMessageBuilder.buildRoutingChange(
-                        getSourceAddress(), activePath, currentPath);
+                        getSourceAddress(), newActive.physicalAddress, current.physicalAddress);
                 mService.sendCecCommand(routingChange);
-                tv.addAndStartAction(new RoutingControlAction(tv, currentPath, true, mCallback));
+                tv.addAndStartAction(
+                        new RoutingControlAction(tv, current.physicalAddress, true, mCallback));
             }
         }
     }
index d4fffcf..018b34d 100644 (file)
@@ -23,6 +23,7 @@ import android.hardware.hdmi.IHdmiControlCallback;
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
 import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
 
 /**
@@ -130,10 +131,10 @@ final class DeviceSelectAction extends FeatureAction {
                 return false;
             case STATE_WAIT_FOR_ACTIVE_SOURCE:
                 if (opcode == Constants.MESSAGE_ACTIVE_SOURCE) {
-                    int activePath = HdmiUtils.twoBytesToInt(params);
+                    int physicalAddress = HdmiUtils.twoBytesToInt(params);
                     ActiveSourceHandler
                             .create((HdmiCecLocalDeviceTv) localDevice(), mCallback)
-                            .process(cmd.getSource(), activePath);
+                            .process(ActiveSource.of(cmd.getSource(), physicalAddress));
                     finish();
                     return true;
                 }
index dbcdfc0..40eb3e4 100644 (file)
@@ -49,9 +49,41 @@ abstract class HdmiCecLocalDevice {
     protected int mPreferredAddress;
     protected HdmiCecDeviceInfo mDeviceInfo;
 
+    static class ActiveSource {
+        int logicalAddress;
+        int physicalAddress;
+
+        public ActiveSource(int logical, int physical) {
+            logicalAddress = logical;
+            physicalAddress = physical;
+        }
+        public static ActiveSource of(int logical, int physical) {
+            return new ActiveSource(logical, physical);
+        }
+        public boolean isValid() {
+            return HdmiUtils.isValidAddress(logicalAddress);
+        }
+        public boolean equals(int logical, int physical) {
+            return logicalAddress == logical && physicalAddress == physical;
+        }
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof ActiveSource) {
+                ActiveSource that = (ActiveSource) obj;
+                return that.logicalAddress == logicalAddress &&
+                       that.physicalAddress == physicalAddress;
+            }
+            return false;
+        }
+        @Override
+        public int hashCode() {
+            return logicalAddress * 29 + physicalAddress;
+        }
+    }
     // Logical address of the active source.
     @GuardedBy("mLock")
-    private int mActiveSource;
+    protected final ActiveSource mActiveSource =
+            new ActiveSource(-1, Constants.INVALID_PHYSICAL_ADDRESS);
 
     // 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
@@ -549,15 +581,24 @@ abstract class HdmiCecLocalDevice {
         return mService.isConnectedToArcPort(path);
     }
 
-    int getActiveSource() {
+    ActiveSource getActiveSource() {
         synchronized (mLock) {
             return mActiveSource;
         }
     }
 
-    void setActiveSource(int source) {
+    void setActiveSource(ActiveSource newActive) {
+        setActiveSource(newActive.logicalAddress, newActive.physicalAddress);
+    }
+
+    void setActiveSource(HdmiCecDeviceInfo info) {
+        setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress());
+    }
+
+    void setActiveSource(int logicalAddress, int physicalAddress) {
         synchronized (mLock) {
-            mActiveSource = source;
+            mActiveSource.logicalAddress = logicalAddress;
+            mActiveSource.physicalAddress = physicalAddress;
         }
     }
 
@@ -596,13 +637,6 @@ abstract class HdmiCecLocalDevice {
         }
     }
 
-    void updateActiveDevice(int logicalAddress, int physicalAddress) {
-        synchronized (mLock) {
-            mActiveSource = logicalAddress;
-            mActiveRoutingPath = physicalAddress;
-        }
-    }
-
     @ServiceThreadOnly
     HdmiCecMessageCache getCecMessageCache() {
         assertRunOnServiceThread();
index 0d457e3..f93d20f 100644 (file)
@@ -147,12 +147,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
         if (targetAddress == Constants.ADDR_INTERNAL) {
             handleSelectInternalSource();
             // Switching to internal source is always successful even when CEC control is disabled.
-            setActiveSource(targetAddress);
+            setActiveSource(targetAddress, mService.getPhysicalAddress());
             invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
             return;
         }
         if (!mService.isControlEnabled()) {
-            setActiveSource(targetAddress);
+            HdmiCecDeviceInfo info = getDeviceInfo(targetAddress);
+            if (info != null) {
+                setActiveSource(info);
+            }
             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
             return;
         }
@@ -169,7 +172,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
     private void handleSelectInternalSource() {
         assertRunOnServiceThread();
         // Seq #18
-        if (mService.isControlEnabled() && getActiveSource() != mAddress) {
+        if (mService.isControlEnabled() && mActiveSource.logicalAddress != mAddress) {
             updateActiveSource(mAddress, mService.getPhysicalAddress());
             // TODO: Check if this comes from <Text/Image View On> - if true, do nothing.
             HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
@@ -179,16 +182,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
     }
 
     @ServiceThreadOnly
-    void updateActiveSource(int activeSource, int activePath) {
+    void updateActiveSource(int logicalAddress, int physicalAddress) {
+        assertRunOnServiceThread();
+        updateActiveSource(ActiveSource.of(logicalAddress, physicalAddress));
+    }
+
+    @ServiceThreadOnly
+    void updateActiveSource(ActiveSource newActive) {
         assertRunOnServiceThread();
         // Seq #14
-        if (activeSource == getActiveSource() && activePath == getActivePath()) {
+        if (mActiveSource.equals(newActive)) {
             return;
         }
-        setActiveSource(activeSource);
-        setActivePath(activePath);
-        if (getDeviceInfo(activeSource) != null && activeSource != mAddress) {
-            if (mService.pathToPortId(activePath) == getActivePortId()) {
+        setActiveSource(newActive);
+        int logicalAddress = newActive.logicalAddress;
+        if (getDeviceInfo(logicalAddress) != null && logicalAddress != mAddress) {
+            if (mService.pathToPortId(newActive.physicalAddress) == getActivePortId()) {
                 setPrevPortId(getActivePortId());
             }
             // TODO: Show the OSD banner related to the new active source device.
@@ -222,16 +231,26 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
     }
 
     @ServiceThreadOnly
-    void updateActivePortId(int portId) {
+    void updateActiveInput(int path, boolean notifyInputChange) {
         assertRunOnServiceThread();
         // Seq #15
+        int portId = mService.pathToPortId(path);
         if (portId == getActivePortId()) {
             return;
         }
+        setActivePath(path);
         setPrevPortId(portId);
-        // TODO: Actually switch the physical port here. Handle PAP/PIP as well.
-        //       Show OSD port change banner
-        mService.invokeInputChangeListener(getActiveSource());
+        // TODO: Handle PAP/PIP case.
+        // Show OSD port change banner
+        if (notifyInputChange) {
+            ActiveSource activeSource = getActiveSource();
+            HdmiCecDeviceInfo info = getDeviceInfo(activeSource.logicalAddress);
+            if (info == null) {
+                info = new HdmiCecDeviceInfo(Constants.ADDR_INVALID, path, portId,
+                        HdmiCecDeviceInfo.DEVICE_RESERVED, 0, null);
+            }
+            mService.invokeInputChangeListener(info);
+        }
     }
 
     @ServiceThreadOnly
@@ -242,26 +261,25 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
             return;
         }
+        if (portId == getActivePortId()) {
+            invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
+            return;
+        }
+        setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS);
         if (!mService.isControlEnabled()) {
             setActivePortId(portId);
             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
             return;
         }
-        if (portId == getActivePortId()) {
-            invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
-            return;
-        }
-        setActivePortId(portId);
         // TODO: Return immediately if the operation is triggered by <Text/Image View On>
-        //       and this is the first notification about the active input after power-on
-        //       (switch to HDMI didn't happen so far but is expected to happen soon).
-        removeAction(RoutingControlAction.class);
-
-        int oldPath = mService.portIdToPath(mService.portIdToPath(getActivePortId()));
+        // and this is the first notification about the active input after power-on
+        // (switch to HDMI didn't happen so far but is expected to happen soon).
+        int oldPath = mService.portIdToPath(getActivePortId());
         int newPath = mService.portIdToPath(portId);
         HdmiCecMessage routingChange =
                 HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
         mService.sendCecCommand(routingChange);
+        removeAction(RoutingControlAction.class);
         addAndStartAction(new RoutingControlAction(this, newPath, false, callback));
     }
 
@@ -284,7 +302,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
             action.get(0).processKeyEvent(keyCode, isPressed);
         } else {
             if (isPressed) {
-                addAndStartAction(new SendKeyAction(this, getActiveSource(), keyCode));
+                int logicalAddress = getActiveSource().logicalAddress;
+                addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode));
             } else {
                 Slog.w(TAG, "Discard key release event");
             }
@@ -306,12 +325,13 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
     @ServiceThreadOnly
     protected boolean handleActiveSource(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        int address = message.getSource();
-        int path = HdmiUtils.twoBytesToInt(message.getParams());
-        if (getDeviceInfo(address) == null) {
-            handleNewDeviceAtTheTailOfActivePath(path);
+        int logicalAddress = message.getSource();
+        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+        if (getDeviceInfo(logicalAddress) == null) {
+            handleNewDeviceAtTheTailOfActivePath(physicalAddress);
         } else {
-            ActiveSourceHandler.create(this, null).process(address, path);
+            ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
+            ActiveSourceHandler.create(this, null).process(activeSource);
         }
         return true;
     }
@@ -323,7 +343,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
         // Seq #10
 
         // Ignore <Inactive Source> from non-active source device.
-        if (getActiveSource() != message.getSource()) {
+        if (getActiveSource().logicalAddress != message.getSource()) {
             return true;
         }
         if (isProhibitMode()) {
@@ -342,7 +362,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
             }
             // TODO: Switch the TV freeze mode off
 
-            setActivePortId(portId);
             doManualPortSwitching(portId, null);
             setPrevPortId(Constants.INVALID_PORT_ID);
         }
@@ -354,7 +373,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
     protected boolean handleRequestActiveSource(HdmiCecMessage message) {
         assertRunOnServiceThread();
         // Seq #19
-        if (mAddress == getActiveSource()) {
+        if (mAddress == getActiveSource().logicalAddress) {
             mService.sendCecCommand(
                     HdmiCecMessageBuilder.buildActiveSource(mAddress, getActivePath()));
         }
@@ -392,11 +411,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
         if (!isInDeviceList(path, address)) {
             handleNewDeviceAtTheTailOfActivePath(path);
         }
-        startNewDeviceAction(address, path);
+        startNewDeviceAction(ActiveSource.of(address, path));
         return true;
     }
 
-    void startNewDeviceAction(int address, int path) {
+    void startNewDeviceAction(ActiveSource activeSource) {
         for (NewDeviceAction action : getActions(NewDeviceAction.class)) {
             // If there is new device action which has the same logical address and path
             // ignore new request.
@@ -406,12 +425,13 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
             // in. However, TV can detect a new device from HotPlugDetectionAction,
             // which sends <Give Physical Address> to the source for newly detected
             // device.
-            if (action.isActionOf(address, path)) {
+            if (action.isActionOf(activeSource)) {
                 return;
             }
         }
 
-        addAndStartAction(new NewDeviceAction(this, address, path));
+        addAndStartAction(new NewDeviceAction(this, activeSource.logicalAddress,
+                activeSource.physicalAddress));
     }
 
     private void handleNewDeviceAtTheTailOfActivePath(int path) {
index 3532213..37c297b 100644 (file)
@@ -724,6 +724,10 @@ public final class HdmiControlService extends SystemService {
             runOnServiceThread(new Runnable() {
                 @Override
                 public void run() {
+                    if (callback == null) {
+                        Slog.e(TAG, "Callback cannot be null");
+                        return;
+                    }
                     HdmiCecLocalDeviceTv tv = tv();
                     if (tv == null) {
                         Slog.w(TAG, "Local tv device not available");
@@ -741,6 +745,10 @@ public final class HdmiControlService extends SystemService {
             runOnServiceThread(new Runnable() {
                 @Override
                 public void run() {
+                    if (callback == null) {
+                        Slog.e(TAG, "Callback cannot be null");
+                        return;
+                    }
                     HdmiCecLocalDeviceTv tv = tv();
                     if (tv == null) {
                         Slog.w(TAG, "Local tv device not available");
@@ -1218,12 +1226,11 @@ public final class HdmiControlService extends SystemService {
         }
     }
 
-    void invokeInputChangeListener(int activeAddress) {
+    void invokeInputChangeListener(HdmiCecDeviceInfo info) {
         synchronized (mLock) {
             if (mInputChangeListener != null) {
-                HdmiCecDeviceInfo activeSource = getDeviceInfo(activeAddress);
                 try {
-                    mInputChangeListener.onChanged(activeSource);
+                    mInputChangeListener.onChanged(info);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
                 }
index 4ef7c96..907015b 100644 (file)
@@ -18,6 +18,7 @@ package com.android.server.hdmi;
 import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.util.Slog;
 
+import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
 import java.io.UnsupportedEncodingException;
 
 /**
@@ -190,7 +191,8 @@ final class NewDeviceAction extends FeatureAction {
         }
     }
 
-    boolean isActionOf(int address, int path) {
-        return (mDeviceLogicalAddress == address) && (mDevicePhysicalAddress == path);
+    boolean isActionOf(ActiveSource activeSource) {
+        return (mDeviceLogicalAddress == activeSource.logicalAddress)
+                && (mDevicePhysicalAddress == activeSource.physicalAddress);
     }
 }
index 8296f69..f05394f 100644 (file)
@@ -60,6 +60,12 @@ final class RoutingControlAction extends FeatureAction {
     // true if <Give Power Status> should be sent once the new active routing path is determined.
     private final boolean mQueryDevicePowerStatus;
 
+    // If set to true, call {@link HdmiControlService#invokeInputChangeListener()} when
+    // the routing control/active source change happens. The listener should be called if
+    // the events are triggered by external events such as manual switch port change or incoming
+    // <Inactive Source> command.
+    private final boolean mNotifyInputChange;
+
     @Nullable private final IHdmiControlCallback mCallback;
 
     // The latest routing path. Updated by each <Routing Information> from CEC switches.
@@ -71,6 +77,11 @@ final class RoutingControlAction extends FeatureAction {
         mCallback = callback;
         mCurrentRoutingPath = path;
         mQueryDevicePowerStatus = queryDevicePowerStatus;
+        // Callback is non-null when routing control action is brought up by binder API. Use
+        // this as an indicator for the input change notification. These API calls will get
+        // the result through this callback, not through notification. Any other events that
+        // trigger the routing control is external, for which notifcation is used.
+        mNotifyInputChange = (callback == null);
     }
 
     @Override
@@ -111,7 +122,7 @@ final class RoutingControlAction extends FeatureAction {
             if (isPowerOnOrTransient(devicePowerStatus)) {
                 sendSetStreamPath();
             } else {
-                tv().updateActivePortId(tv().pathToPortId(mCurrentRoutingPath));
+                tv().updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
             }
         }
         finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
@@ -155,13 +166,13 @@ final class RoutingControlAction extends FeatureAction {
                         }
                     });
                 } else {
-                    tv().updateActivePortId(tv().pathToPortId(mCurrentRoutingPath));
+                    tv().updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
                     finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
                 }
                 return;
             case STATE_WAIT_FOR_REPORT_POWER_STATUS:
                 if (isPowerOnOrTransient(getTvPowerStatus())) {
-                    tv().updateActivePortId(tv().pathToPortId(mCurrentRoutingPath));
+                    tv().updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
                     sendSetStreamPath();
                 }
                 finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
@@ -179,7 +190,7 @@ final class RoutingControlAction extends FeatureAction {
             mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
             addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
         } else {
-            tv().updateActivePortId(tv().pathToPortId(mCurrentRoutingPath));
+            tv().updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
             sendSetStreamPath();
             finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
         }