Loading core/java/android/hardware/hdmi/HdmiCec.java +2 −0 Original line number Diff line number Diff line Loading @@ -160,6 +160,8 @@ public final class HdmiCec { public static final int MESSAGE_SET_EXTERNAL_TIMER = 0xA2; public static final int MESSAGE_ABORT = 0xFF; public static final int UNKNOWN_VENDOR_ID = 0xFFFFFF; public static final int POWER_STATUS_UNKNOWN = -1; public static final int POWER_STATUS_ON = 0; public static final int POWER_STATUS_STANDBY = 1; Loading core/java/android/hardware/hdmi/HdmiCecMessage.java +4 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package android.hardware.hdmi; import android.os.Parcel; import android.os.Parcelable; import libcore.util.EmptyArray; import java.util.Arrays; /** Loading @@ -28,6 +30,8 @@ import java.util.Arrays; */ public final class HdmiCecMessage implements Parcelable { public static final byte[] EMPTY_PARAM = EmptyArray.BYTE; private static final int MAX_MESSAGE_LENGTH = 16; private final int mSource; Loading services/core/java/com/android/server/hdmi/FeatureAction.java 0 → 100644 +211 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecMessage; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; /** * Encapsulates a sequence of CEC/MHL command exchange for a certain feature. * * <p>Many CEC/MHL features are accomplished by CEC devices on the bus exchanging * more than one command. {@link FeatureAction} represents the life cycle of the communication, * manages the state as the process progresses, and if necessary, returns the result * to the caller which initiates the action, through the callback given at the creation * of the object. All the actual action classes inherit FeatureAction. * * <p>More than one FeatureAction objects can be up and running simultaneously, * maintained by {@link HdmiControlService}. Each action is passed a new command * arriving from the bus, and either consumes it if the command is what the action expects, * or yields it to other action. * * Declared as package private, accessed by {@link HdmiControlService} only. */ abstract class FeatureAction { private static final String TAG = "FeatureAction"; // Timer handler message used for timeout event protected static final int MSG_TIMEOUT = 100; // Default timeout for the incoming command to arrive in response to a request protected static final int TIMEOUT_MS = 1000; // Default state used in common by all the feature actions. protected static final int STATE_NONE = 0; // Internal state indicating the progress of action. protected int mState = STATE_NONE; protected final HdmiControlService mService; // Logical address of the device for which the feature action is taken. The commands // generated in an action all use this field as source address. protected final int mSourceAddress; // Timer that manages timeout events. protected ActionTimer mActionTimer; FeatureAction(HdmiControlService service, int sourceAddress) { mService = service; mSourceAddress = sourceAddress; mActionTimer = createActionTimer(service.getServiceLooper()); } @VisibleForTesting void setActionTimer(ActionTimer actionTimer) { mActionTimer = actionTimer; } /** * Called right after the action is created. Initialization or first step to take * for the action can be done in this method. * * @return true if the operation is successful; otherwise false. */ abstract boolean start(); /** * Process the command. Called whenever a new command arrives. * * @param cmd command to process * @return true if the command was consumed in the process; Otherwise false, which * indicates that the command shall be handled by other actions. */ abstract boolean processCommand(HdmiCecMessage cmd); /** * Called when the action should handle the timer event it created before. * * <p>CEC standard mandates each command transmission should be responded within * certain period of time. The method is called when the timer it created as it transmitted * a command gets expired. Inner logic should take an appropriate action. * * @param state the state associated with the time when the timer was created */ abstract void handleTimerEvent(int state); /** * Timer handler interface used for FeatureAction classes. */ interface ActionTimer { /** * Send a timer message. * * Also carries the state of the action when the timer is created. Later this state is * compared to the one the action is in when it receives the timer to let the action tell * the right timer to handle. * * @param state state of the action is in * @param delayMillis amount of delay for the timer */ void sendTimerMessage(int state, long delayMillis); } private class ActionTimerHandler extends Handler implements ActionTimer { public ActionTimerHandler(Looper looper) { super(looper); } @Override public void sendTimerMessage(int state, long delayMillis) { sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state), delayMillis); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_TIMEOUT: handleTimerEvent(msg.arg1); break; default: Slog.w(TAG, "Unsupported message:" + msg.what); break; } } } private ActionTimer createActionTimer(Looper looper) { return new ActionTimerHandler(looper); } // Add a new timer. The timer event will come to mActionTimer.handleMessage() in // delayMillis. protected void addTimer(int state, int delayMillis) { mActionTimer.sendTimerMessage(state, delayMillis); } static HdmiCecMessage buildCommand(int src, int dst, int opcode, byte[] params) { return new HdmiCecMessage(src, dst, opcode, params); } // Build a CEC command that does not have parameter. static HdmiCecMessage buildCommand(int src, int dst, int opcode) { return new HdmiCecMessage(src, dst, opcode, HdmiCecMessage.EMPTY_PARAM); } protected final void sendCommand(HdmiCecMessage cmd) { mService.sendCecCommand(cmd); } protected final void sendBroadcastCommand(int opcode, byte[] param) { sendCommand(buildCommand(mSourceAddress, HdmiCec.ADDR_BROADCAST, opcode, param)); } /** * Finish up the action. Reset the state, and remove itself from the action queue. */ protected void finish() { mState = STATE_NONE; removeAction(this); } /** * Remove the action from the action queue. This is called after the action finishes * its role. * * @param action */ private void removeAction(FeatureAction action) { mService.removeAction(action); } // Utility methods for generating parameter byte arrays for CEC commands. protected static byte[] uiCommandParam(int uiCommand) { return new byte[] {(byte) uiCommand}; } protected static byte[] physicalAddressParam(int physicalAddress) { return new byte[] { (byte) ((physicalAddress >> 8) & 0xFF), (byte) (physicalAddress & 0xFF) }; } protected static byte[] pathPairParam(int oldPath, int newPath) { return new byte[] { (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF), (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF) }; } } services/core/java/com/android/server/hdmi/HdmiControlService.java +39 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.server.hdmi; import android.annotation.Nullable; import android.content.Context; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; import android.os.HandlerThread; import android.os.Looper; import android.util.Slog; Loading Loading @@ -77,4 +79,41 @@ public final class HdmiControlService extends SystemService { Looper getServiceLooper() { return Looper.myLooper(); } /** * Add a new {@link FeatureAction} to the action queue. * * @param action {@link FeatureAction} to add */ void addAction(FeatureAction action) { // TODO: Implement this. } /** * Remove the given {@link FeatureAction} object from the action queue. * * @param action {@link FeatureAction} to add */ void removeAction(FeatureAction action) { // TODO: Implement this. } /** * Transmit a CEC command to CEC bus. * * @param command CEC command to send out */ void sendCecCommand(HdmiCecMessage command) { // TODO: Implement this. } /** * Add a new {@link HdmiCecDeviceInfo} to controller. * * @param deviceInfo new device information object to add */ void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) { // TODO: Implement this. } } services/core/java/com/android/server/hdmi/NewDeviceAction.java 0 → 100644 +165 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; import android.util.Slog; import java.io.UnsupportedEncodingException; /** * Feature action that discovers the information of a newly found logical device. * * This action is created when receiving <Report Physical Address>, a CEC command a newly * connected HDMI-CEC device broadcasts to announce its advent. Additional commands are issued in * this action to gather more information on the device such as OSD name and device vendor ID. * * <p>The result is made in the form of {@link HdmiCecDeviceInfo} object, and passed to service * for the management through its life cycle. * * <p>Package-private, accessed by {@link HdmiControlService} only. */ final class NewDeviceAction extends FeatureAction { private static final String TAG = "NewDeviceAction"; // State in which the action sent <Give OSD Name> and is waiting for <Set OSD Name> // that contains the name of the device for display on screen. static final int STATE_WAITING_FOR_SET_OSD_NAME = 1; // State in which the action sent <Give Device Vendor ID> and is waiting for // <Device Vendor ID> that contains the vendor ID of the device. static final int STATE_WAITING_FOR_DEVICE_VENDOR_ID = 2; private final int mDeviceLogicalAddress; private final int mDevicePhysicalAddress; private int mVendorId; private String mDisplayName; /** * Constructor. * * @param service {@link HdmiControlService} instance * @param sourceAddress logical address to be used as source address * @param deviceLogicalAddress logical address of the device in interest * @param devicePhysicalAddress physical address of the device in interest */ NewDeviceAction(HdmiControlService service, int sourceAddress, int deviceLogicalAddress, int devicePhysicalAddress) { super(service, sourceAddress); mDeviceLogicalAddress = deviceLogicalAddress; mDevicePhysicalAddress = devicePhysicalAddress; mVendorId = HdmiCec.UNKNOWN_VENDOR_ID; } @Override public boolean start() { sendCommand( buildCommand(mSourceAddress, mDeviceLogicalAddress, HdmiCec.MESSAGE_GET_OSD_NAME)); mState = STATE_WAITING_FOR_SET_OSD_NAME; addTimer(mState, TIMEOUT_MS); return true; } @Override public boolean processCommand(HdmiCecMessage cmd) { // For the logical device in interest, we want two more pieces of information - // osd name and vendor id. They are requested in sequence. In case we don't // get the expected responses (either by timeout or by receiving <feature abort> command), // set them to a default osd name and unknown vendor id respectively. int opcode = cmd.getOpcode(); int src = cmd.getSource(); byte[] params = cmd.getParams(); if (mDeviceLogicalAddress != src) { return false; } if (mState == STATE_WAITING_FOR_SET_OSD_NAME) { if (opcode == HdmiCec.MESSAGE_SET_OSD_NAME) { try { mDisplayName = new String(params, "US-ASCII"); } catch (UnsupportedEncodingException e) { Slog.e(TAG, "Failed to get OSD name: " + e.getMessage()); } mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; requestVendorId(); return true; } else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) { int requestOpcode = params[1]; if (requestOpcode == HdmiCec.MESSAGE_SET_OSD_NAME) { mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; requestVendorId(); return true; } } } else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { if (opcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) { if (params.length == 3) { mVendorId = (params[0] << 16) + (params[1] << 8) + params[2]; } else { Slog.e(TAG, "Failed to get device vendor ID: "); } addDeviceInfo(); finish(); return true; } else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) { int requestOpcode = params[1]; if (requestOpcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) { addDeviceInfo(); finish(); return true; } } } return false; } private void requestVendorId() { sendCommand(buildCommand(mSourceAddress, mDeviceLogicalAddress, HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID)); addTimer(mState, TIMEOUT_MS); } private void addDeviceInfo() { if (mDisplayName == null) { mDisplayName = HdmiCec.getDefaultDeviceName(mDeviceLogicalAddress); } mService.addDeviceInfo(new HdmiCecDeviceInfo( mDeviceLogicalAddress, mDevicePhysicalAddress, HdmiCec.getTypeFromAddress(mDeviceLogicalAddress), mVendorId, mDisplayName)); } @Override public void handleTimerEvent(int state) { if (mState == STATE_NONE || mState != state) { return; } if (state == STATE_WAITING_FOR_SET_OSD_NAME) { // Osd name request timed out. Try vendor id mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; requestVendorId(); } else if (state == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { // vendor id timed out. Go ahead creating the device info what we've got so far. addDeviceInfo(); finish(); } } } Loading
core/java/android/hardware/hdmi/HdmiCec.java +2 −0 Original line number Diff line number Diff line Loading @@ -160,6 +160,8 @@ public final class HdmiCec { public static final int MESSAGE_SET_EXTERNAL_TIMER = 0xA2; public static final int MESSAGE_ABORT = 0xFF; public static final int UNKNOWN_VENDOR_ID = 0xFFFFFF; public static final int POWER_STATUS_UNKNOWN = -1; public static final int POWER_STATUS_ON = 0; public static final int POWER_STATUS_STANDBY = 1; Loading
core/java/android/hardware/hdmi/HdmiCecMessage.java +4 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package android.hardware.hdmi; import android.os.Parcel; import android.os.Parcelable; import libcore.util.EmptyArray; import java.util.Arrays; /** Loading @@ -28,6 +30,8 @@ import java.util.Arrays; */ public final class HdmiCecMessage implements Parcelable { public static final byte[] EMPTY_PARAM = EmptyArray.BYTE; private static final int MAX_MESSAGE_LENGTH = 16; private final int mSource; Loading
services/core/java/com/android/server/hdmi/FeatureAction.java 0 → 100644 +211 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecMessage; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; /** * Encapsulates a sequence of CEC/MHL command exchange for a certain feature. * * <p>Many CEC/MHL features are accomplished by CEC devices on the bus exchanging * more than one command. {@link FeatureAction} represents the life cycle of the communication, * manages the state as the process progresses, and if necessary, returns the result * to the caller which initiates the action, through the callback given at the creation * of the object. All the actual action classes inherit FeatureAction. * * <p>More than one FeatureAction objects can be up and running simultaneously, * maintained by {@link HdmiControlService}. Each action is passed a new command * arriving from the bus, and either consumes it if the command is what the action expects, * or yields it to other action. * * Declared as package private, accessed by {@link HdmiControlService} only. */ abstract class FeatureAction { private static final String TAG = "FeatureAction"; // Timer handler message used for timeout event protected static final int MSG_TIMEOUT = 100; // Default timeout for the incoming command to arrive in response to a request protected static final int TIMEOUT_MS = 1000; // Default state used in common by all the feature actions. protected static final int STATE_NONE = 0; // Internal state indicating the progress of action. protected int mState = STATE_NONE; protected final HdmiControlService mService; // Logical address of the device for which the feature action is taken. The commands // generated in an action all use this field as source address. protected final int mSourceAddress; // Timer that manages timeout events. protected ActionTimer mActionTimer; FeatureAction(HdmiControlService service, int sourceAddress) { mService = service; mSourceAddress = sourceAddress; mActionTimer = createActionTimer(service.getServiceLooper()); } @VisibleForTesting void setActionTimer(ActionTimer actionTimer) { mActionTimer = actionTimer; } /** * Called right after the action is created. Initialization or first step to take * for the action can be done in this method. * * @return true if the operation is successful; otherwise false. */ abstract boolean start(); /** * Process the command. Called whenever a new command arrives. * * @param cmd command to process * @return true if the command was consumed in the process; Otherwise false, which * indicates that the command shall be handled by other actions. */ abstract boolean processCommand(HdmiCecMessage cmd); /** * Called when the action should handle the timer event it created before. * * <p>CEC standard mandates each command transmission should be responded within * certain period of time. The method is called when the timer it created as it transmitted * a command gets expired. Inner logic should take an appropriate action. * * @param state the state associated with the time when the timer was created */ abstract void handleTimerEvent(int state); /** * Timer handler interface used for FeatureAction classes. */ interface ActionTimer { /** * Send a timer message. * * Also carries the state of the action when the timer is created. Later this state is * compared to the one the action is in when it receives the timer to let the action tell * the right timer to handle. * * @param state state of the action is in * @param delayMillis amount of delay for the timer */ void sendTimerMessage(int state, long delayMillis); } private class ActionTimerHandler extends Handler implements ActionTimer { public ActionTimerHandler(Looper looper) { super(looper); } @Override public void sendTimerMessage(int state, long delayMillis) { sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state), delayMillis); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_TIMEOUT: handleTimerEvent(msg.arg1); break; default: Slog.w(TAG, "Unsupported message:" + msg.what); break; } } } private ActionTimer createActionTimer(Looper looper) { return new ActionTimerHandler(looper); } // Add a new timer. The timer event will come to mActionTimer.handleMessage() in // delayMillis. protected void addTimer(int state, int delayMillis) { mActionTimer.sendTimerMessage(state, delayMillis); } static HdmiCecMessage buildCommand(int src, int dst, int opcode, byte[] params) { return new HdmiCecMessage(src, dst, opcode, params); } // Build a CEC command that does not have parameter. static HdmiCecMessage buildCommand(int src, int dst, int opcode) { return new HdmiCecMessage(src, dst, opcode, HdmiCecMessage.EMPTY_PARAM); } protected final void sendCommand(HdmiCecMessage cmd) { mService.sendCecCommand(cmd); } protected final void sendBroadcastCommand(int opcode, byte[] param) { sendCommand(buildCommand(mSourceAddress, HdmiCec.ADDR_BROADCAST, opcode, param)); } /** * Finish up the action. Reset the state, and remove itself from the action queue. */ protected void finish() { mState = STATE_NONE; removeAction(this); } /** * Remove the action from the action queue. This is called after the action finishes * its role. * * @param action */ private void removeAction(FeatureAction action) { mService.removeAction(action); } // Utility methods for generating parameter byte arrays for CEC commands. protected static byte[] uiCommandParam(int uiCommand) { return new byte[] {(byte) uiCommand}; } protected static byte[] physicalAddressParam(int physicalAddress) { return new byte[] { (byte) ((physicalAddress >> 8) & 0xFF), (byte) (physicalAddress & 0xFF) }; } protected static byte[] pathPairParam(int oldPath, int newPath) { return new byte[] { (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF), (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF) }; } }
services/core/java/com/android/server/hdmi/HdmiControlService.java +39 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.server.hdmi; import android.annotation.Nullable; import android.content.Context; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; import android.os.HandlerThread; import android.os.Looper; import android.util.Slog; Loading Loading @@ -77,4 +79,41 @@ public final class HdmiControlService extends SystemService { Looper getServiceLooper() { return Looper.myLooper(); } /** * Add a new {@link FeatureAction} to the action queue. * * @param action {@link FeatureAction} to add */ void addAction(FeatureAction action) { // TODO: Implement this. } /** * Remove the given {@link FeatureAction} object from the action queue. * * @param action {@link FeatureAction} to add */ void removeAction(FeatureAction action) { // TODO: Implement this. } /** * Transmit a CEC command to CEC bus. * * @param command CEC command to send out */ void sendCecCommand(HdmiCecMessage command) { // TODO: Implement this. } /** * Add a new {@link HdmiCecDeviceInfo} to controller. * * @param deviceInfo new device information object to add */ void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) { // TODO: Implement this. } }
services/core/java/com/android/server/hdmi/NewDeviceAction.java 0 → 100644 +165 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; import android.util.Slog; import java.io.UnsupportedEncodingException; /** * Feature action that discovers the information of a newly found logical device. * * This action is created when receiving <Report Physical Address>, a CEC command a newly * connected HDMI-CEC device broadcasts to announce its advent. Additional commands are issued in * this action to gather more information on the device such as OSD name and device vendor ID. * * <p>The result is made in the form of {@link HdmiCecDeviceInfo} object, and passed to service * for the management through its life cycle. * * <p>Package-private, accessed by {@link HdmiControlService} only. */ final class NewDeviceAction extends FeatureAction { private static final String TAG = "NewDeviceAction"; // State in which the action sent <Give OSD Name> and is waiting for <Set OSD Name> // that contains the name of the device for display on screen. static final int STATE_WAITING_FOR_SET_OSD_NAME = 1; // State in which the action sent <Give Device Vendor ID> and is waiting for // <Device Vendor ID> that contains the vendor ID of the device. static final int STATE_WAITING_FOR_DEVICE_VENDOR_ID = 2; private final int mDeviceLogicalAddress; private final int mDevicePhysicalAddress; private int mVendorId; private String mDisplayName; /** * Constructor. * * @param service {@link HdmiControlService} instance * @param sourceAddress logical address to be used as source address * @param deviceLogicalAddress logical address of the device in interest * @param devicePhysicalAddress physical address of the device in interest */ NewDeviceAction(HdmiControlService service, int sourceAddress, int deviceLogicalAddress, int devicePhysicalAddress) { super(service, sourceAddress); mDeviceLogicalAddress = deviceLogicalAddress; mDevicePhysicalAddress = devicePhysicalAddress; mVendorId = HdmiCec.UNKNOWN_VENDOR_ID; } @Override public boolean start() { sendCommand( buildCommand(mSourceAddress, mDeviceLogicalAddress, HdmiCec.MESSAGE_GET_OSD_NAME)); mState = STATE_WAITING_FOR_SET_OSD_NAME; addTimer(mState, TIMEOUT_MS); return true; } @Override public boolean processCommand(HdmiCecMessage cmd) { // For the logical device in interest, we want two more pieces of information - // osd name and vendor id. They are requested in sequence. In case we don't // get the expected responses (either by timeout or by receiving <feature abort> command), // set them to a default osd name and unknown vendor id respectively. int opcode = cmd.getOpcode(); int src = cmd.getSource(); byte[] params = cmd.getParams(); if (mDeviceLogicalAddress != src) { return false; } if (mState == STATE_WAITING_FOR_SET_OSD_NAME) { if (opcode == HdmiCec.MESSAGE_SET_OSD_NAME) { try { mDisplayName = new String(params, "US-ASCII"); } catch (UnsupportedEncodingException e) { Slog.e(TAG, "Failed to get OSD name: " + e.getMessage()); } mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; requestVendorId(); return true; } else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) { int requestOpcode = params[1]; if (requestOpcode == HdmiCec.MESSAGE_SET_OSD_NAME) { mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; requestVendorId(); return true; } } } else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { if (opcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) { if (params.length == 3) { mVendorId = (params[0] << 16) + (params[1] << 8) + params[2]; } else { Slog.e(TAG, "Failed to get device vendor ID: "); } addDeviceInfo(); finish(); return true; } else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) { int requestOpcode = params[1]; if (requestOpcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) { addDeviceInfo(); finish(); return true; } } } return false; } private void requestVendorId() { sendCommand(buildCommand(mSourceAddress, mDeviceLogicalAddress, HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID)); addTimer(mState, TIMEOUT_MS); } private void addDeviceInfo() { if (mDisplayName == null) { mDisplayName = HdmiCec.getDefaultDeviceName(mDeviceLogicalAddress); } mService.addDeviceInfo(new HdmiCecDeviceInfo( mDeviceLogicalAddress, mDevicePhysicalAddress, HdmiCec.getTypeFromAddress(mDeviceLogicalAddress), mVendorId, mDisplayName)); } @Override public void handleTimerEvent(int state) { if (mState == STATE_NONE || mState != state) { return; } if (state == STATE_WAITING_FOR_SET_OSD_NAME) { // Osd name request timed out. Try vendor id mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; requestVendorId(); } else if (state == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { // vendor id timed out. Go ahead creating the device info what we've got so far. addDeviceInfo(); finish(); } } }