OSDN Git Service

a8f6954405c33f3ea14fc136a44014c61b4f4f15
[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.PowerManager;
23 import android.os.PowerManager.WakeLock;
24 import android.os.RemoteException;
25 import android.os.SystemProperties;
26 import android.util.Slog;
27
28 import com.android.internal.util.IndentingPrintWriter;
29 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
30
31 /**
32  * Represent a logical device of type Playback residing in Android system.
33  */
34 final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
35     private static final String TAG = "HdmiCecLocalDevicePlayback";
36
37     private boolean mIsActiveSource = false;
38
39     // Used to keep the device awake while it is the active source. For devices that
40     // cannot wake up via CEC commands, this address the inconvenience of having to
41     // turn them on.
42     // Lazily initialized - should call getWakeLock() to get the instance.
43     private WakeLock mWakeLock;
44
45     HdmiCecLocalDevicePlayback(HdmiControlService service) {
46         super(service, HdmiDeviceInfo.DEVICE_PLAYBACK);
47     }
48
49     @Override
50     @ServiceThreadOnly
51     protected void onAddressAllocated(int logicalAddress, int reason) {
52         assertRunOnServiceThread();
53         mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
54                 mAddress, mService.getPhysicalAddress(), mDeviceType));
55         mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
56                 mAddress, mService.getVendorId()));
57         startQueuedActions();
58     }
59
60     @Override
61     @ServiceThreadOnly
62     protected int getPreferredAddress() {
63         assertRunOnServiceThread();
64         return SystemProperties.getInt(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
65                 Constants.ADDR_UNREGISTERED);
66     }
67
68     @Override
69     @ServiceThreadOnly
70     protected void setPreferredAddress(int addr) {
71         assertRunOnServiceThread();
72         SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
73                 String.valueOf(addr));
74     }
75
76     @ServiceThreadOnly
77     void oneTouchPlay(IHdmiControlCallback callback) {
78         assertRunOnServiceThread();
79         if (hasAction(OneTouchPlayAction.class)) {
80             Slog.w(TAG, "oneTouchPlay already in progress");
81             invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS);
82             return;
83         }
84
85         // TODO: Consider the case of multiple TV sets. For now we always direct the command
86         //       to the primary one.
87         OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV,
88                 callback);
89         if (action == null) {
90             Slog.w(TAG, "Cannot initiate oneTouchPlay");
91             invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
92             return;
93         }
94         addAndStartAction(action);
95     }
96
97     @ServiceThreadOnly
98     void queryDisplayStatus(IHdmiControlCallback callback) {
99         assertRunOnServiceThread();
100         if (hasAction(DevicePowerStatusAction.class)) {
101             Slog.w(TAG, "queryDisplayStatus already in progress");
102             invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS);
103             return;
104         }
105         DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
106                 Constants.ADDR_TV, callback);
107         if (action == null) {
108             Slog.w(TAG, "Cannot initiate queryDisplayStatus");
109             invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
110             return;
111         }
112         addAndStartAction(action);
113     }
114
115     @ServiceThreadOnly
116     private void invokeCallback(IHdmiControlCallback callback, int result) {
117         assertRunOnServiceThread();
118         try {
119             callback.onComplete(result);
120         } catch (RemoteException e) {
121             Slog.e(TAG, "Invoking callback failed:" + e);
122         }
123     }
124
125     @Override
126     @ServiceThreadOnly
127     void onHotplug(int portId, boolean connected) {
128         assertRunOnServiceThread();
129         mCecMessageCache.flushAll();
130         // We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3.
131         if (connected && mService.isPowerStandbyOrTransient()) {
132             mService.wakeUp();
133         }
134         if (!connected) {
135             getWakeLock().release();
136         }
137     }
138
139     @ServiceThreadOnly
140     void setActiveSource(boolean on) {
141         assertRunOnServiceThread();
142         mIsActiveSource = on;
143         if (on) {
144             getWakeLock().acquire();
145             HdmiLogger.debug("active source: %b. Wake lock acquired", mIsActiveSource);
146         } else {
147             getWakeLock().release();
148             HdmiLogger.debug("Wake lock released");
149         }
150     }
151
152     @ServiceThreadOnly
153     private WakeLock getWakeLock() {
154         assertRunOnServiceThread();
155         if (mWakeLock == null) {
156             mWakeLock = mService.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
157             mWakeLock.setReferenceCounted(false);
158         }
159         return mWakeLock;
160     }
161
162     @Override
163     protected boolean canGoToStandby() {
164         return !getWakeLock().isHeld();
165     }
166
167     @Override
168     @ServiceThreadOnly
169     protected boolean handleActiveSource(HdmiCecMessage message) {
170         assertRunOnServiceThread();
171         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
172         mayResetActiveSource(physicalAddress);
173         return true;  // Broadcast message.
174     }
175
176     private void mayResetActiveSource(int physicalAddress) {
177         if (physicalAddress != mService.getPhysicalAddress()) {
178             setActiveSource(false);
179         }
180     }
181
182     @Override
183     @ServiceThreadOnly
184     protected boolean handleSetStreamPath(HdmiCecMessage message) {
185         assertRunOnServiceThread();
186         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
187         maySetActiveSource(physicalAddress);
188         maySendActiveSource(message.getSource());
189         wakeUpIfActiveSource();
190         return true;  // Broadcast message.
191     }
192
193     // Samsung model we tested sends <Routing Change> and <Request Active Source>
194     // in a row, and then changes the input to the internal source if there is no
195     // <Active Source> in response. To handle this, we'll set ActiveSource aggressively.
196     @Override
197     @ServiceThreadOnly
198     protected boolean handleRoutingChange(HdmiCecMessage message) {
199         assertRunOnServiceThread();
200         int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2);
201         maySetActiveSource(newPath);
202         return true;  // Broadcast message.
203     }
204
205     @Override
206     @ServiceThreadOnly
207     protected boolean handleRoutingInformation(HdmiCecMessage message) {
208         assertRunOnServiceThread();
209         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
210         maySetActiveSource(physicalAddress);
211         return true;  // Broadcast message.
212     }
213
214     private void maySetActiveSource(int physicalAddress) {
215         setActiveSource(physicalAddress == mService.getPhysicalAddress());
216     }
217
218     private void wakeUpIfActiveSource() {
219         if (mIsActiveSource && mService.isPowerStandbyOrTransient()) {
220             mService.wakeUp();
221         }
222     }
223
224     private void maySendActiveSource(int dest) {
225         if (mIsActiveSource) {
226             mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
227                     mAddress, mService.getPhysicalAddress()));
228             // Always reports menu-status active to receive RCP.
229             mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus(
230                     mAddress, dest, Constants.MENU_STATE_ACTIVATED));
231         }
232     }
233
234     @Override
235     @ServiceThreadOnly
236     protected boolean handleRequestActiveSource(HdmiCecMessage message) {
237         assertRunOnServiceThread();
238         maySendActiveSource(message.getSource());
239         return true;  // Broadcast message.
240     }
241
242     @Override
243     @ServiceThreadOnly
244     protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
245         super.disableDevice(initiatedByCec, callback);
246
247         assertRunOnServiceThread();
248         if (!initiatedByCec && mIsActiveSource) {
249             mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
250                     mAddress, mService.getPhysicalAddress()));
251         }
252         setActiveSource(false);
253         checkIfPendingActionsCleared();
254     }
255
256     @Override
257     protected void dump(final IndentingPrintWriter pw) {
258         super.dump(pw);
259         pw.println("mIsActiveSource: " + mIsActiveSource);
260     }
261 }