HdmiCecLocalDevicePlayback.java revision 8913a0e1e8559d96e82b1ff408dd312ed279021b
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.HdmiControlManager; 20import android.hardware.hdmi.HdmiDeviceInfo; 21import android.hardware.hdmi.IHdmiControlCallback; 22import android.os.PowerManager; 23import android.os.PowerManager.WakeLock; 24import android.os.RemoteException; 25import android.os.SystemProperties; 26import android.util.Slog; 27 28import com.android.internal.util.IndentingPrintWriter; 29import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; 30 31/** 32 * Represent a logical device of type Playback residing in Android system. 33 */ 34final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { 35 private static final String TAG = "HdmiCecLocalDevicePlayback"; 36 37 private boolean mIsActiveSource = false; 38 39 // Used to keep the device awake while it is the active source. For devices that 40 // cannot wake up via CEC commands, this address the inconvenience of having to 41 // turn them on. 42 // Lazily initialized - should call getWakeLock() to get the instance. 43 private WakeLock mWakeLock; 44 45 HdmiCecLocalDevicePlayback(HdmiControlService service) { 46 super(service, HdmiDeviceInfo.DEVICE_PLAYBACK); 47 } 48 49 @Override 50 @ServiceThreadOnly 51 protected void onAddressAllocated(int logicalAddress, int reason) { 52 assertRunOnServiceThread(); 53 mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( 54 mAddress, mService.getPhysicalAddress(), mDeviceType)); 55 mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( 56 mAddress, mService.getVendorId())); 57 startQueuedActions(); 58 59 // Switch TV input after bootup. 60 setActiveSource(true); 61 maySendActiveSource(Constants.ADDR_TV); 62 } 63 64 @Override 65 @ServiceThreadOnly 66 protected int getPreferredAddress() { 67 assertRunOnServiceThread(); 68 return SystemProperties.getInt(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, 69 Constants.ADDR_UNREGISTERED); 70 } 71 72 @Override 73 @ServiceThreadOnly 74 protected void setPreferredAddress(int addr) { 75 assertRunOnServiceThread(); 76 SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, 77 String.valueOf(addr)); 78 } 79 80 @ServiceThreadOnly 81 void oneTouchPlay(IHdmiControlCallback callback) { 82 assertRunOnServiceThread(); 83 if (hasAction(OneTouchPlayAction.class)) { 84 Slog.w(TAG, "oneTouchPlay already in progress"); 85 invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS); 86 return; 87 } 88 89 // TODO: Consider the case of multiple TV sets. For now we always direct the command 90 // to the primary one. 91 OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV, 92 callback); 93 if (action == null) { 94 Slog.w(TAG, "Cannot initiate oneTouchPlay"); 95 invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION); 96 return; 97 } 98 addAndStartAction(action); 99 } 100 101 @ServiceThreadOnly 102 void queryDisplayStatus(IHdmiControlCallback callback) { 103 assertRunOnServiceThread(); 104 if (hasAction(DevicePowerStatusAction.class)) { 105 Slog.w(TAG, "queryDisplayStatus already in progress"); 106 invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS); 107 return; 108 } 109 DevicePowerStatusAction action = DevicePowerStatusAction.create(this, 110 Constants.ADDR_TV, callback); 111 if (action == null) { 112 Slog.w(TAG, "Cannot initiate queryDisplayStatus"); 113 invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION); 114 return; 115 } 116 addAndStartAction(action); 117 } 118 119 @ServiceThreadOnly 120 private void invokeCallback(IHdmiControlCallback callback, int result) { 121 assertRunOnServiceThread(); 122 try { 123 callback.onComplete(result); 124 } catch (RemoteException e) { 125 Slog.e(TAG, "Invoking callback failed:" + e); 126 } 127 } 128 129 @Override 130 @ServiceThreadOnly 131 void onHotplug(int portId, boolean connected) { 132 assertRunOnServiceThread(); 133 mCecMessageCache.flushAll(); 134 // We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3. 135 if (connected && mService.isPowerStandbyOrTransient()) { 136 mService.wakeUp(); 137 } 138 if (!connected) { 139 getWakeLock().release(); 140 } 141 } 142 143 @ServiceThreadOnly 144 void setActiveSource(boolean on) { 145 assertRunOnServiceThread(); 146 mIsActiveSource = on; 147 if (on) { 148 getWakeLock().acquire(); 149 HdmiLogger.debug("active source: %b. Wake lock acquired", mIsActiveSource); 150 } else { 151 getWakeLock().release(); 152 HdmiLogger.debug("Wake lock released"); 153 } 154 } 155 156 @ServiceThreadOnly 157 private WakeLock getWakeLock() { 158 assertRunOnServiceThread(); 159 if (mWakeLock == null) { 160 mWakeLock = mService.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); 161 mWakeLock.setReferenceCounted(false); 162 } 163 return mWakeLock; 164 } 165 166 @Override 167 protected boolean canGoToStandby() { 168 return !getWakeLock().isHeld(); 169 } 170 171 @Override 172 @ServiceThreadOnly 173 protected boolean handleActiveSource(HdmiCecMessage message) { 174 assertRunOnServiceThread(); 175 int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 176 mayResetActiveSource(physicalAddress); 177 return true; // Broadcast message. 178 } 179 180 private void mayResetActiveSource(int physicalAddress) { 181 if (physicalAddress != mService.getPhysicalAddress()) { 182 setActiveSource(false); 183 } 184 } 185 186 @Override 187 @ServiceThreadOnly 188 protected boolean handleSetStreamPath(HdmiCecMessage message) { 189 assertRunOnServiceThread(); 190 int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 191 maySetActiveSource(physicalAddress); 192 maySendActiveSource(message.getSource()); 193 wakeUpIfActiveSource(); 194 return true; // Broadcast message. 195 } 196 197 // Samsung model we tested sends <Routing Change> and <Request Active Source> 198 // in a row, and then changes the input to the internal source if there is no 199 // <Active Source> in response. To handle this, we'll set ActiveSource aggressively. 200 @Override 201 @ServiceThreadOnly 202 protected boolean handleRoutingChange(HdmiCecMessage message) { 203 assertRunOnServiceThread(); 204 int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2); 205 maySetActiveSource(newPath); 206 return true; // Broadcast message. 207 } 208 209 @Override 210 @ServiceThreadOnly 211 protected boolean handleRoutingInformation(HdmiCecMessage message) { 212 assertRunOnServiceThread(); 213 int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 214 maySetActiveSource(physicalAddress); 215 return true; // Broadcast message. 216 } 217 218 private void maySetActiveSource(int physicalAddress) { 219 setActiveSource(physicalAddress == mService.getPhysicalAddress()); 220 } 221 222 private void wakeUpIfActiveSource() { 223 if (mIsActiveSource && mService.isPowerStandbyOrTransient()) { 224 mService.wakeUp(); 225 } 226 } 227 228 private void maySendActiveSource(int dest) { 229 if (mIsActiveSource) { 230 mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource( 231 mAddress, mService.getPhysicalAddress())); 232 // Always reports menu-status active to receive RCP. 233 mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus( 234 mAddress, dest, Constants.MENU_STATE_ACTIVATED)); 235 } 236 } 237 238 @Override 239 @ServiceThreadOnly 240 protected boolean handleRequestActiveSource(HdmiCecMessage message) { 241 assertRunOnServiceThread(); 242 maySendActiveSource(message.getSource()); 243 return true; // Broadcast message. 244 } 245 246 @Override 247 @ServiceThreadOnly 248 protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) { 249 super.disableDevice(initiatedByCec, callback); 250 251 assertRunOnServiceThread(); 252 if (!initiatedByCec && mIsActiveSource) { 253 mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource( 254 mAddress, mService.getPhysicalAddress())); 255 } 256 setActiveSource(false); 257 checkIfPendingActionsCleared(); 258 } 259 260 @Override 261 protected void dump(final IndentingPrintWriter pw) { 262 super.dump(pw); 263 pw.println("mIsActiveSource: " + mIsActiveSource); 264 } 265} 266