Home | History | Annotate | Download | only in hdmi
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.hdmi;
     18 
     19 import android.hardware.hdmi.HdmiDeviceInfo;
     20 import java.util.ArrayList;
     21 import java.util.Iterator;
     22 
     23 /**
     24  * Buffer storage to keep incoming messages for later processing. Used to
     25  * handle messages that arrive when the device is not ready. Useful when
     26  * keeping the messages from a connected device which are not discovered yet.
     27  */
     28 final class DelayedMessageBuffer {
     29     private final ArrayList<HdmiCecMessage> mBuffer = new ArrayList<>();
     30     private final HdmiCecLocalDevice mDevice;
     31 
     32     DelayedMessageBuffer(HdmiCecLocalDevice device) {
     33         mDevice = device;
     34     }
     35 
     36     /**
     37      * Add a new message to the buffer. The buffer keeps selected messages in
     38      * the order they are received.
     39      *
     40      * @param message {@link HdmiCecMessage} to add
     41      */
     42     void add(HdmiCecMessage message) {
     43         boolean buffered = true;
     44 
     45         // Note that all the messages are not handled in the same manner.
     46         // For &lt;Active Source&gt; we keep the latest one only.
     47         // TODO: This might not be the best way to choose the active source.
     48         //       Devise a better way to pick up the best one.
     49         switch (message.getOpcode()) {
     50             case Constants.MESSAGE_ACTIVE_SOURCE:
     51                 removeActiveSource();
     52                 mBuffer.add(message);
     53                 break;
     54             case Constants.MESSAGE_INITIATE_ARC:
     55             case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
     56                 mBuffer.add(message);
     57                 break;
     58             default:
     59                 buffered = false;
     60                 break;
     61         }
     62         if (buffered) {
     63             HdmiLogger.debug("Buffering message:" + message);
     64         }
     65     }
     66 
     67     private void removeActiveSource() {
     68         // Uses iterator to remove elements while looping through the list.
     69         for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) {
     70             HdmiCecMessage message = iter.next();
     71             if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) {
     72                 iter.remove();
     73             }
     74         }
     75     }
     76 
     77     boolean isBuffered(int opcode) {
     78         for (HdmiCecMessage message : mBuffer) {
     79             if (message.getOpcode() == opcode) {
     80                 return true;
     81             }
     82         }
     83         return false;
     84     }
     85 
     86     void processAllMessages() {
     87         // Use the copied buffer.
     88         ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
     89         mBuffer.clear();
     90         for (HdmiCecMessage message : copiedBuffer) {
     91             mDevice.onMessage(message);
     92             HdmiLogger.debug("Processing message:" + message);
     93         }
     94     }
     95 
     96     /**
     97      * Process messages from a given logical device. Called by
     98      * {@link NewDeviceAction} actions when they finish adding the device
     99      * information.
    100      * <p>&lt;Active Source&gt; is processed only when the TV input is ready.
    101      * If not, {@link #processActiveSource()} will be invoked later to handle it.
    102      *
    103      * @param address logical address of CEC device which the messages to process
    104      *        are associated with
    105      */
    106     void processMessagesForDevice(int address) {
    107         ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
    108         mBuffer.clear();
    109         HdmiLogger.debug("Checking message for address:" + address);
    110         for (HdmiCecMessage message : copiedBuffer) {
    111             if (message.getSource() != address) {
    112                 mBuffer.add(message);
    113                 continue;
    114             }
    115             if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
    116                     && !mDevice.isInputReady(HdmiDeviceInfo.idForCecDevice(address))) {
    117                 mBuffer.add(message);
    118                 continue;
    119             }
    120             mDevice.onMessage(message);
    121             HdmiLogger.debug("Processing message:" + message);
    122         }
    123     }
    124 
    125     /**
    126      * Process &lt;Active Source&gt;.
    127      *
    128      * <p>The message has a dependency on TV input framework. Should be invoked
    129      * after we get the callback
    130      * {@link android.media.tv.TvInputManager.TvInputCallback#onInputAdded(String)}
    131      * to ensure the processing of the message takes effect when transformed
    132      * to input change callback.
    133      *
    134      * @param address logical address of the device to be the active source
    135      */
    136     void processActiveSource(int address) {
    137         ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
    138         mBuffer.clear();
    139         for (HdmiCecMessage message : copiedBuffer) {
    140             if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
    141                     && message.getSource() == address) {
    142                 mDevice.onMessage(message);
    143                 HdmiLogger.debug("Processing message:" + message);
    144             } else {
    145                 mBuffer.add(message);
    146             }
    147         }
    148     }
    149 }
    150