HdmiCecLocalDevicePlayback.java revision 229c422c7bccddf4f55bad5fde8161828347906c
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.RemoteException; 23import android.os.SystemProperties; 24import android.util.Slog; 25 26import com.android.internal.util.IndentingPrintWriter; 27import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; 28 29/** 30 * Represent a logical device of type Playback residing in Android system. 31 */ 32final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { 33 private static final String TAG = "HdmiCecLocalDevicePlayback"; 34 35 private boolean mIsActiveSource = false; 36 37 HdmiCecLocalDevicePlayback(HdmiControlService service) { 38 super(service, HdmiDeviceInfo.DEVICE_PLAYBACK); 39 } 40 41 @Override 42 @ServiceThreadOnly 43 protected void onAddressAllocated(int logicalAddress, int reason) { 44 assertRunOnServiceThread(); 45 mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( 46 mAddress, mService.getPhysicalAddress(), mDeviceType)); 47 } 48 49 @Override 50 @ServiceThreadOnly 51 protected int getPreferredAddress() { 52 assertRunOnServiceThread(); 53 return SystemProperties.getInt(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, 54 Constants.ADDR_UNREGISTERED); 55 } 56 57 @Override 58 @ServiceThreadOnly 59 protected void setPreferredAddress(int addr) { 60 assertRunOnServiceThread(); 61 SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK, 62 String.valueOf(addr)); 63 } 64 65 @ServiceThreadOnly 66 void oneTouchPlay(IHdmiControlCallback callback) { 67 assertRunOnServiceThread(); 68 if (hasAction(OneTouchPlayAction.class)) { 69 Slog.w(TAG, "oneTouchPlay already in progress"); 70 invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS); 71 return; 72 } 73 74 // TODO: Consider the case of multiple TV sets. For now we always direct the command 75 // to the primary one. 76 OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV, 77 callback); 78 if (action == null) { 79 Slog.w(TAG, "Cannot initiate oneTouchPlay"); 80 invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION); 81 return; 82 } 83 addAndStartAction(action); 84 } 85 86 @ServiceThreadOnly 87 void queryDisplayStatus(IHdmiControlCallback callback) { 88 assertRunOnServiceThread(); 89 if (hasAction(DevicePowerStatusAction.class)) { 90 Slog.w(TAG, "queryDisplayStatus already in progress"); 91 invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS); 92 return; 93 } 94 DevicePowerStatusAction action = DevicePowerStatusAction.create(this, 95 Constants.ADDR_TV, callback); 96 if (action == null) { 97 Slog.w(TAG, "Cannot initiate queryDisplayStatus"); 98 invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION); 99 return; 100 } 101 addAndStartAction(action); 102 } 103 104 @ServiceThreadOnly 105 private void invokeCallback(IHdmiControlCallback callback, int result) { 106 assertRunOnServiceThread(); 107 try { 108 callback.onComplete(result); 109 } catch (RemoteException e) { 110 Slog.e(TAG, "Invoking callback failed:" + e); 111 } 112 } 113 114 @Override 115 @ServiceThreadOnly 116 void onHotplug(int portId, boolean connected) { 117 assertRunOnServiceThread(); 118 mCecMessageCache.flushAll(); 119 // We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3. 120 if (connected && mService.isPowerStandbyOrTransient()) { 121 mService.wakeUp(); 122 } 123 } 124 125 @ServiceThreadOnly 126 void markActiveSource() { 127 assertRunOnServiceThread(); 128 mIsActiveSource = true; 129 } 130 131 @Override 132 @ServiceThreadOnly 133 protected boolean handleActiveSource(HdmiCecMessage message) { 134 assertRunOnServiceThread(); 135 int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 136 mayResetActiveSource(physicalAddress); 137 return true; // Broadcast message. 138 } 139 140 private void mayResetActiveSource(int physicalAddress) { 141 if (physicalAddress != mService.getPhysicalAddress()) { 142 mIsActiveSource = false; 143 } 144 } 145 146 @Override 147 @ServiceThreadOnly 148 protected boolean handleSetStreamPath(HdmiCecMessage message) { 149 assertRunOnServiceThread(); 150 int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 151 maySetActiveSource(physicalAddress); 152 maySendActiveSource(); 153 wakeUpIfActiveSource(); 154 return true; // Broadcast message. 155 } 156 157 // Samsung model, we tested, sends <RoutingChange> and <RequestActiveSource> consecutively, 158 // Then if there is no <ActiveSource> response, it will change the input to 159 // the internal source. To handle this, we'll set ActiveSource aggressively. 160 @Override 161 @ServiceThreadOnly 162 protected boolean handleRoutingChange(HdmiCecMessage message) { 163 assertRunOnServiceThread(); 164 int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2); 165 maySetActiveSource(newPath); 166 return true; // Broadcast message. 167 } 168 169 @Override 170 @ServiceThreadOnly 171 protected boolean handleRoutingInformation(HdmiCecMessage message) { 172 assertRunOnServiceThread(); 173 int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); 174 maySetActiveSource(physicalAddress); 175 return true; // Broadcast message. 176 } 177 178 private void maySetActiveSource(int physicalAddress) { 179 if (physicalAddress == mService.getPhysicalAddress()) { 180 mIsActiveSource = true; 181 } else { 182 mIsActiveSource = false; 183 } 184 } 185 186 private void wakeUpIfActiveSource() { 187 if (mIsActiveSource && mService.isPowerStandbyOrTransient()) { 188 mService.wakeUp(); 189 } 190 } 191 192 private void maySendActiveSource() { 193 if (mIsActiveSource) { 194 mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource( 195 mAddress, mService.getPhysicalAddress())); 196 } 197 } 198 199 @Override 200 @ServiceThreadOnly 201 protected boolean handleRequestActiveSource(HdmiCecMessage message) { 202 assertRunOnServiceThread(); 203 maySendActiveSource(); 204 return true; // Broadcast message. 205 } 206 207 @Override 208 @ServiceThreadOnly 209 protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) { 210 super.disableDevice(initiatedByCec, callback); 211 212 assertRunOnServiceThread(); 213 if (!initiatedByCec && mIsActiveSource) { 214 mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource( 215 mAddress, mService.getPhysicalAddress())); 216 } 217 mIsActiveSource = false; 218 checkIfPendingActionsCleared(); 219 } 220 221 @Override 222 protected void dump(final IndentingPrintWriter pw) { 223 super.dump(pw); 224 pw.println("mIsActiveSource: " + mIsActiveSource); 225 } 226}