HdmiCecController.java revision e9c77c88ea34a66f83a94f960547275c0ff6bd07
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.HdmiCec; 20import android.hardware.hdmi.HdmiCecMessage; 21import android.os.Handler; 22import android.os.Looper; 23import android.os.Message; 24import android.util.Slog; 25 26import java.util.Arrays; 27 28/** 29 * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command 30 * and pass it to CEC HAL so that it sends message to other device. For incoming 31 * message it translates the message and delegates it to proper module. 32 * 33 * <p>It can be created only by {@link HdmiCecController#create} 34 * 35 * <p>Declared as package-private, accessed by {@link HdmiControlService} only. 36 */ 37class HdmiCecController { 38 private static final String TAG = "HdmiCecController"; 39 40 // A message to pass cec send command to IO looper. 41 private static final int MSG_SEND_CEC_COMMAND = 1; 42 43 // Message types to handle incoming message in main service looper. 44 private final static int MSG_RECEIVE_CEC_COMMAND = 1; 45 46 // TODO: move these values to HdmiCec.java once make it internal constant class. 47 // CEC's ABORT reason values. 48 private static final int ABORT_UNRECOGNIZED_MODE = 0; 49 private static final int ABORT_NOT_IN_CORRECT_MODE = 1; 50 private static final int ABORT_CANNOT_PROVIDE_SOURCE = 2; 51 private static final int ABORT_INVALID_OPERAND = 3; 52 private static final int ABORT_REFUSED = 4; 53 private static final int ABORT_UNABLE_TO_DETERMINE = 5; 54 55 // Handler instance to process synchronous I/O (mainly send) message. 56 private Handler mIoHandler; 57 58 // Handler instance to process various messages coming from other CEC 59 // device or issued by internal state change. 60 private Handler mControlHandler; 61 62 // Stores the pointer to the native implementation of the service that 63 // interacts with HAL. 64 private long mNativePtr; 65 66 // Private constructor. Use HdmiCecController.create(). 67 private HdmiCecController() { 68 } 69 70 /** 71 * A factory method to get {@link HdmiCecController}. If it fails to initialize 72 * inner device or has no device it will return {@code null}. 73 * 74 * <p>Declared as package-private, accessed by {@link HdmiControlService} only. 75 * @param service {@link HdmiControlService} instance used to create internal handler 76 * and to pass callback for incoming message or event. 77 * @return {@link HdmiCecController} if device is initialized successfully. Otherwise, 78 * returns {@code null}. 79 */ 80 static HdmiCecController create(HdmiControlService service) { 81 HdmiCecController handler = new HdmiCecController(); 82 long nativePtr = nativeInit(handler); 83 if (nativePtr == 0L) { 84 handler = null; 85 return null; 86 } 87 88 handler.init(service, nativePtr); 89 return handler; 90 } 91 92 private static byte[] buildBody(int opcode, byte[] params) { 93 byte[] body = new byte[params.length + 1]; 94 body[0] = (byte) opcode; 95 System.arraycopy(params, 0, body, 1, params.length); 96 return body; 97 } 98 99 private final class IoHandler extends Handler { 100 private IoHandler(Looper looper) { 101 super(looper); 102 } 103 104 @Override 105 public void handleMessage(Message msg) { 106 switch (msg.what) { 107 case MSG_SEND_CEC_COMMAND: 108 HdmiCecMessage cecMessage = (HdmiCecMessage) msg.obj; 109 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams()); 110 nativeSendCecCommand(mNativePtr, cecMessage.getSource(), 111 cecMessage.getDestination(), body); 112 break; 113 default: 114 Slog.w(TAG, "Unsupported CEC Io request:" + msg.what); 115 break; 116 } 117 } 118 } 119 120 private final class ControlHandler extends Handler { 121 private ControlHandler(Looper looper) { 122 super(looper); 123 } 124 125 @Override 126 public void handleMessage(Message msg) { 127 switch (msg.what) { 128 case MSG_RECEIVE_CEC_COMMAND: 129 // TODO: delegate it to HdmiControl service. 130 onReceiveCommand((HdmiCecMessage) msg.obj); 131 break; 132 default: 133 Slog.i(TAG, "Unsupported message type:" + msg.what); 134 break; 135 } 136 } 137 } 138 139 private void init(HdmiControlService service, long nativePtr) { 140 mIoHandler = new IoHandler(service.getServiceLooper()); 141 mControlHandler = new ControlHandler(service.getServiceLooper()); 142 mNativePtr = nativePtr; 143 } 144 145 private void onReceiveCommand(HdmiCecMessage message) { 146 // TODO: Handle message according to opcode type. 147 148 // TODO: Use device's source address for broadcast message. 149 int sourceAddress = message.getDestination() != HdmiCec.ADDR_BROADCAST ? 150 message.getDestination() : 0; 151 // Reply <Feature Abort> to initiator (source) for all requests. 152 sendFeatureAbort(sourceAddress, message.getSource(), message.getOpcode(), 153 ABORT_REFUSED); 154 } 155 156 private void sendFeatureAbort(int srcAddress, int destAddress, int originalOpcode, 157 int reason) { 158 byte[] params = new byte[2]; 159 params[0] = (byte) originalOpcode; 160 params[1] = (byte) reason; 161 162 HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, destAddress, 163 HdmiCec.MESSAGE_FEATURE_ABORT, params); 164 Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage); 165 mIoHandler.sendMessage(message); 166 } 167 168 /** 169 * Called by native when incoming CEC message arrived. 170 */ 171 private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) { 172 byte opcode = body[0]; 173 byte params[] = Arrays.copyOfRange(body, 1, body.length); 174 HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params); 175 176 // Delegate message to main handler so that it handles in main thread. 177 Message message = mControlHandler.obtainMessage( 178 MSG_RECEIVE_CEC_COMMAND, cecMessage); 179 mControlHandler.sendMessage(message); 180 } 181 182 /** 183 * Called by native when a hotplug event issues. 184 */ 185 private void handleHotplug(boolean connected) { 186 // TODO: Delegate event to main message handler. 187 } 188 189 private static native long nativeInit(HdmiCecController handler); 190 private static native int nativeSendCecCommand(long contollerPtr, int srcAddress, 191 int dstAddress, byte[] body); 192} 193