NewDeviceAction.java revision bdf27fbf746bee11430c4db2ea6dfd026bae77fe
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 */ 16package com.android.server.hdmi; 17 18import android.hardware.hdmi.HdmiDeviceInfo; 19import android.util.Slog; 20 21import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource; 22import java.io.UnsupportedEncodingException; 23 24/** 25 * Feature action that discovers the information of a newly found logical device. 26 * 27 * This action is created when receiving <Report Physical Address>, a CEC command a newly 28 * connected HDMI-CEC device broadcasts to announce its advent. Additional commands are issued in 29 * this action to gather more information on the device such as OSD name and device vendor ID. 30 * 31 * <p>The result is made in the form of {@link HdmiDeviceInfo} object, and passed to service 32 * for the management through its life cycle. 33 * 34 * <p>Package-private, accessed by {@link HdmiControlService} only. 35 */ 36final class NewDeviceAction extends HdmiCecFeatureAction { 37 38 private static final String TAG = "NewDeviceAction"; 39 40 // State in which the action sent <Give OSD Name> and is waiting for <Set OSD Name> 41 // that contains the name of the device for display on screen. 42 static final int STATE_WAITING_FOR_SET_OSD_NAME = 1; 43 44 // State in which the action sent <Give Device Vendor ID> and is waiting for 45 // <Device Vendor ID> that contains the vendor ID of the device. 46 static final int STATE_WAITING_FOR_DEVICE_VENDOR_ID = 2; 47 48 private final int mDeviceLogicalAddress; 49 private final int mDevicePhysicalAddress; 50 private final int mDeviceType; 51 52 private int mVendorId; 53 private String mDisplayName; 54 55 /** 56 * Constructor. 57 * 58 * @param source {@link HdmiCecLocalDevice} instance 59 * @param deviceLogicalAddress logical address of the device in interest 60 * @param devicePhysicalAddress physical address of the device in interest 61 * @param deviceType type of the device 62 */ 63 NewDeviceAction(HdmiCecLocalDevice source, int deviceLogicalAddress, 64 int devicePhysicalAddress, int deviceType) { 65 super(source); 66 mDeviceLogicalAddress = deviceLogicalAddress; 67 mDevicePhysicalAddress = devicePhysicalAddress; 68 mDeviceType = deviceType; 69 mVendorId = Constants.UNKNOWN_VENDOR_ID; 70 } 71 72 @Override 73 public boolean start() { 74 mState = STATE_WAITING_FOR_SET_OSD_NAME; 75 if (mayProcessCommandIfCached(mDeviceLogicalAddress, Constants.MESSAGE_SET_OSD_NAME)) { 76 return true; 77 } 78 79 sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(), 80 mDeviceLogicalAddress)); 81 addTimer(mState, HdmiConfig.TIMEOUT_MS); 82 return true; 83 } 84 85 @Override 86 public boolean processCommand(HdmiCecMessage cmd) { 87 // For the logical device in interest, we want two more pieces of information - 88 // osd name and vendor id. They are requested in sequence. In case we don't 89 // get the expected responses (either by timeout or by receiving <feature abort> command), 90 // set them to a default osd name and unknown vendor id respectively. 91 int opcode = cmd.getOpcode(); 92 int src = cmd.getSource(); 93 byte[] params = cmd.getParams(); 94 95 if (mDeviceLogicalAddress != src) { 96 return false; 97 } 98 99 if (mState == STATE_WAITING_FOR_SET_OSD_NAME) { 100 if (opcode == Constants.MESSAGE_SET_OSD_NAME) { 101 try { 102 mDisplayName = new String(params, "US-ASCII"); 103 } catch (UnsupportedEncodingException e) { 104 Slog.e(TAG, "Failed to get OSD name: " + e.getMessage()); 105 } 106 requestVendorId(); 107 return true; 108 } else if (opcode == Constants.MESSAGE_FEATURE_ABORT) { 109 int requestOpcode = params[0] & 0xFF; 110 if (requestOpcode == Constants.MESSAGE_GIVE_OSD_NAME) { 111 requestVendorId(); 112 return true; 113 } 114 } 115 } else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { 116 if (opcode == Constants.MESSAGE_DEVICE_VENDOR_ID) { 117 mVendorId = HdmiUtils.threeBytesToInt(params); 118 addDeviceInfo(); 119 finish(); 120 return true; 121 } else if (opcode == Constants.MESSAGE_FEATURE_ABORT) { 122 int requestOpcode = params[0] & 0xFF; 123 if (requestOpcode == Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID) { 124 addDeviceInfo(); 125 finish(); 126 return true; 127 } 128 } 129 } 130 return false; 131 } 132 133 private boolean mayProcessCommandIfCached(int destAddress, int opcode) { 134 HdmiCecMessage message = getCecMessageCache().getMessage(destAddress, opcode); 135 if (message != null) { 136 return processCommand(message); 137 } 138 return false; 139 } 140 141 private void requestVendorId() { 142 // At first, transit to waiting status for <Device Vendor Id>. 143 mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; 144 // If the message is already in cache, process it. 145 if (mayProcessCommandIfCached(mDeviceLogicalAddress, 146 Constants.MESSAGE_DEVICE_VENDOR_ID)) { 147 return; 148 } 149 sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(), 150 mDeviceLogicalAddress)); 151 addTimer(mState, HdmiConfig.TIMEOUT_MS); 152 } 153 154 private void addDeviceInfo() { 155 if (mDisplayName == null) { 156 mDisplayName = HdmiUtils.getDefaultDeviceName(mDeviceLogicalAddress); 157 } 158 HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo( 159 mDeviceLogicalAddress, mDevicePhysicalAddress, 160 tv().getPortId(mDevicePhysicalAddress), 161 mDeviceType, mVendorId, mDisplayName); 162 tv().addCecDevice(deviceInfo); 163 164 if (HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress) 165 == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) { 166 tv().onNewAvrAdded(deviceInfo); 167 } 168 } 169 170 @Override 171 public void handleTimerEvent(int state) { 172 if (mState == STATE_NONE || mState != state) { 173 return; 174 } 175 if (state == STATE_WAITING_FOR_SET_OSD_NAME) { 176 // Osd name request timed out. Try vendor id 177 requestVendorId(); 178 } else if (state == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { 179 // vendor id timed out. Go ahead creating the device info what we've got so far. 180 addDeviceInfo(); 181 finish(); 182 } 183 } 184 185 boolean isActionOf(ActiveSource activeSource) { 186 return (mDeviceLogicalAddress == activeSource.logicalAddress) 187 && (mDevicePhysicalAddress == activeSource.physicalAddress); 188 } 189} 190