FileDocCategorySizeDatePackage
DeviceDiscoveryAction.javaAPI DocAndroid 5.1 API13846Thu Mar 12 22:22:42 GMT 2015com.android.server.hdmi

DeviceDiscoveryAction

public final class DeviceDiscoveryAction extends HdmiCecFeatureAction
Feature action that handles device discovery sequences. Device discovery is launched when TV device is woken from "Standby" state or enabled "Control for Hdmi" from disabled state.

Device discovery goes through the following steps.

  1. Poll all non-local devices by sending <Polling Message>
  2. Gather "Physical address" and "device type" of all acknowledged devices
  3. Gather "OSD (display) name" of all acknowledge devices
  4. Gather "Vendor id" of all acknowledge devices
We attempt to get OSD name/vendor ID up to 5 times in case the communication fails.

Fields Summary
private static final String
TAG
private static final int
STATE_WAITING_FOR_DEVICE_POLLING
private static final int
STATE_WAITING_FOR_PHYSICAL_ADDRESS
private static final int
STATE_WAITING_FOR_OSD_NAME
private static final int
STATE_WAITING_FOR_VENDOR_ID
private final ArrayList
mDevices
private final DeviceDiscoveryCallback
mCallback
private int
mProcessedDeviceCount
private int
mTimeoutRetry
Constructors Summary
DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback)
Constructor.

param
source an instance of {@link HdmiCecLocalDevice}.


                 
        
        super(source);
        mCallback = Preconditions.checkNotNull(callback);
    
Methods Summary
private voidallocateDevices(java.util.List addresses)

        for (Integer i : addresses) {
            DeviceInfo info = new DeviceInfo(i);
            mDevices.add(info);
        }
    
private voidcheckAndProceedStage()

        if (mDevices.isEmpty()) {
            wrapUpAndFinish();
            return;
        }

        // If finished current stage, move on to next stage.
        if (mProcessedDeviceCount == mDevices.size()) {
            mProcessedDeviceCount = 0;
            switch (mState) {
                case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
                    startOsdNameStage();
                    return;
                case STATE_WAITING_FOR_OSD_NAME:
                    startVendorIdStage();
                    return;
                case STATE_WAITING_FOR_VENDOR_ID:
                    wrapUpAndFinish();
                    return;
                default:
                    return;
            }
        } else {
            sendQueryCommand();
        }
    
private intgetPortId(int physicalAddress)

        return tv().getPortId(physicalAddress);
    
private voidhandleReportPhysicalAddress(HdmiCecMessage cmd)

        Preconditions.checkState(mProcessedDeviceCount < mDevices.size());

        DeviceInfo current = mDevices.get(mProcessedDeviceCount);
        if (current.mLogicalAddress != cmd.getSource()) {
            Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:" +
                    cmd.getSource());
            return;
        }

        byte params[] = cmd.getParams();
        current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params);
        current.mPortId = getPortId(current.mPhysicalAddress);
        current.mDeviceType = params[2] & 0xFF;

        tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
                    current.mPhysicalAddress);

        increaseProcessedDeviceCount();
        checkAndProceedStage();
    
private voidhandleSetOsdName(HdmiCecMessage cmd)

        Preconditions.checkState(mProcessedDeviceCount < mDevices.size());

        DeviceInfo current = mDevices.get(mProcessedDeviceCount);
        if (current.mLogicalAddress != cmd.getSource()) {
            Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:" +
                    cmd.getSource());
            return;
        }

        String displayName = null;
        try {
            displayName = new String(cmd.getParams(), "US-ASCII");
        } catch (UnsupportedEncodingException e) {
            Slog.w(TAG, "Failed to decode display name: " + cmd.toString());
            // If failed to get display name, use the default name of device.
            displayName = HdmiUtils.getDefaultDeviceName(current.mLogicalAddress);
        }
        current.mDisplayName = displayName;
        increaseProcessedDeviceCount();
        checkAndProceedStage();
    
voidhandleTimerEvent(int state)

        if (mState == STATE_NONE || mState != state) {
            return;
        }

        if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) {
            sendQueryCommand();
            return;
        }
        mTimeoutRetry = 0;
        Slog.v(TAG, "Timeout[State=" + mState + ", Processed=" + mProcessedDeviceCount);
        removeDevice(mProcessedDeviceCount);
        checkAndProceedStage();
    
private voidhandleVendorId(HdmiCecMessage cmd)

        Preconditions.checkState(mProcessedDeviceCount < mDevices.size());

        DeviceInfo current = mDevices.get(mProcessedDeviceCount);
        if (current.mLogicalAddress != cmd.getSource()) {
            Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:" +
                    cmd.getSource());
            return;
        }

        byte[] params = cmd.getParams();
        int vendorId = HdmiUtils.threeBytesToInt(params);
        current.mVendorId = vendorId;
        increaseProcessedDeviceCount();
        checkAndProceedStage();
    
private voidincreaseProcessedDeviceCount()

        mProcessedDeviceCount++;
        mTimeoutRetry = 0;
    
private booleanmayProcessMessageIfCached(int address, int opcode)

        HdmiCecMessage message = getCecMessageCache().getMessage(address, opcode);
        if (message != null) {
            processCommand(message);
            return true;
        }
        return false;
    
booleanprocessCommand(HdmiCecMessage cmd)

        switch (mState) {
            case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
                if (cmd.getOpcode() == Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS) {
                    handleReportPhysicalAddress(cmd);
                    return true;
                }
                return false;
            case STATE_WAITING_FOR_OSD_NAME:
                if (cmd.getOpcode() == Constants.MESSAGE_SET_OSD_NAME) {
                    handleSetOsdName(cmd);
                    return true;
                }
                return false;
            case STATE_WAITING_FOR_VENDOR_ID:
                if (cmd.getOpcode() == Constants.MESSAGE_DEVICE_VENDOR_ID) {
                    handleVendorId(cmd);
                    return true;
                }
                return false;
            case STATE_WAITING_FOR_DEVICE_POLLING:
                // Fall through.
            default:
                return false;
        }
    
private voidqueryOsdName(int address)

        if (!verifyValidLogicalAddress(address)) {
            checkAndProceedStage();
            return;
        }

        mActionTimer.clearTimerMessage();

        if (mayProcessMessageIfCached(address, Constants.MESSAGE_SET_OSD_NAME)) {
            return;
        }
        sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(), address));
        addTimer(mState, HdmiConfig.TIMEOUT_MS);
    
private voidqueryPhysicalAddress(int address)

        if (!verifyValidLogicalAddress(address)) {
            checkAndProceedStage();
            return;
        }

        mActionTimer.clearTimerMessage();

        // Check cache first and send request if not exist.
        if (mayProcessMessageIfCached(address, Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS)) {
            return;
        }
        sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(), address));
        addTimer(mState, HdmiConfig.TIMEOUT_MS);
    
private voidqueryVendorId(int address)

        if (!verifyValidLogicalAddress(address)) {
            checkAndProceedStage();
            return;
        }

        mActionTimer.clearTimerMessage();

        if (mayProcessMessageIfCached(address, Constants.MESSAGE_DEVICE_VENDOR_ID)) {
            return;
        }
        sendCommand(
                HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(), address));
        addTimer(mState, HdmiConfig.TIMEOUT_MS);
    
private voidremoveDevice(int index)

        mDevices.remove(index);
    
private voidsendQueryCommand()

        int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress;
        switch (mState) {
            case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
                queryPhysicalAddress(address);
                return;
            case STATE_WAITING_FOR_OSD_NAME:
                queryOsdName(address);
                return;
            case STATE_WAITING_FOR_VENDOR_ID:
                queryVendorId(address);
            default:
                return;
        }
    
booleanstart()

        mDevices.clear();
        mState = STATE_WAITING_FOR_DEVICE_POLLING;

        pollDevices(new DevicePollingCallback() {
            @Override
            public void onPollingFinished(List<Integer> ackedAddress) {
                if (ackedAddress.isEmpty()) {
                    Slog.v(TAG, "No device is detected.");
                    wrapUpAndFinish();
                    return;
                }

                Slog.v(TAG, "Device detected: " + ackedAddress);
                allocateDevices(ackedAddress);
                startPhysicalAddressStage();
            }
        }, Constants.POLL_ITERATION_REVERSE_ORDER
            | Constants.POLL_STRATEGY_REMOTES_DEVICES, HdmiConfig.DEVICE_POLLING_RETRY);
        return true;
    
private voidstartOsdNameStage()

        Slog.v(TAG, "Start [Osd Name Stage]:" + mDevices.size());
        mProcessedDeviceCount = 0;
        mState = STATE_WAITING_FOR_OSD_NAME;

        checkAndProceedStage();
    
private voidstartPhysicalAddressStage()

        Slog.v(TAG, "Start [Physical Address Stage]:" + mDevices.size());
        mProcessedDeviceCount = 0;
        mState = STATE_WAITING_FOR_PHYSICAL_ADDRESS;

        checkAndProceedStage();
    
private voidstartVendorIdStage()

        Slog.v(TAG, "Start [Vendor Id Stage]:" + mDevices.size());

        mProcessedDeviceCount = 0;
        mState = STATE_WAITING_FOR_VENDOR_ID;

        checkAndProceedStage();
    
private booleanverifyValidLogicalAddress(int address)

        return address >= Constants.ADDR_TV && address < Constants.ADDR_UNREGISTERED;
    
private voidwrapUpAndFinish()

        Slog.v(TAG, "---------Wrap up Device Discovery:[" + mDevices.size() + "]---------");
        ArrayList<HdmiDeviceInfo> result = new ArrayList<>();
        for (DeviceInfo info : mDevices) {
            HdmiDeviceInfo cecDeviceInfo = info.toHdmiDeviceInfo();
            Slog.v(TAG, " DeviceInfo: " + cecDeviceInfo);
            result.add(cecDeviceInfo);
        }
        Slog.v(TAG, "--------------------------------------------");
        mCallback.onDeviceDiscoveryDone(result);
        finish();
        // Process any commands buffered while device discovery action was in progress.
        tv().processAllDelayedMessages();