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

Commit 71b197a0 authored by Yuncheol Heo's avatar Yuncheol Heo Committed by Android (Google) Code Review
Browse files

Merge "Add SystemAudioAction(FromAvr|FromTv)."

parents c7771856 63a2e069
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -505,7 +505,7 @@ final class HdmiCecController {
            // Reply <Feature Abort> to initiator (source) for all requests.
            HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
                    sourceAddress, message.getSource(), message.getOpcode(),
                    HdmiCecMessageBuilder.ABORT_REFUSED);
                    HdmiConstants.ABORT_REFUSED);
            sendCommand(cecMessage, null);
        }
    }
+58 −9
Original line number Diff line number Diff line
@@ -26,15 +26,6 @@ import java.util.Arrays;
 * A helper class to build {@link HdmiCecMessage} from various cec commands.
 */
public class HdmiCecMessageBuilder {
    // TODO: move these values to HdmiCec.java once make it internal constant class.
    // CEC's ABORT reason values.
    static final int ABORT_UNRECOGNIZED_MODE = 0;
    static final int ABORT_NOT_IN_CORRECT_MODE = 1;
    static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
    static final int ABORT_INVALID_OPERAND = 3;
    static final int ABORT_REFUSED = 4;
    static final int ABORT_UNABLE_TO_DETERMINE = 5;

    private static final int OSD_NAME_MAX_LENGTH = 13;

    private HdmiCecMessageBuilder() {}
@@ -289,6 +280,64 @@ public class HdmiCecMessageBuilder {
        return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS);
    }

    /**
     * Build &lt;System Audio Mode Request&gt; command.
     *
     * @param src source address of command
     * @param avr destination address of command, it should be AVR
     * @param avrPhysicalAddress physical address of AVR
     * @param enableSystemAudio whether to enable System Audio Mode or not
     * @return newly created {@link HdmiCecMessage}
     */
    static HdmiCecMessage buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress,
            boolean enableSystemAudio) {
        if (enableSystemAudio) {
            return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
                    physicalAddressToParam(avrPhysicalAddress));
        } else {
            return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST);
        }
    }

    /**
     * Build &lt;Give Audio Status&gt; command.
     *
     * @param src source address of command
     * @param dest destination address of command
     * @return newly created {@link HdmiCecMessage}
     */
    static HdmiCecMessage buildGiveAudioStatus(int src, int dest) {
        return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_AUDIO_STATUS);
    }

    /**
     * Build &lt;User Control Pressed&gt; command.
     *
     * @param src source address of command
     * @param dest destination address of command
     * @param uiCommand keycode that user pressed
     * @return newly created {@link HdmiCecMessage}
     */
    static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) {
        byte[] params = new byte[] {
                (byte) uiCommand
        };
        return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_PRESSED, params);
    }

    /**
     * Build &lt;User Control Released&gt; command.
     *
     * @param src source address of command
     * @param dest destination address of command
     * @return newly created {@link HdmiCecMessage}
     */
    static HdmiCecMessage buildUserControlReleased(int src, int dest) {
        return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_RELEASED);
    }

    /***** Please ADD new buildXXX() methods above. ******/

    /**
     * Build a {@link HdmiCecMessage} without extra parameter.
     *
+47 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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;

/**
 * Defines constants related to HDMI-CEC protocol internal implementation.
 * If a constant will be used in the public api, it should be located in
 * {@link android.hardware.hdmi.HdmiCec}.
 */
final class HdmiConstants {

    // Constants related to operands of HDMI CEC commands.
    // Refer to CEC Table 29 in HDMI Spec v1.4b.
    // [Abort Reason]
    static final int ABORT_UNRECOGNIZED_MODE = 0;
    static final int ABORT_NOT_IN_CORRECT_MODE = 1;
    static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
    static final int ABORT_INVALID_OPERAND = 3;
    static final int ABORT_REFUSED = 4;
    static final int ABORT_UNABLE_TO_DETERMINE = 5;

    // [Audio Status]
    static final int SYSTEM_AUDIO_STATUS_OFF = 0;
    static final int SYSTEM_AUDIO_STATUS_ON = 1;

    // Constants related to UI Command Codes.
    // Refer to CEC Table 30 in HDMI Spec v1.4b.
    static final int UI_COMMAND_MUTE = 0x43;
    static final int UI_COMMAND_MUTE_FUNCTION = 0x65;
    static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66;

    private HdmiConstants() { /* cannot be instantiated */ }
}
+95 −22
Original line number Diff line number Diff line
@@ -118,8 +118,11 @@ public final class HdmiControlService extends SystemService {
    // TODO: it may need to hold lock if it's accessed from others.
    private boolean mArcStatusEnabled = false;

    // Whether SystemAudioMode is "On" or not.
    private boolean mSystemAudioMode;

    // Handler running on service thread. It's used to run a task in service thread.
    private Handler mHandler = new Handler();
    private final Handler mHandler = new Handler();

    public HdmiControlService(Context context) {
        super(context);
@@ -158,6 +161,9 @@ public final class HdmiControlService extends SystemService {
        }

        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());

        // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
        // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
    }

    /**
@@ -211,35 +217,43 @@ public final class HdmiControlService extends SystemService {
     * @param action {@link FeatureAction} to remove
     */
    void removeAction(final FeatureAction action) {
        runOnServiceThread(new Runnable() {
            @Override
            public void run() {
        assertRunOnServiceThread();
        mActions.remove(action);
    }
        });
    }

    // Remove all actions matched with the given Class type.
    private <T extends FeatureAction> void removeAction(final Class<T> clazz) {
        runOnServiceThread(new Runnable() {
            @Override
            public void run() {
        removeActionExcept(clazz, null);
    }

    // Remove all actions matched with the given Class type besides |exception|.
    <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
            final FeatureAction exception) {
        assertRunOnServiceThread();
        Iterator<FeatureAction> iter = mActions.iterator();
        while (iter.hasNext()) {
            FeatureAction action = iter.next();
                    if (action.getClass().equals(clazz)) {
            if (action != exception && action.getClass().equals(clazz)) {
                action.clear();
                mActions.remove(action);
            }
        }
    }
        });
    }

    private void runOnServiceThread(Runnable runnable) {
        mHandler.post(runnable);
    }

    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
        mHandler.postAtFrontOfQueue(runnable);
    }

    private void assertRunOnServiceThread() {
        if (Looper.myLooper() != mHandler.getLooper()) {
            throw new IllegalStateException("Should run on service thread.");
        }
    }

    /**
     * Change ARC status into the given {@code enabled} status.
     *
@@ -306,8 +320,12 @@ public final class HdmiControlService extends SystemService {
            case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
                handleReportPhysicalAddress(message);
                return true;
            // TODO: Add remaining system information query such as
            // <Give Device Power Status> and <Request Active Source> handler.
            case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
                handleSetSystemAudioMode(message);
                return true;
            case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
                handleSystemAudioModeStatus(message);
                return true;
            default:
                return dispatchMessageToAction(message);
        }
@@ -413,7 +431,7 @@ public final class HdmiControlService extends SystemService {
            sendCecCommand(
                    HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(),
                            message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE,
                            HdmiCecMessageBuilder.ABORT_UNRECOGNIZED_MODE));
                            HdmiConstants.ABORT_UNRECOGNIZED_MODE));
            return;
        }

@@ -438,6 +456,33 @@ public final class HdmiControlService extends SystemService {
        return false;
    }

    private void handleSetSystemAudioMode(HdmiCecMessage message) {
        if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) {
            return;
        }
        SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
                message.getDestination(), message.getSource(),
                HdmiUtils.parseCommandParamSystemAudioStatus(message));
        addAndStartAction(action);
    }

    private void handleSystemAudioModeStatus(HdmiCecMessage message) {
        if (!isMessageForSystemAudio(message)) {
            return;
        }
        setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
    }

    private boolean isMessageForSystemAudio(HdmiCecMessage message) {
        if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
                || message.getDestination() != HdmiCec.ADDR_TV
                || getAvrDeviceInfo() == null) {
            Slog.w(TAG, "Skip abnormal CecMessage: " + message);
            return false;
        }
        return true;
    }

    // Record class that monitors the event of the caller of being killed. Used to clean up
    // the listener list and record list accordingly.
    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
@@ -627,4 +672,32 @@ public final class HdmiControlService extends SystemService {
            Slog.e(TAG, "Invoking callback failed:" + e);
        }
    }

    HdmiCecDeviceInfo getAvrDeviceInfo() {
        return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
    }

    void setSystemAudioMode(boolean newMode) {
        assertRunOnServiceThread();
        if (newMode != mSystemAudioMode) {
            // 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.
            mSystemAudioMode = newMode;
        }
    }

    boolean getSystemAudioMode() {
        assertRunOnServiceThread();
        return mSystemAudioMode;
    }

    void setAudioStatus(boolean mute, int volume) {
        // TODO: Hook up with AudioManager.
    }

    boolean isInPresetInstallationMode() {
        // TODO: Implement this.
        return false;
    }
}
+74 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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 android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
import android.util.Slog;

/**
 * Various utilities to handle HDMI CEC messages.
 */
final class HdmiUtils {

    private HdmiUtils() { /* cannot be instantiated */ }

    /**
     * Verify if the given address is for the given device type.  If not it will throw
     * {@link IllegalArgumentException}.
     *
     * @param logicalAddress the logical address to verify
     * @param deviceType the device type to check
     * @throw IllegalArgumentException
     */
    static void verifyAddressType(int logicalAddress, int deviceType) {
        int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress);
        if (actualDeviceType != deviceType) {
            throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
                    + ", Actual:" + actualDeviceType);
        }
    }

    /**
     * Check if the given CEC message come from the given address.
     *
     * @param cmd the CEC message to check
     * @param expectedAddress the expected source address of the given message
     * @param tag the tag of caller module (for log message)
     * @return true if the CEC message comes from the given address
     */
    static boolean checkCommandSource(HdmiCecMessage cmd, int expectedAddress, String tag) {
        int src = cmd.getSource();
        if (src != expectedAddress) {
            Slog.w(tag, "Invalid source [Expected:" + expectedAddress + ", Actual:" + src + "]");
            return false;
        }
        return true;
    }

    /**
     * Parse the parameter block of CEC message as [System Audio Status].
     *
     * @param cmd the CEC message to parse
     * @return true if the given parameter has [ON] value
     */
    static boolean parseCommandParamSystemAudioStatus(HdmiCecMessage cmd) {
        // TODO: Handle the exception when the length is wrong.
        return cmd.getParams().length > 0
                && cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON;
    }
}
Loading