Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 736cbcd8 authored by Shubang Lu's avatar Shubang Lu Committed by Android (Google) Code Review
Browse files

Merge changes I59ce940b,I0292b221,I6fbeb748

* changes:
  Add HdmiCecLOcalDeviceSource to extract shareable handlers of all the Hdmi CEC source devices into a parent class.
  Moving routing logic to playback device.
  Add SystemAudioModeRequest from non TV device logic.
parents a9852803 848a9f29
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -279,9 +279,9 @@ final class Constants {
     * <p>True means enabling muting logic.
     * <p>False means never mute device.
     */
    // TODO(OEM): set to true to disable muting.
    // TODO(OEM): Change property to ro and set to true to disable muting.
    static final String PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE =
            "ro.hdmi.property_system_audio_mode_muting_enable";
            "persist.sys.hdmi.property_system_audio_mode_muting_enable";

    // Set to false to allow playback device to go to suspend mode even
    // when it's an active source. True by default.
+4 −0
Original line number Diff line number Diff line
@@ -252,6 +252,10 @@ abstract class HdmiCecFeatureAction {
        return (HdmiCecLocalDevicePlayback) mSource;
    }

    protected final HdmiCecLocalDeviceSource source() {
        return (HdmiCecLocalDeviceSource) mSource;
    }

    protected final HdmiCecLocalDeviceTv tv() {
        return (HdmiCecLocalDeviceTv) mSource;
    }
+52 −0
Original line number Diff line number Diff line
@@ -27,10 +27,12 @@ import android.util.Slog;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -51,6 +53,11 @@ abstract class HdmiCecLocalDevice {
    // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior.
    // When it expires, we can assume <User Control Release> is received.
    private static final int FOLLOWER_SAFETY_TIMEOUT = 550;
    /**
     * Return value of {@link #getLocalPortFromPhysicalAddress(int)}
     */
    private static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1;
    private static final int TARGET_SAME_PHYSICAL_ADDRESS = 0;

    protected final HdmiControlService mService;
    protected final int mDeviceType;
@@ -434,10 +441,14 @@ abstract class HdmiCecLocalDevice {
        return true;
    }

    // Audio System device with no Playback device type
    // needs to refactor this function if it's also a switch
    protected boolean handleRoutingChange(HdmiCecMessage message) {
        return false;
    }

    // Audio System device with no Playback device type
    // needs to refactor this function if it's also a switch
    protected boolean handleRoutingInformation(HdmiCecMessage message) {
        return false;
    }
@@ -1033,4 +1044,45 @@ abstract class HdmiCecLocalDevice {
        pw.println("mActiveSource: " + mActiveSource);
        pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath));
    }

    /**
     * Method to parse target physical address to the port number on the current device.
     *
     * <p>This check assumes target address is valid.
     * @param targetPhysicalAddress is the physical address of the target device
     * @return
     * <p>If the target device is under the current device, return the port number of current device
     * that the target device is connected to.
     *
     * <p>If the target device has the same physical address as the current device, return
     * {@link #TARGET_SAME_PHYSICAL_ADDRESS}.
     *
     * <p>If the target device is not under the current device, return
     * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}.
     */
    protected int getLocalPortFromPhysicalAddress(int targetPhysicalAddress) {
        int myPhysicalAddress = mService.getPhysicalAddress();
        if (myPhysicalAddress == targetPhysicalAddress) {
            return TARGET_SAME_PHYSICAL_ADDRESS;
        }
        int finalMask = 0xF000;
        int mask;
        int port = 0;
        for (mask = 0x0F00; mask > 0x000F;  mask >>= 4) {
            if ((myPhysicalAddress & mask) == 0)  {
                port = mask & targetPhysicalAddress;
                break;
            } else {
                finalMask |= mask;
            }
        }
        if (finalMask != 0xFFFF && (finalMask & targetPhysicalAddress) == myPhysicalAddress) {
            while (mask != 0x000F) {
                mask >>= 4;
                port >>= 4;
            }
            return port;
        }
        return TARGET_NOT_UNDER_LOCAL_DEVICE;
    }
}
+80 −88
Original line number Diff line number Diff line
@@ -36,7 +36,7 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
 * system.
 */
public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {

    private static final String TAG = "HdmiCecLocalDeviceAudioSystem";

@@ -148,32 +148,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
        }
    }

    @ServiceThreadOnly
    protected boolean handleActiveSource(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int logicalAddress = message.getSource();
        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
        ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
        if (!mActiveSource.equals(activeSource)) {
            setActiveSource(activeSource);
        }
        return true;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleSetStreamPath(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
        // If current device is the target path, playback device should handle it.
        // If the path is under the current device, should switch
        int port = getLocalPortFromPhysicalAddress(physicalAddress);
        if (port > 0) {
            routeToPort(port);
        }
        return true;
    }

    @Override
    @ServiceThreadOnly
    protected int getPreferredAddress() {
@@ -333,7 +307,23 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
    protected boolean handleSystemAudioModeRequest(HdmiCecMessage message) {
        assertRunOnServiceThread();
        boolean systemAudioStatusOn = message.getParams().length != 0;
        if (!setSystemAudioMode(systemAudioStatusOn)) {
        // Check if the request comes from a non-TV device.
        // Need to check if TV supports System Audio Control
        // if non-TV device tries to turn on the feature
        if (message.getSource() != Constants.ADDR_TV) {
            if (systemAudioStatusOn) {
                handleSystemAudioModeOnFromNonTvDevice(message);
                return true;
            }
        } else {
            // If TV request the feature on
            // cache TV supporting System Audio Control
            // until Audio System loses its physical address.
            setTvSystemAudioModeSupport(true);
        }
        // If TV or Audio System does not support the feature,
        // will send abort command.
        if (!checkSupportAndSetSystemAudioMode(systemAudioStatusOn)) {
            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
            return true;
        }
@@ -348,7 +338,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
    @ServiceThreadOnly
    protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
        assertRunOnServiceThread();
        if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
        if (!checkSupportAndSetSystemAudioMode(
                HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
        }
        return true;
@@ -358,7 +349,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
    @ServiceThreadOnly
    protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
        assertRunOnServiceThread();
        if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
        if (!checkSupportAndSetSystemAudioMode(
                HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
        }
        return true;
@@ -406,7 +398,16 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
                        mAddress, message.getSource(), scaledVolume, mute));
    }

    protected boolean setSystemAudioMode(boolean newSystemAudioMode) {
    /**
     * Method to check if device support System Audio Control. If so, wake up device if necessary.
     *
     * <p> then call {@link #setSystemAudioMode(boolean)} to turn on or off System Audio Mode
     * @param newSystemAudioMode turning feature on or off. True is on. False is off.
     * @return true or false.
     *
     * <p>False when device does not support the feature. Otherwise returns true.
     */
    protected boolean checkSupportAndSetSystemAudioMode(boolean newSystemAudioMode) {
        if (!isSystemAudioControlFeatureEnabled()) {
            HdmiLogger.debug(
                    "Cannot turn "
@@ -422,6 +423,17 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
        if (newSystemAudioMode && mService.isPowerStandbyOrTransient()) {
            mService.wakeUp();
        }
        setSystemAudioMode(newSystemAudioMode);
        return true;
    }

    /**
     * Real work to turn on or off System Audio Mode.
     *
     * Use {@link #checkSupportAndSetSystemAudioMode(boolean)}
     * if trying to turn on or off the feature.
     */
    private void setSystemAudioMode(boolean newSystemAudioMode) {
        int targetPhysicalAddress = getActiveSource().physicalAddress;
        int port = getLocalPortFromPhysicalAddress(targetPhysicalAddress);
        if (newSystemAudioMode && port >= 0) {
@@ -449,48 +461,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
                mService.announceSystemAudioModeChange(newSystemAudioMode);
            }
        }
        return true;
    }

    /**
     * Method to parse target physical address to the port number on the current device.
     *
     * <p>This check assumes target address is valid.
     * @param targetPhysicalAddress is the physical address of the target device
     * @return
     * <p>If the target device is under the current device, return the port number of current device
     * that the target device is connected to.
     *
     * <p>If the target device has the same physical address as the current device, return
     * {@link #TARGET_SAME_PHYSICAL_ADDRESS}.
     *
     * <p>If the target device is not under the current device, return
     * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}.
     */
    protected int getLocalPortFromPhysicalAddress(int targetPhysicalAddress) {
        int myPhysicalAddress = mService.getPhysicalAddress();
        if (myPhysicalAddress == targetPhysicalAddress) {
            return TARGET_SAME_PHYSICAL_ADDRESS;
        }
        int finalMask = 0xF000;
        int mask;
        int port = 0;
        for (mask = 0x0F00; mask > 0x000F;  mask >>= 4) {
            if ((myPhysicalAddress & mask) == 0)  {
                port = mask & targetPhysicalAddress;
                break;
            } else {
                finalMask |= mask;
            }
        }
        if (finalMask != 0xFFFF && (finalMask & targetPhysicalAddress) == myPhysicalAddress) {
            while (mask != 0x000F) {
                mask >>= 4;
                port >>= 4;
            }
            return port;
        }
        return TARGET_NOT_UNDER_LOCAL_DEVICE;
    }

    protected void switchToAudioInput() {
@@ -534,7 +504,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
            return;
        }

        if (setSystemAudioMode(false)) {
        if (checkSupportAndSetSystemAudioMode(false)) {
            // send <Set System Audio Mode> [“Off”]
            mService.sendCecCommand(
                    HdmiCecMessageBuilder.buildSetSystemAudioMode(
@@ -557,6 +527,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
     * <p>The result of the query may be cached until Audio device type is put in standby or loses
     * its physical address.
     */
    // TODO(amyjojo): making mTvSystemAudioModeSupport null originally and fix the logic.
    void queryTvSystemAudioModeSupport(TvSystemAudioModeSupportedCallback callback) {
        if (!mTvSystemAudioModeSupport) {
            addAndStartAction(new DetectTvSystemAudioModeSupportAction(this, callback));
@@ -565,6 +536,37 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
        }
    }

    /**
     * Handler of System Audio Mode Request on from non TV device
     */
    void handleSystemAudioModeOnFromNonTvDevice(HdmiCecMessage message) {
        if (!isSystemAudioControlFeatureEnabled()) {
            HdmiLogger.debug(
                    "Cannot turn on" + "system audio mode "
                            + "because the System Audio Control feature is disabled.");
            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
            return;
        }
        // Wake up device if it is still on standby
        if (mService.isPowerStandbyOrTransient()) {
            mService.wakeUp();
        }
        // Check if TV supports System Audio Control.
        // Handle broadcasting setSystemAudioMode on or aborting message on callback.
        queryTvSystemAudioModeSupport(new TvSystemAudioModeSupportedCallback() {
            public void onResult(boolean supported) {
                if (supported) {
                    setSystemAudioMode(true);
                    mService.sendCecCommand(
                            HdmiCecMessageBuilder.buildSetSystemAudioMode(
                                    mAddress, Constants.ADDR_BROADCAST, true));
                } else {
                    mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
                }
            }
        });
    }

    void setTvSystemAudioModeSupport(boolean supported) {
        mTvSystemAudioModeSupport = supported;
    }
@@ -588,14 +590,4 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
        assertRunOnServiceThread();
        mAutoDeviceOff = autoDeviceOff;
    }

    private void routeToPort(int portId) {
        // TODO(AMYJOJO): route to specific input of the port
        mLocalActivePath = portId;
    }

    @VisibleForTesting
    protected int getLocalActivePath() {
        return mLocalActivePath;
    }
}
+32 −49
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.os.SystemProperties;
import android.provider.Settings.Global;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LocalePicker;
import com.android.internal.app.LocalePicker.LocaleInfo;
import com.android.internal.util.IndentingPrintWriter;
@@ -35,12 +36,10 @@ import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Locale;

import java.util.List;

/**
 * Represent a logical device of type Playback residing in Android system.
 */
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
    private static final String TAG = "HdmiCecLocalDevicePlayback";

    private static final boolean WAKE_ON_HOTPLUG =
@@ -62,6 +61,11 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
    // If true, turn off TV upon standby. False by default.
    private boolean mAutoTvOff;

    // Local active port number used for Routing Control.
    // Default 0 means HOME is the current active path. Temp solution only.
    // TODO(amyjojo): adding system constants for input ports to TIF mapping.
    private int mLocalActivePath = 0;

    HdmiCecLocalDevicePlayback(HdmiControlService service) {
        super(service, HdmiDeviceInfo.DEVICE_PLAYBACK);

@@ -99,25 +103,6 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
                String.valueOf(addr));
    }

    @ServiceThreadOnly
    void oneTouchPlay(IHdmiControlCallback callback) {
        assertRunOnServiceThread();
        List<OneTouchPlayAction> actions = getActions(OneTouchPlayAction.class);
        if (!actions.isEmpty()) {
            Slog.i(TAG, "oneTouchPlay already in progress");
            actions.get(0).addCallback(callback);
            return;
        }
        OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV,
                callback);
        if (action == null) {
            Slog.w(TAG, "Cannot initiate oneTouchPlay");
            invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
            return;
        }
        addAndStartAction(action);
    }

    @ServiceThreadOnly
    void queryDisplayStatus(IHdmiControlCallback callback) {
        assertRunOnServiceThread();
@@ -227,21 +212,6 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
        return !getWakeLock().isHeld();
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleActiveSource(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
        mayResetActiveSource(physicalAddress);
        return true;  // Broadcast message.
    }

    private void mayResetActiveSource(int physicalAddress) {
        if (physicalAddress != mService.getPhysicalAddress()) {
            setActiveSource(false);
        }
    }

    @ServiceThreadOnly
    protected boolean handleUserControlPressed(HdmiCecMessage message) {
        assertRunOnServiceThread();
@@ -254,10 +224,21 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
    protected boolean handleSetStreamPath(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
        maySetActiveSource(physicalAddress);
        // If current device is the target path, set to Active Source.
        // If the path is under the current device, should switch
        int port = getLocalPortFromPhysicalAddress(physicalAddress);
        if (port == 0) {
            setActiveSource(true);
            maySendActiveSource(message.getSource());
            wakeUpIfActiveSource();
        return true;  // Broadcast message.
        } else if (port > 0) {
            // Wake up the device if the power is in standby mode for routing
            if (mService.isPowerStandbyOrTransient()) {
                mService.wakeUp();
            }
            routeToPort(port);
        }
        return true;
    }

    // Samsung model we tested sends <Routing Change> and <Request Active Source>
@@ -306,14 +287,6 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
        }
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleRequestActiveSource(HdmiCecMessage message) {
        assertRunOnServiceThread();
        maySendActiveSource(message.getSource());
        return true;  // Broadcast message.
    }

    @ServiceThreadOnly
    protected boolean handleSetMenuLanguage(HdmiCecMessage message) {
        assertRunOnServiceThread();
@@ -383,6 +356,16 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
        checkIfPendingActionsCleared();
    }

    private void routeToPort(int portId) {
        // TODO(AMYJOJO): route to specific input of the port
        mLocalActivePath = portId;
    }

    @VisibleForTesting
    protected int getLocalActivePath() {
        return mLocalActivePath;
    }

    @Override
    protected void dump(final IndentingPrintWriter pw) {
        super.dump(pw);
Loading