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