HdmiCecLocalDevice.java revision b6591b8e5399099dc6b7693e0fc719b613aba89c
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
191a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jangimport android.hardware.hdmi.HdmiCecDeviceInfo;
204fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jangimport android.os.Handler;
2179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport android.os.Looper;
224fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jangimport android.os.Message;
23af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kimimport android.os.SystemProperties;
24092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jangimport android.util.Slog;
252918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
2679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport com.android.internal.annotations.GuardedBy;
27a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
2879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
2979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport java.util.ArrayList;
3079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport java.util.Iterator;
3179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport java.util.LinkedList;
3279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport java.util.List;
3379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
342918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim/**
352918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * Class that models a logical CEC device hosted in this system. Handles initialization,
362918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim * CEC commands that call for actions customized per device type.
372918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim */
382918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kimabstract class HdmiCecLocalDevice {
39092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private static final String TAG = "HdmiCecLocalDevice";
402918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
414fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1;
424fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    // Timeout in millisecond for device clean up (5s).
434fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    // Normal actions timeout is 2s but some of them would have several sequence of timeout.
444fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private static final int DEVICE_CLEANUP_TIMEOUT = 5000;
454fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
463ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    protected final HdmiControlService mService;
472918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    protected final int mDeviceType;
482918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    protected int mAddress;
492918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    protected int mPreferredAddress;
501a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    protected HdmiCecDeviceInfo mDeviceInfo;
512918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
5279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Logical address of the active source.
5379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    @GuardedBy("mLock")
5479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private int mActiveSource;
5579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
5679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Active routing path. Physical address of the active source but not all the time, such as
5779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // when the new active source does not claim itself to be one. Note that we don't keep
5879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // the active port id (or active input) since it can be gotten by {@link #pathToPortId(int)}.
5979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    @GuardedBy("mLock")
6079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private int mActiveRoutingPath;
6179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
6279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
6379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected final Object mLock;
6479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
6579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // A collection of FeatureAction.
6679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Note that access to this collection should happen in service thread.
6779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private final LinkedList<FeatureAction> mActions = new LinkedList<>();
6879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
69c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo    private final Handler mHandler = new Handler () {
704fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        @Override
714fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        public void handleMessage(Message msg) {
724fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            switch (msg.what) {
734fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                case MSG_DISABLE_DEVICE_TIMEOUT:
744fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    handleDisableDeviceTimeout();
754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    break;
764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
774fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
784fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    };
794fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
804fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    /**
814fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * A callback interface to get notified when all pending action is cleared.
824fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * It can be called when timeout happened.
834fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     */
844fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    interface PendingActionClearedCallback {
854fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        void onCleared(HdmiCecLocalDevice device);
864fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
874fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
884fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    protected PendingActionClearedCallback mPendingActionClearedCallback;
894fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
903ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
913ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        mService = service;
922918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        mDeviceType = deviceType;
93c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mAddress = Constants.ADDR_UNREGISTERED;
9479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mLock = service.getServiceLock();
952918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
962918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
972918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    // Factory method that returns HdmiCecLocalDevice of corresponding type.
983ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    static HdmiCecLocalDevice create(HdmiControlService service, int deviceType) {
992918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        switch (deviceType) {
100c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        case HdmiCecDeviceInfo.DEVICE_TV:
1013ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            return new HdmiCecLocalDeviceTv(service);
102c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        case HdmiCecDeviceInfo.DEVICE_PLAYBACK:
1033ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            return new HdmiCecLocalDevicePlayback(service);
1042918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        default:
1052918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim            return null;
1062918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        }
1072918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
1082918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
109a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
1103ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    void init() {
111a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
112af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim        mPreferredAddress = getPreferredAddress();
1133ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
1142918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
1158b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang    /**
1163ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Called once a logical address of the local device is allocated.
1178b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     */
118a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    protected abstract void onAddressAllocated(int logicalAddress, boolean fromBootup);
1198b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
120092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
121af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     * Get the preferred logical address from system properties.
122af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     */
123af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    protected abstract int getPreferredAddress();
124af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim
125af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    /**
126af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     * Set the preferred logical address to system properties.
127af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim     */
128af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    protected abstract void setPreferredAddress(int addr);
129af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim
130af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim    /**
131092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Dispatch incoming message.
132092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     *
133092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @param message incoming message
134092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @return true if consumed a message; otherwise, return false.
135092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
136a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
137092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    final boolean dispatchMessage(HdmiCecMessage message) {
13879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
139092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int dest = message.getDestination();
140c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
141092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            return false;
142092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
14379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        // Cache incoming message. Note that it caches only white-listed one.
14479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecMessageCache.cacheMessage(message);
145092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return onMessage(message);
146092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
147092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
148a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
14960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    protected final boolean onMessage(HdmiCecMessage message) {
15079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
15179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (dispatchMessageToAction(message)) {
15279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return true;
15379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
154092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        switch (message.getOpcode()) {
155c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_ACTIVE_SOURCE:
1568333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                return handleActiveSource(message);
157c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_INACTIVE_SOURCE:
1588333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                return handleInactiveSource(message);
159c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
1608333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                return handleRequestActiveSource(message);
161c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GET_MENU_LANGUAGE:
162092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGetMenuLanguage(message);
163c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS:
164092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGivePhysicalAddress();
165c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_OSD_NAME:
166092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGiveOsdName(message);
167c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
168092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGiveDeviceVendorId();
169c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GET_CEC_VERSION:
170092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return handleGetCecVersion(message);
171c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
17260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                return handleReportPhysicalAddress(message);
173c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_ROUTING_CHANGE:
17492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                return handleRoutingChange(message);
175c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_INITIATE_ARC:
17679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleInitiateArc(message);
177c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_TERMINATE_ARC:
17879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleTerminateArc(message);
179c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
18079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleSetSystemAudioMode(message);
181c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
18279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return handleSystemAudioModeStatus(message);
183c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_REPORT_AUDIO_STATUS:
1848fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang                return handleReportAudioStatus(message);
185c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_STANDBY:
18638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleStandby(message);
187c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_TEXT_VIEW_ON:
18838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleTextViewOn(message);
189c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_IMAGE_VIEW_ON:
19038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleImageViewOn(message);
191c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_USER_CONTROL_PRESSED:
19238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleUserControlPressed(message);
193c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_SET_STREAM_PATH:
19438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleSetStreamPath(message);
195c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
19638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                return handleGiveDevicePowerStatus(message);
197119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            case Constants.MESSAGE_VENDOR_COMMAND:
198119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                return handleVendorCommand(message);
199119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID:
200119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                return handleVendorCommandWithId(message);
2018f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang            case Constants.MESSAGE_SET_OSD_NAME:
2028f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang                return handleSetOsdName(message);
203b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            case Constants.MESSAGE_RECORD_TV_SCREEN:
204b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                return handleRecordTvScreen(message);
205092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            default:
206092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return false;
207092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
208092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
209092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
210a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
21179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private boolean dispatchMessageToAction(HdmiCecMessage message) {
212a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
21379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (FeatureAction action : mActions) {
21479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (action.processCommand(message)) {
21579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return true;
21679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            }
21779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
21879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
21979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
22079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
221a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
222092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGivePhysicalAddress() {
22379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
22479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
225092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int physicalAddress = mService.getPhysicalAddress();
226092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
227092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                mAddress, physicalAddress, mDeviceType);
228092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        mService.sendCecCommand(cecMessage);
229092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
230092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
231092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
232a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
233092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGiveDeviceVendorId() {
23479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
235092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int vendorId = mService.getVendorId();
236092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
237092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                mAddress, vendorId);
238092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        mService.sendCecCommand(cecMessage);
239092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
240092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
241092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
242a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
243092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGetCecVersion(HdmiCecMessage message) {
24479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
245092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        int version = mService.getCecVersion();
246092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
247092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                message.getSource(), version);
248092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        mService.sendCecCommand(cecMessage);
249092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
250092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
251092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
252a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
2538333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    protected boolean handleActiveSource(HdmiCecMessage message) {
2548333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        return false;
2558333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
2568333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
2578333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    @ServiceThreadOnly
2588333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    protected boolean handleInactiveSource(HdmiCecMessage message) {
2598333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        return false;
2608333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
2618333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
2628333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    @ServiceThreadOnly
2638333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    protected boolean handleRequestActiveSource(HdmiCecMessage message) {
2648333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        return false;
2658333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
2668333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
2678333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    @ServiceThreadOnly
268092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
26979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
270092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
271092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        mService.sendCecCommand(
272092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
273c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        message.getSource(), Constants.MESSAGE_GET_MENU_LANGUAGE,
274c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        Constants.ABORT_UNRECOGNIZED_MODE));
275092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
276092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
277092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
278a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
279092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    protected boolean handleGiveOsdName(HdmiCecMessage message) {
28079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
281092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        // Note that since this method is called after logical address allocation is done,
282092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        // mDeviceInfo should not be null.
283092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
284092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                mAddress, message.getSource(), mDeviceInfo.getDisplayName());
285092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        if (cecMessage != null) {
286092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            mService.sendCecCommand(cecMessage);
287092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        } else {
288092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            Slog.w(TAG, "Failed to build <Get Osd Name>:" + mDeviceInfo.getDisplayName());
289092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
290092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return true;
291092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
292092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
29392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    protected boolean handleRoutingChange(HdmiCecMessage message) {
29492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        return false;
29592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
29692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
29760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
29860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
29960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
30060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
30179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
30279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
30379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
30479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
30579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
30679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
30779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
30879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
30979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleTerminateArc(HdmiCecMessage message) {
31079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
31179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
31279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
31379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected boolean handleInitiateArc(HdmiCecMessage message) {
31479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
3158fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang    }
3168fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang
3178fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang    protected boolean handleReportAudioStatus(HdmiCecMessage message) {
3188fa36b110be29d92a9aba070fa4666eefb14b584Jungshik Jang        return false;
31979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
32079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
321a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
32238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleStandby(HdmiCecMessage message) {
32338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
32438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // Seq #12
3254d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        if (mService.isControlEnabled() && !mService.isProhibitMode()
32638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                && mService.isPowerOnOrTransient()) {
32738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            mService.standby();
32838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return true;
32938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
33038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
33138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
33238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
33338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
33438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleUserControlPressed(HdmiCecMessage message) {
33538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
33638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mService.isPowerOnOrTransient() && isPowerOffOrToggleCommand(message)) {
33738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            mService.standby();
33838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return true;
33938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else if (mService.isPowerStandbyOrTransient() && isPowerOnOrToggleCommand(message)) {
34038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            mService.wakeUp();
34138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return true;
34238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
34338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
34438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
34538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
34638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) {
34738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        byte[] params = message.getParams();
348c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
349210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
350210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION
351210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
35238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
35338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
35438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
35538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        byte[] params = message.getParams();
356c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
357210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
358210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
359210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
36038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
36138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
36238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleTextViewOn(HdmiCecMessage message) {
36338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
36438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
36538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
36638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleImageViewOn(HdmiCecMessage message) {
36738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
36838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
36938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
37038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleSetStreamPath(HdmiCecMessage message) {
37138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return false;
37238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
37338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
37438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected boolean handleGiveDevicePowerStatus(HdmiCecMessage message) {
37538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPowerStatus(
37638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                mAddress, message.getSource(), mService.getPowerStatus()));
37738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return true;
37838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
37938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
380119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    protected boolean handleVendorCommand(HdmiCecMessage message) {
381119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        mService.invokeVendorCommandListeners(mDeviceType, message.getSource(),
382119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                message.getParams(), false);
383119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        return true;
384119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
385119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
386119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    protected boolean handleVendorCommandWithId(HdmiCecMessage message) {
387119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        byte[] params = message.getParams();
388119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        int vendorId = HdmiUtils.threeBytesToInt(params);
389119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        if (vendorId == mService.getVendorId()) {
390119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), params, true);
391119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } else if (message.getDestination() != Constants.ADDR_BROADCAST &&
392119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                message.getSource() != Constants.ADDR_UNREGISTERED) {
393119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
394119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
395119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    message.getSource(), Constants.MESSAGE_VENDOR_COMMAND_WITH_ID,
396119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    Constants.ABORT_UNRECOGNIZED_MODE));
397119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } else {
398119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
399119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
400119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        return true;
401119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
402119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
4038f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang    protected boolean handleSetOsdName(HdmiCecMessage message) {
4048f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang        // The default behavior of <Set Osd Name> is doing nothing.
4058f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang        return true;
4068f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang    }
4078f2ed357a23fac4a55da43d20138b438b4ac79a7Jungshik Jang
408b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    protected boolean handleRecordTvScreen(HdmiCecMessage message) {
409b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        // The default behavior of <Record TV Screen> is replying <Feature Abort> with "Refused".
410b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
411b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                message.getSource(), message.getOpcode(), Constants.ABORT_REFUSED));
412b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        return true;
413b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
414b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
41538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
416a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    final void handleAddressAllocated(int logicalAddress, boolean fromBootup) {
41779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
4183ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        mAddress = mPreferredAddress = logicalAddress;
419a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        onAddressAllocated(logicalAddress, fromBootup);
420af2acf0447aff34450cde2bcfb35dff9cf631729Jinsuk Kim        setPreferredAddress(logicalAddress);
4211a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    }
4221a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang
423a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
4241a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    HdmiCecDeviceInfo getDeviceInfo() {
42579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
4261a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang        return mDeviceInfo;
4271a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    }
4281a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang
429a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
4301a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    void setDeviceInfo(HdmiCecDeviceInfo info) {
43179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
4321a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang        mDeviceInfo = info;
4331a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang    }
4341a4485dcd25ed036fb8de1a271b37121d8135f4eJungshik Jang
4352918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    // Returns true if the logical address is same as the argument.
436a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
4372918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    boolean isAddressOf(int addr) {
43879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
4392918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim        return addr == mAddress;
4402918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
4412918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
4422918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    // Resets the logical address to unregistered(15), meaning the logical device is invalid.
443a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
4442918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    void clearAddress() {
44579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
446c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mAddress = Constants.ADDR_UNREGISTERED;
4472918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim    }
4482918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim
449a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
45079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void addAndStartAction(final FeatureAction action) {
45179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
45238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mService.isPowerStandbyOrTransient()) {
45338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.w(TAG, "Skip the action during Standby: " + action);
45438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
45538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
45679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mActions.add(action);
45779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        action.start();
45879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
45979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
46079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // See if we have an action of a given type in progress.
461a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
46279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
46379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
46479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (FeatureAction action : mActions) {
46579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (action.getClass().equals(clazz)) {
46679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                return true;
46779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            }
46879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
46979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return false;
47079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
47179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
47279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Returns all actions matched with given class type.
473a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
47479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
47579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
47679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        ArrayList<T> actions = new ArrayList<>();
47779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (FeatureAction action : mActions) {
47879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (action.getClass().equals(clazz)) {
47979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                actions.add((T) action);
48079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            }
48179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
48279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return actions;
48379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
48479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
48579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    /**
48679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * Remove the given {@link FeatureAction} object from the action queue.
48779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     *
48879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * @param action {@link FeatureAction} to remove
48979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     */
490a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
49179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void removeAction(final FeatureAction action) {
49279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
493c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo        action.finish(false);
49479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mActions.remove(action);
49538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        checkIfPendingActionsCleared();
49679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
49779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
49879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Remove all actions matched with the given Class type.
499a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
50079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    <T extends FeatureAction> void removeAction(final Class<T> clazz) {
501a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
50279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        removeActionExcept(clazz, null);
50379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
50479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
50579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    // Remove all actions matched with the given Class type besides |exception|.
506a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
50779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
50879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            final FeatureAction exception) {
50979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
51079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        Iterator<FeatureAction> iter = mActions.iterator();
51179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        while (iter.hasNext()) {
51279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            FeatureAction action = iter.next();
51379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (action != exception && action.getClass().equals(clazz)) {
514c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo                action.finish(false);
515c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo                iter.remove();
51679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            }
51779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
51838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        checkIfPendingActionsCleared();
51979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
52079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
52138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    protected void checkIfPendingActionsCleared() {
5224fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mActions.isEmpty() && mPendingActionClearedCallback != null) {
5234fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mPendingActionClearedCallback.onCleared(this);
52438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
52538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
5264fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
52779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    protected void assertRunOnServiceThread() {
52879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (Looper.myLooper() != mService.getServiceLooper()) {
52979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            throw new IllegalStateException("Should run on service thread.");
53079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
53179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
53279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
53379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    /**
53479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * Called when a hot-plug event issued.
53579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     *
53679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * @param portId id of port where a hot-plug event happened
53779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * @param connected whether to connected or not on the event
53879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     */
53979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void onHotplug(int portId, boolean connected) {
54079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
54179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
54279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    final HdmiControlService getService() {
54379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mService;
54479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
54579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
546a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
54779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    final boolean isConnectedToArcPort(int path) {
548a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
54979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mService.isConnectedToArcPort(path);
55079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
55179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
55279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    int getActiveSource() {
55379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        synchronized (mLock) {
55479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return mActiveSource;
55579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
55679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
55779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
5588333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    void setActiveSource(int source) {
5598333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        synchronized (mLock) {
5608333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim            mActiveSource = source;
5618333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        }
5628333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
5638333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
56479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    int getActivePath() {
56579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        synchronized (mLock) {
56679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return mActiveRoutingPath;
56779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
56879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
56979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
5708333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    void setActivePath(int path) {
5718333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        synchronized (mLock) {
5728333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim            mActiveRoutingPath = path;
5738333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim        }
5748333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim    }
5758333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim
57679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    /**
577a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * Returns the ID of the active HDMI port. The active port is the one that has the active
578a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * routing path connected to it directly or indirectly under the device hierarchy.
57979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     */
580a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    int getActivePortId() {
58179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        synchronized (mLock) {
58279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return mService.pathToPortId(mActiveRoutingPath);
58379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
58479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
58579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
586a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    /**
587a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * Update the active port.
588a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     *
589a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     * @param portId the new active port id
590a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim     */
591a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    void setActivePortId(int portId) {
592a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        synchronized (mLock) {
593a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            // We update active routing path instead, since we get the active port id from
594a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            // the active routing path.
595a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            mActiveRoutingPath = mService.portIdToPath(portId);
596a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
597a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim    }
598a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
59979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void updateActiveDevice(int logicalAddress, int physicalAddress) {
60079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        synchronized (mLock) {
60179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            mActiveSource = logicalAddress;
60279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            mActiveRoutingPath = physicalAddress;
60379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
60479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
60579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
606a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
60779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    HdmiCecMessageCache getCecMessageCache() {
60879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
60979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mCecMessageCache;
61079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
61179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
612a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
61379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    int pathToPortId(int newPath) {
61479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
61579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mService.pathToPortId(newPath);
61679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
61738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
61838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    /**
6194fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * Called when the system goes to standby mode.
62038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     *
62138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     * @param initiatedByCec true if this power sequence is initiated
622b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang     *        by the reception the CEC messages like &lt;Standby&gt;
62338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     */
6244fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    protected void onStandby(boolean initiatedByCec) {}
62538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
62638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    /**
6274fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * Disable device. {@code callback} is used to get notified when all pending
6284fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * actions are completed or timeout is issued.
62938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     *
6304fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * @param initiatedByCec true if this sequence is initiated
631b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang     *        by the reception the CEC messages like &lt;Standby&gt;
632b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang     * @param origialCallback callback interface to get notified when all pending actions are
633b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang     *        cleared
63438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     */
635b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang    protected void disableDevice(boolean initiatedByCec,
636b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang            final PendingActionClearedCallback origialCallback) {
637b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang        mPendingActionClearedCallback = new PendingActionClearedCallback() {
638b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang            @Override
639b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
640b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang                mHandler.removeMessages(MSG_DISABLE_DEVICE_TIMEOUT);
641b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang                origialCallback.onCleared(device);
642b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang            }
643b3e114af17c91a409e766e111e472f600f7b866cJungshik Jang        };
6444fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_DISABLE_DEVICE_TIMEOUT),
6454fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                DEVICE_CLEANUP_TIMEOUT);
6464fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
6474fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
6484fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
6494fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void handleDisableDeviceTimeout() {
6504fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
6514fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
6524fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        // If all actions are not cleared in DEVICE_CLEANUP_TIMEOUT, enforce to finish them.
6534fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        // onCleard will be called at the last action's finish method.
6544fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        Iterator<FeatureAction> iter = mActions.iterator();
6554fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        while (iter.hasNext()) {
6564fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            FeatureAction action = iter.next();
657c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo            action.finish(false);
6584fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            iter.remove();
6594fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
6604fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
661c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim
662c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim    /**
663c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     * Send a key event to other device.
664c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     *
6654fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang     * @param keyCode key code defined in {@link android.view.KeyEvent}
666c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     * @param isPressed {@code true} for key down event
667c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim     */
668c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim    protected void sendKeyEvent(int keyCode, boolean isPressed) {
669c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        Slog.w(TAG, "sendKeyEvent not implemented");
670c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim    }
6712918e9e133de8066ab497a5f8dac1c310c792767Jinsuk Kim}
672