HdmiCecLocalDevice.java revision e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9
12918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim/*
22918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * Copyright (C) 2014 The Android Open Source Project
32918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim *
42918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * Licensed under the Apache License, Version 2.0 (the "License");
52918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * you may not use this file except in compliance with the License.
62918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * You may obtain a copy of the License at
72918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim *
82918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim *      http://www.apache.org/licenses/LICENSE-2.0
92918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim *
102918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * Unless required by applicable law or agreed to in writing, software
112918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * distributed under the License is distributed on an "AS IS" BASIS,
122918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * See the License for the specific language governing permissions and
142918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * limitations under the License.
152918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim */
162918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
172918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kimpackage com.android.server.hdmi;
182918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
1961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
203e1564ee397ef833cba351153029317786f3d6bbTerry Heoimport android.hardware.input.InputManager;
214fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jangimport android.os.Handler;
2279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport android.os.Looper;
234fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jangimport android.os.Message;
243e1564ee397ef833cba351153029317786f3d6bbTerry Heoimport android.os.SystemClock;
25092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jangimport android.util.Slog;
263e1564ee397ef833cba351153029317786f3d6bbTerry Heoimport android.view.InputDevice;
273e1564ee397ef833cba351153029317786f3d6bbTerry Heoimport android.view.KeyCharacterMap;
283e1564ee397ef833cba351153029317786f3d6bbTerry Heoimport android.view.KeyEvent;
292918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
3079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport com.android.internal.annotations.GuardedBy;
31a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
3279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
3379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport java.util.ArrayList;
3443c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kimimport java.util.Collections;
3579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport java.util.Iterator;
3679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport java.util.LinkedList;
3779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport java.util.List;
3879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
392918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim/**
402918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * Class that models a logical CEC device hosted in this system. Handles initialization,
412918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * CEC commands that call for actions customized per device type.
422918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim */
432918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kimabstract class HdmiCecLocalDevice {
44092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private static final String TAG = "HdmiCecLocalDevice";
452918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
464fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1;
473e1564ee397ef833cba351153029317786f3d6bbTerry Heo    private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2;
484fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    // Timeout in millisecond for device clean up (5s).
494fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    // Normal actions timeout is 2s but some of them would have several sequence of timeout.
504fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private static final int DEVICE_CLEANUP_TIMEOUT = 5000;
513e1564ee397ef833cba351153029317786f3d6bbTerry Heo    // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior.
523e1564ee397ef833cba351153029317786f3d6bbTerry Heo    // When it expires, we can assume <User Control Release> is received.
533e1564ee397ef833cba351153029317786f3d6bbTerry Heo    private static final int FOLLOWER_SAFETY_TIMEOUT = 550;
544fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    protected final HdmiControlService mService;
562918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    protected final int mDeviceType;
572918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    protected int mAddress;
582918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    protected int mPreferredAddress;
5961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    protected HdmiDeviceInfo mDeviceInfo;
603e1564ee397ef833cba351153029317786f3d6bbTerry Heo    protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
613e1564ee397ef833cba351153029317786f3d6bbTerry Heo    protected int mLastKeyRepeatCount = 0;
622918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
6372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    static class ActiveSource {
6472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        int logicalAddress;
6572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        int physicalAddress;
6672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim
6743c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim        public ActiveSource() {
6843c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim            invalidate();
6943c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim        }
7072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        public ActiveSource(int logical, int physical) {
7172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            logicalAddress = logical;
7272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            physicalAddress = physical;
7372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        }
7472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        public static ActiveSource of(int logical, int physical) {
7572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            return new ActiveSource(logical, physical);
7672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        }
7772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        public boolean isValid() {
7872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            return HdmiUtils.isValidAddress(logicalAddress);
7972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        }
8043c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim        public void invalidate() {
8143c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim            logicalAddress = Constants.ADDR_INVALID;
8243c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim            physicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
8343c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim        }
8472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        public boolean equals(int logical, int physical) {
8572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            return logicalAddress == logical && physicalAddress == physical;
8672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        }
8772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        @Override
8872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        public boolean equals(Object obj) {
8972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            if (obj instanceof ActiveSource) {
9072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                ActiveSource that = (ActiveSource) obj;
9172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                return that.logicalAddress == logicalAddress &&
9272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                       that.physicalAddress == physicalAddress;
9372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            }
9472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            return false;
9572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        }
9672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        @Override
9772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        public int hashCode() {
9872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            return logicalAddress * 29 + physicalAddress;
9972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        }
10072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    }
10179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Logical address of the active source.
10279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    @GuardedBy("mLock")
10343c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim    protected final ActiveSource mActiveSource = new ActiveSource();
10479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
10579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Active routing path. Physical address of the active source but not all the time, such as
10679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // when the new active source does not claim itself to be one. Note that we don't keep
10779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
10879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    @GuardedBy("mLock")
10979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private int mActiveRoutingPath;
11079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
11179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
11279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected final Object mLock;
11379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
11479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // A collection of FeatureAction.
11579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Note that access to this collection should happen in service thread.
116b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    private final LinkedList<HdmiCecFeatureAction> mActions = new LinkedList<>();
11779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
118c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo    private final Handler mHandler = new Handler () {
1194fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        @Override
1204fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        public void handleMessage(Message msg) {
1214fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            switch (msg.what) {
1224fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                case MSG_DISABLE_DEVICE_TIMEOUT:
1234fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    handleDisableDeviceTimeout();
1244fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    break;
1253e1564ee397ef833cba351153029317786f3d6bbTerry Heo                case MSG_USER_CONTROL_RELEASE_TIMEOUT:
1263e1564ee397ef833cba351153029317786f3d6bbTerry Heo                    handleUserControlReleased();
1273e1564ee397ef833cba351153029317786f3d6bbTerry Heo                    break;
1284fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
1294fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
1304fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    };
1314fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1324fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    /**
1334fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * A callback interface to get notified when all pending action is cleared.
1344fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * It can be called when timeout happened.
1354fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     */
1364fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    interface PendingActionClearedCallback {
1374fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        void onCleared(HdmiCecLocalDevice device);
1384fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
1394fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1404fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    protected PendingActionClearedCallback mPendingActionClearedCallback;
1414fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1423ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
1433ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        mService = service;
1442918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        mDeviceType = deviceType;
145c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mAddress = Constants.ADDR_UNREGISTERED;
14679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mLock = service.getServiceLock();
1472918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
1482918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
1492918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    // Factory method that returns HdmiCecLocalDevice of corresponding type.
1503ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    static HdmiCecLocalDevice create(HdmiControlService service, int deviceType) {
1512918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        switch (deviceType) {
15261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        case HdmiDeviceInfo.DEVICE_TV:
1533ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            return new HdmiCecLocalDeviceTv(service);
15461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        case HdmiDeviceInfo.DEVICE_PLAYBACK:
1553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            return new HdmiCecLocalDevicePlayback(service);
1562918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        default:
1572918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim            return null;
1582918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        }
1592918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
1602918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
161a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
1623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    void init() {
163a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
164af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim        mPreferredAddress = getPreferredAddress();
1653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
1662918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
1678b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang    /**
1683ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Called once a logical address of the local device is allocated.
1698b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     */
170fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    protected abstract void onAddressAllocated(int logicalAddress, int reason);
1718b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
172092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
173af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     * Get the preferred logical address from system properties.
174af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     */
175af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    protected abstract int getPreferredAddress();
176af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim
177af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    /**
178af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     * Set the preferred logical address to system properties.
179af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     */
180af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    protected abstract void setPreferredAddress(int addr);
181af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim
182af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    /**
183092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Dispatch incoming message.
184092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     *
185092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @param message incoming message
186092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @return true if consumed a message; otherwise, return false.
187092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
188a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
18925c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    boolean dispatchMessage(HdmiCecMessage message) {
19079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
191092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int dest = message.getDestination();
192c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
193092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            return false;
194092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
19579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        // Cache incoming message. Note that it caches only white-listed one.
19679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecMessageCache.cacheMessage(message);
197092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return onMessage(message);
198092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
199092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
200a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
20160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    protected final boolean onMessage(HdmiCecMessage message) {
20279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
20379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (dispatchMessageToAction(message)) {
20479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return true;
20579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
206092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        switch (message.getOpcode()) {
207c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_ACTIVE_SOURCE:
2088333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                return handleActiveSource(message);
209c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_INACTIVE_SOURCE:
2108333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                return handleInactiveSource(message);
211c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
2128333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                return handleRequestActiveSource(message);
213c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GET_MENU_LANGUAGE:
214092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGetMenuLanguage(message);
215c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS:
216092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGivePhysicalAddress();
217c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_OSD_NAME:
218092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGiveOsdName(message);
219c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
220092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGiveDeviceVendorId();
221c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GET_CEC_VERSION:
222092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGetCecVersion(message);
223c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
22460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                return handleReportPhysicalAddress(message);
225c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_ROUTING_CHANGE:
22692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                return handleRoutingChange(message);
22764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo            case Constants.MESSAGE_ROUTING_INFORMATION:
22864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo                return handleRoutingInformation(message);
229c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_INITIATE_ARC:
23079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleInitiateArc(message);
231c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_TERMINATE_ARC:
23279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleTerminateArc(message);
233c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
23479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleSetSystemAudioMode(message);
235c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
23679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleSystemAudioModeStatus(message);
237c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_REPORT_AUDIO_STATUS:
2388fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang                return handleReportAudioStatus(message);
239c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_STANDBY:
24038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleStandby(message);
241c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_TEXT_VIEW_ON:
24238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleTextViewOn(message);
243c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_IMAGE_VIEW_ON:
24438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleImageViewOn(message);
245c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_USER_CONTROL_PRESSED:
24638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleUserControlPressed(message);
2473e1564ee397ef833cba351153029317786f3d6bbTerry Heo            case Constants.MESSAGE_USER_CONTROL_RELEASED:
2483e1564ee397ef833cba351153029317786f3d6bbTerry Heo                return handleUserControlReleased();
249c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_SET_STREAM_PATH:
25038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleSetStreamPath(message);
251c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
25238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleGiveDevicePowerStatus(message);
2533e1564ee397ef833cba351153029317786f3d6bbTerry Heo            case Constants.MESSAGE_MENU_REQUEST:
2543e1564ee397ef833cba351153029317786f3d6bbTerry Heo                return handleGiveDeviceMenuStatus(message);
255119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            case Constants.MESSAGE_VENDOR_COMMAND:
256119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                return handleVendorCommand(message);
257119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID:
258119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                return handleVendorCommandWithId(message);
2598f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang            case Constants.MESSAGE_SET_OSD_NAME:
2608f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang                return handleSetOsdName(message);
261b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            case Constants.MESSAGE_RECORD_TV_SCREEN:
262b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                return handleRecordTvScreen(message);
263e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            case Constants.MESSAGE_TIMER_CLEARED_STATUS:
264e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                return handleTimerClearedStatus(message);
265092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            default:
266092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return false;
267092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
268092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
269092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
270a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
27179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private boolean dispatchMessageToAction(HdmiCecMessage message) {
272a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
273b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang        for (HdmiCecFeatureAction action : mActions) {
27479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (action.processCommand(message)) {
27579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return true;
27679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            }
27779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
27879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
27979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
28079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
281a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
282092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGivePhysicalAddress() {
28379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
28479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
285092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int physicalAddress = mService.getPhysicalAddress();
286092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
287092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                mAddress, physicalAddress, mDeviceType);
288092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        mService.sendCecCommand(cecMessage);
289092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
290092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
291092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
292a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
293092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGiveDeviceVendorId() {
29479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
295092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int vendorId = mService.getVendorId();
296092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
297092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                mAddress, vendorId);
298092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        mService.sendCecCommand(cecMessage);
299092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
300092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
301092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
302a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
303092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGetCecVersion(HdmiCecMessage message) {
30479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
305092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int version = mService.getCecVersion();
306092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
307092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                message.getSource(), version);
308092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        mService.sendCecCommand(cecMessage);
309092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
310092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
311092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
312a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
3138333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    protected boolean handleActiveSource(HdmiCecMessage message) {
3148333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        return false;
3158333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
3168333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
3178333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    @ServiceThreadOnly
3188333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    protected boolean handleInactiveSource(HdmiCecMessage message) {
3198333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        return false;
3208333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
3218333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
3228333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    @ServiceThreadOnly
3238333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    protected boolean handleRequestActiveSource(HdmiCecMessage message) {
3248333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        return false;
3258333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
3268333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
3278333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    @ServiceThreadOnly
328092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
32979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
330092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
3316aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        // 'return false' will cause to reply with <Feature Abort>.
3326aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        return false;
333092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
334092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
335a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
336092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGiveOsdName(HdmiCecMessage message) {
33779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
338092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        // Note that since this method is called after logical address allocation is done,
339092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        // mDeviceInfo should not be null.
340092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
341092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                mAddress, message.getSource(), mDeviceInfo.getDisplayName());
342092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        if (cecMessage != null) {
343092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            mService.sendCecCommand(cecMessage);
344092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        } else {
345092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            Slog.w(TAG, "Failed to build <Get Osd Name>:" + mDeviceInfo.getDisplayName());
346092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
347092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
348092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
349092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
35092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    protected boolean handleRoutingChange(HdmiCecMessage message) {
35192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        return false;
35292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
35392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
35464bafd9108b532102bc13c47966444b073c33fafYuncheol Heo    protected boolean handleRoutingInformation(HdmiCecMessage message) {
35564bafd9108b532102bc13c47966444b073c33fafYuncheol Heo        return false;
35664bafd9108b532102bc13c47966444b073c33fafYuncheol Heo    }
35764bafd9108b532102bc13c47966444b073c33fafYuncheol Heo
35860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
35960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
36060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
36160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
36279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
36379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
36479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
36579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
36679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
36779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
36879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
36979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
37079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleTerminateArc(HdmiCecMessage message) {
37179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
37279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
37379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
37479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleInitiateArc(HdmiCecMessage message) {
37579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
3768fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang    }
3778fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang
3788fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang    protected boolean handleReportAudioStatus(HdmiCecMessage message) {
3798fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang        return false;
38079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
38179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
382a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
38338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleStandby(HdmiCecMessage message) {
38438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
38538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // Seq #12
3864d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        if (mService.isControlEnabled() && !mService.isProhibitMode()
38738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                && mService.isPowerOnOrTransient()) {
38838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            mService.standby();
38938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return true;
39038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
39138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
39238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
39338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
39438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
39538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleUserControlPressed(HdmiCecMessage message) {
39638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
3973e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT);
39838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mService.isPowerOnOrTransient() && isPowerOffOrToggleCommand(message)) {
39938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            mService.standby();
40038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return true;
40138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else if (mService.isPowerStandbyOrTransient() && isPowerOnOrToggleCommand(message)) {
40238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            mService.wakeUp();
40338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return true;
40438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
4053e1564ee397ef833cba351153029317786f3d6bbTerry Heo
4063e1564ee397ef833cba351153029317786f3d6bbTerry Heo        final long downTime = SystemClock.uptimeMillis();
4073e1564ee397ef833cba351153029317786f3d6bbTerry Heo        final byte[] params = message.getParams();
4085691b2f2297b29dc83a7f83f77da517035b11cceJungshik Jang        // Note that we don't support parameterized keycode now.
4095691b2f2297b29dc83a7f83f77da517035b11cceJungshik Jang        // TODO: translate parameterized keycode as well.
4105691b2f2297b29dc83a7f83f77da517035b11cceJungshik Jang        final int keycode = HdmiCecKeycode.cecKeyToAndroidKey(params[0]);
4113e1564ee397ef833cba351153029317786f3d6bbTerry Heo        int keyRepeatCount = 0;
4123e1564ee397ef833cba351153029317786f3d6bbTerry Heo        if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
4133e1564ee397ef833cba351153029317786f3d6bbTerry Heo            if (keycode == mLastKeycode) {
4143e1564ee397ef833cba351153029317786f3d6bbTerry Heo                keyRepeatCount = mLastKeyRepeatCount + 1;
4153e1564ee397ef833cba351153029317786f3d6bbTerry Heo            } else {
4163e1564ee397ef833cba351153029317786f3d6bbTerry Heo                injectKeyEvent(downTime, KeyEvent.ACTION_UP, mLastKeycode, 0);
4173e1564ee397ef833cba351153029317786f3d6bbTerry Heo            }
4183e1564ee397ef833cba351153029317786f3d6bbTerry Heo        }
4193e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mLastKeycode = keycode;
4203e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mLastKeyRepeatCount = keyRepeatCount;
4213e1564ee397ef833cba351153029317786f3d6bbTerry Heo
4223e1564ee397ef833cba351153029317786f3d6bbTerry Heo        if (keycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
4233e1564ee397ef833cba351153029317786f3d6bbTerry Heo            injectKeyEvent(downTime, KeyEvent.ACTION_DOWN, keycode, keyRepeatCount);
4243e1564ee397ef833cba351153029317786f3d6bbTerry Heo            mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT),
4253e1564ee397ef833cba351153029317786f3d6bbTerry Heo                    FOLLOWER_SAFETY_TIMEOUT);
4263e1564ee397ef833cba351153029317786f3d6bbTerry Heo            return true;
4273e1564ee397ef833cba351153029317786f3d6bbTerry Heo        }
4283e1564ee397ef833cba351153029317786f3d6bbTerry Heo        return false;
4293e1564ee397ef833cba351153029317786f3d6bbTerry Heo    }
4303e1564ee397ef833cba351153029317786f3d6bbTerry Heo
4313e1564ee397ef833cba351153029317786f3d6bbTerry Heo    @ServiceThreadOnly
4323e1564ee397ef833cba351153029317786f3d6bbTerry Heo    protected boolean handleUserControlReleased() {
4333e1564ee397ef833cba351153029317786f3d6bbTerry Heo        assertRunOnServiceThread();
4343e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT);
4353e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mLastKeyRepeatCount = 0;
4363e1564ee397ef833cba351153029317786f3d6bbTerry Heo        if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
4373e1564ee397ef833cba351153029317786f3d6bbTerry Heo            final long upTime = SystemClock.uptimeMillis();
4383e1564ee397ef833cba351153029317786f3d6bbTerry Heo            injectKeyEvent(upTime, KeyEvent.ACTION_UP, mLastKeycode, 0);
4393e1564ee397ef833cba351153029317786f3d6bbTerry Heo            mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
4403e1564ee397ef833cba351153029317786f3d6bbTerry Heo            return true;
4413e1564ee397ef833cba351153029317786f3d6bbTerry Heo        }
44238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
44338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
44438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
4453e1564ee397ef833cba351153029317786f3d6bbTerry Heo    static void injectKeyEvent(long time, int action, int keycode, int repeat) {
4463e1564ee397ef833cba351153029317786f3d6bbTerry Heo        KeyEvent keyEvent = KeyEvent.obtain(time, time, action, keycode,
4473e1564ee397ef833cba351153029317786f3d6bbTerry Heo                repeat, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
4483e1564ee397ef833cba351153029317786f3d6bbTerry Heo                InputDevice.SOURCE_HDMI, null);
4493e1564ee397ef833cba351153029317786f3d6bbTerry Heo        InputManager.getInstance().injectInputEvent(keyEvent,
4503e1564ee397ef833cba351153029317786f3d6bbTerry Heo                InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
4513e1564ee397ef833cba351153029317786f3d6bbTerry Heo        keyEvent.recycle();
4523e1564ee397ef833cba351153029317786f3d6bbTerry Heo   }
4533e1564ee397ef833cba351153029317786f3d6bbTerry Heo
45425c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) {
45538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        byte[] params = message.getParams();
456c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
457210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
458210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION
459210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
46038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
46138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
46225c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
46338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        byte[] params = message.getParams();
464c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
465210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
466210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
467210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
46838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
46938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
47038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleTextViewOn(HdmiCecMessage message) {
47138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
47238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
47338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
47438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleImageViewOn(HdmiCecMessage message) {
47538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
47638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
47738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
47838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleSetStreamPath(HdmiCecMessage message) {
47938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
48038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
48138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
48238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleGiveDevicePowerStatus(HdmiCecMessage message) {
48338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPowerStatus(
48438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                mAddress, message.getSource(), mService.getPowerStatus()));
48538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return true;
48638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
48738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
4883e1564ee397ef833cba351153029317786f3d6bbTerry Heo    protected boolean handleGiveDeviceMenuStatus(HdmiCecMessage message) {
4893e1564ee397ef833cba351153029317786f3d6bbTerry Heo        // Always report menu active to receive Remote Control.
4903e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus(
4913e1564ee397ef833cba351153029317786f3d6bbTerry Heo                mAddress, message.getSource(), Constants.MENU_STATE_ACTIVATED));
4923e1564ee397ef833cba351153029317786f3d6bbTerry Heo        return true;
4933e1564ee397ef833cba351153029317786f3d6bbTerry Heo    }
4943e1564ee397ef833cba351153029317786f3d6bbTerry Heo
495119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    protected boolean handleVendorCommand(HdmiCecMessage message) {
496119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        mService.invokeVendorCommandListeners(mDeviceType, message.getSource(),
497119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                message.getParams(), false);
498119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        return true;
499119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
500119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
501119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    protected boolean handleVendorCommandWithId(HdmiCecMessage message) {
502119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        byte[] params = message.getParams();
503119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        int vendorId = HdmiUtils.threeBytesToInt(params);
504119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        if (vendorId == mService.getVendorId()) {
505119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), params, true);
506119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } else if (message.getDestination() != Constants.ADDR_BROADCAST &&
507119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                message.getSource() != Constants.ADDR_UNREGISTERED) {
508119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
5096aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo            mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
510119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } else {
511119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
512119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
513119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        return true;
514119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
515119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
5168f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang    protected boolean handleSetOsdName(HdmiCecMessage message) {
5178f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang        // The default behavior of <Set Osd Name> is doing nothing.
5188f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang        return true;
5198f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang    }
5208f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang
521b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    protected boolean handleRecordTvScreen(HdmiCecMessage message) {
52225c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        // The default behavior of <Record TV Screen> is replying <Feature Abort> with
52325c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        // "Cannot provide source".
5246aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        mService.maySendFeatureAbortCommand(message, Constants.ABORT_CANNOT_PROVIDE_SOURCE);
525b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        return true;
526b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
527b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
528e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    protected boolean handleTimerClearedStatus(HdmiCecMessage message) {
529e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        return false;
530e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    }
531e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang
53238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
533fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    final void handleAddressAllocated(int logicalAddress, int reason) {
53479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
5353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        mAddress = mPreferredAddress = logicalAddress;
536fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        onAddressAllocated(logicalAddress, reason);
537af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim        setPreferredAddress(logicalAddress);
5381a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    }
5391a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang
540a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
54161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    HdmiDeviceInfo getDeviceInfo() {
54279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
5431a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang        return mDeviceInfo;
5441a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    }
5451a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang
546a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
54761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void setDeviceInfo(HdmiDeviceInfo info) {
54879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
5491a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang        mDeviceInfo = info;
5501a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    }
5511a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang
5522918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    // Returns true if the logical address is same as the argument.
553a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
5542918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    boolean isAddressOf(int addr) {
55579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
5562918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        return addr == mAddress;
5572918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
5582918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
5592918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    // Resets the logical address to unregistered(15), meaning the logical device is invalid.
560a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
5612918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    void clearAddress() {
56279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
563c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mAddress = Constants.ADDR_UNREGISTERED;
5642918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
5652918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
566a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
567b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    void addAndStartAction(final HdmiCecFeatureAction action) {
56879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
56938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mService.isPowerStandbyOrTransient()) {
57038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.w(TAG, "Skip the action during Standby: " + action);
57138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
57238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
57379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mActions.add(action);
57479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        action.start();
57579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
57679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
57779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // See if we have an action of a given type in progress.
578a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
579b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    <T extends HdmiCecFeatureAction> boolean hasAction(final Class<T> clazz) {
58079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
581b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang        for (HdmiCecFeatureAction action : mActions) {
58279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (action.getClass().equals(clazz)) {
58379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return true;
58479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            }
58579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
58679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
58779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
58879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
58979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Returns all actions matched with given class type.
590a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
591b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    <T extends HdmiCecFeatureAction> List<T> getActions(final Class<T> clazz) {
59279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
59343c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim        List<T> actions = Collections.<T>emptyList();
594b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang        for (HdmiCecFeatureAction action : mActions) {
59579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (action.getClass().equals(clazz)) {
59643c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim                if (actions.isEmpty()) {
59743c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim                    actions = new ArrayList<T>();
59843c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim                }
59979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                actions.add((T) action);
60079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            }
60179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
60279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return actions;
60379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
60479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
60579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    /**
606b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang     * Remove the given {@link HdmiCecFeatureAction} object from the action queue.
60779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     *
608b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang     * @param action {@link HdmiCecFeatureAction} to remove
60979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     */
610a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
611b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    void removeAction(final HdmiCecFeatureAction action) {
61279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
613c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo        action.finish(false);
61479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mActions.remove(action);
61538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        checkIfPendingActionsCleared();
61679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
61779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
61879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Remove all actions matched with the given Class type.
619a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
620b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    <T extends HdmiCecFeatureAction> void removeAction(final Class<T> clazz) {
621a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
62279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        removeActionExcept(clazz, null);
62379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
62479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
62579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Remove all actions matched with the given Class type besides |exception|.
626a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
627b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz,
628b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang            final HdmiCecFeatureAction exception) {
62979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
630b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang        Iterator<HdmiCecFeatureAction> iter = mActions.iterator();
63179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        while (iter.hasNext()) {
632b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang            HdmiCecFeatureAction action = iter.next();
63379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (action != exception && action.getClass().equals(clazz)) {
634c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo                action.finish(false);
635c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo                iter.remove();
63679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            }
63779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
63838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        checkIfPendingActionsCleared();
63979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
64079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
64138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected void checkIfPendingActionsCleared() {
6424fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mActions.isEmpty() && mPendingActionClearedCallback != null) {
64326ba7fddcaeb052710ca8672889830dabcbfd3acYuncheol Heo            PendingActionClearedCallback callback = mPendingActionClearedCallback;
64426ba7fddcaeb052710ca8672889830dabcbfd3acYuncheol Heo            // To prevent from calling the callback again during handling the callback itself.
64526ba7fddcaeb052710ca8672889830dabcbfd3acYuncheol Heo            mPendingActionClearedCallback = null;
64626ba7fddcaeb052710ca8672889830dabcbfd3acYuncheol Heo            callback.onCleared(this);
64738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
64838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
6494fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
65079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected void assertRunOnServiceThread() {
65179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (Looper.myLooper() != mService.getServiceLooper()) {
65279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            throw new IllegalStateException("Should run on service thread.");
65379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
65479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
65579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
65679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    /**
65779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * Called when a hot-plug event issued.
65879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     *
65979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * @param portId id of port where a hot-plug event happened
66079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * @param connected whether to connected or not on the event
66179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     */
66279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void onHotplug(int portId, boolean connected) {
66379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
66479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
66579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    final HdmiControlService getService() {
66679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mService;
66779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
66879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
669a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
67079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    final boolean isConnectedToArcPort(int path) {
671a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
67279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mService.isConnectedToArcPort(path);
67379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
67479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
67572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    ActiveSource getActiveSource() {
67679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        synchronized (mLock) {
67779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return mActiveSource;
67879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
67979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
68079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
68172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    void setActiveSource(ActiveSource newActive) {
68272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        setActiveSource(newActive.logicalAddress, newActive.physicalAddress);
68372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    }
68472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim
68561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void setActiveSource(HdmiDeviceInfo info) {
68672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress());
68772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    }
68872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim
68972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    void setActiveSource(int logicalAddress, int physicalAddress) {
6908333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        synchronized (mLock) {
69172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            mActiveSource.logicalAddress = logicalAddress;
69272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            mActiveSource.physicalAddress = physicalAddress;
6938333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        }
694e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        mService.setLastInputForMhl(Constants.INVALID_PORT_ID);
6958333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
6968333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
69779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    int getActivePath() {
69879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        synchronized (mLock) {
69979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return mActiveRoutingPath;
70079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
70179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
70279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
7038333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    void setActivePath(int path) {
7048333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        synchronized (mLock) {
7058333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim            mActiveRoutingPath = path;
7068333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        }
707867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        mService.setActivePortId(pathToPortId(path));
7088333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
7098333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
71079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    /**
711a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * Returns the ID of the active HDMI port. The active port is the one that has the active
712a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * routing path connected to it directly or indirectly under the device hierarchy.
71379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     */
714a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    int getActivePortId() {
71579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        synchronized (mLock) {
71679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return mService.pathToPortId(mActiveRoutingPath);
71779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
71879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
71979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
720a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    /**
721a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * Update the active port.
722a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     *
723a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * @param portId the new active port id
724a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     */
725a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    void setActivePortId(int portId) {
726867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        // We update active routing path instead, since we get the active port id from
727867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        // the active routing path.
728867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        setActivePath(mService.portIdToPath(portId));
729a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    }
730a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
731a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
73279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    HdmiCecMessageCache getCecMessageCache() {
73379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
73479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mCecMessageCache;
73579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
73679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
737a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
73879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    int pathToPortId(int newPath) {
73979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
74079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mService.pathToPortId(newPath);
74179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
74238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
74338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    /**
7444fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * Called when the system goes to standby mode.
74538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     *
74638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     * @param initiatedByCec true if this power sequence is initiated
747b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang     *        by the reception the CEC messages like &lt;Standby&gt;
74838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     */
7494fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    protected void onStandby(boolean initiatedByCec) {}
75038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
75138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    /**
7524fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * Disable device. {@code callback} is used to get notified when all pending
7534fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * actions are completed or timeout is issued.
75438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     *
7554fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * @param initiatedByCec true if this sequence is initiated
756b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang     *        by the reception the CEC messages like &lt;Standby&gt;
757b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang     * @param origialCallback callback interface to get notified when all pending actions are
758b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang     *        cleared
75938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     */
760b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang    protected void disableDevice(boolean initiatedByCec,
761b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang            final PendingActionClearedCallback origialCallback) {
762b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang        mPendingActionClearedCallback = new PendingActionClearedCallback() {
763b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang            @Override
764b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
765b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang                mHandler.removeMessages(MSG_DISABLE_DEVICE_TIMEOUT);
766b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang                origialCallback.onCleared(device);
767b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang            }
768b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang        };
7694fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_DISABLE_DEVICE_TIMEOUT),
7704fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                DEVICE_CLEANUP_TIMEOUT);
7714fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
7724fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
7734fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
7744fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void handleDisableDeviceTimeout() {
7754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
7764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
7774fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        // If all actions are not cleared in DEVICE_CLEANUP_TIMEOUT, enforce to finish them.
7784fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        // onCleard will be called at the last action's finish method.
779b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang        Iterator<HdmiCecFeatureAction> iter = mActions.iterator();
7804fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        while (iter.hasNext()) {
781b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang            HdmiCecFeatureAction action = iter.next();
782c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo            action.finish(false);
7834fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            iter.remove();
7844fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
7854fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
786c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim
787c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim    /**
788c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     * Send a key event to other device.
789c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     *
7904fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * @param keyCode key code defined in {@link android.view.KeyEvent}
791c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     * @param isPressed {@code true} for key down event
792c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     */
793c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim    protected void sendKeyEvent(int keyCode, boolean isPressed) {
794c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        Slog.w(TAG, "sendKeyEvent not implemented");
795c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim    }
7962918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim}
797