NewDeviceAction.java revision 75a77e7d6cbfc287c6126efd28b338b48b7ea70c
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 mVendorId = HdmiUtils.threeBytesToInt(params); 131 addDeviceInfo(); 132 finish(); 133 return true; 134 } else if (opcode == Constants.MESSAGE_FEATURE_ABORT) { 135 int requestOpcode = params[1] & 0xFF; 136 if (requestOpcode == Constants.MESSAGE_DEVICE_VENDOR_ID) { 137 addDeviceInfo(); 138 finish(); 139 return true; 140 } 141 } 142 } 143 return false; 144 } 145 146 private boolean mayProcessCommandIfCached(int destAddress, int opcode) { 147 HdmiCecMessage message = getCecMessageCache().getMessage(destAddress, opcode); 148 if (message != null) { 149 return processCommand(message); 150 } 151 return false; 152 } 153 154 private void requestVendorId() { 155 // At first, transit to waiting status for <Device Vendor Id>. 156 mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; 157 // If the message is already in cache, process it. 158 if (mayProcessCommandIfCached(mDeviceLogicalAddress, 159 Constants.MESSAGE_DEVICE_VENDOR_ID)) { 160 return; 161 } 162 sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(), 163 mDeviceLogicalAddress)); 164 addTimer(mState, TIMEOUT_MS); 165 } 166 167 private void addDeviceInfo() { 168 if (mDisplayName == null) { 169 mDisplayName = HdmiUtils.getDefaultDeviceName(mDeviceLogicalAddress); 170 } 171 tv().addCecDevice(new HdmiCecDeviceInfo( 172 mDeviceLogicalAddress, mDevicePhysicalAddress, 173 HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress), 174 mVendorId, mDisplayName)); 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