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

Commit f98b9e87 authored by Jinsuk Kim's avatar Jinsuk Kim
Browse files

CEC: Process missing port/device select request

The timing issue cause some port/device select request
to return error if made while the service is being initialized
waking up from sleep.

This CL buffers the request so that it can be handled as
expected once the service initialization and device discovery
is completed.

Bug: 23084229
Change-Id: I8bcdaf3b708c6b9bf75ea15304c08bd35bd0f6d0
(cherry picked from commit d0b01dfe0aaebd054165dfe2f5f4bd7d68703970)
parent 69b90e94
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -177,6 +177,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        return mTvInputs.containsValue(deviceId);
    }

    private SelectRequestBuffer mSelectRequestBuffer;

    HdmiCecLocalDeviceTv(HdmiControlService service) {
        super(service, HdmiDeviceInfo.DEVICE_TV);
        mPrevPortId = Constants.INVALID_PORT_ID;
@@ -205,6 +207,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        launchRoutingControl(reason != HdmiControlService.INITIATED_BY_ENABLE_CEC &&
                reason != HdmiControlService.INITIATED_BY_BOOT_UP);
        mLocalDeviceAddresses = initLocalDeviceAddresses();
        resetSelectRequestBuffer();
        launchDeviceDiscovery();
    }

@@ -219,6 +222,19 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        return Collections.unmodifiableList(addresses);
    }


    @ServiceThreadOnly
    public void setSelectRequestBuffer(SelectRequestBuffer requestBuffer) {
        assertRunOnServiceThread();
        mSelectRequestBuffer = requestBuffer;
    }

    @ServiceThreadOnly
    private void resetSelectRequestBuffer() {
        assertRunOnServiceThread();
        setSelectRequestBuffer(SelectRequestBuffer.EMPTY_BUFFER);
    }

    @Override
    protected int getPreferredAddress() {
        return Constants.ADDR_TV;
@@ -777,6 +793,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
                            addCecDevice(device.getDeviceInfo());
                        }

                        mSelectRequestBuffer.process();
                        resetSelectRequestBuffer();

                        addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
                        addAndStartAction(new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));

+25 −2
Original line number Diff line number Diff line
@@ -77,6 +77,8 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
import com.android.server.hdmi.SelectRequestBuffer.DeviceSelectRequest;
import com.android.server.hdmi.SelectRequestBuffer.PortSelectRequest;

import libcore.util.EmptyArray;

@@ -360,7 +362,9 @@ public final class HdmiControlService extends SystemService {
        }
    }

    private CecMessageBuffer mCecMessageBuffer = new CecMessageBuffer();
    private final CecMessageBuffer mCecMessageBuffer = new CecMessageBuffer();

    private final SelectRequestBuffer mSelectRequestBuffer = new SelectRequestBuffer();

    public HdmiControlService(Context context) {
        super(context);
@@ -582,6 +586,12 @@ public final class HdmiControlService extends SystemService {
        final int[] finished = new int[1];
        mAddressAllocated = allocatingDevices.isEmpty();

        // For TV device, select request can be invoked while address allocation or device
        // discovery is in progress. Initialize the request here at the start of allocation,
        // and process the collected requests later when the allocation and device discovery
        // is all completed.
        mSelectRequestBuffer.clear();

        for (final HdmiCecLocalDevice localDevice : allocatingDevices) {
            mCecController.allocateLogicalAddress(localDevice.getType(),
                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
@@ -623,6 +633,9 @@ public final class HdmiControlService extends SystemService {
            int address = device.getDeviceInfo().getLogicalAddress();
            device.handleAddressAllocated(address, initiatedBy);
        }
        if (isTvDeviceEnabled()) {
            tv().setSelectRequestBuffer(mSelectRequestBuffer);
        }
    }

    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
@@ -1228,6 +1241,11 @@ public final class HdmiControlService extends SystemService {
                    }
                    HdmiCecLocalDeviceTv tv = tv();
                    if (tv == null) {
                        if (!mAddressAllocated) {
                            mSelectRequestBuffer.set(SelectRequestBuffer.newDeviceSelect(
                                    HdmiControlService.this, deviceId, callback));
                            return;
                        }
                        Slog.w(TAG, "Local tv device not available");
                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
                        return;
@@ -1262,6 +1280,11 @@ public final class HdmiControlService extends SystemService {
                    }
                    HdmiCecLocalDeviceTv tv = tv();
                    if (tv == null) {
                        if (!mAddressAllocated) {
                            mSelectRequestBuffer.set(SelectRequestBuffer.newPortSelect(
                                    HdmiControlService.this, portId, callback));
                            return;
                        }
                        Slog.w(TAG, "Local tv device not available");
                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
                        return;
@@ -1912,7 +1935,7 @@ public final class HdmiControlService extends SystemService {
        }
    }

    private HdmiCecLocalDeviceTv tv() {
    public HdmiCecLocalDeviceTv tv() {
        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
    }

+141 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.HdmiControlManager;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;

/**
 * Buffer for portSelect/deviceSelect requests. Requests made before the address allocation
 * are stored in this buffer, and processed when the allocation is completed.
 *
 * <p>This is put into action only if we are a TV device.
 */
public class SelectRequestBuffer {
    private static final String TAG = "SelectRequestBuffer";

    public static final SelectRequestBuffer EMPTY_BUFFER = new SelectRequestBuffer() {
        @Override
        public void process() {
            // Do nothing.
        }
    };

    /**
     * Parent class from which buffer for select requests are inherited. Keeps the callback
     * and the device/port ID.
     */
    public static abstract class SelectRequest {
        protected final HdmiControlService mService;
        protected final IHdmiControlCallback mCallback;
        protected final int mId;

        public SelectRequest(HdmiControlService service, int id, IHdmiControlCallback callback) {
            mService = service;
            mId = id;
            mCallback = callback;
        }

        protected HdmiCecLocalDeviceTv tv() {
            return mService.tv();
        }

        protected boolean isLocalDeviceReady() {
            if (tv() == null) {
                Slog.e(TAG, "Local tv device not available");
                invokeCallback(HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
                return false;
            }
            return true;
        }

        private void invokeCallback(int reason) {
            try {
                if (mCallback != null) {
                    mCallback.onComplete(reason);
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Invoking callback failed:" + e);
            }
        }

        /**
         * Implement this method with a customize action to perform when the request gets
         * invoked in a deferred manner.
         */
        public abstract void process();
    }

    public static class DeviceSelectRequest extends SelectRequest {
        private DeviceSelectRequest(HdmiControlService srv, int id, IHdmiControlCallback callback) {
            super(srv, id, callback);
        }

        @Override
        public void process() {
            if (isLocalDeviceReady()) {
                Slog.v(TAG, "calling delayed deviceSelect id:" + mId);
                tv().deviceSelect(mId, mCallback);
            }
        }
    }

    public static class PortSelectRequest extends SelectRequest {
        private PortSelectRequest(HdmiControlService srv, int id, IHdmiControlCallback callback) {
            super(srv, id, callback);
        }

        @Override
        public void process() {
            if (isLocalDeviceReady()) {
                Slog.v(TAG, "calling delayed portSelect id:" + mId);
                tv().doManualPortSwitching(mId, mCallback);
            }
        }
    }

    public static DeviceSelectRequest newDeviceSelect(HdmiControlService srv, int id,
            IHdmiControlCallback callback) {
        return new DeviceSelectRequest(srv, id, callback);
    }

    public static PortSelectRequest newPortSelect(HdmiControlService srv, int id,
            IHdmiControlCallback callback) {
        return new PortSelectRequest(srv, id, callback);
    }

    // The last select request made by system/app. Note that we do not manage a list of requests
    // but just keep only the last one since it already invalidates the older ones.
    private SelectRequest mRequest;

    public void set(SelectRequest request) {
        mRequest = request;
    }

    public void process() {
        if (mRequest != null) {
            mRequest.process();
            clear();
        }
    }

    public void clear() {
        mRequest = null;
    }
}