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

Commit 5d9eed59 authored by Yan Han's avatar Yan Han
Browse files

Refactor CecMessageBuffer to live in its own file

This allows tests to use Mockito spies instead of anonymous classes to
override methods in HdmiControlService. Using a spy caused runtime errors
because CecMessageBuffer, as an inner class, would call methods on the
original, uninitialized instance of HdmiControlService instead of the spy.

Test: atest
Change-Id: If255e7b73b5f15c27ecd5dfb7a37993663f39a48
parent 3a9b54f7
Loading
Loading
Loading
Loading
+103 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 java.util.ArrayList;
import java.util.List;

/**
 * Buffer for processing the incoming CEC messages while allocating logical addresses.
 */
final class CecMessageBuffer {
    private List<HdmiCecMessage> mBuffer = new ArrayList<>();
    private HdmiControlService mHdmiControlService;

    CecMessageBuffer(HdmiControlService hdmiControlService) {
        mHdmiControlService = hdmiControlService;
    }

    /**
     * Adds a message to the buffer.
     * Only certain types of messages need to be buffered.
     * @param message The message to add to the buffer
     * @return Whether the message was added to the buffer
     */
    public boolean bufferMessage(HdmiCecMessage message) {
        switch (message.getOpcode()) {
            case Constants.MESSAGE_ACTIVE_SOURCE:
                bufferActiveSource(message);
                return true;
            case Constants.MESSAGE_IMAGE_VIEW_ON:
            case Constants.MESSAGE_TEXT_VIEW_ON:
                bufferImageOrTextViewOn(message);
                return true;
            case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST:
                bufferSystemAudioModeRequest(message);
                return true;
            // Add here if new message that needs to buffer
            default:
                // Do not need to buffer messages other than above
                return false;
        }
    }

    /**
     * Process all messages in the buffer.
     */
    public void processMessages() {
        for (final HdmiCecMessage message : mBuffer) {
            mHdmiControlService.runOnServiceThread(new Runnable() {
                @Override
                public void run() {
                    mHdmiControlService.handleCecCommand(message);
                }
            });
        }
        mBuffer.clear();
    }

    private void bufferActiveSource(HdmiCecMessage message) {
        if (!replaceMessageIfBuffered(message, Constants.MESSAGE_ACTIVE_SOURCE)) {
            mBuffer.add(message);
        }
    }

    private void bufferImageOrTextViewOn(HdmiCecMessage message) {
        if (!replaceMessageIfBuffered(message, Constants.MESSAGE_IMAGE_VIEW_ON)
                && !replaceMessageIfBuffered(message, Constants.MESSAGE_TEXT_VIEW_ON)) {
            mBuffer.add(message);
        }
    }

    private void bufferSystemAudioModeRequest(HdmiCecMessage message) {
        if (!replaceMessageIfBuffered(message, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST)) {
            mBuffer.add(message);
        }
    }

    // Returns true if the message is replaced
    private boolean replaceMessageIfBuffered(HdmiCecMessage message, int opcode) {
        for (int i = 0; i < mBuffer.size(); i++) {
            HdmiCecMessage bufferedMessage = mBuffer.get(i);
            if (bufferedMessage.getOpcode() == opcode) {
                mBuffer.set(i, message);
                return true;
            }
        }
        return false;
    }
}
+6 −68
Original line number Diff line number Diff line
@@ -405,74 +405,7 @@ public class HdmiControlService extends SystemService {
    // Use getAtomWriter() instead of accessing directly, to allow dependency injection for testing.
    private HdmiCecAtomWriter mAtomWriter = new HdmiCecAtomWriter();

    // Buffer for processing the incoming cec messages while allocating logical addresses.
    private final class CecMessageBuffer {
        private List<HdmiCecMessage> mBuffer = new ArrayList<>();

        public boolean bufferMessage(HdmiCecMessage message) {
            switch (message.getOpcode()) {
                case Constants.MESSAGE_ACTIVE_SOURCE:
                    bufferActiveSource(message);
                    return true;
                case Constants.MESSAGE_IMAGE_VIEW_ON:
                case Constants.MESSAGE_TEXT_VIEW_ON:
                    bufferImageOrTextViewOn(message);
                    return true;
                case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST:
                    bufferSystemAudioModeRequest(message);
                    return true;
                    // Add here if new message that needs to buffer
                default:
                    // Do not need to buffer messages other than above
                    return false;
            }
        }

        public void processMessages() {
            for (final HdmiCecMessage message : mBuffer) {
                runOnServiceThread(new Runnable() {
                    @Override
                    public void run() {
                        handleCecCommand(message);
                    }
                });
            }
            mBuffer.clear();
        }

        private void bufferActiveSource(HdmiCecMessage message) {
            if (!replaceMessageIfBuffered(message, Constants.MESSAGE_ACTIVE_SOURCE)) {
                mBuffer.add(message);
            }
        }

        private void bufferImageOrTextViewOn(HdmiCecMessage message) {
            if (!replaceMessageIfBuffered(message, Constants.MESSAGE_IMAGE_VIEW_ON) &&
                !replaceMessageIfBuffered(message, Constants.MESSAGE_TEXT_VIEW_ON)) {
                mBuffer.add(message);
            }
        }

        private void bufferSystemAudioModeRequest(HdmiCecMessage message) {
            if (!replaceMessageIfBuffered(message, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST)) {
                mBuffer.add(message);
            }
        }

        // Returns true if the message is replaced
        private boolean replaceMessageIfBuffered(HdmiCecMessage message, int opcode) {
            for (int i = 0; i < mBuffer.size(); i++) {
                HdmiCecMessage bufferedMessage = mBuffer.get(i);
                if (bufferedMessage.getOpcode() == opcode) {
                    mBuffer.set(i, message);
                    return true;
                }
            }
            return false;
        }
    }

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

    private final SelectRequestBuffer mSelectRequestBuffer = new SelectRequestBuffer();

@@ -988,6 +921,11 @@ public class HdmiControlService extends SystemService {
        mMessageValidator = messageValidator;
    }

    @VisibleForTesting
    void setCecMessageBuffer(CecMessageBuffer cecMessageBuffer) {
        this.mCecMessageBuffer = cecMessageBuffer;
    }

    /**
     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
     * for tasks that are running on main service thread.