10f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang/*
20f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * Copyright (C) 2014 The Android Open Source Project
30f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang *
40f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * Licensed under the Apache License, Version 2.0 (the "License");
50f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * you may not use this file except in compliance with the License.
60f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * You may obtain a copy of the License at
70f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang *
80f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang *      http://www.apache.org/licenses/LICENSE-2.0
90f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang *
100f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * Unless required by applicable law or agreed to in writing, software
110f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * distributed under the License is distributed on an "AS IS" BASIS,
120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * See the License for the specific language governing permissions and
140f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * limitations under the License.
150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang */
160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
170f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangpackage com.android.server.hdmi;
180f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
1961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
200f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.util.Slog;
210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
220f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
230f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
240f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport java.util.BitSet;
250f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport java.util.List;
260f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
270f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang/**
280f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * Feature action that handles hot-plug detection mechanism.
290f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * Hot-plug event is initiated by timer after device discovery action.
300f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang *
310f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * <p>Check all devices every 15 secs except for system audio.
320f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * If system audio is on, check hot-plug for audio system every 5 secs.
330f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * For other devices, keep 15 secs period.
340f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang */
35210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jang// Seq #3
36b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jangfinal class HotplugDetectionAction extends HdmiCecFeatureAction {
370f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private static final String TAG = "HotPlugDetectionAction";
380f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
390f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private static final int POLLING_INTERVAL_MS = 5000;
400f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private static final int TIMEOUT_COUNT = 3;
4107600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim    private static final int AVR_COUNT_MAX = 3;
420f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
430f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    // State in which waits for next polling
440f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private static final int STATE_WAIT_FOR_NEXT_POLLING = 1;
450f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
460f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    // All addresses except for broadcast (unregistered address).
47c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private static final int NUM_OF_ADDRESS = Constants.ADDR_SPECIFIC_USE
48c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            - Constants.ADDR_TV + 1;
490f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
500f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int mTimeoutCount = 0;
510f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
5207600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim    // Counter used to ensure the connection to AVR is stable. Occasional failure to get
5307600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim    // polling response from AVR despite its presence leads to unstable status flipping.
5407600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim    // This is a workaround to deal with it, by removing the device only if the removal
5507600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim    // is detected {@code AVR_COUNT_MAX} times in a row.
5607600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim    private int mAvrStatusCount = 0;
5707600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim
580f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    /**
590f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * Constructor
600f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     *
6179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * @param source {@link HdmiCecLocalDevice} instance
620f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     */
6379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    HotplugDetectionAction(HdmiCecLocalDevice source) {
6479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        super(source);
650f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
660f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
670f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    @Override
680f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    boolean start() {
690f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        Slog.v(TAG, "Hot-plug dection started.");
700f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
710f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        mState = STATE_WAIT_FOR_NEXT_POLLING;
720f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        mTimeoutCount = 0;
730f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
740f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        // Start timer without polling.
750f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        // The first check for all devices will be initiated 15 seconds later.
760f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        addTimer(mState, POLLING_INTERVAL_MS);
770f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return true;
780f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
790f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
800f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    @Override
810f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    boolean processCommand(HdmiCecMessage cmd) {
820f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        // No-op
830f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return false;
840f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
850f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
860f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    @Override
870f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    void handleTimerEvent(int state) {
880f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (mState != state) {
890f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            return;
900f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
910f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
920f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (mState == STATE_WAIT_FOR_NEXT_POLLING) {
930f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            mTimeoutCount = (mTimeoutCount + 1) % TIMEOUT_COUNT;
940f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            pollDevices();
950f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
960f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
970f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
9860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    /**
9960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Start device polling immediately.
10060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
10160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    void pollAllDevicesNow() {
10260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        // Clear existing timer to avoid overlapped execution
10360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        mActionTimer.clearTimerMessage();
10460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
10560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        mTimeoutCount = 0;
10660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        mState = STATE_WAIT_FOR_NEXT_POLLING;
10760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        pollAllDevices();
10860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
10960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        addTimer(mState, POLLING_INTERVAL_MS);
11060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
11160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
1120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    // This method is called every 5 seconds.
1130f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private void pollDevices() {
1140f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        // All device check called every 15 seconds.
1150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (mTimeoutCount == 0) {
1160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            pollAllDevices();
1170f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        } else {
118377dcbd53af4529c352d453424539b069909fce4Jungshik Jang            if (tv().isSystemAudioActivated()) {
1190f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                pollAudioSystem();
1200f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            }
1210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
1220f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
1230f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        addTimer(mState, POLLING_INTERVAL_MS);
1240f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
1250f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
1260f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private void pollAllDevices() {
1270f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        Slog.v(TAG, "Poll all devices.");
1280f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
12979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        pollDevices(new DevicePollingCallback() {
1300f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            @Override
1310f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            public void onPollingFinished(List<Integer> ackedAddress) {
1320f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                checkHotplug(ackedAddress, false);
1330f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            }
134c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        }, Constants.POLL_ITERATION_IN_ORDER
1355fba96df30b6b50b3cb9fe1d783320b1cc3bd6eaJinsuk Kim                | Constants.POLL_STRATEGY_REMOTES_DEVICES, HdmiConfig.HOTPLUG_DETECTION_RETRY);
1360f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
1370f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
1380f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private void pollAudioSystem() {
1390f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        Slog.v(TAG, "Poll audio system.");
1400f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
14179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        pollDevices(new DevicePollingCallback() {
1420f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            @Override
1430f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            public void onPollingFinished(List<Integer> ackedAddress) {
144a466929979a92a578d4ba00093fefa57cfb982b4Jungshik Jang                checkHotplug(ackedAddress, true);
1450f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            }
146c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        }, Constants.POLL_ITERATION_IN_ORDER
1475fba96df30b6b50b3cb9fe1d783320b1cc3bd6eaJinsuk Kim                | Constants.POLL_STRATEGY_SYSTEM_AUDIO, HdmiConfig.HOTPLUG_DETECTION_RETRY);
1480f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
1490f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
1500f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
15179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        BitSet currentInfos = infoListToBitSet(tv().getDeviceInfoList(false), audioOnly);
1520f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        BitSet polledResult = addressListToBitSet(ackedAddress);
1530f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
1540f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        // At first, check removed devices.
1550f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        BitSet removed = complement(currentInfos, polledResult);
1560f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        int index = -1;
1570f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        while ((index = removed.nextSetBit(index + 1)) != -1) {
15807600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim            if (index == Constants.ADDR_AUDIO_SYSTEM) {
1597b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim                HdmiDeviceInfo avr = tv().getAvrDeviceInfo();
1607b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim                if (avr != null && tv().isConnected(avr.getPortId())) {
1617b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim                    ++mAvrStatusCount;
1627b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim                    Slog.w(TAG, "Ack not returned from AVR. count: " + mAvrStatusCount);
1637b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim                    if (mAvrStatusCount < AVR_COUNT_MAX) {
1647b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim                        continue;
1657b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim                    }
16607600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim                }
16707600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim            }
1680f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            Slog.v(TAG, "Remove device by hot-plug detection:" + index);
1690f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            removeDevice(index);
1700f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
1710f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
17207600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim        // Reset the counter if the ack is returned from AVR.
17307600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim        if (!removed.get(Constants.ADDR_AUDIO_SYSTEM)) {
17407600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim            mAvrStatusCount = 0;
17507600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim        }
17607600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim
1770f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        // Next, check added devices.
1780f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        BitSet added = complement(polledResult, currentInfos);
1790f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        index = -1;
1800f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        while ((index = added.nextSetBit(index + 1)) != -1) {
1810f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            Slog.v(TAG, "Add device by hot-plug detection:" + index);
1820f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            addDevice(index);
1830f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
1840f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
1850f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
18661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    private static BitSet infoListToBitSet(List<HdmiDeviceInfo> infoList, boolean audioOnly) {
1870f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        BitSet set = new BitSet(NUM_OF_ADDRESS);
18861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        for (HdmiDeviceInfo info : infoList) {
1890f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            if (audioOnly) {
19061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                if (info.getDeviceType() == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
1910f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                    set.set(info.getLogicalAddress());
1920f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                }
1930f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            } else {
1940f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                set.set(info.getLogicalAddress());
1950f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            }
1960f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
1970f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return set;
1980f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
1990f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
2000f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private static BitSet addressListToBitSet(List<Integer> list) {
2010f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        BitSet set = new BitSet(NUM_OF_ADDRESS);
2020f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        for (Integer value : list) {
2030f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            set.set(value);
2040f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
2050f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return set;
2060f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
2070f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
2080f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    // A - B = A & ~B
2090f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private static BitSet complement(BitSet first, BitSet second) {
2100f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        // Need to clone it so that it doesn't touch original set.
2110f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        BitSet clone = (BitSet) first.clone();
2120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        clone.andNot(second);
2130f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return clone;
2140f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
2150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
2160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private void addDevice(int addedAddress) {
21726dc71e7feefb2417fd5f007af68614c1c197cf8Jungshik Jang        // Sending <Give Physical Address> will initiate new device action.
21879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(),
21979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                addedAddress));
2200f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
2210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
2220f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private void removeDevice(int removedAddress) {
22360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        mayChangeRoutingPath(removedAddress);
22460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        mayCancelDeviceSelect(removedAddress);
22560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        mayCancelOneTouchRecord(removedAddress);
22660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        mayDisableSystemAudioAndARC(removedAddress);
22760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
22879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        tv().removeCecDevice(removedAddress);
22960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
23060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
23160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private void mayChangeRoutingPath(int address) {
2328960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim        HdmiDeviceInfo info = tv().getCecDeviceInfo(address);
23326dc71e7feefb2417fd5f007af68614c1c197cf8Jungshik Jang        if (info != null) {
23426dc71e7feefb2417fd5f007af68614c1c197cf8Jungshik Jang            tv().handleRemoveActiveRoutingPath(info.getPhysicalAddress());
23526dc71e7feefb2417fd5f007af68614c1c197cf8Jungshik Jang        }
23660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
23760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
23860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private void mayCancelDeviceSelect(int address) {
23979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        List<DeviceSelectAction> actions = getActions(DeviceSelectAction.class);
24060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        if (actions.isEmpty()) {
24160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            return;
24260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
24360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
244c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo        // Should have only one Device Select Action
24560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        DeviceSelectAction action = actions.get(0);
24660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        if (action.getTargetAddress() == address) {
24779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            removeAction(DeviceSelectAction.class);
24860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
24960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
25060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
25160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private void mayCancelOneTouchRecord(int address) {
2523dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang        List<OneTouchRecordAction> actions = getActions(OneTouchRecordAction.class);
2533dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang        for (OneTouchRecordAction action : actions) {
2543dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang            if (action.getRecorderAddress() == address) {
2553dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang                removeAction(action);
2563dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang            }
2573dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang        }
25860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
25960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
26060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private void mayDisableSystemAudioAndARC(int address) {
26161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        if (HdmiUtils.getTypeFromAddress(address) != HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
26260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            return;
26360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
26460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
265c1fa9afbcd1cafd205d46b2fd0bdaadccb7d29eaDonghyun Cho        tv().setSystemAudioMode(false);
2665bcf5bf6b4203629e153dcb0646596e9b3f7c7c2Jinsuk Kim        if (tv().isArcEstablished()) {
267bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho            tv().enableAudioReturnChannel(false);
26879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            addAndStartAction(new RequestArcTerminationAction(localDevice(), address));
26960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
2700f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
2710f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang}
272