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

Commit 9d2aa028 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Track HDMI CEC Network"

parents 90d0b580 59841e03
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -339,7 +339,8 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction {

        // This is to manager CEC device separately in case they don't have address.
        if (mIsTvDevice) {
            tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
            localDevice().mService.getHdmiCecNetwork().updateCecSwitchInfo(current.mLogicalAddress,
                    current.mDeviceType,
                    current.mPhysicalAddress);
        }
        increaseProcessedDeviceCount();
+4 −71
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ import android.os.Looper;
import android.os.RemoteException;
import android.stats.hdmi.HdmiStatsEnums;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
@@ -97,7 +96,7 @@ final class HdmiCecController {
    private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
        @Override
        public boolean test(Integer address) {
            return !isAllocatedLocalDeviceAddress(address);
            return !mService.getHdmiCecNetwork().isAllocatedLocalDeviceAddress(address);
        }
    };

@@ -118,9 +117,6 @@ final class HdmiCecController {

    private final HdmiControlService mService;

    // Stores the local CEC devices in the system. Device type is used for key.
    private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();

    // Stores recent CEC messages and HDMI Hotplug event history for debugging purpose.
    private final ArrayBlockingQueue<Dumpable> mMessageHistory =
            new ArrayBlockingQueue<>(MAX_HDMI_MESSAGE_HISTORY);
@@ -173,12 +169,6 @@ final class HdmiCecController {
        nativeWrapper.setCallback(new HdmiCecCallback());
    }

    @ServiceThreadOnly
    void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
        assertRunOnServiceThread();
        mLocalDevices.put(deviceType, device);
    }

    /**
     * Allocate a new logical address of the given device type. Allocated
     * address will be reported through {@link AllocateAddressCallback}.
@@ -268,17 +258,6 @@ final class HdmiCecController {
        return mNativeWrapperImpl.nativeGetPortInfos();
    }

    /**
     * Return the locally hosted logical device of a given type.
     *
     * @param deviceType logical device type
     * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
     *          otherwise null.
     */
    HdmiCecLocalDevice getLocalDevice(int deviceType) {
        return mLocalDevices.get(deviceType);
    }

    /**
     * Add a new logical address to the device. Device's HW should be notified
     * when a new logical address is assigned to a device, so that it can accept
@@ -307,18 +286,9 @@ final class HdmiCecController {
    @ServiceThreadOnly
    void clearLogicalAddress() {
        assertRunOnServiceThread();
        for (int i = 0; i < mLocalDevices.size(); ++i) {
            mLocalDevices.valueAt(i).clearAddress();
        }
        mNativeWrapperImpl.nativeClearLogicalAddress();
    }

    @ServiceThreadOnly
    void clearLocalDevices() {
        assertRunOnServiceThread();
        mLocalDevices.clear();
    }

    /**
     * Return the physical address of the device.
     *
@@ -428,17 +398,6 @@ final class HdmiCecController {
        runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback, allocated);
    }

    /**
     * Return a list of all {@link HdmiCecLocalDevice}s.
     *
     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
     */
    @ServiceThreadOnly
    List<HdmiCecLocalDevice> getLocalDeviceList() {
        assertRunOnServiceThread();
        return HdmiUtils.sparseArrayToList(mLocalDevices);
    }

    private List<Integer> pickPollCandidates(int pickStrategy) {
        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
        Predicate<Integer> pickPredicate = null;
@@ -474,17 +433,6 @@ final class HdmiCecController {
        return pollingCandidates;
    }

    @ServiceThreadOnly
    private boolean isAllocatedLocalDeviceAddress(int address) {
        assertRunOnServiceThread();
        for (int i = 0; i < mLocalDevices.size(); ++i) {
            if (mLocalDevices.valueAt(i).isAddressOf(address)) {
                return true;
            }
        }
        return false;
    }

    @ServiceThreadOnly
    private void runDevicePolling(final int sourceAddress,
            final List<Integer> candidates, final int retryCount,
@@ -578,7 +526,7 @@ final class HdmiCecController {
        if (address == Constants.ADDR_BROADCAST) {
            return true;
        }
        return isAllocatedLocalDeviceAddress(address);
        return mService.getHdmiCecNetwork().isAllocatedLocalDeviceAddress(address);
    }

    @ServiceThreadOnly
@@ -682,7 +630,7 @@ final class HdmiCecController {
    private int incomingMessageDirection(int srcAddress, int dstAddress) {
        boolean sourceIsLocal = false;
        boolean destinationIsLocal = false;
        for (HdmiCecLocalDevice localDevice : getLocalDeviceList()) {
        for (HdmiCecLocalDevice localDevice : mService.getHdmiCecNetwork().getLocalDeviceList()) {
            int logicalAddress = localDevice.getDeviceInfo().getLogicalAddress();
            if (logicalAddress == srcAddress) {
                sourceIsLocal = true;
@@ -731,24 +679,9 @@ final class HdmiCecController {
    }

    void dump(final IndentingPrintWriter pw) {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        for (int i = 0; i < mLocalDevices.size(); ++i) {
            pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":");
            pw.increaseIndent();
            mLocalDevices.valueAt(i).dump(pw);

            pw.println("Active Source history:");
            pw.increaseIndent();
            for (Dumpable activeSourceEvent : mLocalDevices.valueAt(i).getActiveSourceHistory()) {
                activeSourceEvent.dump(pw, sdf);
            }
            pw.decreaseIndent();
            pw.decreaseIndent();
        }

        pw.println("CEC message history:");
        pw.increaseIndent();
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (Dumpable record : mMessageHistory) {
            record.dump(pw, sdf);
        }
+24 −3
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.hdmi;

import android.annotation.CallSuper;
import android.annotation.Nullable;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -471,8 +472,27 @@ abstract class HdmiCecLocalDevice {
        return false;
    }

    @CallSuper
    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
        return false;
        // <Report Physical Address>  is also handled in HdmiCecNetwork to update the local network
        // state

        int address = message.getSource();

        // Ignore if [Device Discovery Action] is going on.
        if (hasAction(DeviceDiscoveryAction.class)) {
            Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
            return true;
        }

        HdmiDeviceInfo cecDeviceInfo = mService.getHdmiCecNetwork().getCecDeviceInfo(address);
        // If no non-default display name is available for the device, request the devices OSD name.
        if (cecDeviceInfo.getDisplayName().equals(HdmiUtils.getDefaultDeviceName(address))) {
            mService.sendCecCommand(
                    HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
        }

        return true;
    }

    protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
@@ -708,7 +728,7 @@ abstract class HdmiCecLocalDevice {
    }

    protected boolean handleSetOsdName(HdmiCecMessage message) {
        // The default behavior of <Set Osd Name> is doing nothing.
        // <Set OSD name> is also handled in HdmiCecNetwork to update the local network state
        return true;
    }

@@ -724,7 +744,8 @@ abstract class HdmiCecLocalDevice {
    }

    protected boolean handleReportPowerStatus(HdmiCecMessage message) {
        return false;
        // <Report Power Status> is also handled in HdmiCecNetwork to update the local network state
        return true;
    }

    protected boolean handleTimerStatus(HdmiCecMessage message) {
+7 −259
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import android.os.SystemProperties;
import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -54,15 +53,12 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;


/**
 * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
 * system.
@@ -104,14 +100,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
    @GuardedBy("mLock")
    private final HashMap<String, HdmiDeviceInfo> mTvInputsToDeviceInfo = new HashMap<>();

    // Copy of mDeviceInfos to guarantee thread-safety.
    @GuardedBy("mLock")
    private List<HdmiDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();

    // Map-like container of all cec devices.
    // device id is used as key of container.
    private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();

    // Message buffer used to buffer selected messages to process later. <Active Source>
    // from a source device, for instance, needs to be buffered if the device is not
    // discovered yet. The buffered commands are taken out and when they are ready to
@@ -187,135 +175,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
        return info != null;
    }

    /**
     * Called when a device is newly added or a new device is detected or
     * an existing device is updated.
     *
     * @param info device info of a new device.
     */
    @ServiceThreadOnly
    final void addCecDevice(HdmiDeviceInfo info) {
        assertRunOnServiceThread();
        HdmiDeviceInfo old = addDeviceInfo(info);
        if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
            // The addition of the device itself should not be notified.
            // Note that different logical address could still be the same local device.
            return;
        }
        if (old == null) {
            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
        } else if (!old.equals(info)) {
            invokeDeviceEventListener(old, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
        }
    }

    /**
     * Called when a device is removed or removal of device is detected.
     *
     * @param address a logical address of a device to be removed
     */
    @ServiceThreadOnly
    final void removeCecDevice(int address) {
        assertRunOnServiceThread();
        HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));

        mCecMessageCache.flushMessagesFrom(address);
        invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
    }

    /**
     * Called when a device is updated.
     *
     * @param info device info of the updating device.
     */
    @ServiceThreadOnly
    final void updateCecDevice(HdmiDeviceInfo info) {
        assertRunOnServiceThread();
        HdmiDeviceInfo old = addDeviceInfo(info);

        if (old == null) {
            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
        } else if (!old.equals(info)) {
            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
        }
    }

    /**
    * Add a new {@link HdmiDeviceInfo}. It returns old device info which has the same
     * logical address as new device info's.
     *
     * @param deviceInfo a new {@link HdmiDeviceInfo} to be added.
     * @return {@code null} if it is new device. Otherwise, returns old {@HdmiDeviceInfo}
     *         that has the same logical address as new one has.
     */
    @ServiceThreadOnly
    @VisibleForTesting
    protected HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
        assertRunOnServiceThread();
        mService.checkLogicalAddressConflictAndReallocate(deviceInfo.getLogicalAddress());
        HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
        if (oldDeviceInfo != null) {
            removeDeviceInfo(deviceInfo.getId());
        }
        mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
        updateSafeDeviceInfoList();
        return oldDeviceInfo;
    }

    /**
     * Remove a device info corresponding to the given {@code logicalAddress}.
     * It returns removed {@link HdmiDeviceInfo} if exists.
     *
     * @param id id of device to be removed
     * @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
     */
    @ServiceThreadOnly
    private HdmiDeviceInfo removeDeviceInfo(int id) {
        assertRunOnServiceThread();
        HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
        if (deviceInfo != null) {
            mDeviceInfos.remove(id);
        }
        updateSafeDeviceInfoList();
        return deviceInfo;
    }

    /**
     * Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
     *
     * @param logicalAddress logical address of the device to be retrieved
     * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
     *         Returns null if no logical address matched
     */
    @ServiceThreadOnly
    HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
        assertRunOnServiceThread();
        return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
    }

    @ServiceThreadOnly
    private void updateSafeDeviceInfoList() {
        assertRunOnServiceThread();
        List<HdmiDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos);
        synchronized (mLock) {
            mSafeAllDeviceInfos = copiedDevices;
        }
    }

    @GuardedBy("mLock")
    List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
        ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
        for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
            infoList.add(info);
        }
        return infoList;
    }

    private void invokeDeviceEventListener(HdmiDeviceInfo info, int status) {
        mService.invokeDeviceEventListeners(info, status);
    }

    @Override
    @ServiceThreadOnly
    void onHotplug(int portId, boolean connected) {
@@ -342,7 +201,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
            }
            // Update with TIF on the device removal. TIF callback will update
            // mPortIdToTvInputs and mPortIdToTvInputs.
            removeCecDevice(info.getLogicalAddress());
            mService.getHdmiCecNetwork().removeCecDevice(this, info.getLogicalAddress());
        }
    }

@@ -399,7 +258,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
        boolean lastSystemAudioControlStatus =
                SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
        systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
        clearDeviceInfoList();
        mService.getHdmiCecNetwork().clearDeviceList();
        launchDeviceDiscovery();
        startQueuedActions();
    }
@@ -458,7 +317,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
        // If the new Active Source is under the current device, check if the device info and the TV
        // input is ready to switch to the new Active Source. If not ready, buffer the cec command
        // to handle later when the device is ready.
        HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
        HdmiDeviceInfo info = mService.getHdmiCecNetwork().getCecDeviceInfo(logicalAddress);
        if (info == null) {
            HdmiLogger.debug("Device info %X not found; buffering the command", logicalAddress);
            mDelayedMessageBuffer.add(message);
@@ -472,79 +331,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
        return true;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
        assertRunOnServiceThread();
        int path = HdmiUtils.twoBytesToInt(message.getParams());
        int address = message.getSource();
        int type = message.getParams()[2];

        // Ignore if [Device Discovery Action] is going on.
        if (hasAction(DeviceDiscoveryAction.class)) {
            Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
            return true;
        }

        // Update the device info with TIF, note that the same device info could have added in
        // device discovery and we do not want to override it with default OSD name. Therefore we
        // need the following check to skip redundant device info updating.
        HdmiDeviceInfo oldDevice = getCecDeviceInfo(address);
        if (oldDevice == null || oldDevice.getPhysicalAddress() != path) {
            addCecDevice(new HdmiDeviceInfo(
                    address, path, mService.pathToPortId(path), type,
                    Constants.UNKNOWN_VENDOR_ID, ""));
            // if we are adding a new device info, send out a give osd name command
            // to update the name of the device in TIF
            mService.sendCecCommand(
                    HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
            return true;
        }

        Slog.w(TAG, "Device info exists. Not updating on Physical Address.");
        return true;
    }

    @Override
    protected boolean handleReportPowerStatus(HdmiCecMessage command) {
        int newStatus = command.getParams()[0] & 0xFF;
        updateDevicePowerStatus(command.getSource(), newStatus);
        return true;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleSetOsdName(HdmiCecMessage message) {
        int source = message.getSource();
        String osdName;
        HdmiDeviceInfo deviceInfo = getCecDeviceInfo(source);
        // If the device is not in device list, ignore it.
        if (deviceInfo == null) {
            Slog.i(TAG, "No source device info for <Set Osd Name>." + message);
            return true;
        }
        try {
            osdName = new String(message.getParams(), "US-ASCII");
        } catch (UnsupportedEncodingException e) {
            Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
            return true;
        }

        if (deviceInfo.getDisplayName() != null
            && deviceInfo.getDisplayName().equals(osdName)) {
            Slog.d(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
            return true;
        }

        Slog.d(TAG, "Updating device OSD name from "
                + deviceInfo.getDisplayName()
                + " to " + osdName);
        updateCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
                deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
                deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName));
        return true;
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleInitiateArc(HdmiCecMessage message) {
@@ -864,14 +650,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
                            != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE) {
                return true;
            }
            boolean isDeviceInCecDeviceList = false;
            for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
                if (info.getPhysicalAddress() == sourcePhysicalAddress) {
                    isDeviceInCecDeviceList = true;
                    break;
                }
            }
            if (!isDeviceInCecDeviceList) {
            HdmiDeviceInfo safeDeviceInfoByPath =
                    mService.getHdmiCecNetwork().getSafeDeviceInfoByPath(sourcePhysicalAddress);
            if (safeDeviceInfoByPath == null) {
                switchInputOnReceivingNewActivePath(sourcePhysicalAddress);
            }
        }
@@ -1345,24 +1126,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
        routeToInputFromPortId(getRoutingPort());
    }

    protected void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
        HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
        if (info == null) {
            Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
            return;
        }

        if (info.getDevicePowerStatus() == newPowerStatus) {
            return;
        }

        HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus);
        // addDeviceInfo replaces old device info with new one if exists.
        addDeviceInfo(newInfo);

        invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
    }

    @ServiceThreadOnly
    private void launchDeviceDiscovery() {
        assertRunOnServiceThread();
@@ -1375,27 +1138,13 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
                    @Override
                    public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
                        for (HdmiDeviceInfo info : deviceInfos) {
                            addCecDevice(info);
                            mService.getHdmiCecNetwork().addCecDevice(info);
                        }
                    }
                });
        addAndStartAction(action);
    }

    // Clear all device info.
    @ServiceThreadOnly
    private void clearDeviceInfoList() {
        assertRunOnServiceThread();
        for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
            if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
                continue;
            }
            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
        }
        mDeviceInfos.clear();
        updateSafeDeviceInfoList();
    }

    @Override
    protected void dump(IndentingPrintWriter pw) {
        pw.println("HdmiCecLocalDeviceAudioSystem:");
@@ -1409,7 +1158,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
        pw.println("mLocalActivePort: " + getLocalActivePort());
        HdmiUtils.dumpMap(pw, "mPortIdToTvInputs:", mPortIdToTvInputs);
        HdmiUtils.dumpMap(pw, "mTvInputsToDeviceInfo:", mTvInputsToDeviceInfo);
        HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos);
        pw.decreaseIndent();
        super.dump(pw);
    }
+22 −444

File changed.

Preview size limit exceeded, changes collapsed.

Loading