1 /* 2 * Copyright (C) 2020 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 */ 16 17 package com.android.server.hdmi; 18 19 import static android.media.tv.flags.Flags.hdmiControlCollectPhysicalAddress; 20 21 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_ARC_PENDING; 22 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_EARC_CONNECTED; 23 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_EARC_PENDING; 24 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_IDLE; 25 26 import android.stats.hdmi.HdmiStatsEnums; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 import com.android.internal.util.FrameworkStatsLog; 30 31 /** 32 * Provides methods for writing HDMI-CEC statsd atoms. 33 */ 34 @VisibleForTesting 35 public class HdmiCecAtomWriter { 36 37 @VisibleForTesting 38 protected static final int FEATURE_ABORT_OPCODE_UNKNOWN = 0x100; 39 private static final int ERROR_CODE_UNKNOWN = -1; 40 @VisibleForTesting 41 protected static final int PHYSICAL_ADDRESS_INVALID = 0xFFFF; 42 43 /** 44 * Writes a HdmiCecMessageReported atom representing an HDMI CEC message. 45 * Should only be directly used for sent messages; for received messages, 46 * use the overloaded version with the errorCode argument omitted. 47 * 48 * @param message The HDMI CEC message 49 * @param direction Whether the message is incoming, outgoing, or neither 50 * @param errorCode The error code from the final attempt to send the message 51 * @param callingUid The calling uid of the app that triggered this message 52 */ messageReported( HdmiCecMessage message, int direction, int callingUid, int errorCode)53 public void messageReported( 54 HdmiCecMessage message, int direction, int callingUid, int errorCode) { 55 MessageReportedGenericArgs genericArgs = createMessageReportedGenericArgs( 56 message, direction, errorCode, callingUid); 57 MessageReportedSpecialArgs specialArgs = createMessageReportedSpecialArgs(message); 58 messageReportedBase(genericArgs, specialArgs); 59 } 60 61 /** 62 * Version of messageReported for received messages, where no error code is present. 63 * 64 * @param message The HDMI CEC message 65 * @param direction Whether the message is incoming, outgoing, or neither 66 * @param callingUid The calling uid of the app that triggered this message 67 */ messageReported(HdmiCecMessage message, int direction, int callingUid)68 public void messageReported(HdmiCecMessage message, int direction, int callingUid) { 69 messageReported(message, direction, callingUid, ERROR_CODE_UNKNOWN); 70 } 71 72 /** 73 * Constructs the generic arguments for logging a HDMI CEC message. 74 * 75 * @param message The HDMI CEC message 76 * @param direction Whether the message is incoming, outgoing, or neither 77 * @param errorCode The error code of the message if it's outgoing; 78 * otherwise, ERROR_CODE_UNKNOWN 79 */ createMessageReportedGenericArgs( HdmiCecMessage message, int direction, int errorCode, int callingUid)80 private MessageReportedGenericArgs createMessageReportedGenericArgs( 81 HdmiCecMessage message, int direction, int errorCode, int callingUid) { 82 int sendMessageResult = errorCode == ERROR_CODE_UNKNOWN 83 ? HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN 84 : errorCode + 10; 85 return new MessageReportedGenericArgs(callingUid, direction, message.getSource(), 86 message.getDestination(), message.getOpcode(), sendMessageResult); 87 } 88 89 /** 90 * Constructs the special arguments for logging an HDMI CEC message. 91 * 92 * @param message The HDMI CEC message to log 93 * @return An object containing the special arguments for the message 94 */ createMessageReportedSpecialArgs(HdmiCecMessage message)95 private MessageReportedSpecialArgs createMessageReportedSpecialArgs(HdmiCecMessage message) { 96 // Special arguments depend on message opcode 97 switch (message.getOpcode()) { 98 case Constants.MESSAGE_USER_CONTROL_PRESSED: 99 return createUserControlPressedSpecialArgs(message); 100 case Constants.MESSAGE_FEATURE_ABORT: 101 return createFeatureAbortSpecialArgs(message); 102 case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS: 103 if (hdmiControlCollectPhysicalAddress()) { 104 return createReportPhysicalAddressSpecialArgs(message); 105 } 106 return new MessageReportedSpecialArgs(); 107 default: 108 return new MessageReportedSpecialArgs(); 109 } 110 } 111 112 /** 113 * Constructs the special arguments for a <User Control Pressed> message. 114 * 115 * @param message The HDMI CEC message to log 116 */ createUserControlPressedSpecialArgs( HdmiCecMessage message)117 private MessageReportedSpecialArgs createUserControlPressedSpecialArgs( 118 HdmiCecMessage message) { 119 MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs(); 120 121 if (message.getParams().length > 0) { 122 int keycode = message.getParams()[0]; 123 if (keycode >= 0x1E && keycode <= 0x29) { 124 specialArgs.mUserControlPressedCommand = HdmiStatsEnums.NUMBER; 125 } else { 126 specialArgs.mUserControlPressedCommand = keycode + 0x100; 127 } 128 } 129 130 return specialArgs; 131 } 132 133 /** 134 * Constructs the special arguments for a <Feature Abort> message. 135 * 136 * @param message The HDMI CEC message to log 137 */ createFeatureAbortSpecialArgs(HdmiCecMessage message)138 private MessageReportedSpecialArgs createFeatureAbortSpecialArgs(HdmiCecMessage message) { 139 MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs(); 140 141 if (message.getParams().length > 0) { 142 specialArgs.mFeatureAbortOpcode = message.getParams()[0] & 0xFF; // Unsigned byte 143 if (message.getParams().length > 1) { 144 specialArgs.mFeatureAbortReason = message.getParams()[1] + 10; 145 } 146 } 147 148 return specialArgs; 149 } 150 151 /** 152 * Constructs the special arguments for a <Report Physical Address> message. 153 * 154 * @param message The HDMI CEC message to log 155 */ createReportPhysicalAddressSpecialArgs( HdmiCecMessage message)156 private MessageReportedSpecialArgs createReportPhysicalAddressSpecialArgs( 157 HdmiCecMessage message) { 158 MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs(); 159 160 if (message.getParams().length > 1) { 161 int physicalAddress = (message.getParams()[0] << 8) | message.getParams()[1]; 162 specialArgs.mPhysicalAddress = physicalAddress; 163 } 164 165 return specialArgs; 166 } 167 168 /** 169 * Writes a HdmiCecMessageReported atom. 170 * 171 * @param genericArgs Generic arguments; shared by all HdmiCecMessageReported atoms 172 * @param specialArgs Special arguments; depends on the opcode of the message 173 */ messageReportedBase(MessageReportedGenericArgs genericArgs, MessageReportedSpecialArgs specialArgs)174 private void messageReportedBase(MessageReportedGenericArgs genericArgs, 175 MessageReportedSpecialArgs specialArgs) { 176 writeHdmiCecMessageReportedAtom( 177 genericArgs.mUid, 178 genericArgs.mDirection, 179 genericArgs.mInitiatorLogicalAddress, 180 genericArgs.mDestinationLogicalAddress, 181 genericArgs.mOpcode, 182 genericArgs.mSendMessageResult, 183 specialArgs.mUserControlPressedCommand, 184 specialArgs.mFeatureAbortOpcode, 185 specialArgs.mFeatureAbortReason, 186 specialArgs.mPhysicalAddress); 187 } 188 189 /** 190 * Writes a HdmiCecMessageReported atom representing an incoming or outgoing HDMI-CEC message. 191 */ 192 @VisibleForTesting writeHdmiCecMessageReportedAtom(int uid, int direction, int initiatorLogicalAddress, int destinationLogicalAddress, int opcode, int sendMessageResult, int userControlPressedCommand, int featureAbortOpcode, int featureAbortReason, int physicalAddress)193 protected void writeHdmiCecMessageReportedAtom(int uid, int direction, 194 int initiatorLogicalAddress, int destinationLogicalAddress, int opcode, 195 int sendMessageResult, int userControlPressedCommand, int featureAbortOpcode, 196 int featureAbortReason, int physicalAddress) { 197 FrameworkStatsLog.write( 198 FrameworkStatsLog.HDMI_CEC_MESSAGE_REPORTED, 199 uid, 200 direction, 201 initiatorLogicalAddress, 202 destinationLogicalAddress, 203 opcode, 204 sendMessageResult, 205 userControlPressedCommand, 206 featureAbortOpcode, 207 featureAbortReason, 208 physicalAddress); 209 } 210 211 /** 212 * Writes a HdmiCecActiveSourceChanged atom representing a change in the active source. 213 * 214 * @param logicalAddress The Logical Address of the new active source 215 * @param physicalAddress The Physical Address of the new active source 216 * @param relationshipToActiveSource The relationship between this device and the active source 217 */ activeSourceChanged(int logicalAddress, int physicalAddress, @Constants.PathRelationship int relationshipToActiveSource)218 public void activeSourceChanged(int logicalAddress, int physicalAddress, 219 @Constants.PathRelationship int relationshipToActiveSource) { 220 FrameworkStatsLog.write( 221 FrameworkStatsLog.HDMI_CEC_ACTIVE_SOURCE_CHANGED, 222 logicalAddress, 223 physicalAddress, 224 relationshipToActiveSource 225 ); 226 } 227 228 /** 229 * Writes a HdmiEarcStatusReported atom representing a eARC status change. 230 * @param isSupported Whether the hardware supports eARC. 231 * @param isEnabled Whether eARC is enabled. 232 * @param oldConnectionState If enumLogReason == HdmiStatsEnums.LOG_REASON_EARC_STATUS_CHANGED, 233 * the state just before the change. Otherwise, the current state. 234 * @param newConnectionState If enumLogReason == HdmiStatsEnums.LOG_REASON_EARC_STATUS_CHANGED, 235 * the state just after the change. Otherwise, the current state. 236 * @param enumLogReason The event that triggered the log. 237 */ earcStatusChanged(boolean isSupported, boolean isEnabled, int oldConnectionState, int newConnectionState, int enumLogReason)238 public void earcStatusChanged(boolean isSupported, boolean isEnabled, int oldConnectionState, 239 int newConnectionState, int enumLogReason) { 240 int enumOldConnectionState = earcStateToEnum(oldConnectionState); 241 int enumNewConnectionState = earcStateToEnum(newConnectionState); 242 243 FrameworkStatsLog.write( 244 FrameworkStatsLog.HDMI_EARC_STATUS_REPORTED, 245 isSupported, 246 isEnabled, 247 enumOldConnectionState, 248 enumNewConnectionState, 249 enumLogReason 250 ); 251 } 252 253 /** 254 * Writes a HdmiSoundbarModeStatusReported atom representing a Dynamic soundbar mode status 255 * change. 256 * @param isSupported Whether the hardware supports ARC. 257 * @param isEnabled Whether DSM is enabled. 258 * @param enumLogReason The event that triggered the log. 259 */ dsmStatusChanged(boolean isSupported, boolean isEnabled, int enumLogReason)260 public void dsmStatusChanged(boolean isSupported, boolean isEnabled, int enumLogReason) { 261 FrameworkStatsLog.write( 262 FrameworkStatsLog.HDMI_SOUNDBAR_MODE_STATUS_REPORTED, 263 isSupported, 264 isEnabled, 265 enumLogReason); 266 } 267 268 /** 269 * Writes a HdmiPowerStateChangeOnActiveSourceLostToggled atom representing a 270 * HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST setting change. 271 * @param isEnabled Whether the setting is enabled. 272 * @param enumLogReason The event that triggered the log. 273 * @param manufacturerPnpId Manufacturer PNP ID reported in the EDID. 274 * @param manufacturerYear Manufacture year reported in the EDID. 275 * @param manufacturerWeek Manufacture week reporter in the EDID. 276 */ powerStateChangeOnActiveSourceLostChanged(boolean isEnabled, int enumLogReason, String manufacturerPnpId, int manufacturerYear, int manufacturerWeek)277 public void powerStateChangeOnActiveSourceLostChanged(boolean isEnabled, int enumLogReason, 278 String manufacturerPnpId, int manufacturerYear, int manufacturerWeek) { 279 FrameworkStatsLog.write( 280 FrameworkStatsLog.HDMI_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLED, 281 isEnabled, 282 enumLogReason, 283 manufacturerPnpId, 284 manufacturerYear, 285 manufacturerWeek); 286 } 287 earcStateToEnum(int earcState)288 private int earcStateToEnum(int earcState) { 289 switch (earcState) { 290 case HDMI_EARC_STATUS_IDLE: 291 return HdmiStatsEnums.HDMI_EARC_STATUS_IDLE; 292 case HDMI_EARC_STATUS_EARC_PENDING: 293 return HdmiStatsEnums.HDMI_EARC_STATUS_EARC_PENDING; 294 case HDMI_EARC_STATUS_ARC_PENDING: 295 return HdmiStatsEnums.HDMI_EARC_STATUS_ARC_PENDING; 296 case HDMI_EARC_STATUS_EARC_CONNECTED: 297 return HdmiStatsEnums.HDMI_EARC_STATUS_EARC_CONNECTED; 298 default: 299 return HdmiStatsEnums.HDMI_EARC_STATUS_UNKNOWN; 300 } 301 } 302 303 /** 304 * Contains the required arguments for creating any HdmiCecMessageReported atom 305 */ 306 private class MessageReportedGenericArgs { 307 final int mUid; 308 final int mDirection; 309 final int mInitiatorLogicalAddress; 310 final int mDestinationLogicalAddress; 311 final int mOpcode; 312 final int mSendMessageResult; 313 MessageReportedGenericArgs(int uid, int direction, int initiatorLogicalAddress, int destinationLogicalAddress, int opcode, int sendMessageResult)314 MessageReportedGenericArgs(int uid, int direction, int initiatorLogicalAddress, 315 int destinationLogicalAddress, int opcode, int sendMessageResult) { 316 this.mUid = uid; 317 this.mDirection = direction; 318 this.mInitiatorLogicalAddress = initiatorLogicalAddress; 319 this.mDestinationLogicalAddress = destinationLogicalAddress; 320 this.mOpcode = opcode; 321 this.mSendMessageResult = sendMessageResult; 322 } 323 } 324 325 /** 326 * Contains the opcode-dependent arguments for creating a HdmiCecMessageReported atom. Each 327 * field is initialized to a null-like value by default. Therefore, a freshly constructed 328 * instance of this object represents a HDMI CEC message whose type does not require any 329 * additional arguments. 330 */ 331 private class MessageReportedSpecialArgs { 332 int mUserControlPressedCommand = HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN; 333 int mFeatureAbortOpcode = FEATURE_ABORT_OPCODE_UNKNOWN; 334 int mFeatureAbortReason = HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN; 335 int mPhysicalAddress = PHYSICAL_ADDRESS_INVALID; 336 } 337 } 338