OSDN Git Service

Refine new device action.
authorJungshik Jang <jayjang@google.com>
Thu, 12 Jun 2014 09:03:04 +0000 (18:03 +0900)
committerJungshik Jang <jayjang@google.com>
Mon, 16 Jun 2014 04:33:07 +0000 (13:33 +0900)
There are many ways to initiate new device action
1. When receives <Report Physcial Address>
2. When receives <Active Source> from unregistered device.

If new device is audio system, it should start
ARC and system audio initiation action.

Along with this consolidate device remove actions.

Change-Id: I189afd8bec7270d6a1734a28632593b71932d9e8

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/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/HdmiConstants.java
services/core/java/com/android/server/hdmi/HdmiControlService.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/RoutingControlAction.java

index 32bcb69..bcd08eb 100644 (file)
@@ -67,7 +67,10 @@ final class ActiveSourceHandler {
         }
         HdmiCecDeviceInfo device = mService.getDeviceInfo(deviceLogicalAddress);
         if (device == null) {
-            // TODO: Start new device action (Device Discovery) sequence 5.
+            // "New device action" initiated by <Active Source> does not require
+            // "Routing change action".
+            mService.addAndStartAction(new NewDeviceAction(mService, mSourceAddress,
+                    deviceLogicalAddress, routingPath, false));
         }
 
         if (!mService.isInPresetInstallationMode()) {
index d36fc2c..f7392e9 100644 (file)
@@ -55,9 +55,6 @@ final class DeviceDiscoveryAction extends FeatureAction {
 
     private static final int DEVICE_POLLING_RETRY = 1;
 
-    // TODO: Move this to common place
-    private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
-
     /**
      * Interface used to report result of device discovery.
      */
@@ -75,7 +72,7 @@ final class DeviceDiscoveryAction extends FeatureAction {
     private static final class DeviceInfo {
         private final int mLogicalAddress;
 
-        private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+        private int mPhysicalAddress = HdmiConstants.INVALID_PHYSICAL_ADDRESS;
         private int mVendorId = HdmiCec.UNKNOWN_VENDOR_ID;
         private String mDisplayName = "";
         private int mDeviceType = HdmiCec.DEVICE_INACTIVE;
index f170de0..a8696a1 100644 (file)
@@ -220,4 +220,8 @@ final class DeviceSelectAction extends FeatureAction {
             Slog.e(TAG, "Callback failed:" + e);
         }
     }
+
+    int getTargetAddress() {
+        return mTarget.getLogicalAddress();
+    }
 }
index 7a08f99..7a2a6cc 100644 (file)
@@ -76,7 +76,7 @@ abstract class HdmiCecLocalDevice {
         return onMessage(message);
     }
 
-    protected boolean onMessage(HdmiCecMessage message) {
+    protected final boolean onMessage(HdmiCecMessage message) {
         switch (message.getOpcode()) {
             case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
                 return handleGetMenuLanguage(message);
@@ -88,6 +88,8 @@ abstract class HdmiCecLocalDevice {
                 return handleGiveDeviceVendorId();
             case HdmiCec.MESSAGE_GET_CEC_VERSION:
                 return handleGetCecVersion(message);
+            case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
+                return handleReportPhysicalAddress(message);
             default:
                 return false;
         }
@@ -143,6 +145,10 @@ abstract class HdmiCecLocalDevice {
         return false;
     }
 
+    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
+        return false;
+    }
+
     final void handleAddressAllocated(int logicalAddress) {
         mAddress = mPreferredAddress = logicalAddress;
         onAddressAllocated(logicalAddress);
index aa1769e..625b256 100644 (file)
 
 package com.android.server.hdmi;
 
-import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.hdmi.HdmiCec;
 import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiControlCallback;
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
+
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -46,20 +48,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
         mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
                 mAddress, mService.getVendorId()));
 
-        mService.launchDeviceDiscovery(mAddress);
+        launchDeviceDiscovery();
         // TODO: Start routing control action, device discovery action.
     }
 
-    @Override
-    protected boolean onMessage(HdmiCecMessage message) {
-        switch (message.getOpcode()) {
-            case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
-                return handleReportPhysicalAddress(message);
-            default:
-                return super.onMessage(message);
-        }
-    }
-
     /**
      * Performs the action 'device select', or 'one touch play' initiated by TV.
      *
@@ -98,7 +90,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
         return true;
     }
 
-    private boolean handleReportPhysicalAddress(HdmiCecMessage message) {
+    @Override
+    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
         // Ignore if [Device Discovery Action] is going on.
         if (mService.hasAction(DeviceDiscoveryAction.class)) {
             Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
@@ -107,9 +100,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
         }
 
         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
-        mService.addAndStartAction(new NewDeviceAction(mService,
-                mAddress, message.getSource(), physicalAddress));
+        int logicalAddress = message.getSource();
 
+        // 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,
+                requireRoutingChange));
         return true;
     }
 
@@ -137,4 +136,29 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
                 HdmiConstants.ABORT_REFUSED));
         return true;
     }
+
+    private void launchDeviceDiscovery() {
+        mService.clearAllDeviceInfo();
+        // TODO: Move the following callback to HdmiLocalDeviceTv.
+        DeviceDiscoveryAction action = new DeviceDiscoveryAction(mService, mAddress,
+                new DeviceDiscoveryCallback() {
+                    @Override
+                    public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
+                        for (HdmiCecDeviceInfo info : deviceInfos) {
+                            mService.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());
+                        }
+
+                        mService.addAndStartAction(new HotplugDetectionAction(mService,
+                                mAddress));
+                    }
+                });
+        mService.addAndStartAction(action);
+    }
 }
index 8f319ea..2ab87d6 100644 (file)
@@ -75,5 +75,8 @@ final class HdmiConstants {
      */
     static final int FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL = 3;
 
+    static final int INVALID_PORT_ID = -1;
+    static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+
     private HdmiConstants() { /* cannot be instantiated */ }
 }
index ea375f6..bccfa36 100644 (file)
@@ -21,6 +21,7 @@ import android.content.Context;
 import android.hardware.hdmi.HdmiCec;
 import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiHotplugEvent;
 import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.hdmi.IHdmiControlService;
@@ -37,7 +38,6 @@ import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.SystemService;
-import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
 import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
 
 import java.util.ArrayList;
@@ -296,7 +296,7 @@ public final class HdmiControlService extends SystemService {
         HdmiPortInfo portInfo = getPortInfo(portId);
         if (portInfo == null) {
             Slog.e(TAG, "Cannot find the port info: " + portId);
-            return 0xFFFF;  // Use HdmiConstants.INVALID_PHYSICAL_ADDRESS;
+            return HdmiConstants.INVALID_PHYSICAL_ADDRESS;
         }
         return portInfo.getAddress();
     }
@@ -314,7 +314,7 @@ public final class HdmiControlService extends SystemService {
                 return info.getId();
             }
         }
-        return -1;  // Use HdmiConstants.INVALID_PORT_ID;
+        return HdmiConstants.INVALID_PORT_ID;
     }
 
     /**
@@ -446,7 +446,12 @@ public final class HdmiControlService extends SystemService {
 
     void setSystemAudioMode(boolean on) {
         synchronized (mLock) {
-            mSystemAudioMode = on;
+            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.
+            }
         }
     }
 
@@ -456,6 +461,19 @@ public final class HdmiControlService extends SystemService {
         }
     }
 
+    /**
+     * Whether a device of the specified physical address is connected to ARC enabled port.
+     */
+    boolean isConnectedToArcPort(int physicalAddress) {
+        for (HdmiPortInfo portInfo : mPortInfo) {
+            if (hasSameTopPort(portInfo.getAddress(), physicalAddress)
+                    && portInfo.isArcSupported()) {
+                return true;
+            }
+        }
+        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) {
@@ -466,6 +484,17 @@ public final class HdmiControlService extends SystemService {
         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.
      *
@@ -530,6 +559,15 @@ public final class HdmiControlService extends SystemService {
     }
 
     /**
+     * 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
@@ -573,12 +611,23 @@ public final class HdmiControlService extends SystemService {
         return dispatchMessageToLocalDevice(message);
     }
 
+    private boolean dispatchMessageToAction(HdmiCecMessage message) {
+        for (FeatureAction action : mActions) {
+            if (action.processCommand(message)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
         for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
             if (device.dispatchMessage(message)) {
                 return true;
             }
         }
+
+        Slog.w(TAG, "Unhandled cec command:" + message);
         return false;
     }
 
@@ -589,7 +638,18 @@ public final class HdmiControlService extends SystemService {
      * @param connected whether to be plugged in or not
      */
     void onHotplug(int portNo, boolean connected) {
-        // TODO: Start "RequestArcInitiationAction" if ARC port.
+        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();
+        }
+
+        announceHotplugEvent(portNo, connected);
     }
 
     /**
@@ -617,35 +677,32 @@ public final class HdmiControlService extends SystemService {
         return strategy | iterationStrategy;
     }
 
+    void clearAllDeviceInfo() {
+        assertRunOnServiceThread();
+        mCecController.clearDeviceInfoList();
+    }
+
+    List<HdmiCecLocalDevice> getAllLocalDevices() {
+        assertRunOnServiceThread();
+        return mCecController.getLocalDeviceList();
+    }
+
     /**
-     * Launch device discovery sequence. It starts with clearing the existing device info list.
-     * Note that it assumes that logical address of all local devices is already allocated.
+     * 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 sourceAddress a logical address of tv
+     * @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
      */
-    void launchDeviceDiscovery(final int sourceAddress) {
-        // At first, clear all existing device infos.
-        mCecController.clearDeviceInfoList();
-        // TODO: flush cec message cache when CEC is turned off.
-
-        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress,
-                new DeviceDiscoveryCallback() {
-                    @Override
-                    public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
-                        for (HdmiCecDeviceInfo info : deviceInfos) {
-                            addCecDevice(info);
-                        }
-
-                        // Add device info of all local devices.
-                        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
-                            addCecDevice(device.getDeviceInfo());
-                        }
-
-                        addAndStartAction(new HotplugDetectionAction(HdmiControlService.this,
-                                sourceAddress));
-                    }
-                });
-        addAndStartAction(action);
+    boolean isInDeviceList(int physicalAddress, int logicalAddress) {
+        assertRunOnServiceThread();
+        HdmiCecDeviceInfo device = mCecController.getDeviceInfo(logicalAddress);
+        if (device == null) {
+            return false;
+        }
+        return device.getPhysicalAddress() == physicalAddress;
     }
 
     private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
@@ -675,16 +732,6 @@ public final class HdmiControlService extends SystemService {
         addAndStartAction(action);
     }
 
-    private boolean dispatchMessageToAction(HdmiCecMessage message) {
-        for (FeatureAction action : mActions) {
-            if (action.processCommand(message)) {
-                return true;
-            }
-        }
-        Slog.w(TAG, "Unsupported cec command:" + message);
-        return false;
-    }
-
     private void handleSetSystemAudioMode(HdmiCecMessage message) {
         if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) {
             return;
@@ -730,10 +777,6 @@ public final class HdmiControlService extends SystemService {
         }
     }
 
-    void addCecDevice(HdmiCecDeviceInfo info) {
-        mCecController.addDeviceInfo(info);
-    }
-
     private void enforceAccessPermission() {
         getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
     }
@@ -768,7 +811,6 @@ public final class HdmiControlService extends SystemService {
             });
         }
 
-
         @Override
         public void oneTouchPlay(final IHdmiControlCallback callback) {
             enforceAccessPermission();
@@ -911,6 +953,17 @@ public final class HdmiControlService extends SystemService {
     }
 
     /**
+     * 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
@@ -918,9 +971,61 @@ public final class HdmiControlService extends SystemService {
     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) {
+            for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
+                invokeHotplugEventListener(listener, event);
+            }
+        }
+    }
+
+    private void invokeHotplugEventListener(IHdmiHotplugEventListener listener,
+            HdmiHotplugEvent event) {
+        try {
+            listener.onReceived(event);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
+        }
     }
 
     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;
+    }
 }
index c7a813d..ae20eda 100644 (file)
@@ -90,6 +90,20 @@ final class HotplugDetectionAction extends FeatureAction {
         }
     }
 
+    /**
+     * Start device polling immediately.
+     */
+    void pollAllDevicesNow() {
+        // Clear existing timer to avoid overlapped execution
+        mActionTimer.clearTimerMessage();
+
+        mTimeoutCount = 0;
+        mState = STATE_WAIT_FOR_NEXT_POLLING;
+        pollAllDevices();
+
+        addTimer(mState, POLLING_INTERVAL_MS);
+    }
+
     // This method is called every 5 seconds.
     private void pollDevices() {
         // All device check called every 15 seconds.
@@ -180,15 +194,56 @@ final class HotplugDetectionAction extends FeatureAction {
     }
 
     private void addDevice(int addedAddress) {
-        // TODO: implement this.
+        // Send <Give Physical Address>.
+        sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, addedAddress));
     }
 
     private void removeDevice(int removedAddress) {
+        // TODO: move the following logic to local device once move many logic to
+        //       local device.
+        mayChangeRoutingPath(removedAddress);
+        mayCancelDeviceSelect(removedAddress);
+        mayCancelOneTouchRecord(removedAddress);
+        mayDisableSystemAudioAndARC(removedAddress);
+
         mService.removeCecDevice(removedAddress);
-        // TODO: implements following steps.
-        // 1. Launch routing control sequence
-        // 2. Stop one touch play sequence if removed device is the device to be selected.
-        // 3. If audio system, start system audio off and arc off
-        // 4. Inform device remove to others
+    }
+
+    private void mayChangeRoutingPath(int address) {
+        // TODO: if removed address is current active source, it should change active source
+        // path new one. we can keep previous selection or can choose default input,
+        // such as internal tuner. Consider send intent to app so that app
+        // can handle it.
+    }
+
+    private void mayCancelDeviceSelect(int address) {
+        List<DeviceSelectAction> actions = mService.getActions(DeviceSelectAction.class);
+        if (actions.isEmpty()) {
+            return;
+        }
+
+        // Should ave only one Device Select Action
+        DeviceSelectAction action = actions.get(0);
+        if (action.getTargetAddress() == address) {
+            mService.removeAction(DeviceSelectAction.class);
+        }
+    }
+
+    private void mayCancelOneTouchRecord(int address) {
+        // TODO: implement this.
+    }
+
+    private void mayDisableSystemAudioAndARC(int address) {
+        if (HdmiCec.getTypeFromAddress(address) != HdmiCec.DEVICE_AUDIO_SYSTEM) {
+            return;
+        }
+
+        // Turn off system audio mode.
+        mService.setSystemAudioMode(false);
+        if (mService.getArcStatus()) {
+            mService.addAndStartAction(
+                    new RequestArcTerminationAction(mService, mSourceAddress, address));
+        }
+
     }
 }
index c284d10..2cae507 100644 (file)
@@ -48,6 +48,7 @@ final class NewDeviceAction extends FeatureAction {
 
     private final int mDeviceLogicalAddress;
     private final int mDevicePhysicalAddress;
+    private final boolean mRequireRoutingChange;
 
     private int mVendorId;
     private String mDisplayName;
@@ -59,17 +60,36 @@ final class NewDeviceAction extends FeatureAction {
      * @param sourceAddress logical address to be used as source address
      * @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,
-            int devicePhysicalAddress) {
+            int devicePhysicalAddress, boolean requireRoutingChange) {
         super(service, sourceAddress);
         mDeviceLogicalAddress = deviceLogicalAddress;
         mDevicePhysicalAddress = devicePhysicalAddress;
         mVendorId = HdmiCec.UNKNOWN_VENDOR_ID;
+        mRequireRoutingChange = requireRoutingChange;
     }
 
     @Override
     public boolean start() {
+        if (HdmiCec.getTypeFromAddress(mSourceAddress) == HdmiCec.DEVICE_AUDIO_SYSTEM) {
+            if (mService.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 (mRequireRoutingChange) {
+            startRoutingChange();
+        }
+
         mState = STATE_WAITING_FOR_SET_OSD_NAME;
         if (mayProcessCommandIfCached(mDeviceLogicalAddress, HdmiCec.MESSAGE_SET_OSD_NAME)) {
             return true;
@@ -133,6 +153,22 @@ final class NewDeviceAction extends FeatureAction {
         return false;
     }
 
+    private void startRoutingChange() {
+        // Stop existing routing control.
+        mService.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));
+    }
+
+    private static int toTopMostPortPath(int physicalAddress) {
+        return physicalAddress & HdmiConstants.ROUTING_PATH_TOP_MASK;
+    }
+
     private boolean mayProcessCommandIfCached(int destAddress, int opcode) {
         HdmiCecMessage message = mService.getCecMessageCache().getMessage(destAddress, opcode);
         if (message != null) {
index 65615a4..19974ea 100644 (file)
 
 package com.android.server.hdmi;
 
-import java.util.concurrent.TimeUnit;
-
-import android.hardware.hdmi.IHdmiControlCallback;
+import android.annotation.Nullable;
 import android.hardware.hdmi.HdmiCec;
 import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiControlCallback;
 import android.os.RemoteException;
 import android.util.Slog;
 
@@ -59,7 +58,7 @@ public class RoutingControlAction extends FeatureAction {
     // Time out in milliseconds used for <Report Power Status>
     private static final int TIMEOUT_REPORT_POWER_STATUS_MS = 1000;
 
-    private final IHdmiControlCallback mCallback;
+    @Nullable private final IHdmiControlCallback mCallback;
 
     // The latest routing path. Updated by each <Routing Information> from CEC switches.
     private int mCurrentRoutingPath;
@@ -167,7 +166,7 @@ public class RoutingControlAction extends FeatureAction {
             case STATE_WAIT_FOR_ROUTING_INFORMATION:
                 HdmiCecDeviceInfo device = mService.getDeviceInfoByPath(mCurrentRoutingPath);
                 if (device == null) {
-                    maybeChangeActiveInput(mCurrentRoutingPath);
+                    maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
                 } else {
                     // TODO: Also check followings and then proceed:
                     //       if routing change was neither triggered by TV at CEC enable time, nor
@@ -185,7 +184,7 @@ public class RoutingControlAction extends FeatureAction {
             case STATE_WAIT_FOR_REPORT_POWER_STATUS:
                 int tvPowerStatus = getTvPowerStatus();
                 if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) {
-                    if (!maybeChangeActiveInput(mCurrentRoutingPath)) {
+                    if (!maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath))) {
                         sendSetStreamPath();
                     }
                 }
@@ -217,15 +216,10 @@ public class RoutingControlAction extends FeatureAction {
             mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
             addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
         } else {
-            maybeChangeActiveInput(mCurrentRoutingPath);
+            maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath));
         }
     }
 
-    // Given the HDMI port id, return the port address.
-    private int portToPath(int portId) {
-        return mService.getPortInfo(portId).getAddress();
-    }
-
     private void invokeCallback(int result) {
         if (mCallback == null) {
             return;