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