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