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 17 package com.android.server.hdmi; 18 19 import android.hardware.hdmi.HdmiDeviceInfo; 20 import android.util.Slog; 21 22 import com.android.server.hdmi.HdmiControlService.DevicePollingCallback; 23 24 import java.util.BitSet; 25 import java.util.List; 26 27 /** 28 * Feature action that handles hot-plug detection mechanism. 29 * Hot-plug event is initiated by timer after device discovery action. 30 * 31 * <p>TV checks all devices every 15 secs except for system audio. 32 * If system audio is on, check hot-plug for audio system every 5 secs. 33 * For other devices, keep 15 secs period. 34 * 35 * <p>Playback devices check all devices every 1 minute. 36 */ 37 // Seq #3 38 final class HotplugDetectionAction extends HdmiCecFeatureAction { 39 private static final String TAG = "HotPlugDetectionAction"; 40 41 public static final int POLLING_INTERVAL_MS_FOR_TV = 5000; 42 public static final int POLLING_INTERVAL_MS_FOR_PLAYBACK = 60000; 43 public static final int TIMEOUT_COUNT = 3; 44 private static final int AVR_COUNT_MAX = 3; 45 46 // State in which waits for next polling 47 private static final int STATE_WAIT_FOR_NEXT_POLLING = 1; 48 49 // All addresses except for broadcast (unregistered address). 50 private static final int NUM_OF_ADDRESS = Constants.ADDR_SPECIFIC_USE 51 - Constants.ADDR_TV + 1; 52 53 private int mTimeoutCount = 0; 54 55 // Counter used to ensure the connection to AVR is stable. Occasional failure to get 56 // polling response from AVR despite its presence leads to unstable status flipping. 57 // This is a workaround to deal with it, by removing the device only if the removal 58 // is detected {@code AVR_COUNT_MAX} times in a row. 59 private int mAvrStatusCount = 0; 60 61 private final boolean mIsTvDevice = localDevice().mService.isTvDevice(); 62 63 /** 64 * Constructor 65 * 66 * @param source {@link HdmiCecLocalDevice} instance 67 */ HotplugDetectionAction(HdmiCecLocalDevice source)68 HotplugDetectionAction(HdmiCecLocalDevice source) { 69 super(source); 70 } 71 getPollingInterval()72 private int getPollingInterval() { 73 return mIsTvDevice ? POLLING_INTERVAL_MS_FOR_TV : POLLING_INTERVAL_MS_FOR_PLAYBACK; 74 } 75 76 @Override start()77 boolean start() { 78 Slog.v(TAG, "Hot-plug detection started."); 79 80 mState = STATE_WAIT_FOR_NEXT_POLLING; 81 mTimeoutCount = 0; 82 83 // Start timer without polling. 84 // The first check for all devices will be initiated 15 seconds later for TV panels and 60 85 // seconds later for playback devices. 86 addTimer(mState, getPollingInterval()); 87 return true; 88 } 89 90 @Override processCommand(HdmiCecMessage cmd)91 boolean processCommand(HdmiCecMessage cmd) { 92 // No-op 93 return false; 94 } 95 96 @Override handleTimerEvent(int state)97 void handleTimerEvent(int state) { 98 if (mState != state) { 99 return; 100 } 101 102 if (mState == STATE_WAIT_FOR_NEXT_POLLING) { 103 if (mIsTvDevice) { 104 mTimeoutCount = (mTimeoutCount + 1) % TIMEOUT_COUNT; 105 if (mTimeoutCount == 0) { 106 pollAllDevices(); 107 } else if (tv().isSystemAudioActivated()) { 108 pollAudioSystem(); 109 } 110 addTimer(mState, POLLING_INTERVAL_MS_FOR_TV); 111 return; 112 } 113 pollAllDevices(); 114 addTimer(mState, POLLING_INTERVAL_MS_FOR_PLAYBACK); 115 } 116 } 117 118 /** 119 * Start device polling immediately. This method is called only by 120 * {@link HdmiCecLocalDeviceTv#onHotplug}. 121 */ pollAllDevicesNow()122 void pollAllDevicesNow() { 123 // Clear existing timer to avoid overlapped execution 124 mActionTimer.clearTimerMessage(); 125 126 mTimeoutCount = 0; 127 mState = STATE_WAIT_FOR_NEXT_POLLING; 128 pollAllDevices(); 129 130 addTimer(mState, getPollingInterval()); 131 } 132 pollAllDevices()133 private void pollAllDevices() { 134 Slog.v(TAG, "Poll all devices."); 135 136 pollDevices(new DevicePollingCallback() { 137 @Override 138 public void onPollingFinished(List<Integer> ackedAddress) { 139 checkHotplug(ackedAddress, false); 140 } 141 }, Constants.POLL_ITERATION_IN_ORDER 142 | Constants.POLL_STRATEGY_REMOTES_DEVICES, HdmiConfig.HOTPLUG_DETECTION_RETRY); 143 } 144 pollAudioSystem()145 private void pollAudioSystem() { 146 Slog.v(TAG, "Poll audio system."); 147 148 pollDevices(new DevicePollingCallback() { 149 @Override 150 public void onPollingFinished(List<Integer> ackedAddress) { 151 checkHotplug(ackedAddress, true); 152 } 153 }, Constants.POLL_ITERATION_IN_ORDER 154 | Constants.POLL_STRATEGY_SYSTEM_AUDIO, HdmiConfig.HOTPLUG_DETECTION_RETRY); 155 } 156 checkHotplug(List<Integer> ackedAddress, boolean audioOnly)157 private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) { 158 List<HdmiDeviceInfo> deviceInfoList = 159 localDevice().mService.getHdmiCecNetwork().getDeviceInfoList(false); 160 BitSet currentInfos = infoListToBitSet(deviceInfoList, audioOnly, false); 161 BitSet polledResult = addressListToBitSet(ackedAddress); 162 163 // At first, check removed devices. 164 BitSet removed = complement(currentInfos, polledResult); 165 int index = -1; 166 while ((index = removed.nextSetBit(index + 1)) != -1) { 167 if (mIsTvDevice && index == Constants.ADDR_AUDIO_SYSTEM) { 168 HdmiDeviceInfo avr = tv().getAvrDeviceInfo(); 169 if (avr != null && tv().isConnected(avr.getPortId())) { 170 ++mAvrStatusCount; 171 Slog.w(TAG, "Ack not returned from AVR. count: " + mAvrStatusCount); 172 if (mAvrStatusCount < AVR_COUNT_MAX) { 173 continue; 174 } 175 } 176 } 177 Slog.v(TAG, "Remove device by hot-plug detection:" + index); 178 removeDevice(index); 179 } 180 181 // Reset the counter if the ack is returned from AVR. 182 if (!removed.get(Constants.ADDR_AUDIO_SYSTEM)) { 183 mAvrStatusCount = 0; 184 } 185 186 // Next, check added devices. 187 BitSet currentInfosWithPhysicalAddress = infoListToBitSet(deviceInfoList, audioOnly, true); 188 BitSet added = complement(polledResult, currentInfosWithPhysicalAddress); 189 index = -1; 190 while ((index = added.nextSetBit(index + 1)) != -1) { 191 Slog.v(TAG, "Add device by hot-plug detection:" + index); 192 addDevice(index); 193 } 194 } 195 infoListToBitSet( List<HdmiDeviceInfo> infoList, boolean audioOnly, boolean requirePhysicalAddress)196 private static BitSet infoListToBitSet( 197 List<HdmiDeviceInfo> infoList, boolean audioOnly, boolean requirePhysicalAddress) { 198 BitSet set = new BitSet(NUM_OF_ADDRESS); 199 for (HdmiDeviceInfo info : infoList) { 200 boolean audioOnlyConditionMet = !audioOnly 201 || (info.getDeviceType() == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); 202 boolean requirePhysicalAddressConditionMet = !requirePhysicalAddress 203 || (info.getPhysicalAddress() != HdmiDeviceInfo.PATH_INVALID); 204 if (audioOnlyConditionMet && requirePhysicalAddressConditionMet) { 205 set.set(info.getLogicalAddress()); 206 } 207 } 208 return set; 209 } 210 addressListToBitSet(List<Integer> list)211 private static BitSet addressListToBitSet(List<Integer> list) { 212 BitSet set = new BitSet(NUM_OF_ADDRESS); 213 for (Integer value : list) { 214 set.set(value); 215 } 216 return set; 217 } 218 219 // A - B = A & ~B complement(BitSet first, BitSet second)220 private static BitSet complement(BitSet first, BitSet second) { 221 // Need to clone it so that it doesn't touch original set. 222 BitSet clone = (BitSet) first.clone(); 223 clone.andNot(second); 224 return clone; 225 } 226 addDevice(int addedAddress)227 private void addDevice(int addedAddress) { 228 // Sending <Give Physical Address> will initiate new device action. 229 sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(), 230 addedAddress)); 231 } 232 removeDevice(int removedAddress)233 private void removeDevice(int removedAddress) { 234 if (mIsTvDevice) { 235 mayChangeRoutingPath(removedAddress); 236 mayCancelOneTouchRecord(removedAddress); 237 mayDisableSystemAudioAndARC(removedAddress); 238 } 239 mayCancelDeviceSelect(removedAddress); 240 localDevice().mService.getHdmiCecNetwork().removeCecDevice(localDevice(), removedAddress); 241 } 242 mayChangeRoutingPath(int address)243 private void mayChangeRoutingPath(int address) { 244 HdmiDeviceInfo info = localDevice().mService.getHdmiCecNetwork().getCecDeviceInfo(address); 245 if (info != null) { 246 tv().handleRemoveActiveRoutingPath(info.getPhysicalAddress()); 247 } 248 } 249 mayCancelDeviceSelect(int address)250 private void mayCancelDeviceSelect(int address) { 251 List<DeviceSelectActionFromTv> actionsFromTv = getActions(DeviceSelectActionFromTv.class); 252 for (DeviceSelectActionFromTv action : actionsFromTv) { 253 if (action.getTargetAddress() == address) { 254 removeAction(DeviceSelectActionFromTv.class); 255 } 256 } 257 258 List<DeviceSelectActionFromPlayback> actionsFromPlayback = getActions( 259 DeviceSelectActionFromPlayback.class); 260 for (DeviceSelectActionFromPlayback action : actionsFromPlayback) { 261 if (action.getTargetAddress() == address) { 262 removeAction(DeviceSelectActionFromTv.class); 263 } 264 } 265 } 266 mayCancelOneTouchRecord(int address)267 private void mayCancelOneTouchRecord(int address) { 268 List<OneTouchRecordAction> actions = getActions(OneTouchRecordAction.class); 269 for (OneTouchRecordAction action : actions) { 270 if (action.getRecorderAddress() == address) { 271 removeAction(action); 272 } 273 } 274 } 275 mayDisableSystemAudioAndARC(int address)276 private void mayDisableSystemAudioAndARC(int address) { 277 if (!HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, address)) { 278 return; 279 } 280 281 tv().setSystemAudioMode(false); 282 if (tv().isArcEstablished()) { 283 tv().enableAudioReturnChannel(false); 284 addAndStartAction(new RequestArcTerminationAction(localDevice(), address)); 285 } 286 } 287 } 288