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