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