Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 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 | |
| 17 | package com.android.server.hdmi; |
| 18 | |
| 19 | import android.hardware.hdmi.HdmiCec; |
| 20 | import android.hardware.hdmi.HdmiCecMessage; |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 21 | import android.hardware.hdmi.IHdmiCecListener; |
| 22 | import android.os.Binder; |
| 23 | import android.os.RemoteException; |
| 24 | import android.util.Log; |
| 25 | |
| 26 | import java.util.ArrayList; |
| 27 | import java.util.List; |
| 28 | |
| 29 | /** |
Jinsuk Kim | 4e90fcd | 2014-03-24 18:02:29 +0900 | [diff] [blame] | 30 | * HdmiCecDevice class represents a CEC logical device characterized |
| 31 | * by its device type. It is a superclass of those serving concrete device type. |
| 32 | * Currently we're interested in playback(one of sources), display(sink) device type |
| 33 | * only. The support for the other types like recorder, audio system will come later. |
| 34 | * |
| 35 | * <p>A physical device can contain the functions of |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 36 | * more than one logical device, in which case it should create |
| 37 | * as many logical devices as necessary. |
| 38 | * |
| 39 | * <p>Note that if a physical device has multiple instances of a particular |
| 40 | * functionality, it should advertize only one instance. For instance, if |
| 41 | * a device has multiple tuners, it should only expose one for control |
| 42 | * via CEC. In this case, it is up to the device itself to manage multiple tuners. |
| 43 | * |
| 44 | * <p>The version of HDMI-CEC protocol supported in this class is 1.3a. |
| 45 | * |
| 46 | * <p>Declared as package-private, accessed by HdmiCecService only. |
| 47 | */ |
Jinsuk Kim | 4e90fcd | 2014-03-24 18:02:29 +0900 | [diff] [blame] | 48 | abstract class HdmiCecDevice { |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 49 | private static final String TAG = "HdmiCecDevice"; |
| 50 | |
| 51 | private final int mType; |
| 52 | |
| 53 | // List of listeners to the message/event coming to the device. |
| 54 | private final List<IHdmiCecListener> mListeners = new ArrayList<IHdmiCecListener>(); |
| 55 | private final Binder mBinder = new Binder(); |
Jinsuk Kim | 4e90fcd | 2014-03-24 18:02:29 +0900 | [diff] [blame] | 56 | private final HdmiCecService mService; |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 57 | |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 58 | private boolean mIsActiveSource; |
| 59 | |
| 60 | /** |
Jinsuk Kim | 4e90fcd | 2014-03-24 18:02:29 +0900 | [diff] [blame] | 61 | * Factory method that creates HdmiCecDevice instance to the device type. |
| 62 | */ |
| 63 | public static HdmiCecDevice create(HdmiCecService service, int type) { |
| 64 | if (type == HdmiCec.DEVICE_PLAYBACK) { |
| 65 | return new HdmiCecDevicePlayback(service, type); |
| 66 | } else if (type == HdmiCec.DEVICE_TV) { |
| 67 | return new HdmiCecDeviceTv(service, type); |
| 68 | } |
| 69 | return null; |
| 70 | } |
| 71 | |
| 72 | /** |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 73 | * Constructor. |
| 74 | */ |
Jinsuk Kim | 4e90fcd | 2014-03-24 18:02:29 +0900 | [diff] [blame] | 75 | public HdmiCecDevice(HdmiCecService service, int type) { |
| 76 | mService = service; |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 77 | mType = type; |
| 78 | mIsActiveSource = false; |
| 79 | } |
| 80 | |
| 81 | /** |
Jinsuk Kim | 4e90fcd | 2014-03-24 18:02:29 +0900 | [diff] [blame] | 82 | * Called right after the class is instantiated. This method can be used to |
| 83 | * implement any initialization tasks for the instance. |
| 84 | */ |
| 85 | abstract public void initialize(); |
| 86 | |
| 87 | /** |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 88 | * Return the binder token that identifies this instance. |
| 89 | */ |
| 90 | public Binder getToken() { |
| 91 | return mBinder; |
| 92 | } |
| 93 | |
| 94 | /** |
Jinsuk Kim | 4e90fcd | 2014-03-24 18:02:29 +0900 | [diff] [blame] | 95 | * Return the service instance. |
| 96 | */ |
| 97 | public HdmiCecService getService() { |
| 98 | return mService; |
| 99 | } |
| 100 | |
| 101 | /** |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 102 | * Return the type of this device. |
| 103 | */ |
| 104 | public int getType() { |
| 105 | return mType; |
| 106 | } |
| 107 | |
| 108 | /** |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 109 | * Register a listener to be invoked when events occur. |
| 110 | * |
| 111 | * @param listener the listern that will run |
| 112 | */ |
| 113 | public void addListener(IHdmiCecListener listener) { |
| 114 | mListeners.add(listener); |
| 115 | } |
| 116 | |
| 117 | /** |
| 118 | * Remove the listener that was previously registered. |
| 119 | * |
| 120 | * @param listener IHdmiCecListener instance to be removed |
| 121 | */ |
| 122 | public void removeListener(IHdmiCecListener listener) { |
| 123 | mListeners.remove(listener); |
| 124 | } |
| 125 | |
| 126 | /** |
| 127 | * Indicate if the device has listeners. |
| 128 | * |
| 129 | * @return true if there are listener instances for this device |
| 130 | */ |
| 131 | public boolean hasListener() { |
| 132 | return !mListeners.isEmpty(); |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * Handle HDMI-CEC message coming to the device by invoking the registered |
| 137 | * listeners. |
| 138 | */ |
| 139 | public void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) { |
| 140 | if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE) { |
| 141 | mIsActiveSource = false; |
| 142 | } |
Jinsuk Kim | 4e90fcd | 2014-03-24 18:02:29 +0900 | [diff] [blame] | 143 | |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 144 | if (mListeners.size() == 0) { |
| 145 | return; |
| 146 | } |
| 147 | HdmiCecMessage message = new HdmiCecMessage(srcAddress, dstAddress, opcode, params); |
| 148 | for (IHdmiCecListener listener : mListeners) { |
| 149 | try { |
| 150 | listener.onMessageReceived(message); |
| 151 | } catch (RemoteException e) { |
| 152 | Log.e(TAG, "listener.onMessageReceived failed."); |
| 153 | } |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | public void handleHotplug(boolean connected) { |
| 158 | for (IHdmiCecListener listener : mListeners) { |
| 159 | try { |
| 160 | listener.onCableStatusChanged(connected); |
| 161 | } catch (RemoteException e) { |
| 162 | Log.e(TAG, "listener.onCableStatusChanged failed."); |
| 163 | } |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | /** |
| 168 | * Return the active status of the device. |
| 169 | * |
| 170 | * @return true if the device is the active source among the connected |
| 171 | * HDMI-CEC-enabled devices; otherwise false. |
| 172 | */ |
| 173 | public boolean isActiveSource() { |
| 174 | return mIsActiveSource; |
| 175 | } |
| 176 | |
| 177 | /** |
| 178 | * Update the active source state of the device. |
| 179 | */ |
| 180 | public void setIsActiveSource(boolean state) { |
| 181 | mIsActiveSource = state; |
| 182 | } |
Jinsuk Kim | 4e90fcd | 2014-03-24 18:02:29 +0900 | [diff] [blame] | 183 | |
| 184 | /** |
Jinsuk Kim | 6179107 | 2014-04-07 13:39:17 +0900 | [diff] [blame] | 185 | * Send <Active Source> command. The default implementation does nothing. Should be |
| 186 | * overriden by subclass. |
| 187 | */ |
| 188 | public void sendActiveSource(int physicalAddress) { |
| 189 | logWarning("<Active Source> not valid for the device type: " + mType |
| 190 | + " address:" + physicalAddress); |
| 191 | } |
| 192 | |
| 193 | /** |
| 194 | * Send <Inactive Source> command. The default implementation does nothing. Should be |
| 195 | * overriden by subclass. |
| 196 | */ |
| 197 | public void sendInactiveSource(int physicalAddress) { |
| 198 | logWarning("<Inactive Source> not valid for the device type: " + mType |
| 199 | + " address:" + physicalAddress); |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * Send <Image View On> command. The default implementation does nothing. Should be |
| 204 | * overriden by subclass. |
| 205 | */ |
| 206 | public void sendImageViewOn() { |
| 207 | logWarning("<Image View On> not valid for the device type: " + mType); |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Send <Text View On> command. The default implementation does nothing. Should be |
| 212 | * overriden by subclass. |
| 213 | */ |
| 214 | public void sendTextViewOn() { |
| 215 | logWarning("<Text View On> not valid for the device type: " + mType); |
| 216 | } |
| 217 | |
| 218 | /** |
Jinsuk Kim | 4e90fcd | 2014-03-24 18:02:29 +0900 | [diff] [blame] | 219 | * Check if the connected sink device is in powered-on state. The default implementation |
| 220 | * simply returns false. Should be overriden by subclass to report the correct state. |
| 221 | */ |
| 222 | public boolean isSinkDeviceOn() { |
Jinsuk Kim | 6179107 | 2014-04-07 13:39:17 +0900 | [diff] [blame] | 223 | logWarning("isSinkDeviceOn() not valid for the device type: " + mType); |
Jinsuk Kim | 4e90fcd | 2014-03-24 18:02:29 +0900 | [diff] [blame] | 224 | return false; |
| 225 | } |
Jinsuk Kim | 6179107 | 2014-04-07 13:39:17 +0900 | [diff] [blame] | 226 | |
| 227 | private void logWarning(String msg) { |
| 228 | Log.w(TAG, msg); |
| 229 | } |
Jinsuk Kim | 4f512fb | 2014-02-28 17:41:17 +0900 | [diff] [blame] | 230 | } |