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