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

Commit 339227da authored by Jungshik Jang's avatar Jungshik Jang
Browse files

Fix several bugs in HdmiControlService.

1. Fix HdmniLogger null pointer exception
2. Should check arc enabled port for arc requests
3. Disallow ARC action coming from indirect AVR device.
4. Check original opcode of feature action
5. Add bitmasking to all parameters of cec message.

Bug: 17243701, Bug: 17238394, Bug: 17241401
Change-Id: Iff0da78b0de9a29fb00e683c261528e0baea66af
parent 93b18bda
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -48,6 +48,12 @@ public final class HdmiControlManager {
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";

    // --- Messages for ACTION_OSD_MESSAGE ---
    /**
     * Message that ARC enabled device is connected to invalid port (non-ARC port).
     */
    public static final int OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT = 1;

    /**
     * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the ID of
     * the message to display on screen.
+63 −21
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.hardware.hdmi.HdmiControlManager.CLEAR_TIMER_STATUS_FAIL_T
import static android.hardware.hdmi.HdmiControlManager.ONE_TOUCH_RECORD_CEC_DISABLED;
import static android.hardware.hdmi.HdmiControlManager.ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION;
import static android.hardware.hdmi.HdmiControlManager.ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN;
import static android.hardware.hdmi.HdmiControlManager.OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT;
import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED;
import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION;
import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE;
@@ -29,7 +30,6 @@ import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANAL
import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL;
import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL;

import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiRecordSources;
@@ -39,7 +39,6 @@ import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings.Global;
import android.util.ArraySet;
import android.util.Slog;
@@ -57,7 +56,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

/**
 * Represent a logical device of type TV residing in Android system.
@@ -70,8 +68,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    @ServiceThreadOnly
    private boolean mArcEstablished = false;

    // Whether ARC feature is enabled or not.
    private boolean mArcFeatureEnabled = false;
    // Whether ARC feature is enabled or not. The default value is true.
    // TODO: once adding system setting for it, read the value to it.
    private boolean mArcFeatureEnabled = true;

    // Whether System audio mode is activated or not.
    // This becomes true only when all system audio sequences are finished.
@@ -642,19 +641,26 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
                        // If there is AVR, initiate System Audio Auto initiation action,
                        // which turns on and off system audio according to last system
                        // audio setting.
                        if (mSystemAudioActivated && getAvrDeviceInfo() != null) {
                            addAndStartAction(new SystemAudioAutoInitiationAction(
                                    HdmiCecLocalDeviceTv.this,
                                    getAvrDeviceInfo().getLogicalAddress()));
                            if (mArcEstablished) {
                                startArcAction(true);
                            }
                        HdmiDeviceInfo avr = getAvrDeviceInfo();
                        if (avr != null) {
                            onNewAvrAdded(avr);
                        }
                    }
                });
        addAndStartAction(action);
    }

    @ServiceThreadOnly
    void onNewAvrAdded(HdmiDeviceInfo avr) {
        assertRunOnServiceThread();
        if (getSystemAudioModeSetting()) {
            addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
        }
        if (isArcFeatureEnabled()) {
            startArcAction(true);
        }
    }

    // Clear all device info.
    @ServiceThreadOnly
    private void clearDeviceInfoList() {
@@ -757,6 +763,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        assertRunOnServiceThread();

        if (mArcFeatureEnabled != enabled) {
            mArcFeatureEnabled = enabled;
            if (enabled) {
                if (!mArcEstablished) {
                    startArcAction(true);
@@ -766,7 +773,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
                    startArcAction(false);
                }
            }
            mArcFeatureEnabled = enabled;
        }
    }

@@ -777,13 +783,18 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    }

    @ServiceThreadOnly
    private void startArcAction(boolean enabled) {
    void startArcAction(boolean enabled) {
        assertRunOnServiceThread();
        HdmiDeviceInfo info = getAvrDeviceInfo();
        if (info == null) {
            Slog.w(TAG, "Failed to start arc action; No AVR device.");
            return;
        }
        if (!isConnectedToArcPort(info.getPhysicalAddress())) {
        if (!canStartArcUpdateAction(info.getLogicalAddress(), enabled)) {
            Slog.w(TAG, "Failed to start arc action; ARC configuration check failed.");
            if (enabled && !isConnectedToArcPort(info.getPhysicalAddress())) {
                displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT);
            }
            return;
        }

@@ -801,6 +812,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        }
    }

    private boolean isDirectConnectAddress(int physicalAddress) {
        return (physicalAddress & Constants.ROUTING_PATH_TOP_MASK) == physicalAddress;
    }

    void setAudioStatus(boolean mute, int volume) {
        synchronized (mLock) {
            mSystemAudioMute = mute;
@@ -857,6 +872,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
    @ServiceThreadOnly
    protected boolean handleInitiateArc(HdmiCecMessage message) {
        assertRunOnServiceThread();

        if (!canStartArcUpdateAction(message.getSource(), true)) {
            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
            if (!isConnectedToArcPort(message.getSource())) {
                displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT);
            }
            return true;
        }

        // In case where <Initiate Arc> is started by <Request ARC Initiation>
        // need to clean up RequestArcInitiationAction.
        removeAction(RequestArcInitiationAction.class);
@@ -866,10 +890,29 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        return true;
    }

    private boolean canStartArcUpdateAction(int avrAddress, boolean shouldCheckArcFeatureEnabled) {
        HdmiDeviceInfo avr = getAvrDeviceInfo();
        if (avr != null
                && (avrAddress == avr.getLogicalAddress())
                && isConnectedToArcPort(avr.getPhysicalAddress())
                && isDirectConnectAddress(avr.getPhysicalAddress())) {
            if (shouldCheckArcFeatureEnabled) {
                return isArcFeatureEnabled();
            } else {
                return true;
            }
        } else {
            return false;
        }
    }

    @Override
    @ServiceThreadOnly
    protected boolean handleTerminateArc(HdmiCecMessage message) {
        assertRunOnServiceThread();
        // In cast of termination, do not check ARC configuration in that AVR device
        // might be removed already.

        // In case where <Terminate Arc> is started by <Request ARC Termination>
        // need to clean up RequestArcInitiationAction.
        removeAction(RequestArcTerminationAction.class);
@@ -1000,7 +1043,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
     * Return a list of all {@link HdmiDeviceInfo}.
     *
     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
     * This is not thread-safe. For thread safety, call {@link #getSafeExternalInputs} which
     * This is not thread-safe. For thread safety, call {@link #getSafeExternalInputsLocked} which
     * does not include local device.
     */
    @ServiceThreadOnly
@@ -1116,7 +1159,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
     *
     * This is not thread-safe. For thread safety, call {@link #getSafeCecDeviceInfo(int)}.
     *
     * @param address logical address of the device to be retrieved
     * @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
     */
@@ -1388,11 +1431,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        return mService.isPowerStandbyOrTransient();
    }

    @ServiceThreadOnly
    void displayOsd(int messageId) {
        Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
        mService.getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
                HdmiControlService.PERMISSION);
        assertRunOnServiceThread();
        mService.displayOsd(messageId);
    }

    // Seq #54 and #55
+11 −11
Original line number Diff line number Diff line
@@ -54,8 +54,8 @@ public class HdmiCecMessageBuilder {
    static HdmiCecMessage buildFeatureAbortCommand(int src, int dest, int originalOpcode,
            int reason) {
        byte[] params = new byte[] {
                (byte) originalOpcode,
                (byte) reason,
                (byte) (originalOpcode & 0xFF),
                (byte) (reason & 0xFF),
        };
        return buildCommand(src, dest, Constants.MESSAGE_FEATURE_ABORT, params);
    }
@@ -110,9 +110,9 @@ public class HdmiCecMessageBuilder {
        // Hdmi CEC uses lower-cased ISO 639-2 (3 letters code).
        String normalized = language.toLowerCase();
        byte[] params = new byte[] {
                (byte) normalized.charAt(0),
                (byte) normalized.charAt(1),
                (byte) normalized.charAt(2),
                (byte) (normalized.charAt(0) & 0xFF),
                (byte) (normalized.charAt(1) & 0xFF),
                (byte) (normalized.charAt(2) & 0xFF),
        };
        // <Set Menu Language> is broadcast message.
        return buildCommand(src, Constants.ADDR_BROADCAST,
@@ -155,7 +155,7 @@ public class HdmiCecMessageBuilder {
                (byte) ((address >> 8) & 0xFF),
                (byte) (address & 0xFF),
                // One byte device type
                (byte) deviceType
                (byte) (deviceType & 0xFF)
        };
        // <Report Physical Address> is broadcast message.
        return buildCommand(src, Constants.ADDR_BROADCAST,
@@ -194,7 +194,7 @@ public class HdmiCecMessageBuilder {
     */
    static HdmiCecMessage buildCecVersion(int src, int dest, int version) {
        byte[] params = new byte[] {
                (byte) version
                (byte) (version & 0xFF)
        };
        return buildCommand(src, dest, Constants.MESSAGE_CEC_VERSION, params);
    }
@@ -332,7 +332,7 @@ public class HdmiCecMessageBuilder {
     */
    static HdmiCecMessage buildReportPowerStatus(int src, int dest, int powerStatus) {
        byte[] param = new byte[] {
                (byte) (powerStatus)
                (byte) (powerStatus & 0xFF)
        };
        return buildCommand(src, dest, Constants.MESSAGE_REPORT_POWER_STATUS, param);
    }
@@ -347,7 +347,7 @@ public class HdmiCecMessageBuilder {
     */
    static HdmiCecMessage buildReportMenuStatus(int src, int dest, int menuStatus) {
        byte[] param = new byte[] {
                (byte) (menuStatus)
                (byte) (menuStatus & 0xFF)
        };
        return buildCommand(src, dest, Constants.MESSAGE_MENU_STATUS, param);
    }
@@ -391,7 +391,7 @@ public class HdmiCecMessageBuilder {
     * @return newly created {@link HdmiCecMessage}
     */
    static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) {
        return buildUserControlPressed(src, dest, new byte[] { (byte) uiCommand });
        return buildUserControlPressed(src, dest, new byte[] { (byte) (uiCommand & 0xFF) });
    }

    /**
@@ -594,7 +594,7 @@ public class HdmiCecMessageBuilder {

    private static byte[] physicalAddressToParam(int physicalAddress) {
        return new byte[] {
                (byte) (physicalAddress >> 8),
                (byte) ((physicalAddress >> 8) & 0xFF),
                (byte) (physicalAddress & 0xFF)
        };
    }
+10 −1
Original line number Diff line number Diff line
@@ -606,7 +606,7 @@ public final class HdmiControlService extends SystemService {
     * Whether a device of the specified physical address is connected to ARC enabled port.
     */
    boolean isConnectedToArcPort(int physicalAddress) {
        int portId = mPortIdMap.get(physicalAddress);
        int portId = pathToPortId(physicalAddress);
        if (portId != Constants.INVALID_PORT_ID) {
            return mPortInfoMap.get(portId).isArcSupported();
        }
@@ -1980,4 +1980,13 @@ public final class HdmiControlService extends SystemService {
            return mMhlInputChangeEnabled;
        }
    }

    @ServiceThreadOnly
    void displayOsd(int messageId) {
        assertRunOnServiceThread();
        Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
                HdmiControlService.PERMISSION);
    }
}
+6 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.hdmi;

import android.annotation.Nullable;
import android.os.SystemClock;
import android.util.Pair;
import android.util.Slog;
@@ -47,7 +48,7 @@ final class HdmiLogger {
        long curTime = SystemClock.uptimeMillis();
        Pair<Long, Integer> timing = mWarningTimingCache.get(logMessage);
        if (shouldLogNow(timing, curTime)) {
            Slog.w(mTag, buildMessage(logMessage, timing, curTime));
            Slog.w(mTag, buildMessage(logMessage, timing));
            mWarningTimingCache.put(logMessage, new Pair<>(curTime, 1));
        } else {
            increaseLogCount(mWarningTimingCache, logMessage);
@@ -58,16 +59,16 @@ final class HdmiLogger {
        long curTime = SystemClock.uptimeMillis();
        Pair<Long, Integer> timing = mErrorTimingCache.get(logMessage);
        if (shouldLogNow(timing, curTime)) {
            Slog.e(mTag, buildMessage(logMessage, timing, curTime));
            Slog.e(mTag, buildMessage(logMessage, timing));
            mErrorTimingCache.put(logMessage, new Pair<>(curTime, 1));
        } else {
            increaseLogCount(mErrorTimingCache, logMessage);
        }
    }

    private String buildMessage(String message, Pair<Long, Integer> timing, long curTime) {
    private String buildMessage(String message, @Nullable Pair<Long, Integer> timing) {
        return new StringBuilder()
            .append("[").append(timing == null ? curTime : timing.second).append("]:")
            .append("[").append(timing == null ? 1 : timing.second).append("]:")
            .append(message).toString();
    }

@@ -78,7 +79,7 @@ final class HdmiLogger {
        }
    }

    private boolean shouldLogNow(Pair<Long, Integer> timing, long curTime) {
    private boolean shouldLogNow(@Nullable Pair<Long, Integer> timing, long curTime) {
        return timing == null || curTime - timing.first > ERROR_LOG_DURATTION_MILLIS;
    }
}
Loading