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

Commit aefab64b authored by Amy's avatar Amy Committed by shubang
Browse files

Moving routing logic to playback device.

cherry-pick ag/4852475

Test: atest and local test.
Change-Id: I0292b221ce180ef3e3c1b82681ca5c8d9dee42ae
parent 06dc4cde
Loading
Loading
Loading
Loading
+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;
    }
}
+0 −65
Original line number Diff line number Diff line
@@ -160,20 +160,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
        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() {
@@ -489,47 +475,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
        }
    }

    /**
     * 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() {
        // TODO(b/111396634): switch input according to PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT
    }
@@ -657,14 +602,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;
    }
}
+31 −6
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,8 +36,6 @@ 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.
 */
@@ -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);

@@ -254,10 +258,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>
@@ -383,6 +398,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);
+0 −9
Original line number Diff line number Diff line
@@ -524,15 +524,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
    }

    @Test
    public void handleSetStreamPath_underCurrentDevice() {
        assertThat(mHdmiCecLocalDeviceAudioSystem.getLocalActivePath()).isEqualTo(0);
        HdmiCecMessage message =
                HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2100);
        assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetStreamPath(message)).isTrue();
        assertThat(mHdmiCecLocalDeviceAudioSystem.getLocalActivePath()).isEqualTo(1);
    }

    public void handleSystemAudioModeRequest_fromNonTV_tVNotSupport() {
        HdmiCecMessage message =
                HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+91 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.hdmi;

import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;

import static com.google.common.truth.Truth.assertThat;

import android.os.Looper;
import android.os.test.TestLooper;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.util.ArrayList;

@SmallTest
@RunWith(JUnit4.class)
/** Tests for {@link HdmiCecLocalDevicePlayback} class. */
public class HdmiCecLocalDevicePlaybackTest {

    private HdmiControlService mHdmiControlService;
    private HdmiCecController mHdmiCecController;
    private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
    private FakeNativeWrapper mNativeWrapper;
    private Looper mMyLooper;
    private TestLooper mTestLooper = new TestLooper();
    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
    private int mPlaybackPhysicalAddress;

    @Before
    public void setUp() {
        mHdmiControlService =
            new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
                @Override
                void wakeUp() {
                }

                @Override
                boolean isControlEnabled() {
                    return true;
                }
            };

        mMyLooper = mTestLooper.getLooper();
        mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService);
        mHdmiCecLocalDevicePlayback.init();
        mHdmiControlService.setIoLooper(mMyLooper);
        mNativeWrapper = new FakeNativeWrapper();
        mHdmiCecController =
            HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
        mHdmiControlService.setCecController(mHdmiCecController);
        mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
        mLocalDevices.add(mHdmiCecLocalDevicePlayback);
        mHdmiControlService.initPortInfo();
        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
        mTestLooper.dispatchAll();
        mNativeWrapper.clearResultMessages();
        mPlaybackPhysicalAddress = 0x2000;
        mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
    }

    @Test
    public void handleSetStreamPath_underCurrentDevice() {
        assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(0);
        HdmiCecMessage message =
                HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2100);
        assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue();
        assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(1);
    }
}