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