OSDN Git Service

CEC: Prevents HdmiControlService from turning on TV on wakeup
[android-x86/frameworks-base.git] / services / core / java / com / android / server / hdmi / HdmiCecLocalDevicePlayback.java
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
17 package com.android.server.hdmi;
18
19 import android.hardware.hdmi.HdmiControlManager;
20 import android.hardware.hdmi.HdmiDeviceInfo;
21 import android.hardware.hdmi.IHdmiControlCallback;
22 import android.os.RemoteException;
23 import android.os.SystemProperties;
24 import android.util.Slog;
25
26 import com.android.internal.util.IndentingPrintWriter;
27 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
28
29 /**
30  * Represent a logical device of type Playback residing in Android system.
31  */
32 final 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 }