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