HotplugDetectionAction.java revision 0f8b4b770c49b83fa8260833d8e1ec5c721a05d3
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.hdmi;
18
19import android.hardware.hdmi.HdmiCec;
20import android.hardware.hdmi.HdmiCecDeviceInfo;
21import android.hardware.hdmi.HdmiCecMessage;
22import android.util.Slog;
23
24import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
25
26import java.util.BitSet;
27import java.util.List;
28
29/**
30 * Feature action that handles hot-plug detection mechanism.
31 * Hot-plug event is initiated by timer after device discovery action.
32 *
33 * <p>Check all devices every 15 secs except for system audio.
34 * If system audio is on, check hot-plug for audio system every 5 secs.
35 * For other devices, keep 15 secs period.
36 */
37final class HotplugDetectionAction extends FeatureAction {
38    private static final String TAG = "HotPlugDetectionAction";
39
40    private static final int POLLING_INTERVAL_MS = 5000;
41    private static final int TIMEOUT_COUNT = 3;
42    private static final int POLL_RETRY_COUNT = 2;
43
44    // State in which waits for next polling
45    private static final int STATE_WAIT_FOR_NEXT_POLLING = 1;
46
47    // All addresses except for broadcast (unregistered address).
48    private static final int NUM_OF_ADDRESS = HdmiCec.ADDR_SPECIFIC_USE - HdmiCec.ADDR_TV + 1;
49
50    private int mTimeoutCount = 0;
51
52    /**
53     * Constructor
54     *
55     * @param service instance of {@link HdmiControlService}
56     * @param sourceAddress logical address of a device that initiate this action
57     */
58    HotplugDetectionAction(HdmiControlService service, int sourceAddress) {
59        super(service, sourceAddress);
60    }
61
62    @Override
63    boolean start() {
64        Slog.v(TAG, "Hot-plug dection started.");
65
66        mState = STATE_WAIT_FOR_NEXT_POLLING;
67        mTimeoutCount = 0;
68
69        // Start timer without polling.
70        // The first check for all devices will be initiated 15 seconds later.
71        addTimer(mState, POLLING_INTERVAL_MS);
72        return true;
73    }
74
75    @Override
76    boolean processCommand(HdmiCecMessage cmd) {
77        // No-op
78        return false;
79    }
80
81    @Override
82    void handleTimerEvent(int state) {
83        if (mState != state) {
84            return;
85        }
86
87        if (mState == STATE_WAIT_FOR_NEXT_POLLING) {
88            mTimeoutCount = (mTimeoutCount + 1) % TIMEOUT_COUNT;
89            pollDevices();
90        }
91    }
92
93    // This method is called every 5 seconds.
94    private void pollDevices() {
95        // All device check called every 15 seconds.
96        if (mTimeoutCount == 0) {
97            pollAllDevices();
98        } else {
99            if (mService.getSystemAudioMode()) {
100                pollAudioSystem();
101            }
102        }
103
104        addTimer(mState, POLLING_INTERVAL_MS);
105    }
106
107    private void pollAllDevices() {
108        Slog.v(TAG, "Poll all devices.");
109
110        mService.pollDevices(new DevicePollingCallback() {
111            @Override
112            public void onPollingFinished(List<Integer> ackedAddress) {
113                checkHotplug(ackedAddress, false);
114            }
115        }, HdmiControlService.POLL_ITERATION_IN_ORDER
116                | HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES, POLL_RETRY_COUNT);
117    }
118
119    private void pollAudioSystem() {
120        Slog.v(TAG, "Poll audio system.");
121
122        mService.pollDevices(new DevicePollingCallback() {
123            @Override
124            public void onPollingFinished(List<Integer> ackedAddress) {
125                checkHotplug(ackedAddress, false);
126            }
127        }, HdmiControlService.POLL_ITERATION_IN_ORDER
128                | HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO, POLL_RETRY_COUNT);
129    }
130
131    private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
132        BitSet currentInfos = infoListToBitSet(mService.getDeviceInfoList(false), audioOnly);
133        BitSet polledResult = addressListToBitSet(ackedAddress);
134
135        // At first, check removed devices.
136        BitSet removed = complement(currentInfos, polledResult);
137        int index = -1;
138        while ((index = removed.nextSetBit(index + 1)) != -1) {
139            Slog.v(TAG, "Remove device by hot-plug detection:" + index);
140            removeDevice(index);
141        }
142
143        // Next, check added devices.
144        BitSet added = complement(polledResult, currentInfos);
145        index = -1;
146        while ((index = added.nextSetBit(index + 1)) != -1) {
147            Slog.v(TAG, "Add device by hot-plug detection:" + index);
148            addDevice(index);
149        }
150    }
151
152    private static BitSet infoListToBitSet(List<HdmiCecDeviceInfo> infoList, boolean audioOnly) {
153        BitSet set = new BitSet(NUM_OF_ADDRESS);
154        for (HdmiCecDeviceInfo info : infoList) {
155            if (audioOnly) {
156                if (info.getDeviceType() == HdmiCec.DEVICE_AUDIO_SYSTEM) {
157                    set.set(info.getLogicalAddress());
158                }
159            } else {
160                set.set(info.getLogicalAddress());
161            }
162        }
163        return set;
164    }
165
166    private static BitSet addressListToBitSet(List<Integer> list) {
167        BitSet set = new BitSet(NUM_OF_ADDRESS);
168        for (Integer value : list) {
169            set.set(value);
170        }
171        return set;
172    }
173
174    // A - B = A & ~B
175    private static BitSet complement(BitSet first, BitSet second) {
176        // Need to clone it so that it doesn't touch original set.
177        BitSet clone = (BitSet) first.clone();
178        clone.andNot(second);
179        return clone;
180    }
181
182    private void addDevice(int addedAddress) {
183        // TODO: implement this.
184    }
185
186    private void removeDevice(int removedAddress) {
187        // TODO: implements following steps.
188        // 1. Launch routing control sequence
189        // 2. Stop one touch play sequence if removed device is the device to be selected.
190        // 3. If audio system, start system audio off and arc off
191        // 4. Inform device remove to others
192    }
193}
194