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

Commit 0340bbc8 authored by Jinsuk Kim's avatar Jinsuk Kim
Browse files

RoutingControlAction for HdmiControlService

Routing control action is initiated in various cases, such as manual
TV input port switching, routing change of a different CEC switch,
and so on. The action determines the device to be a new active source.

Change-Id: I1efcd6ff1919dd94d6fa0e0ffa6e430c48d4e9c6
parent 056396ed
Loading
Loading
Loading
Loading
+19 −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 android.hardware.hdmi;

parcelable HdmiPortInfo;
+164 −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 android.hardware.hdmi;

import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;

/**
 * A class to encapsulate HDMI port information. Contains the capability of the ports such as
 * HDMI-CEC, MHL, ARC(Audio Return Channel), and physical address assigned to each port.
 *
 * @hide
 */
@SystemApi
public final class HdmiPortInfo implements Parcelable {
    /** HDMI port type: Input */
    public static final int PORT_INPUT = 0;

    /** HDMI port type: Output */
    public static final int PORT_OUTPUT = 1;

    private final int mId;
    private final int mType;
    private final int mAddress;
    private final boolean mCecSupported;
    private final boolean mArcSupported;
    private final boolean mMhlSupported;

    /**
     * Constructor.
     *
     * @param id identifier assigned to each port. 1 for HDMI port 1
     * @param type HDMI port input/output type
     * @param address physical address of the port
     * @param cec {@code true} if HDMI-CEC is supported on the port
     * @param mhl {@code true} if MHL is supported on the port
     * @param arc {@code true} if audio return channel is supported on the port
     */
    public HdmiPortInfo(int id, int type, int address, boolean cec, boolean mhl, boolean arc) {
        mId = id;
        mType = type;
        mAddress = address;
        mCecSupported = cec;
        mArcSupported = arc;
        mMhlSupported = mhl;
    }

    /**
     * Returns the port id.
     *
     * @return port id
     */
    public int getId() {
        return mId;
    }

    /**
     * Returns the port type.
     *
     * @return port type
     */
    public int getType() {
        return mType;
    }

    /**
     * Returns the port address.
     *
     * @return port address
     */
    public int getAddress() {
        return mAddress;
    }

    /**
     * Returns {@code true} if the port supports HDMI-CEC signaling.
     *
     * @return {@code true} if the port supports HDMI-CEC signaling.
     */
    public boolean isCecSupported() {
        return mCecSupported;
    }

    /**
     * Returns {@code true} if the port supports MHL signaling.
     *
     * @return {@code true} if the port supports MHL signaling.
     */
    public boolean isMhlSupported() {
        return mMhlSupported;
    }

    /**
     * Returns {@code true} if the port supports audio return channel.
     *
     * @return {@code true} if the port supports audio return channel
     */
    public boolean isArcSupported() {
        return mArcSupported;
    }

    /**
     * Describe the kinds of special objects contained in this Parcelable's
     * marshalled representation.
     */
    @Override
    public int describeContents() {
        return 0;
    }


    /**
     * A helper class to deserialize {@link HdmiPortInfo} for a parcel.
     */
    public static final Parcelable.Creator<HdmiPortInfo> CREATOR =
            new Parcelable.Creator<HdmiPortInfo>() {
                @Override
                public HdmiPortInfo createFromParcel(Parcel source) {
                    int id = source.readInt();
                    int type = source.readInt();
                    int address = source.readInt();
                    boolean cec = (source.readInt() == 1);
                    boolean arc = (source.readInt() == 1);
                    boolean mhl = (source.readInt() == 1);
                    return new HdmiPortInfo(id, type, address, cec, arc, mhl);
                }

                @Override
                public HdmiPortInfo[] newArray(int size) {
                    return new HdmiPortInfo[size];
                }
            };

    /**
     * Serialize this object into a {@link Parcel}.
     *
     * @param dest The Parcel in which the object should be written.
     * @param flags Additional flags about how the object should be written.
     *        May be 0 or {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}.
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mId);
        dest.writeInt(mType);
        dest.writeInt(mAddress);
        dest.writeInt(mCecSupported ? 1 : 0);
        dest.writeInt(mArcSupported ? 1 : 0);
        dest.writeInt(mMhlSupported ? 1 : 0);
    }
}
+6 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
@@ -308,6 +309,10 @@ final class HdmiCecController {
        return mDeviceInfos.get(logicalAddress);
    }

    HdmiPortInfo[] getPortInfos() {
        return nativeGetPortInfos(mNativePtr);
    }

    /**
     * Return the locally hosted logical device of a given type.
     *
@@ -641,8 +646,8 @@ final class HdmiCecController {
    private static native int nativeGetPhysicalAddress(long controllerPtr);
    private static native int nativeGetVersion(long controllerPtr);
    private static native int nativeGetVendorId(long controllerPtr);
    private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
    private static native void nativeSetOption(long controllerPtr, int flag, int value);
    private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag);
    private static native boolean nativeIsConnected(long controllerPtr, int port);

}
+100 −13
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.content.Context;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiHotplugEventListener;
@@ -40,6 +41,7 @@ import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -105,9 +107,8 @@ public final class HdmiControlService extends SystemService {
    // Used to synchronize the access to the service.
    private final Object mLock = new Object();

    // Type of logical devices hosted in the system.
    @GuardedBy("mLock")
    private final int[] mLocalDevices;
    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
    private final List<Integer> mLocalDevices;

    // List of listeners registered by callers that want to get notified of
    // hotplug events.
@@ -117,6 +118,9 @@ public final class HdmiControlService extends SystemService {
    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
            new ArrayList<>();

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

    private final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();

    @Nullable
@@ -125,6 +129,10 @@ public final class HdmiControlService extends SystemService {
    @Nullable
    private HdmiMhlController mMhlController;

    // HDMI port information. Stored in the unmodifiable list to keep the static information
    // from being modified.
    private List<HdmiPortInfo> mPortInfo;

    // Logical address of the active source.
    @GuardedBy("mLock")
    private int mActiveSource;
@@ -149,13 +157,10 @@ public final class HdmiControlService extends SystemService {
    // 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 final Handler mHandler = new Handler();

    public HdmiControlService(Context context) {
        super(context);
        mLocalDevices = getContext().getResources().getIntArray(
                com.android.internal.R.array.config_hdmiCecLogicalDeviceType);
        mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
                com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
        // TODO: Get control flag from persistent storage
        mInputChangeEnabled = true;
    }
@@ -175,14 +180,14 @@ public final class HdmiControlService extends SystemService {
        if (mMhlController == null) {
            Slog.i(TAG, "Device does not support MHL-control.");
        }

        mPortInfo = initPortInfo();
        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.
    }

    private void initializeLocalDevices(final int[] deviceTypes) {
    private void initializeLocalDevices(final List<Integer> deviceTypes) {
        // A container for [Logical Address, Local device info].
        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
        final SparseIntArray finished = new SparseIntArray();
@@ -206,7 +211,7 @@ public final class HdmiControlService extends SystemService {

                    // Once finish address allocation for all devices, notify
                    // it to each device.
                    if (deviceTypes.length == finished.size()) {
                    if (deviceTypes.size() == finished.size()) {
                        notifyAddressAllocated(devices);
                    }
                }
@@ -222,6 +227,66 @@ public final class HdmiControlService extends SystemService {
        }
    }

    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
    // keep them in one place.
    private List<HdmiPortInfo> initPortInfo() {
        HdmiPortInfo[] cecPortInfo = null;

        // CEC HAL provides majority of the info while MHL does only MHL support flag for
        // each port. Return empty array if CEC HAL didn't provide the info.
        if (mCecController != null) {
            cecPortInfo = mCecController.getPortInfos();
        }
        if (cecPortInfo == null) {
            return Collections.emptyList();
        }

        HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0];
        if (mMhlController != null) {
            // TODO: Implement plumbing logic to get MHL port information.
            // mhlPortInfo = mMhlController.getPortInfos();
        }

        // Use the id (port number) to find the matched info between CEC and MHL to combine them
        // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found.
        ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
        for (int i = 0; i < cecPortInfo.length; ++i) {
            HdmiPortInfo cec = cecPortInfo[i];
            int id = cec.getId();
            boolean mhlInfoFound = false;
            for (HdmiPortInfo mhl : mhlPortInfo) {
                if (id == mhl.getId()) {
                    result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(),
                            cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported()));
                    mhlInfoFound = true;
                    break;
                }
            }
            if (!mhlInfoFound) {
                result.add(cec);
            }
        }

        return Collections.unmodifiableList(result);
    }

    /**
     * Returns HDMI port information for the given port id.
     *
     * @param portId HDMI port id
     * @return {@link HdmiPortInfo} for the given port
     */
    HdmiPortInfo getPortInfo(int portId) {
        // mPortInfo is an unmodifiable list and the only reference to its inner list.
        // No lock is necessary.
        for (HdmiPortInfo info : mPortInfo) {
            if (portId == info.getId()) {
                return info;
            }
        }
        return null;
    }

    /**
     * Returns {@link Looper} for IO operation.
     *
@@ -291,6 +356,7 @@ public final class HdmiControlService extends SystemService {
    }

    HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
        assertRunOnServiceThread();
        return mCecController.getDeviceInfo(logicalAddress);
    }

@@ -311,6 +377,24 @@ public final class HdmiControlService extends SystemService {
        return mCecController.getDeviceInfoList(includeLocalDevice);
    }

    /**
     * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches
     * the given routing path. CEC devices use routing path for its physical address to
     * describe the hierarchy of the devices in the network.
     *
     * @param path routing path or physical address
     * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null
     */
    HdmiCecDeviceInfo getDeviceInfoByPath(int path) {
        assertRunOnServiceThread();
        for (HdmiCecDeviceInfo info : mCecController.getDeviceInfoList(false)) {
            if (info.getPhysicalAddress() == path) {
                return info;
            }
        }
        return null;
    }

    /**
     * Add and start a new {@link FeatureAction} to the action queue.
     *
@@ -625,9 +709,12 @@ public final class HdmiControlService extends SystemService {
        @Override
        public int[] getSupportedTypes() {
            enforceAccessPermission();
            synchronized (mLock) {
                return mLocalDevices;
            // mLocalDevices is an unmodifiable list - no lock necesary.
            int[] localDevices = new int[mLocalDevices.size()];
            for (int i = 0; i < localDevices.length; ++i) {
                localDevices[i] = mLocalDevices.get(i);
            }
            return localDevices;
        }

        @Override
+20 −0
Original line number Diff line number Diff line
@@ -20,6 +20,10 @@ import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
import android.util.Slog;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Various utilities to handle HDMI CEC messages.
 */
@@ -72,6 +76,22 @@ final class HdmiUtils {
                && cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON;
    }

    /**
     * Convert integer array to list of {@link Integer}.
     *
     * <p>The result is immutable.
     *
     * @param is integer array
     * @return {@link List} instance containing the elements in the given array
     */
    static List<Integer> asImmutableList(final int[] is) {
        ArrayList<Integer> list = new ArrayList<>(is.length);
        for (int type : is) {
            list.add(type);
        }
        return Collections.unmodifiableList(list);
    }

    /**
     * Assemble two bytes into single integer value.
     *
Loading