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

Commit f04ee7e3 authored by Jungshik Jang's avatar Jungshik Jang Committed by Android (Google) Code Review
Browse files

Merge "Refactor HdmiCecLocalDevice and logical address allocation logic."

parents d0ca7b62 3ee65720
Loading
Loading
Loading
Loading
+20 −37
Original line number Diff line number Diff line
@@ -47,6 +47,21 @@ import java.util.List;
final class HdmiCecController {
    private static final String TAG = "HdmiCecController";

    /**
     * Interface to report allocated logical address.
     */
    interface AllocateAddressCallback {
        /**
         * Called when a new logical address is allocated.
         *
         * @param deviceType requested device type to allocate logical address
         * @param logicalAddress allocated logical address. If it is
         *                       {@link HdmiCec#ADDR_UNREGISTERED}, it means that
         *                       it failed to allocate logical address for the given device type
         */
        void onAllocated(int deviceType, int logicalAddress);
    }

    private static final byte[] EMPTY_BODY = EmptyArray.BYTE;

    // A message to pass cec send command to IO looper.
@@ -116,45 +131,13 @@ final class HdmiCecController {
        mNativePtr = nativePtr;
    }

    /**
     * Perform initialization for each hosted device.
     *
     * @param deviceTypes array of device types
     */
    void initializeLocalDevices(int[] deviceTypes,
            HdmiCecLocalDevice.AddressAllocationCallback callback) {
        assertRunOnServiceThread();
        for (int type : deviceTypes) {
            HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type, callback);
            if (device == null) {
                continue;
            }
            // TODO: Consider restoring the local device addresses from persistent storage
            //       to allocate the same addresses again if possible.
            device.setPreferredAddress(HdmiCec.ADDR_UNREGISTERED);
            mLocalDevices.put(type, device);
            device.init();
        }
    }

    /**
     * Interface to report allocated logical address.
     */
    interface AllocateLogicalAddressCallback {
        /**
         * Called when a new logical address is allocated.
         *
         * @param deviceType requested device type to allocate logical address
         * @param logicalAddress allocated logical address. If it is
         *                       {@link HdmiCec#ADDR_UNREGISTERED}, it means that
         *                       it failed to allocate logical address for the given device type
         */
        void onAllocated(int deviceType, int logicalAddress);
    void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
        mLocalDevices.put(deviceType, device);
    }

    /**
     * Allocate a new logical address of the given device type. Allocated
     * address will be reported through {@link AllocateLogicalAddressCallback}.
     * address will be reported through {@link AllocateAddressCallback}.
     *
     * <p> Declared as package-private, accessed by {@link HdmiControlService} only.
     *
@@ -166,7 +149,7 @@ final class HdmiCecController {
     * @param callback callback interface to report allocated logical address to caller
     */
    void allocateLogicalAddress(final int deviceType, final int preferredAddress,
            final AllocateLogicalAddressCallback callback) {
            final AllocateAddressCallback callback) {
        assertRunOnServiceThread();

        runOnIoThread(new Runnable() {
@@ -178,7 +161,7 @@ final class HdmiCecController {
    }

    private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
            final AllocateLogicalAddressCallback callback) {
            final AllocateAddressCallback callback) {
        assertRunOnIoThread();
        int startAddress = preferredAddress;
        // If preferred address is "unregistered", start address will be the smallest
+18 −57
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.server.hdmi;

import com.android.server.hdmi.HdmiCecController.AllocateLogicalAddressCallback;

import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;

@@ -27,84 +25,43 @@ import android.hardware.hdmi.HdmiCecDeviceInfo;
 */
abstract class HdmiCecLocalDevice {

    protected final HdmiCecController mController;
    protected final HdmiControlService mService;
    protected final int mDeviceType;
    protected final AddressAllocationCallback mAllocationCallback;
    protected int mAddress;
    protected int mPreferredAddress;
    protected HdmiCecDeviceInfo mDeviceInfo;

    /**
     * Callback interface to notify newly allocated logical address of the given
     * local device.
     */
    interface AddressAllocationCallback {
        /**
         * Called when a logical address of the given device is allocated.
         *
         * @param deviceType original device type
         * @param logicalAddress newly allocated logical address
         */
        void onAddressAllocated(int deviceType, int logicalAddress);
    }

    protected HdmiCecLocalDevice(HdmiCecController controller, int deviceType,
            AddressAllocationCallback callback) {
        mController = controller;
    protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
        mService = service;
        mDeviceType = deviceType;
        mAllocationCallback = callback;
        mAddress = HdmiCec.ADDR_UNREGISTERED;
    }

    // Factory method that returns HdmiCecLocalDevice of corresponding type.
    static HdmiCecLocalDevice create(HdmiCecController controller, int deviceType,
            AddressAllocationCallback callback) {
    static HdmiCecLocalDevice create(HdmiControlService service, int deviceType) {
        switch (deviceType) {
        case HdmiCec.DEVICE_TV:
            return new HdmiCecLocalDeviceTv(controller, callback);
            return new HdmiCecLocalDeviceTv(service);
        case HdmiCec.DEVICE_PLAYBACK:
            return new HdmiCecLocalDevicePlayback(controller, callback);
            return new HdmiCecLocalDevicePlayback(service);
        default:
            return null;
        }
    }

    abstract void init();
    void init() {
        mPreferredAddress = HdmiCec.ADDR_UNREGISTERED;
        // TODO: load preferred address from permanent storage.
    }

    /**
     * Called when a logical address of the local device is allocated.
     * Note that internal variables are updated before it's called.
     * Called once a logical address of the local device is allocated.
     */
    protected abstract void onAddressAllocated(int logicalAddress);

    protected void allocateAddress(int type) {
        mController.allocateLogicalAddress(type, mPreferredAddress,
                new AllocateLogicalAddressCallback() {
            @Override
            public void onAllocated(int deviceType, int logicalAddress) {
    final void handleAddressAllocated(int logicalAddress) {
        mAddress = mPreferredAddress = logicalAddress;

                // Create and set device info.
                HdmiCecDeviceInfo deviceInfo = createDeviceInfo(mAddress, deviceType);
                setDeviceInfo(deviceInfo);
                mController.addDeviceInfo(deviceInfo);

                mController.addLogicalAddress(logicalAddress);
        onAddressAllocated(logicalAddress);
                if (mAllocationCallback != null) {
                    mAllocationCallback.onAddressAllocated(deviceType, logicalAddress);
                }
            }
        });
    }

    private final HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
        int vendorId = mController.getVendorId();
        int physicalAddress = mController.getPhysicalAddress();
        // TODO: get device name read from system configuration.
        String displayName = HdmiCec.getDefaultDeviceName(logicalAddress);
        return new HdmiCecDeviceInfo(logicalAddress,
                physicalAddress, deviceType, vendorId, displayName);
    }

    HdmiCecDeviceInfo getDeviceInfo() {
@@ -128,4 +85,8 @@ abstract class HdmiCecLocalDevice {
    void setPreferredAddress(int addr) {
        mPreferredAddress = addr;
    }

    int getPreferredAddress() {
        return mPreferredAddress;
    }
}
+4 −9
Original line number Diff line number Diff line
@@ -23,18 +23,13 @@ import android.hardware.hdmi.HdmiCec;
 */
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {

    HdmiCecLocalDevicePlayback(HdmiCecController controller, AddressAllocationCallback callback) {
        super(controller, HdmiCec.DEVICE_PLAYBACK, callback);
    }

    @Override
    void init() {
        allocateAddress(mDeviceType);
    HdmiCecLocalDevicePlayback(HdmiControlService service) {
        super(service, HdmiCec.DEVICE_PLAYBACK);
    }

    @Override
    protected void onAddressAllocated(int logicalAddress) {
        mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                mAddress, mController.getPhysicalAddress(), mDeviceType));
        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                mAddress, mService.getPhysicalAddress(), mDeviceType));
    }
}
+7 −11
Original line number Diff line number Diff line
@@ -23,24 +23,20 @@ import android.hardware.hdmi.HdmiCec;
 */
final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {

    HdmiCecLocalDeviceTv(HdmiCecController controller, AddressAllocationCallback callback) {
        super(controller, HdmiCec.DEVICE_TV, callback);
    }

    @Override
    void init() {
        allocateAddress(mDeviceType);
    HdmiCecLocalDeviceTv(HdmiControlService service) {
        super(service, HdmiCec.DEVICE_TV);
    }

    @Override
    protected void onAddressAllocated(int logicalAddress) {
        // TODO: vendor-specific initialization here.

        mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                mAddress, mController.getPhysicalAddress(), mDeviceType));
        mController.sendCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
                mAddress, mController.getVendorId()));
        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                mAddress, mService.getPhysicalAddress(), mDeviceType));
        mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
                mAddress, mService.getVendorId()));

        mService.launchDeviceDiscovery(mAddress);
        // TODO: Start routing control action, device discovery action.
    }
}
+97 −44
Original line number Diff line number Diff line
@@ -30,12 +30,13 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;

import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
import com.android.server.hdmi.HdmiCecLocalDevice.AddressAllocationCallback;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;

import java.util.ArrayList;
import java.util.Iterator;
@@ -134,23 +135,9 @@ public final class HdmiControlService extends SystemService {
    public void onStart() {
        mIoThread.start();
        mCecController = HdmiCecController.create(this);
        if (mCecController != null) {
            mCecController.initializeLocalDevices(mLocalDevices, new AddressAllocationCallback() {
                private final SparseIntArray mAllocated = new SparseIntArray();

                @Override
                public void onAddressAllocated(int deviceType, int logicalAddress) {
                    mAllocated.append(deviceType, logicalAddress);
                    // TODO: get HdmiLCecLocalDevice and call onAddressAllocated here.

                    // Once all logical allocation is done, launch device discovery
                    // action if one of local device is TV.
                    int tvAddress = mAllocated.get(HdmiCec.DEVICE_TV, -1);
                    if (mLocalDevices.length == mAllocated.size() && tvAddress != -1) {
                        launchDeviceDiscovery(tvAddress);
                    }
                }
            });
        if (mCecController != null) {
            initializeLocalDevices(mLocalDevices);
        } else {
            Slog.i(TAG, "Device does not support HDMI-CEC.");
        }
@@ -166,6 +153,46 @@ public final class HdmiControlService extends SystemService {
        // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
    }

    private void initializeLocalDevices(final int[] deviceTypes) {
        // A container for [Logical Address, Local device info].
        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
        final SparseIntArray finished = new SparseIntArray();
        for (int type : deviceTypes) {
            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
            localDevice.init();
            mCecController.allocateLogicalAddress(type,
                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
                @Override
                public void onAllocated(int deviceType, int logicalAddress) {
                    if (logicalAddress == HdmiCec.ADDR_UNREGISTERED) {
                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
                    } else {
                        HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
                        localDevice.setDeviceInfo(deviceInfo);
                        mCecController.addLocalDevice(deviceType, localDevice);
                        mCecController.addLogicalAddress(logicalAddress);
                        devices.append(logicalAddress, localDevice);
                    }
                    finished.append(deviceType, logicalAddress);

                    // Once finish address allocation for all devices, notify
                    // it to each device.
                    if (deviceTypes.length == finished.size()) {
                        notifyAddressAllocated(devices);
                    }
                }
            });
        }
    }

    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) {
        for (int i = 0; i < devices.size(); ++i) {
            int address = devices.keyAt(i);
            HdmiCecLocalDevice device = devices.valueAt(i);
            device.onAddressAllocated(address);
        }
    }

    /**
     * Returns {@link Looper} for IO operation.
     *
@@ -185,6 +212,20 @@ public final class HdmiControlService extends SystemService {
        return mHandler.getLooper();
    }

    /**
     * Returns physical address of the device.
     */
    int getPhysicalAddress() {
        return mCecController.getPhysicalAddress();
    }

    /**
     * Returns vendor id of CEC service.
     */
    int getVendorId() {
        return mCecController.getVendorId();
    }

    /**
     * Add and start a new {@link FeatureAction} to the action queue.
     *
@@ -352,6 +393,45 @@ public final class HdmiControlService extends SystemService {
        mCecController.pollDevices(callback, retryCount);
    }


    /**
     * Launch device discovery sequence. It starts with clearing the existing device info list.
     * Note that it assumes that logical address of all local devices is already allocated.
     *
     * @param sourceAddress a logical address of tv
     */
    void launchDeviceDiscovery(int sourceAddress) {
        // At first, clear all existing device infos.
        mCecController.clearDeviceInfoList();

        // TODO: check whether TV is one of local devices.
        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress,
                new DeviceDiscoveryCallback() {
                    @Override
                    public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
                        for (HdmiCecDeviceInfo info : deviceInfos) {
                            mCecController.addDeviceInfo(info);
                        }

                        // Add device info of all local devices.
                        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
                            mCecController.addDeviceInfo(device.getDeviceInfo());
                        }

                        // TODO: start hot-plug detection sequence here.
                        // addAndStartAction(new HotplugDetectionAction());
                    }
                });
        addAndStartAction(action);
    }

    private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
        // TODO: get device name read from system configuration.
        String displayName = HdmiCec.getDefaultDeviceName(logicalAddress);
        return new HdmiCecDeviceInfo(logicalAddress,
                getPhysicalAddress(), deviceType, getVendorId(), displayName);
    }

    private void handleReportPhysicalAddress(HdmiCecMessage message) {
        // At first, try to consume it.
        if (dispatchMessageToAction(message)) {
@@ -505,33 +585,6 @@ public final class HdmiControlService extends SystemService {
        mCecController.addDeviceInfo(info);
    }

    // Launch device discovery sequence.
    // It starts with clearing the existing device info list.
    // Note that it assumes that logical address of all local devices is already allocated.
    private void launchDeviceDiscovery(int sourceAddress) {
        // At first, clear all existing device infos.
        mCecController.clearDeviceInfoList();

        // TODO: check whether TV is one of local devices.
        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress,
                new DeviceDiscoveryCallback() {
                    @Override
                    public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
                        for (HdmiCecDeviceInfo info : deviceInfos) {
                            mCecController.addDeviceInfo(info);
                        }

                        // Add device info of all local devices.
                        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
                            mCecController.addDeviceInfo(device.getDeviceInfo());
                        }

                        // TODO: start hot-plug detection sequence here.
                        // addAndStartAction(new HotplugDetectionAction());
                    }
                });
        addAndStartAction(action);
    }

    private void enforceAccessPermission() {
        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);