HotplugDetectionAction.java revision a466929979a92a578d4ba00093fefa57cfb982b4
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, true); 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 mService.removeCecDevice(removedAddress); 188 // TODO: implements following steps. 189 // 1. Launch routing control sequence 190 // 2. Stop one touch play sequence if removed device is the device to be selected. 191 // 3. If audio system, start system audio off and arc off 192 // 4. Inform device remove to others 193 } 194} 195