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;
31959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport com.android.internal.util.IndentingPrintWriter;
32a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
3379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
3479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport java.util.ArrayList;
3543c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kimimport java.util.Collections;
3679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport java.util.Iterator;
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        }
7404f813c9b3d1d15bd9462a728ab983f21d937281Jinsuk Kim        public static ActiveSource of(ActiveSource source) {
7504f813c9b3d1d15bd9462a728ab983f21d937281Jinsuk Kim            return new ActiveSource(source.logicalAddress, source.physicalAddress);
7604f813c9b3d1d15bd9462a728ab983f21d937281Jinsuk Kim        }
7772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        public static ActiveSource of(int logical, int physical) {
7872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            return new ActiveSource(logical, physical);
7972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        }
8072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        public boolean isValid() {
8172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            return HdmiUtils.isValidAddress(logicalAddress);
8272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        }
8343c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim        public void invalidate() {
8443c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim            logicalAddress = Constants.ADDR_INVALID;
8543c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim            physicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
8643c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim        }
8772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        public boolean equals(int logical, int physical) {
8872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            return logicalAddress == logical && physicalAddress == physical;
8972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        }
9072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        @Override
9172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        public boolean equals(Object obj) {
9272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            if (obj instanceof ActiveSource) {
9372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                ActiveSource that = (ActiveSource) obj;
9472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                return that.logicalAddress == logicalAddress &&
9572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                       that.physicalAddress == physicalAddress;
9672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            }
9772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            return false;
9872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        }
9972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        @Override
10072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        public int hashCode() {
10172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            return logicalAddress * 29 + physicalAddress;
10272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        }
103959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        @Override
104959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        public String toString() {
105959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            StringBuffer s = new StringBuffer();
106959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            String logicalAddressString = (logicalAddress == Constants.ADDR_INVALID)
107959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                    ? "invalid" : String.format("0x%02x", logicalAddress);
10804f813c9b3d1d15bd9462a728ab983f21d937281Jinsuk Kim            s.append("(").append(logicalAddressString);
109959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            String physicalAddressString = (physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS)
110959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                    ? "invalid" : String.format("0x%04x", physicalAddress);
11104f813c9b3d1d15bd9462a728ab983f21d937281Jinsuk Kim            s.append(", ").append(physicalAddressString).append(")");
112959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            return s.toString();
113959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        }
11472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    }
11579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Logical address of the active source.
11679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    @GuardedBy("mLock")
11743c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim    protected final ActiveSource mActiveSource = new ActiveSource();
11879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
11979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Active routing path. Physical address of the active source but not all the time, such as
12079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // when the new active source does not claim itself to be one. Note that we don't keep
12179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
12279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    @GuardedBy("mLock")
12379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private int mActiveRoutingPath;
12479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
12579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
12679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected final Object mLock;
12779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
12879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // A collection of FeatureAction.
12979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Note that access to this collection should happen in service thread.
1305352081c662299b618335bf3024058fa04ef2dfdJungshik Jang    private final ArrayList<HdmiCecFeatureAction> mActions = new ArrayList<>();
13179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
132c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo    private final Handler mHandler = new Handler () {
1334fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        @Override
1344fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        public void handleMessage(Message msg) {
1354fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            switch (msg.what) {
1364fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                case MSG_DISABLE_DEVICE_TIMEOUT:
1374fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    handleDisableDeviceTimeout();
1384fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    break;
1393e1564ee397ef833cba351153029317786f3d6bbTerry Heo                case MSG_USER_CONTROL_RELEASE_TIMEOUT:
1403e1564ee397ef833cba351153029317786f3d6bbTerry Heo                    handleUserControlReleased();
1413e1564ee397ef833cba351153029317786f3d6bbTerry Heo                    break;
1424fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
1434fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
1444fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    };
1454fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1464fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    /**
1474fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * A callback interface to get notified when all pending action is cleared.
1484fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * It can be called when timeout happened.
1494fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     */
1504fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    interface PendingActionClearedCallback {
1514fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        void onCleared(HdmiCecLocalDevice device);
1524fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
1534fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1544fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    protected PendingActionClearedCallback mPendingActionClearedCallback;
1554fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
1573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        mService = service;
1582918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        mDeviceType = deviceType;
159c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mAddress = Constants.ADDR_UNREGISTERED;
16079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mLock = service.getServiceLock();
1612918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
1622918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
1632918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    // Factory method that returns HdmiCecLocalDevice of corresponding type.
1643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    static HdmiCecLocalDevice create(HdmiControlService service, int deviceType) {
1652918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        switch (deviceType) {
16661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        case HdmiDeviceInfo.DEVICE_TV:
1673ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            return new HdmiCecLocalDeviceTv(service);
16861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        case HdmiDeviceInfo.DEVICE_PLAYBACK:
1693ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            return new HdmiCecLocalDevicePlayback(service);
1702918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        default:
1712918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim            return null;
1722918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        }
1732918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
1742918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
175a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
1763ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    void init() {
177a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
178af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim        mPreferredAddress = getPreferredAddress();
1797e4b480a0bf9f93a428f7c46bfea77ebfdb92d40Jinsuk Kim        mPendingActionClearedCallback = null;
1803ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
1812918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
1828b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang    /**
1833ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Called once a logical address of the local device is allocated.
1848b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     */
185fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    protected abstract void onAddressAllocated(int logicalAddress, int reason);
1868b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
187092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
188af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     * Get the preferred logical address from system properties.
189af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     */
190af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    protected abstract int getPreferredAddress();
191af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim
192af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    /**
193af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     * Set the preferred logical address to system properties.
194af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     */
195af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    protected abstract void setPreferredAddress(int addr);
196af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim
197af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    /**
1986e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim     * Returns true if the TV input associated with the CEC device is ready
1996e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim     * to accept further processing such as input switching. This is used
2006e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim     * to buffer certain CEC commands and process it later if the input is not
2016e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim     * ready yet. For other types of local devices(non-TV), this method returns
2026e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim     * true by default to let the commands be processed right away.
2036e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim     */
2046e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim    protected boolean isInputReady(int deviceId) {
2056e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim        return true;
2066e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim    }
2076e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim
2086e26f7f7b09dfd8495ec5478a7a4713dab346bc1Jinsuk Kim    /**
209e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim     * Returns true if the local device allows the system to be put to standby.
210e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim     * The default implementation returns true.
211e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim     */
212e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim    protected boolean canGoToStandby() {
213e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim        return true;
214e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim    }
215e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim
216e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim    /**
217092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Dispatch incoming message.
218092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     *
219092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @param message incoming message
220092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @return true if consumed a message; otherwise, return false.
221092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
222a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
22325c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    boolean dispatchMessage(HdmiCecMessage message) {
22479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
225092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int dest = message.getDestination();
226c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
227092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            return false;
228092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
22979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        // Cache incoming message. Note that it caches only white-listed one.
23079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecMessageCache.cacheMessage(message);
231092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return onMessage(message);
232092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
233092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
234a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
23560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    protected final boolean onMessage(HdmiCecMessage message) {
23679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
23779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (dispatchMessageToAction(message)) {
23879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return true;
23979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
240092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        switch (message.getOpcode()) {
241c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_ACTIVE_SOURCE:
2428333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                return handleActiveSource(message);
243c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_INACTIVE_SOURCE:
2448333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                return handleInactiveSource(message);
245c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
2468333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                return handleRequestActiveSource(message);
247c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GET_MENU_LANGUAGE:
248092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGetMenuLanguage(message);
249795415b57b7271a11d16ca42703251a325df1097Terry Heo            case Constants.MESSAGE_SET_MENU_LANGUAGE:
250795415b57b7271a11d16ca42703251a325df1097Terry Heo                return handleSetMenuLanguage(message);
251c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS:
252092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGivePhysicalAddress();
253c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_OSD_NAME:
254092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGiveOsdName(message);
255c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
256092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGiveDeviceVendorId();
257c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GET_CEC_VERSION:
258092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGetCecVersion(message);
259c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
26060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                return handleReportPhysicalAddress(message);
261c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_ROUTING_CHANGE:
26292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                return handleRoutingChange(message);
26364bafd9108b532102bc13c47966444b073c33fafYuncheol Heo            case Constants.MESSAGE_ROUTING_INFORMATION:
26464bafd9108b532102bc13c47966444b073c33fafYuncheol Heo                return handleRoutingInformation(message);
265c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_INITIATE_ARC:
26679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleInitiateArc(message);
267c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_TERMINATE_ARC:
26879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleTerminateArc(message);
269c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
27079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleSetSystemAudioMode(message);
271c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
27279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleSystemAudioModeStatus(message);
273c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_REPORT_AUDIO_STATUS:
2748fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang                return handleReportAudioStatus(message);
275c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_STANDBY:
27638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleStandby(message);
277c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_TEXT_VIEW_ON:
27838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleTextViewOn(message);
279c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_IMAGE_VIEW_ON:
28038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleImageViewOn(message);
281c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_USER_CONTROL_PRESSED:
28238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleUserControlPressed(message);
2833e1564ee397ef833cba351153029317786f3d6bbTerry Heo            case Constants.MESSAGE_USER_CONTROL_RELEASED:
2843e1564ee397ef833cba351153029317786f3d6bbTerry Heo                return handleUserControlReleased();
285c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_SET_STREAM_PATH:
28638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleSetStreamPath(message);
287c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
28838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleGiveDevicePowerStatus(message);
2893e1564ee397ef833cba351153029317786f3d6bbTerry Heo            case Constants.MESSAGE_MENU_REQUEST:
290184b124ec22a796327642e3546d366179e693f07Yuncheol Heo                return handleMenuRequest(message);
291184b124ec22a796327642e3546d366179e693f07Yuncheol Heo            case Constants.MESSAGE_MENU_STATUS:
292184b124ec22a796327642e3546d366179e693f07Yuncheol Heo                return handleMenuStatus(message);
293119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            case Constants.MESSAGE_VENDOR_COMMAND:
294119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                return handleVendorCommand(message);
295119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID:
296119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                return handleVendorCommandWithId(message);
2978f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang            case Constants.MESSAGE_SET_OSD_NAME:
2988f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang                return handleSetOsdName(message);
299b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            case Constants.MESSAGE_RECORD_TV_SCREEN:
300b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                return handleRecordTvScreen(message);
301e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            case Constants.MESSAGE_TIMER_CLEARED_STATUS:
302e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                return handleTimerClearedStatus(message);
3034480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang            case Constants.MESSAGE_REPORT_POWER_STATUS:
3044480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang                return handleReportPowerStatus(message);
3054480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang            case Constants.MESSAGE_TIMER_STATUS:
3064480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang                return handleTimerStatus(message);
3074480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang            case Constants.MESSAGE_RECORD_STATUS:
3084480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang                return handleRecordStatus(message);
309092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            default:
310092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return false;
311092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
312092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
313092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
314a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
31579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private boolean dispatchMessageToAction(HdmiCecMessage message) {
316a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
3175352081c662299b618335bf3024058fa04ef2dfdJungshik Jang        boolean processed = false;
3185352081c662299b618335bf3024058fa04ef2dfdJungshik Jang        // Use copied action list in that processCommand may remove itself.
3195352081c662299b618335bf3024058fa04ef2dfdJungshik Jang        for (HdmiCecFeatureAction action : new ArrayList<>(mActions)) {
3205352081c662299b618335bf3024058fa04ef2dfdJungshik Jang            // Iterates all actions to check whether incoming message is consumed.
3215352081c662299b618335bf3024058fa04ef2dfdJungshik Jang            boolean result = action.processCommand(message);
3225352081c662299b618335bf3024058fa04ef2dfdJungshik Jang            processed = processed || result;
32379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
3245352081c662299b618335bf3024058fa04ef2dfdJungshik Jang        return processed;
32579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
32679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
327a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
328092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGivePhysicalAddress() {
32979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
33079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
331092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int physicalAddress = mService.getPhysicalAddress();
332092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
333092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                mAddress, physicalAddress, mDeviceType);
334092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        mService.sendCecCommand(cecMessage);
335092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
336092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
337092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
338a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
339092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGiveDeviceVendorId() {
34079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
341092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int vendorId = mService.getVendorId();
342092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
343092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                mAddress, vendorId);
344092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        mService.sendCecCommand(cecMessage);
345092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
346092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
347092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
348a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
349092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGetCecVersion(HdmiCecMessage message) {
35079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
351092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int version = mService.getCecVersion();
352092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
353092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                message.getSource(), version);
354092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        mService.sendCecCommand(cecMessage);
355092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
356092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
357092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
358a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
3598333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    protected boolean handleActiveSource(HdmiCecMessage message) {
3608333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        return false;
3618333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
3628333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
3638333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    @ServiceThreadOnly
3648333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    protected boolean handleInactiveSource(HdmiCecMessage message) {
3658333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        return false;
3668333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
3678333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
3688333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    @ServiceThreadOnly
3698333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    protected boolean handleRequestActiveSource(HdmiCecMessage message) {
3708333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        return false;
3718333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
3728333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
3738333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    @ServiceThreadOnly
374092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
37579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
376092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
3776aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        // 'return false' will cause to reply with <Feature Abort>.
3786aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        return false;
379092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
380092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
381a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
382795415b57b7271a11d16ca42703251a325df1097Terry Heo    protected boolean handleSetMenuLanguage(HdmiCecMessage message) {
383795415b57b7271a11d16ca42703251a325df1097Terry Heo        assertRunOnServiceThread();
384795415b57b7271a11d16ca42703251a325df1097Terry Heo        Slog.w(TAG, "Only Playback device can handle <Set Menu Language>:" + message.toString());
385795415b57b7271a11d16ca42703251a325df1097Terry Heo        // 'return false' will cause to reply with <Feature Abort>.
386795415b57b7271a11d16ca42703251a325df1097Terry Heo        return false;
387795415b57b7271a11d16ca42703251a325df1097Terry Heo    }
388795415b57b7271a11d16ca42703251a325df1097Terry Heo
389795415b57b7271a11d16ca42703251a325df1097Terry Heo    @ServiceThreadOnly
390092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGiveOsdName(HdmiCecMessage message) {
39179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
392092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        // Note that since this method is called after logical address allocation is done,
393092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        // mDeviceInfo should not be null.
394092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
395092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                mAddress, message.getSource(), mDeviceInfo.getDisplayName());
396092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        if (cecMessage != null) {
397092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            mService.sendCecCommand(cecMessage);
398092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        } else {
399092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            Slog.w(TAG, "Failed to build <Get Osd Name>:" + mDeviceInfo.getDisplayName());
400092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
401092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
402092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
403092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
40492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    protected boolean handleRoutingChange(HdmiCecMessage message) {
40592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        return false;
40692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
40792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
40864bafd9108b532102bc13c47966444b073c33fafYuncheol Heo    protected boolean handleRoutingInformation(HdmiCecMessage message) {
40964bafd9108b532102bc13c47966444b073c33fafYuncheol Heo        return false;
41064bafd9108b532102bc13c47966444b073c33fafYuncheol Heo    }
41164bafd9108b532102bc13c47966444b073c33fafYuncheol Heo
41260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
41360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
41460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
41560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
41679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
41779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
41879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
41979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
42079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
42179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
42279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
42379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
42479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleTerminateArc(HdmiCecMessage message) {
42579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
42679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
42779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
42879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleInitiateArc(HdmiCecMessage message) {
42979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
4308fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang    }
4318fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang
4328fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang    protected boolean handleReportAudioStatus(HdmiCecMessage message) {
4338fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang        return false;
43479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
43579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
436a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
43738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleStandby(HdmiCecMessage message) {
43838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
43938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // Seq #12
4404d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        if (mService.isControlEnabled() && !mService.isProhibitMode()
44138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                && mService.isPowerOnOrTransient()) {
44238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            mService.standby();
44338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return true;
44438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
44538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
44638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
44738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
44838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
44938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleUserControlPressed(HdmiCecMessage message) {
45038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
4513e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT);
45238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mService.isPowerOnOrTransient() && isPowerOffOrToggleCommand(message)) {
45338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            mService.standby();
45438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return true;
45538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else if (mService.isPowerStandbyOrTransient() && isPowerOnOrToggleCommand(message)) {
45638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            mService.wakeUp();
45738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return true;
45838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
4593e1564ee397ef833cba351153029317786f3d6bbTerry Heo
4603e1564ee397ef833cba351153029317786f3d6bbTerry Heo        final long downTime = SystemClock.uptimeMillis();
4613e1564ee397ef833cba351153029317786f3d6bbTerry Heo        final byte[] params = message.getParams();
46273483b6bc9046cbb7a54748c31ee724358a631efJungshik Jang        final int keycode = HdmiCecKeycode.cecKeycodeAndParamsToAndroidKey(params);
4633e1564ee397ef833cba351153029317786f3d6bbTerry Heo        int keyRepeatCount = 0;
4643e1564ee397ef833cba351153029317786f3d6bbTerry Heo        if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
4653e1564ee397ef833cba351153029317786f3d6bbTerry Heo            if (keycode == mLastKeycode) {
4663e1564ee397ef833cba351153029317786f3d6bbTerry Heo                keyRepeatCount = mLastKeyRepeatCount + 1;
4673e1564ee397ef833cba351153029317786f3d6bbTerry Heo            } else {
4683e1564ee397ef833cba351153029317786f3d6bbTerry Heo                injectKeyEvent(downTime, KeyEvent.ACTION_UP, mLastKeycode, 0);
4693e1564ee397ef833cba351153029317786f3d6bbTerry Heo            }
4703e1564ee397ef833cba351153029317786f3d6bbTerry Heo        }
4713e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mLastKeycode = keycode;
4723e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mLastKeyRepeatCount = keyRepeatCount;
4733e1564ee397ef833cba351153029317786f3d6bbTerry Heo
4743e1564ee397ef833cba351153029317786f3d6bbTerry Heo        if (keycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
4753e1564ee397ef833cba351153029317786f3d6bbTerry Heo            injectKeyEvent(downTime, KeyEvent.ACTION_DOWN, keycode, keyRepeatCount);
4763e1564ee397ef833cba351153029317786f3d6bbTerry Heo            mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT),
4773e1564ee397ef833cba351153029317786f3d6bbTerry Heo                    FOLLOWER_SAFETY_TIMEOUT);
4783e1564ee397ef833cba351153029317786f3d6bbTerry Heo            return true;
4793e1564ee397ef833cba351153029317786f3d6bbTerry Heo        }
4803e1564ee397ef833cba351153029317786f3d6bbTerry Heo        return false;
4813e1564ee397ef833cba351153029317786f3d6bbTerry Heo    }
4823e1564ee397ef833cba351153029317786f3d6bbTerry Heo
4833e1564ee397ef833cba351153029317786f3d6bbTerry Heo    @ServiceThreadOnly
4843e1564ee397ef833cba351153029317786f3d6bbTerry Heo    protected boolean handleUserControlReleased() {
4853e1564ee397ef833cba351153029317786f3d6bbTerry Heo        assertRunOnServiceThread();
4863e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT);
4873e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mLastKeyRepeatCount = 0;
4883e1564ee397ef833cba351153029317786f3d6bbTerry Heo        if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
4893e1564ee397ef833cba351153029317786f3d6bbTerry Heo            final long upTime = SystemClock.uptimeMillis();
4903e1564ee397ef833cba351153029317786f3d6bbTerry Heo            injectKeyEvent(upTime, KeyEvent.ACTION_UP, mLastKeycode, 0);
4913e1564ee397ef833cba351153029317786f3d6bbTerry Heo            mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
4923e1564ee397ef833cba351153029317786f3d6bbTerry Heo            return true;
4933e1564ee397ef833cba351153029317786f3d6bbTerry Heo        }
49438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
49538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
49638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
4973e1564ee397ef833cba351153029317786f3d6bbTerry Heo    static void injectKeyEvent(long time, int action, int keycode, int repeat) {
4983e1564ee397ef833cba351153029317786f3d6bbTerry Heo        KeyEvent keyEvent = KeyEvent.obtain(time, time, action, keycode,
4993e1564ee397ef833cba351153029317786f3d6bbTerry Heo                repeat, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
5003e1564ee397ef833cba351153029317786f3d6bbTerry Heo                InputDevice.SOURCE_HDMI, null);
5013e1564ee397ef833cba351153029317786f3d6bbTerry Heo        InputManager.getInstance().injectInputEvent(keyEvent,
5023e1564ee397ef833cba351153029317786f3d6bbTerry Heo                InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
5033e1564ee397ef833cba351153029317786f3d6bbTerry Heo        keyEvent.recycle();
5043e1564ee397ef833cba351153029317786f3d6bbTerry Heo   }
5053e1564ee397ef833cba351153029317786f3d6bbTerry Heo
50625c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) {
50738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        byte[] params = message.getParams();
508c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
509210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
510210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION
511210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
51238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
51338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
51425c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
51538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        byte[] params = message.getParams();
516c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
517210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
518210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
519210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
52038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
52138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
52238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleTextViewOn(HdmiCecMessage message) {
52338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
52438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
52538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
52638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleImageViewOn(HdmiCecMessage message) {
52738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
52838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
52938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
53038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleSetStreamPath(HdmiCecMessage message) {
53138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
53238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
53338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
53438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleGiveDevicePowerStatus(HdmiCecMessage message) {
53538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPowerStatus(
53638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                mAddress, message.getSource(), mService.getPowerStatus()));
53738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return true;
53838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
53938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
540184b124ec22a796327642e3546d366179e693f07Yuncheol Heo    protected boolean handleMenuRequest(HdmiCecMessage message) {
5413e1564ee397ef833cba351153029317786f3d6bbTerry Heo        // Always report menu active to receive Remote Control.
5423e1564ee397ef833cba351153029317786f3d6bbTerry Heo        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus(
5433e1564ee397ef833cba351153029317786f3d6bbTerry Heo                mAddress, message.getSource(), Constants.MENU_STATE_ACTIVATED));
5443e1564ee397ef833cba351153029317786f3d6bbTerry Heo        return true;
5453e1564ee397ef833cba351153029317786f3d6bbTerry Heo    }
5463e1564ee397ef833cba351153029317786f3d6bbTerry Heo
547184b124ec22a796327642e3546d366179e693f07Yuncheol Heo    protected boolean handleMenuStatus(HdmiCecMessage message) {
548184b124ec22a796327642e3546d366179e693f07Yuncheol Heo        return false;
549184b124ec22a796327642e3546d366179e693f07Yuncheol Heo    }
550184b124ec22a796327642e3546d366179e693f07Yuncheol Heo
551119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    protected boolean handleVendorCommand(HdmiCecMessage message) {
5520608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        if (!mService.invokeVendorCommandListenersOnReceived(mDeviceType, message.getSource(),
5530608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                message.getDestination(), message.getParams(), false)) {
554d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            // Vendor command listener may not have been registered yet. Respond with
555d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            // <Feature Abort> [NOT_IN_CORRECT_MODE] so that the sender can try again later.
556d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
557d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim        }
558119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        return true;
559119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
560119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
561119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    protected boolean handleVendorCommandWithId(HdmiCecMessage message) {
562119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        byte[] params = message.getParams();
563119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        int vendorId = HdmiUtils.threeBytesToInt(params);
564119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        if (vendorId == mService.getVendorId()) {
5650608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            if (!mService.invokeVendorCommandListenersOnReceived(mDeviceType, message.getSource(),
5660608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    message.getDestination(), params, true)) {
567d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
568d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            }
569119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } else if (message.getDestination() != Constants.ADDR_BROADCAST &&
570119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                message.getSource() != Constants.ADDR_UNREGISTERED) {
571119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
5726aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo            mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
573119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } else {
574119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
575119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
576119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        return true;
577119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
578119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
579d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim    protected void sendStandby(int deviceId) {
580d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim        // Do nothing.
581d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim    }
582d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim
5838f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang    protected boolean handleSetOsdName(HdmiCecMessage message) {
5848f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang        // The default behavior of <Set Osd Name> is doing nothing.
5858f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang        return true;
5868f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang    }
5878f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang
588b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    protected boolean handleRecordTvScreen(HdmiCecMessage message) {
58925c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        // The default behavior of <Record TV Screen> is replying <Feature Abort> with
59025c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        // "Cannot provide source".
5916aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        mService.maySendFeatureAbortCommand(message, Constants.ABORT_CANNOT_PROVIDE_SOURCE);
592b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        return true;
593b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
594b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
595e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    protected boolean handleTimerClearedStatus(HdmiCecMessage message) {
596e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        return false;
597e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    }
598e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang
5994480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang    protected boolean handleReportPowerStatus(HdmiCecMessage message) {
6004480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang        return false;
6014480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang    }
6024480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang
6034480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang    protected boolean handleTimerStatus(HdmiCecMessage message) {
6044480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang        return false;
6054480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang    }
6064480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang
6074480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang    protected boolean handleRecordStatus(HdmiCecMessage message) {
6084480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang        return false;
6094480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang    }
6104480efa05aa5dd44f1432c3260be263546daf838Jungshik Jang
61138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
612fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    final void handleAddressAllocated(int logicalAddress, int reason) {
61379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
6143ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        mAddress = mPreferredAddress = logicalAddress;
615fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        onAddressAllocated(logicalAddress, reason);
616af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim        setPreferredAddress(logicalAddress);
6171a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    }
6181a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang
619b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    int getType() {
620b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        return mDeviceType;
621b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    }
622b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo
623a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
62461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    HdmiDeviceInfo getDeviceInfo() {
62579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
6261a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang        return mDeviceInfo;
6271a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    }
6281a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang
629a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
63061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void setDeviceInfo(HdmiDeviceInfo info) {
63179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
6321a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang        mDeviceInfo = info;
6331a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    }
6341a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang
6352918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    // Returns true if the logical address is same as the argument.
636a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
6372918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    boolean isAddressOf(int addr) {
63879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
6392918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        return addr == mAddress;
6402918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
6412918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
6422918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    // Resets the logical address to unregistered(15), meaning the logical device is invalid.
643a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
6442918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    void clearAddress() {
64579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
646c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mAddress = Constants.ADDR_UNREGISTERED;
6472918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
6482918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
649a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
650b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    void addAndStartAction(final HdmiCecFeatureAction action) {
65179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
6526f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        mActions.add(action);
653a5445ce992a4e8ac5252975acedf3e5aec53867aJinsuk Kim        if (mService.isPowerStandby()) {
6546f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            Slog.i(TAG, "Not ready to start action. Queued for deferred start:" + action);
65538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
65638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
65779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        action.start();
65879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
65979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
6606f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim    @ServiceThreadOnly
6616f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim    void startQueuedActions() {
6626f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        assertRunOnServiceThread();
6636f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        for (HdmiCecFeatureAction action : mActions) {
6646f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            if (!action.started()) {
6656f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim                Slog.i(TAG, "Starting queued action:" + action);
6666f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim                action.start();
6676f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            }
6686f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        }
6696f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim    }
6706f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim
67179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // See if we have an action of a given type in progress.
672a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
673b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    <T extends HdmiCecFeatureAction> boolean hasAction(final Class<T> clazz) {
67479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
675b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang        for (HdmiCecFeatureAction action : mActions) {
67679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (action.getClass().equals(clazz)) {
67779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return true;
67879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            }
67979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
68079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
68179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
68279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
68379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Returns all actions matched with given class type.
684a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
685b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    <T extends HdmiCecFeatureAction> List<T> getActions(final Class<T> clazz) {
68679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
68743c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim        List<T> actions = Collections.<T>emptyList();
688b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang        for (HdmiCecFeatureAction action : mActions) {
68979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (action.getClass().equals(clazz)) {
69043c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim                if (actions.isEmpty()) {
69143c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim                    actions = new ArrayList<T>();
69243c23e273e1b78caf26899eca5a4f51df9d52400Jinsuk Kim                }
69379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                actions.add((T) action);
69479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            }
69579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
69679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return actions;
69779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
69879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
69979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    /**
700b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang     * Remove the given {@link HdmiCecFeatureAction} object from the action queue.
70179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     *
702b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang     * @param action {@link HdmiCecFeatureAction} to remove
70379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     */
704a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
705b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    void removeAction(final HdmiCecFeatureAction action) {
70679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
707c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo        action.finish(false);
70879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mActions.remove(action);
70938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        checkIfPendingActionsCleared();
71079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
71179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
71279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Remove all actions matched with the given Class type.
713a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
714b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    <T extends HdmiCecFeatureAction> void removeAction(final Class<T> clazz) {
715a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
71679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        removeActionExcept(clazz, null);
71779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
71879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
71979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Remove all actions matched with the given Class type besides |exception|.
720a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
721b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang    <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz,
722b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang            final HdmiCecFeatureAction exception) {
72379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
724b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang        Iterator<HdmiCecFeatureAction> iter = mActions.iterator();
72579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        while (iter.hasNext()) {
726b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang            HdmiCecFeatureAction action = iter.next();
72779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (action != exception && action.getClass().equals(clazz)) {
728c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo                action.finish(false);
729c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo                iter.remove();
73079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            }
73179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
73238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        checkIfPendingActionsCleared();
73379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
73479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
73538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected void checkIfPendingActionsCleared() {
7364fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mActions.isEmpty() && mPendingActionClearedCallback != null) {
73726ba7fddcaeb052710ca8672889830dabcbfd3acYuncheol Heo            PendingActionClearedCallback callback = mPendingActionClearedCallback;
73826ba7fddcaeb052710ca8672889830dabcbfd3acYuncheol Heo            // To prevent from calling the callback again during handling the callback itself.
73926ba7fddcaeb052710ca8672889830dabcbfd3acYuncheol Heo            mPendingActionClearedCallback = null;
74026ba7fddcaeb052710ca8672889830dabcbfd3acYuncheol Heo            callback.onCleared(this);
74138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
74238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
7434fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
74479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected void assertRunOnServiceThread() {
74579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (Looper.myLooper() != mService.getServiceLooper()) {
74679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            throw new IllegalStateException("Should run on service thread.");
74779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
74879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
74979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
750e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim    void setAutoDeviceOff(boolean enabled) {
751e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim    }
752e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim
75379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    /**
75479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * Called when a hot-plug event issued.
75579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     *
75679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * @param portId id of port where a hot-plug event happened
75779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * @param connected whether to connected or not on the event
75879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     */
75979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void onHotplug(int portId, boolean connected) {
76079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
76179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
76279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    final HdmiControlService getService() {
76379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mService;
76479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
76579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
766a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
76779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    final boolean isConnectedToArcPort(int path) {
768a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
76979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mService.isConnectedToArcPort(path);
77079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
77179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
77272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    ActiveSource getActiveSource() {
77379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        synchronized (mLock) {
77479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return mActiveSource;
77579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
77679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
77779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
77872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    void setActiveSource(ActiveSource newActive) {
77972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        setActiveSource(newActive.logicalAddress, newActive.physicalAddress);
78072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    }
78172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim
78261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void setActiveSource(HdmiDeviceInfo info) {
78372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim        setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress());
78472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    }
78572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim
78672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    void setActiveSource(int logicalAddress, int physicalAddress) {
7878333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        synchronized (mLock) {
78872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            mActiveSource.logicalAddress = logicalAddress;
78972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim            mActiveSource.physicalAddress = physicalAddress;
7908333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        }
791e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        mService.setLastInputForMhl(Constants.INVALID_PORT_ID);
7928333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
7938333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
79479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    int getActivePath() {
79579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        synchronized (mLock) {
79679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return mActiveRoutingPath;
79779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
79879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
79979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
8008333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    void setActivePath(int path) {
8018333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        synchronized (mLock) {
8028333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim            mActiveRoutingPath = path;
8038333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        }
804867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        mService.setActivePortId(pathToPortId(path));
8058333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
8068333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
80779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    /**
808a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * Returns the ID of the active HDMI port. The active port is the one that has the active
809a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * routing path connected to it directly or indirectly under the device hierarchy.
81079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     */
811a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    int getActivePortId() {
81279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        synchronized (mLock) {
81379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return mService.pathToPortId(mActiveRoutingPath);
81479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
81579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
81679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
817a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    /**
818a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * Update the active port.
819a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     *
820a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * @param portId the new active port id
821a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     */
822a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    void setActivePortId(int portId) {
823867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        // We update active routing path instead, since we get the active port id from
824867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        // the active routing path.
825867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        setActivePath(mService.portIdToPath(portId));
826a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    }
827a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
828a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
82979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    HdmiCecMessageCache getCecMessageCache() {
83079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
83179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mCecMessageCache;
83279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
83379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
834a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
83579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    int pathToPortId(int newPath) {
83679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
83779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mService.pathToPortId(newPath);
83879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
83938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
84038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    /**
8414fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * Called when the system goes to standby mode.
84238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     *
84338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     * @param initiatedByCec true if this power sequence is initiated
844b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang     *        by the reception the CEC messages like &lt;Standby&gt;
845e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim     * @param standbyAction Intent action that drives the standby process,
846e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim     *        either {@link HdmiControlService#STANDBY_SCREEN_OFF} or
847e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim     *        {@link HdmiControlService#STANDBY_SHUTDOWN}
84838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     */
849e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim    protected void onStandby(boolean initiatedByCec, int standbyAction) {}
85038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
85138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    /**
8524fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * Disable device. {@code callback} is used to get notified when all pending
8534fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * actions are completed or timeout is issued.
85438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     *
8554fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * @param initiatedByCec true if this sequence is initiated
856b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang     *        by the reception the CEC messages like &lt;Standby&gt;
8577e4b480a0bf9f93a428f7c46bfea77ebfdb92d40Jinsuk Kim     * @param originalCallback callback interface to get notified when all pending actions are
858b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang     *        cleared
85938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     */
860b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang    protected void disableDevice(boolean initiatedByCec,
8617e4b480a0bf9f93a428f7c46bfea77ebfdb92d40Jinsuk Kim            final PendingActionClearedCallback originalCallback) {
862b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang        mPendingActionClearedCallback = new PendingActionClearedCallback() {
863b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang            @Override
864b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
865b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang                mHandler.removeMessages(MSG_DISABLE_DEVICE_TIMEOUT);
8667e4b480a0bf9f93a428f7c46bfea77ebfdb92d40Jinsuk Kim                originalCallback.onCleared(device);
867b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang            }
868b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang        };
8694fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_DISABLE_DEVICE_TIMEOUT),
8704fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                DEVICE_CLEANUP_TIMEOUT);
8714fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
8724fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
8734fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
8744fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void handleDisableDeviceTimeout() {
8754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
8764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
8774fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        // If all actions are not cleared in DEVICE_CLEANUP_TIMEOUT, enforce to finish them.
8784fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        // onCleard will be called at the last action's finish method.
879b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang        Iterator<HdmiCecFeatureAction> iter = mActions.iterator();
8804fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        while (iter.hasNext()) {
881b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jang            HdmiCecFeatureAction action = iter.next();
882c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo            action.finish(false);
8834fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            iter.remove();
8844fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
8857e4b480a0bf9f93a428f7c46bfea77ebfdb92d40Jinsuk Kim        if (mPendingActionClearedCallback != null) {
8867e4b480a0bf9f93a428f7c46bfea77ebfdb92d40Jinsuk Kim            mPendingActionClearedCallback.onCleared(this);
8877e4b480a0bf9f93a428f7c46bfea77ebfdb92d40Jinsuk Kim        }
8884fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
889c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim
890c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim    /**
891c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     * Send a key event to other device.
892c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     *
8934fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * @param keyCode key code defined in {@link android.view.KeyEvent}
894c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     * @param isPressed {@code true} for key down event
895c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     */
896c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim    protected void sendKeyEvent(int keyCode, boolean isPressed) {
897c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        Slog.w(TAG, "sendKeyEvent not implemented");
898c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim    }
899959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo
9002e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang    void sendUserControlPressedAndReleased(int targetAddress, int cecKeycode) {
9012e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        mService.sendCecCommand(HdmiCecMessageBuilder.buildUserControlPressed(
9022e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                mAddress, targetAddress, cecKeycode));
9032e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        mService.sendCecCommand(HdmiCecMessageBuilder.buildUserControlReleased(
9042e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                mAddress, targetAddress));
9052e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang    }
9062e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang
907959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo    /**
908959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo     * Dump internal status of HdmiCecLocalDevice object.
909959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo     */
910959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo    protected void dump(final IndentingPrintWriter pw) {
911959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        pw.println("mDeviceType: " + mDeviceType);
912959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        pw.println("mAddress: " + mAddress);
913959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        pw.println("mPreferredAddress: " + mPreferredAddress);
914959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        pw.println("mDeviceInfo: " + mDeviceInfo);
915959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        pw.println("mActiveSource: " + mActiveSource);
916959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath));
917959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo    }
9182918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim}
919