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 android.util.Slog;
     21 
     22 import java.util.ArrayList;
     23 import java.util.Iterator;
     24 
     25 /**
     26  * Buffer storage to keep incoming messages for later processing. Used to
     27  * handle messages that arrive when the device is not ready. Useful when
     28  * keeping the messages from a connected device which are not discovered yet.
     29  */
     30 final class DelayedMessageBuffer {
     31     private final ArrayList<HdmiCecMessage> mBuffer = new ArrayList<>();
     32     private final HdmiCecLocalDevice mDevice;
     33 
     34     DelayedMessageBuffer(HdmiCecLocalDevice device) {
     35         mDevice = device;
     36     }
     37 
     38     /**
     39      * Add a new message to the buffer. The buffer keeps selected messages in
     40      * the order they are received.
     41      *
     42      * @param message {@link HdmiCecMessage} to add
     43      */
     44     void add(HdmiCecMessage message) {
     45         boolean buffered = true;
     46 
     47         // Note that all the messages are not handled in the same manner.
     48         // For &lt;Active Source&gt; we keep the latest one only.
     49         // TODO: This might not be the best way to choose the active source.
     50         //       Devise a better way to pick up the best one.
     51         switch (message.getOpcode()) {
     52             case Constants.MESSAGE_ACTIVE_SOURCE:
     53                 removeActiveSource();
     54                 mBuffer.add(message);
     55                 break;
     56             case Constants.MESSAGE_INITIATE_ARC:
     57             case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
     58                 mBuffer.add(message);
     59                 break;
     60             default:
     61                 buffered = false;
     62                 break;
     63         }
     64         if (buffered) {
     65             HdmiLogger.debug("Buffering message:" + message);
     66         }
     67     }
     68 
     69     private void removeActiveSource() {
     70         // Uses iterator to remove elements while looping through the list.
     71         for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) {
     72             HdmiCecMessage message = iter.next();
     73             if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) {
     74                 iter.remove();
     75             }
     76         }
     77     }
     78 
     79     boolean isBuffered(int opcode) {
     80         for (HdmiCecMessage message : mBuffer) {
     81             if (message.getOpcode() == opcode) {
     82                 return true;
     83             }
     84         }
     85         return false;
     86     }
     87 
     88     void processAllMessages() {
     89         // Use the copied buffer.
     90         ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
     91         mBuffer.clear();
     92         for (HdmiCecMessage message : copiedBuffer) {
     93             mDevice.onMessage(message);
     94             HdmiLogger.debug("Processing message:" + message);
     95         }
     96     }
     97 
     98     /**
     99      * Process messages from a given logical device. Called by
    100      * {@link NewDeviceAction} actions when they finish adding the device
    101      * information.
    102      * <p>&lt;Active Source&gt; is processed only when the TV input is ready.
    103      * If not, {@link #processActiveSource()} will be invoked later to handle it.
    104      *
    105      * @param address logical address of CEC device which the messages to process
    106      *        are associated with
    107      */
    108     void processMessagesForDevice(int address) {
    109         ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
    110         mBuffer.clear();
    111         HdmiLogger.debug("Checking message for address:" + address);
    112         for (HdmiCecMessage message : copiedBuffer) {
    113             if (message.getSource() != address) {
    114                 mBuffer.add(message);
    115                 continue;
    116             }
    117             if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
    118                     && !mDevice.isInputReady(HdmiDeviceInfo.idForCecDevice(address))) {
    119                 mBuffer.add(message);
    120                 continue;
    121             }
    122             mDevice.onMessage(message);
    123             HdmiLogger.debug("Processing message:" + message);
    124         }
    125     }
    126 
    127     /**
    128      * Process &lt;Active Source&gt;.
    129      *
    130      * <p>The message has a dependency on TV input framework. Should be invoked
    131      * after we get the callback
    132      * {@link android.media.tv.TvInputManager.TvInputCallback#onInputAdded(String)}
    133      * to ensure the processing of the message takes effect when transformed
    134      * to input change callback.
    135      *
    136      * @param address logical address of the device to be the active source
    137      */
    138     void processActiveSource(int address) {
    139         ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
    140         mBuffer.clear();
    141         for (HdmiCecMessage message : copiedBuffer) {
    142             if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
    143                     && message.getSource() == address) {
    144                 mDevice.onMessage(message);
    145                 HdmiLogger.debug("Processing message:" + message);
    146             } else {
    147                 mBuffer.add(message);
    148             }
    149         }
    150     }
    151 }
    152