blob: f42c212281f2f55124380aa353f1d6021679879e [file] [log] [blame]
Jungshik Jang0792d372014-04-23 17:57:26 +09001/*
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
Jungshik Jange9c77c82014-04-24 20:30:09 +090019import android.hardware.hdmi.HdmiCec;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090020import android.hardware.hdmi.HdmiCecDeviceInfo;
Jungshik Jange9c77c82014-04-24 20:30:09 +090021import android.hardware.hdmi.HdmiCecMessage;
Jungshik Jang0792d372014-04-23 17:57:26 +090022import android.os.Handler;
23import android.os.Looper;
24import android.os.Message;
Jungshik Jange9c77c82014-04-24 20:30:09 +090025import android.util.Slog;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090026import android.util.SparseArray;
Jungshik Jange9c77c82014-04-24 20:30:09 +090027
Jungshik Jang7d9a8432014-04-29 15:12:43 +090028import java.util.ArrayList;
Jungshik Jange9c77c82014-04-24 20:30:09 +090029import java.util.Arrays;
Jungshik Jang7d9a8432014-04-29 15:12:43 +090030import java.util.List;
Jungshik Jang0792d372014-04-23 17:57:26 +090031
32/**
33 * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
34 * and pass it to CEC HAL so that it sends message to other device. For incoming
35 * message it translates the message and delegates it to proper module.
36 *
37 * <p>It can be created only by {@link HdmiCecController#create}
38 *
39 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
40 */
41class HdmiCecController {
42 private static final String TAG = "HdmiCecController";
43
Jungshik Jange9c77c82014-04-24 20:30:09 +090044 // A message to pass cec send command to IO looper.
45 private static final int MSG_SEND_CEC_COMMAND = 1;
46
47 // Message types to handle incoming message in main service looper.
48 private final static int MSG_RECEIVE_CEC_COMMAND = 1;
49
50 // TODO: move these values to HdmiCec.java once make it internal constant class.
51 // CEC's ABORT reason values.
52 private static final int ABORT_UNRECOGNIZED_MODE = 0;
53 private static final int ABORT_NOT_IN_CORRECT_MODE = 1;
54 private static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
55 private static final int ABORT_INVALID_OPERAND = 3;
56 private static final int ABORT_REFUSED = 4;
57 private static final int ABORT_UNABLE_TO_DETERMINE = 5;
58
Jungshik Jang0792d372014-04-23 17:57:26 +090059 // Handler instance to process synchronous I/O (mainly send) message.
60 private Handler mIoHandler;
61
62 // Handler instance to process various messages coming from other CEC
63 // device or issued by internal state change.
Jungshik Jange9c77c82014-04-24 20:30:09 +090064 private Handler mControlHandler;
Jungshik Jang0792d372014-04-23 17:57:26 +090065
66 // Stores the pointer to the native implementation of the service that
67 // interacts with HAL.
68 private long mNativePtr;
69
Jungshik Jang7d9a8432014-04-29 15:12:43 +090070 // Map-like container of all cec devices. A logical address of device is
71 // used as key of container.
72 private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos =
73 new SparseArray<HdmiCecDeviceInfo>();
74
Jungshik Jang0792d372014-04-23 17:57:26 +090075 // Private constructor. Use HdmiCecController.create().
76 private HdmiCecController() {
77 }
78
79 /**
80 * A factory method to get {@link HdmiCecController}. If it fails to initialize
81 * inner device or has no device it will return {@code null}.
82 *
83 * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
Jungshik Jange9c77c82014-04-24 20:30:09 +090084 * @param service {@link HdmiControlService} instance used to create internal handler
85 * and to pass callback for incoming message or event.
Jungshik Jang0792d372014-04-23 17:57:26 +090086 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
87 * returns {@code null}.
88 */
Jungshik Jange9c77c82014-04-24 20:30:09 +090089 static HdmiCecController create(HdmiControlService service) {
Jungshik Jang0792d372014-04-23 17:57:26 +090090 HdmiCecController handler = new HdmiCecController();
91 long nativePtr = nativeInit(handler);
92 if (nativePtr == 0L) {
93 handler = null;
94 return null;
95 }
96
Jungshik Jange9c77c82014-04-24 20:30:09 +090097 handler.init(service, nativePtr);
Jungshik Jang0792d372014-04-23 17:57:26 +090098 return handler;
99 }
100
Jungshik Jange9c77c82014-04-24 20:30:09 +0900101 private static byte[] buildBody(int opcode, byte[] params) {
102 byte[] body = new byte[params.length + 1];
103 body[0] = (byte) opcode;
104 System.arraycopy(params, 0, body, 1, params.length);
105 return body;
106 }
Jungshik Jang0792d372014-04-23 17:57:26 +0900107
Jungshik Jange9c77c82014-04-24 20:30:09 +0900108 private final class IoHandler extends Handler {
109 private IoHandler(Looper looper) {
110 super(looper);
111 }
112
113 @Override
114 public void handleMessage(Message msg) {
115 switch (msg.what) {
116 case MSG_SEND_CEC_COMMAND:
117 HdmiCecMessage cecMessage = (HdmiCecMessage) msg.obj;
118 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
119 nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
120 cecMessage.getDestination(), body);
121 break;
122 default:
123 Slog.w(TAG, "Unsupported CEC Io request:" + msg.what);
124 break;
125 }
126 }
127 }
128
129 private final class ControlHandler extends Handler {
130 private ControlHandler(Looper looper) {
131 super(looper);
132 }
133
134 @Override
135 public void handleMessage(Message msg) {
136 switch (msg.what) {
137 case MSG_RECEIVE_CEC_COMMAND:
138 // TODO: delegate it to HdmiControl service.
139 onReceiveCommand((HdmiCecMessage) msg.obj);
140 break;
141 default:
142 Slog.i(TAG, "Unsupported message type:" + msg.what);
143 break;
144 }
145 }
146 }
147
Jungshik Jang7d9a8432014-04-29 15:12:43 +0900148 /**
149 * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
150 * logical address as new device info's.
151 *
152 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
153 *
154 * @param deviceInfo a new {@link HdmiCecDeviceInfo} to be added.
155 * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
156 * that has the same logical address as new one has.
157 */
158 HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
159 HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
160 if (oldDeviceInfo != null) {
161 removeDeviceInfo(deviceInfo.getLogicalAddress());
162 }
163 mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
164 return oldDeviceInfo;
165 }
166
167 /**
168 * Remove a device info corresponding to the given {@code logicalAddress}.
169 * It returns removed {@link HdmiCecDeviceInfo} if exists.
170 *
171 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
172 *
173 * @param logicalAddress logical address of device to be removed
174 * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
175 */
176 HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
177 HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
178 if (deviceInfo != null) {
179 mDeviceInfos.remove(logicalAddress);
180 }
181 return deviceInfo;
182 }
183
184 /**
185 * Return a list of all {@HdmiCecDeviceInfo}.
186 *
187 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
188 */
189 List<HdmiCecDeviceInfo> getDeviceInfoList() {
190 List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<HdmiCecDeviceInfo>(
191 mDeviceInfos.size());
192 for (int i = 0; i < mDeviceInfos.size(); ++i) {
193 deviceInfoList.add(mDeviceInfos.valueAt(i));
194 }
195 return deviceInfoList;
196 }
197
198 /**
199 * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
200 *
201 * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
202 *
203 * @param logicalAddress logical address to be retrieved
204 * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
205 * Returns null if no logical address matched
206 */
207 HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
208 return mDeviceInfos.get(logicalAddress);
209 }
210
Jungshik Jange9c77c82014-04-24 20:30:09 +0900211 private void init(HdmiControlService service, long nativePtr) {
212 mIoHandler = new IoHandler(service.getServiceLooper());
213 mControlHandler = new ControlHandler(service.getServiceLooper());
Jungshik Jang0792d372014-04-23 17:57:26 +0900214 mNativePtr = nativePtr;
215 }
216
Jungshik Jange9c77c82014-04-24 20:30:09 +0900217 private void onReceiveCommand(HdmiCecMessage message) {
218 // TODO: Handle message according to opcode type.
219
220 // TODO: Use device's source address for broadcast message.
221 int sourceAddress = message.getDestination() != HdmiCec.ADDR_BROADCAST ?
222 message.getDestination() : 0;
223 // Reply <Feature Abort> to initiator (source) for all requests.
224 sendFeatureAbort(sourceAddress, message.getSource(), message.getOpcode(),
225 ABORT_REFUSED);
226 }
227
228 private void sendFeatureAbort(int srcAddress, int destAddress, int originalOpcode,
229 int reason) {
230 byte[] params = new byte[2];
231 params[0] = (byte) originalOpcode;
232 params[1] = (byte) reason;
233
234 HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, destAddress,
235 HdmiCec.MESSAGE_FEATURE_ABORT, params);
236 Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage);
237 mIoHandler.sendMessage(message);
238 }
239
Jungshik Jang0792d372014-04-23 17:57:26 +0900240 /**
Jungshik Jange9c77c82014-04-24 20:30:09 +0900241 * Called by native when incoming CEC message arrived.
Jungshik Jang0792d372014-04-23 17:57:26 +0900242 */
Jungshik Jange9c77c82014-04-24 20:30:09 +0900243 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
244 byte opcode = body[0];
245 byte params[] = Arrays.copyOfRange(body, 1, body.length);
246 HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
247
248 // Delegate message to main handler so that it handles in main thread.
249 Message message = mControlHandler.obtainMessage(
250 MSG_RECEIVE_CEC_COMMAND, cecMessage);
251 mControlHandler.sendMessage(message);
252 }
253
254 /**
255 * Called by native when a hotplug event issues.
256 */
257 private void handleHotplug(boolean connected) {
258 // TODO: Delegate event to main message handler.
Jungshik Jang0792d372014-04-23 17:57:26 +0900259 }
260
261 private static native long nativeInit(HdmiCecController handler);
Jungshik Jange9c77c82014-04-24 20:30:09 +0900262 private static native int nativeSendCecCommand(long contollerPtr, int srcAddress,
263 int dstAddress, byte[] body);
Jungshik Jang0792d372014-04-23 17:57:26 +0900264}