HdmiCecLocalDevicePlayback.java revision 64bafd9108b532102bc13c47966444b073c33faf
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.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
27
28/**
29 * Represent a logical device of type Playback residing in Android system.
30 */
31final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
32    private static final String TAG = "HdmiCecLocalDevicePlayback";
33
34    private boolean mIsActiveSource = false;
35
36    HdmiCecLocalDevicePlayback(HdmiControlService service) {
37        super(service, HdmiDeviceInfo.DEVICE_PLAYBACK);
38    }
39
40    @Override
41    @ServiceThreadOnly
42    protected void onAddressAllocated(int logicalAddress, int reason) {
43        assertRunOnServiceThread();
44        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
45                mAddress, mService.getPhysicalAddress(), mDeviceType));
46        if (reason == HdmiControlService.INITIATED_BY_SCREEN_ON) {
47            oneTouchPlay(new IHdmiControlCallback.Stub() {
48                @Override public void onComplete(int result) throws RemoteException {}
49            });
50        }
51    }
52
53    @Override
54    @ServiceThreadOnly
55    protected int getPreferredAddress() {
56        assertRunOnServiceThread();
57        return SystemProperties.getInt(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
58                Constants.ADDR_UNREGISTERED);
59    }
60
61    @Override
62    @ServiceThreadOnly
63    protected void setPreferredAddress(int addr) {
64        assertRunOnServiceThread();
65        SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
66                String.valueOf(addr));
67    }
68
69    @ServiceThreadOnly
70    void oneTouchPlay(IHdmiControlCallback callback) {
71        assertRunOnServiceThread();
72        if (hasAction(OneTouchPlayAction.class)) {
73            Slog.w(TAG, "oneTouchPlay already in progress");
74            invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS);
75            return;
76        }
77
78        // TODO: Consider the case of multiple TV sets. For now we always direct the command
79        //       to the primary one.
80        OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV,
81                callback);
82        if (action == null) {
83            Slog.w(TAG, "Cannot initiate oneTouchPlay");
84            invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
85            return;
86        }
87        addAndStartAction(action);
88    }
89
90    @ServiceThreadOnly
91    void queryDisplayStatus(IHdmiControlCallback callback) {
92        assertRunOnServiceThread();
93        if (hasAction(DevicePowerStatusAction.class)) {
94            Slog.w(TAG, "queryDisplayStatus already in progress");
95            invokeCallback(callback, HdmiControlManager.RESULT_ALREADY_IN_PROGRESS);
96            return;
97        }
98        DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
99                Constants.ADDR_TV, callback);
100        if (action == null) {
101            Slog.w(TAG, "Cannot initiate queryDisplayStatus");
102            invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
103            return;
104        }
105        addAndStartAction(action);
106    }
107
108    @ServiceThreadOnly
109    private void invokeCallback(IHdmiControlCallback callback, int result) {
110        assertRunOnServiceThread();
111        try {
112            callback.onComplete(result);
113        } catch (RemoteException e) {
114            Slog.e(TAG, "Invoking callback failed:" + e);
115        }
116    }
117
118    @Override
119    @ServiceThreadOnly
120    void onHotplug(int portId, boolean connected) {
121        assertRunOnServiceThread();
122        mCecMessageCache.flushAll();
123        mIsActiveSource = false;
124        if (connected && mService.isPowerStandbyOrTransient()) {
125            mService.wakeUp();
126        }
127    }
128
129    @ServiceThreadOnly
130    void markActiveSource() {
131        assertRunOnServiceThread();
132        mIsActiveSource = true;
133    }
134
135    @Override
136    @ServiceThreadOnly
137    protected boolean handleActiveSource(HdmiCecMessage message) {
138        assertRunOnServiceThread();
139        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
140        mayResetActiveSource(physicalAddress);
141        return true;  // Broadcast message.
142    }
143
144    private void mayResetActiveSource(int physicalAddress) {
145        if (physicalAddress != mService.getPhysicalAddress()) {
146            mIsActiveSource = false;
147        }
148    }
149
150    @Override
151    @ServiceThreadOnly
152    protected boolean handleSetStreamPath(HdmiCecMessage message) {
153        assertRunOnServiceThread();
154        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
155        maySetActiveSource(physicalAddress);
156        maySendActiveSource();
157        wakeUpIfActiveSource();
158        return true;  // Broadcast message.
159    }
160
161    // Samsung model, we tested, sends <RoutingChange> and <RequestActiveSource> consecutively,
162    // Then if there is no <ActiveSource> response, it will change the input to
163    // the internal source.  To handle this, we'll set ActiveSource aggressively.
164    @Override
165    @ServiceThreadOnly
166    protected boolean handleRoutingChange(HdmiCecMessage message) {
167        assertRunOnServiceThread();
168        int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2);
169        maySetActiveSource(newPath);
170        return true;  // Broadcast message.
171    }
172
173    @Override
174    @ServiceThreadOnly
175    protected boolean handleRoutingInformation(HdmiCecMessage message) {
176        assertRunOnServiceThread();
177        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
178        maySetActiveSource(physicalAddress);
179        return true;  // Broadcast message.
180    }
181
182    private void maySetActiveSource(int physicalAddress) {
183        if (physicalAddress == mService.getPhysicalAddress()) {
184            mIsActiveSource = true;
185        }
186    }
187
188    private void wakeUpIfActiveSource() {
189        if (mIsActiveSource && mService.isPowerStandbyOrTransient()) {
190            mService.wakeUp();
191        }
192    }
193
194    private void maySendActiveSource() {
195        if (mIsActiveSource) {
196            mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
197                    mAddress, mService.getPhysicalAddress()));
198        }
199    }
200
201    @Override
202    @ServiceThreadOnly
203    protected boolean handleRequestActiveSource(HdmiCecMessage message) {
204        assertRunOnServiceThread();
205        maySendActiveSource();
206        return true;  // Broadcast message.
207    }
208
209    @Override
210    @ServiceThreadOnly
211    protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
212        super.disableDevice(initiatedByCec, callback);
213
214        assertRunOnServiceThread();
215        if (!initiatedByCec && mIsActiveSource) {
216            mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
217                    mAddress, mService.getPhysicalAddress()));
218        }
219        mIsActiveSource = false;
220        checkIfPendingActionsCleared();
221    }
222}