17fa3a66470d2133796defd14a0600578758882acJinsuk Kim/*
27fa3a66470d2133796defd14a0600578758882acJinsuk Kim * Copyright (C) 2014 The Android Open Source Project
37fa3a66470d2133796defd14a0600578758882acJinsuk Kim *
47fa3a66470d2133796defd14a0600578758882acJinsuk Kim * Licensed under the Apache License, Version 2.0 (the "License");
57fa3a66470d2133796defd14a0600578758882acJinsuk Kim * you may not use this file except in compliance with the License.
67fa3a66470d2133796defd14a0600578758882acJinsuk Kim * You may obtain a copy of the License at
77fa3a66470d2133796defd14a0600578758882acJinsuk Kim *
87fa3a66470d2133796defd14a0600578758882acJinsuk Kim *      http://www.apache.org/licenses/LICENSE-2.0
97fa3a66470d2133796defd14a0600578758882acJinsuk Kim *
107fa3a66470d2133796defd14a0600578758882acJinsuk Kim * Unless required by applicable law or agreed to in writing, software
117fa3a66470d2133796defd14a0600578758882acJinsuk Kim * distributed under the License is distributed on an "AS IS" BASIS,
127fa3a66470d2133796defd14a0600578758882acJinsuk Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137fa3a66470d2133796defd14a0600578758882acJinsuk Kim * See the License for the specific language governing permissions and
147fa3a66470d2133796defd14a0600578758882acJinsuk Kim * limitations under the License.
157fa3a66470d2133796defd14a0600578758882acJinsuk Kim */
167fa3a66470d2133796defd14a0600578758882acJinsuk Kim
177fa3a66470d2133796defd14a0600578758882acJinsuk Kimpackage com.android.server.hdmi;
187fa3a66470d2133796defd14a0600578758882acJinsuk Kim
197fa3a66470d2133796defd14a0600578758882acJinsuk Kimimport android.hardware.hdmi.HdmiDeviceInfo;
207fa3a66470d2133796defd14a0600578758882acJinsuk Kimimport java.util.ArrayList;
217fa3a66470d2133796defd14a0600578758882acJinsuk Kimimport java.util.Iterator;
227fa3a66470d2133796defd14a0600578758882acJinsuk Kim
237fa3a66470d2133796defd14a0600578758882acJinsuk Kim/**
247fa3a66470d2133796defd14a0600578758882acJinsuk Kim * Buffer storage to keep incoming messages for later processing. Used to
257fa3a66470d2133796defd14a0600578758882acJinsuk Kim * handle messages that arrive when the device is not ready. Useful when
267fa3a66470d2133796defd14a0600578758882acJinsuk Kim * keeping the messages from a connected device which are not discovered yet.
277fa3a66470d2133796defd14a0600578758882acJinsuk Kim */
287fa3a66470d2133796defd14a0600578758882acJinsuk Kimfinal class DelayedMessageBuffer {
297fa3a66470d2133796defd14a0600578758882acJinsuk Kim    private final ArrayList<HdmiCecMessage> mBuffer = new ArrayList<>();
307fa3a66470d2133796defd14a0600578758882acJinsuk Kim    private final HdmiCecLocalDevice mDevice;
317fa3a66470d2133796defd14a0600578758882acJinsuk Kim
327fa3a66470d2133796defd14a0600578758882acJinsuk Kim    DelayedMessageBuffer(HdmiCecLocalDevice device) {
337fa3a66470d2133796defd14a0600578758882acJinsuk Kim        mDevice = device;
347fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
357fa3a66470d2133796defd14a0600578758882acJinsuk Kim
367fa3a66470d2133796defd14a0600578758882acJinsuk Kim    /**
377fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * Add a new message to the buffer. The buffer keeps selected messages in
387fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * the order they are received.
397fa3a66470d2133796defd14a0600578758882acJinsuk Kim     *
407fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * @param message {@link HdmiCecMessage} to add
417fa3a66470d2133796defd14a0600578758882acJinsuk Kim     */
427fa3a66470d2133796defd14a0600578758882acJinsuk Kim    void add(HdmiCecMessage message) {
437fa3a66470d2133796defd14a0600578758882acJinsuk Kim        boolean buffered = true;
447fa3a66470d2133796defd14a0600578758882acJinsuk Kim
457fa3a66470d2133796defd14a0600578758882acJinsuk Kim        // Note that all the messages are not handled in the same manner.
467fa3a66470d2133796defd14a0600578758882acJinsuk Kim        // For &lt;Active Source&gt; we keep the latest one only.
477fa3a66470d2133796defd14a0600578758882acJinsuk Kim        // TODO: This might not be the best way to choose the active source.
487fa3a66470d2133796defd14a0600578758882acJinsuk Kim        //       Devise a better way to pick up the best one.
497fa3a66470d2133796defd14a0600578758882acJinsuk Kim        switch (message.getOpcode()) {
507fa3a66470d2133796defd14a0600578758882acJinsuk Kim            case Constants.MESSAGE_ACTIVE_SOURCE:
517fa3a66470d2133796defd14a0600578758882acJinsuk Kim                removeActiveSource();
527fa3a66470d2133796defd14a0600578758882acJinsuk Kim                mBuffer.add(message);
537fa3a66470d2133796defd14a0600578758882acJinsuk Kim                break;
547fa3a66470d2133796defd14a0600578758882acJinsuk Kim            case Constants.MESSAGE_INITIATE_ARC:
55ad1e3d7df42376cd2a257b6c3b2fed540658a6e3Jinsuk Kim            case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
567fa3a66470d2133796defd14a0600578758882acJinsuk Kim                mBuffer.add(message);
577fa3a66470d2133796defd14a0600578758882acJinsuk Kim                break;
587fa3a66470d2133796defd14a0600578758882acJinsuk Kim            default:
597fa3a66470d2133796defd14a0600578758882acJinsuk Kim                buffered = false;
607fa3a66470d2133796defd14a0600578758882acJinsuk Kim                break;
617fa3a66470d2133796defd14a0600578758882acJinsuk Kim        }
627fa3a66470d2133796defd14a0600578758882acJinsuk Kim        if (buffered) {
637fa3a66470d2133796defd14a0600578758882acJinsuk Kim            HdmiLogger.debug("Buffering message:" + message);
647fa3a66470d2133796defd14a0600578758882acJinsuk Kim        }
657fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
667fa3a66470d2133796defd14a0600578758882acJinsuk Kim
677fa3a66470d2133796defd14a0600578758882acJinsuk Kim    private void removeActiveSource() {
687fa3a66470d2133796defd14a0600578758882acJinsuk Kim        // Uses iterator to remove elements while looping through the list.
697fa3a66470d2133796defd14a0600578758882acJinsuk Kim        for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) {
707fa3a66470d2133796defd14a0600578758882acJinsuk Kim            HdmiCecMessage message = iter.next();
717fa3a66470d2133796defd14a0600578758882acJinsuk Kim            if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) {
727fa3a66470d2133796defd14a0600578758882acJinsuk Kim                iter.remove();
737fa3a66470d2133796defd14a0600578758882acJinsuk Kim            }
747fa3a66470d2133796defd14a0600578758882acJinsuk Kim        }
757fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
767fa3a66470d2133796defd14a0600578758882acJinsuk Kim
772ab6d9fff36836c71bc0ee4afa25c11b48a9bd99Jinsuk Kim    boolean isBuffered(int opcode) {
782ab6d9fff36836c71bc0ee4afa25c11b48a9bd99Jinsuk Kim        for (HdmiCecMessage message : mBuffer) {
792ab6d9fff36836c71bc0ee4afa25c11b48a9bd99Jinsuk Kim            if (message.getOpcode() == opcode) {
802ab6d9fff36836c71bc0ee4afa25c11b48a9bd99Jinsuk Kim                return true;
812ab6d9fff36836c71bc0ee4afa25c11b48a9bd99Jinsuk Kim            }
822ab6d9fff36836c71bc0ee4afa25c11b48a9bd99Jinsuk Kim        }
832ab6d9fff36836c71bc0ee4afa25c11b48a9bd99Jinsuk Kim        return false;
842ab6d9fff36836c71bc0ee4afa25c11b48a9bd99Jinsuk Kim    }
852ab6d9fff36836c71bc0ee4afa25c11b48a9bd99Jinsuk Kim
867fa3a66470d2133796defd14a0600578758882acJinsuk Kim    void processAllMessages() {
87964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        // Use the copied buffer.
88964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
89964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        mBuffer.clear();
90964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        for (HdmiCecMessage message : copiedBuffer) {
917fa3a66470d2133796defd14a0600578758882acJinsuk Kim            mDevice.onMessage(message);
927fa3a66470d2133796defd14a0600578758882acJinsuk Kim            HdmiLogger.debug("Processing message:" + message);
937fa3a66470d2133796defd14a0600578758882acJinsuk Kim        }
947fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
957fa3a66470d2133796defd14a0600578758882acJinsuk Kim
967fa3a66470d2133796defd14a0600578758882acJinsuk Kim    /**
977fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * Process messages from a given logical device. Called by
987fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * {@link NewDeviceAction} actions when they finish adding the device
997fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * information.
1006e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim     * <p>&lt;Active Source&gt; is processed only when the TV input is ready.
1016e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim     * If not, {@link #processActiveSource()} will be invoked later to handle it.
1027fa3a66470d2133796defd14a0600578758882acJinsuk Kim     *
1037fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * @param address logical address of CEC device which the messages to process
1047fa3a66470d2133796defd14a0600578758882acJinsuk Kim     *        are associated with
1057fa3a66470d2133796defd14a0600578758882acJinsuk Kim     */
1067fa3a66470d2133796defd14a0600578758882acJinsuk Kim    void processMessagesForDevice(int address) {
107964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
108964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        mBuffer.clear();
1096e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim        HdmiLogger.debug("Checking message for address:" + address);
110964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        for (HdmiCecMessage message : copiedBuffer) {
111964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            if (message.getSource() != address) {
112964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                mBuffer.add(message);
113964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                continue;
114964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            }
1156e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim            if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
116964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    && !mDevice.isInputReady(HdmiDeviceInfo.idForCecDevice(address))) {
117964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                mBuffer.add(message);
118964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                continue;
119964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            }
1206e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim            mDevice.onMessage(message);
1216e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim            HdmiLogger.debug("Processing message:" + message);
1227fa3a66470d2133796defd14a0600578758882acJinsuk Kim        }
1237fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
1247fa3a66470d2133796defd14a0600578758882acJinsuk Kim
1257fa3a66470d2133796defd14a0600578758882acJinsuk Kim    /**
1267fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * Process &lt;Active Source&gt;.
1277fa3a66470d2133796defd14a0600578758882acJinsuk Kim     *
1287fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * <p>The message has a dependency on TV input framework. Should be invoked
1297fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * after we get the callback
1307fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * {@link android.media.tv.TvInputManager.TvInputCallback#onInputAdded(String)}
1317fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * to ensure the processing of the message takes effect when transformed
1327fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * to input change callback.
1337fa3a66470d2133796defd14a0600578758882acJinsuk Kim     *
1347fa3a66470d2133796defd14a0600578758882acJinsuk Kim     * @param address logical address of the device to be the active source
1357fa3a66470d2133796defd14a0600578758882acJinsuk Kim     */
1367fa3a66470d2133796defd14a0600578758882acJinsuk Kim    void processActiveSource(int address) {
137964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
138964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        mBuffer.clear();
139964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        for (HdmiCecMessage message : copiedBuffer) {
1407fa3a66470d2133796defd14a0600578758882acJinsuk Kim            if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
1417fa3a66470d2133796defd14a0600578758882acJinsuk Kim                    && message.getSource() == address) {
1427fa3a66470d2133796defd14a0600578758882acJinsuk Kim                mDevice.onMessage(message);
1437fa3a66470d2133796defd14a0600578758882acJinsuk Kim                HdmiLogger.debug("Processing message:" + message);
144964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            } else {
145964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                mBuffer.add(message);
1467fa3a66470d2133796defd14a0600578758882acJinsuk Kim            }
1477fa3a66470d2133796defd14a0600578758882acJinsuk Kim        }
1487fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
1497fa3a66470d2133796defd14a0600578758882acJinsuk Kim}
150