NewDeviceAction.java revision 72b7d738d5b9254594726304cdb1777b54d95631
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.HdmiCecDeviceInfo; 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 HdmiCecDeviceInfo} 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 FeatureAction { 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 51 private int mVendorId; 52 private String mDisplayName; 53 54 /** 55 * Constructor. 56 * 57 * @param source {@link HdmiCecLocalDevice} instance 58 * @param deviceLogicalAddress logical address of the device in interest 59 * @param devicePhysicalAddress physical address of the device in interest 60 */ 61 NewDeviceAction(HdmiCecLocalDevice source, int deviceLogicalAddress, 62 int devicePhysicalAddress) { 63 super(source); 64 mDeviceLogicalAddress = deviceLogicalAddress; 65 mDevicePhysicalAddress = devicePhysicalAddress; 66 mVendorId = Constants.UNKNOWN_VENDOR_ID; 67 } 68 69 @Override 70 public boolean start() { 71 mState = STATE_WAITING_FOR_SET_OSD_NAME; 72 if (mayProcessCommandIfCached(mDeviceLogicalAddress, Constants.MESSAGE_SET_OSD_NAME)) { 73 return true; 74 } 75 76 sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(), 77 mDeviceLogicalAddress)); 78 addTimer(mState, HdmiConfig.TIMEOUT_MS); 79 return true; 80 } 81 82 @Override 83 public boolean processCommand(HdmiCecMessage cmd) { 84 // For the logical device in interest, we want two more pieces of information - 85 // osd name and vendor id. They are requested in sequence. In case we don't 86 // get the expected responses (either by timeout or by receiving <feature abort> command), 87 // set them to a default osd name and unknown vendor id respectively. 88 int opcode = cmd.getOpcode(); 89 int src = cmd.getSource(); 90 byte[] params = cmd.getParams(); 91 92 if (mDeviceLogicalAddress != src) { 93 return false; 94 } 95 96 if (mState == STATE_WAITING_FOR_SET_OSD_NAME) { 97 if (opcode == Constants.MESSAGE_SET_OSD_NAME) { 98 try { 99 mDisplayName = new String(params, "US-ASCII"); 100 } catch (UnsupportedEncodingException e) { 101 Slog.e(TAG, "Failed to get OSD name: " + e.getMessage()); 102 } 103 requestVendorId(); 104 return true; 105 } else if (opcode == Constants.MESSAGE_FEATURE_ABORT) { 106 int requestOpcode = params[1] & 0xFF; 107 if (requestOpcode == Constants.MESSAGE_SET_OSD_NAME) { 108 requestVendorId(); 109 return true; 110 } 111 } 112 } else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { 113 if (opcode == Constants.MESSAGE_DEVICE_VENDOR_ID) { 114 mVendorId = HdmiUtils.threeBytesToInt(params); 115 addDeviceInfo(); 116 finish(); 117 return true; 118 } else if (opcode == Constants.MESSAGE_FEATURE_ABORT) { 119 int requestOpcode = params[1] & 0xFF; 120 if (requestOpcode == Constants.MESSAGE_DEVICE_VENDOR_ID) { 121 addDeviceInfo(); 122 finish(); 123 return true; 124 } 125 } 126 } 127 return false; 128 } 129 130 private boolean mayProcessCommandIfCached(int destAddress, int opcode) { 131 HdmiCecMessage message = getCecMessageCache().getMessage(destAddress, opcode); 132 if (message != null) { 133 return processCommand(message); 134 } 135 return false; 136 } 137 138 private void requestVendorId() { 139 // At first, transit to waiting status for <Device Vendor Id>. 140 mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; 141 // If the message is already in cache, process it. 142 if (mayProcessCommandIfCached(mDeviceLogicalAddress, 143 Constants.MESSAGE_DEVICE_VENDOR_ID)) { 144 return; 145 } 146 sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(), 147 mDeviceLogicalAddress)); 148 addTimer(mState, HdmiConfig.TIMEOUT_MS); 149 } 150 151 private void addDeviceInfo() { 152 if (mDisplayName == null) { 153 mDisplayName = HdmiUtils.getDefaultDeviceName(mDeviceLogicalAddress); 154 } 155 tv().addCecDevice(new HdmiCecDeviceInfo( 156 mDeviceLogicalAddress, mDevicePhysicalAddress, 157 tv().getPortId(mDevicePhysicalAddress), 158 HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress), 159 mVendorId, mDisplayName)); 160 161 if (HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress) 162 == HdmiCecDeviceInfo.DEVICE_AUDIO_SYSTEM) { 163 if (tv().getSystemAudioModeSetting()) { 164 addAndStartAction(new SystemAudioAutoInitiationAction(localDevice(), 165 mDeviceLogicalAddress)); 166 } 167 168 if (shouldTryArcInitiation()) { 169 addAndStartAction(new RequestArcInitiationAction(localDevice(), 170 mDeviceLogicalAddress)); 171 } 172 } 173 } 174 175 private boolean shouldTryArcInitiation() { 176 return tv().isConnectedToArcPort(mDevicePhysicalAddress) && tv().isArcFeatureEnabled(); 177 } 178 179 @Override 180 public void handleTimerEvent(int state) { 181 if (mState == STATE_NONE || mState != state) { 182 return; 183 } 184 if (state == STATE_WAITING_FOR_SET_OSD_NAME) { 185 // Osd name request timed out. Try vendor id 186 requestVendorId(); 187 } else if (state == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { 188 // vendor id timed out. Go ahead creating the device info what we've got so far. 189 addDeviceInfo(); 190 finish(); 191 } 192 } 193 194 boolean isActionOf(ActiveSource activeSource) { 195 return (mDeviceLogicalAddress == activeSource.logicalAddress) 196 && (mDevicePhysicalAddress == activeSource.physicalAddress); 197 } 198} 199