HdmiCecLocalDevicePlayback.java revision 229c422c7bccddf4f55bad5fde8161828347906c
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    }
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}